> ## Documentation Index
> Fetch the complete documentation index at: https://docs.corbado.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Prepare your backend for Corbado Connect

> Learn how to prepare your backend to support passkey authentication with Corbado Connect by implementing two essential endpoints.

<Columns cols={3}>
  <Card href="https://cognito.cloud.corbado-demo.com">
    Try Demo
  </Card>

  <Card href="https://calendly.com/vincent-delitz">
    Talk to Adoption Engineer
  </Card>

  <Card href="https://www.corbado.com/passkeys/enterprise">
    Whitepaper
  </Card>
</Columns>

## 1. Introduction

To enable passkey-based authentication with **Corbado Connect**, your backend needs to support your web and mobile applications by providing a session management mechanism and two essential endpoints.

In the following sections, we will detail the session handling logic and the implementation of the endpoints.

## 2. Session Management

Once a user's identity is verified, your application needs a way to recognize them across subsequent requests. This process is known as session management.

There are various strategies for handling sessions, such as maintaining server-side session entries in a database. However, for simplicity and broad applicability across both web and mobile clients, we'll use a stateless approach with JSON Web Tokens (JWTs). In our backend and examples, we will refer to these JWTs as **access tokens**, a common industry term.

## 3. Endpoints

The examples use `CORBADO_BACKEND_API_KEY`, loaded from your server's secret configuration. Grant it `connect_tokens:write` for token creation and `passkeys:verify` for login verification. See [Backend API authentication](/api-reference/connect/authentication) for bearer keys and the alternative HTTP Basic format. Bearer authentication requires a versioned Backend API key (prefix `corbado1_v2_`). Legacy API secrets use HTTP Basic with `projectID:apiSecret`.

### 3.1 Create Connect Token

Your backend must expose an endpoint that your frontend applications (web and mobile) can call to obtain a [connect token](/corbado-connect/concepts/connect-token). This endpoint will in turn call **Corbado's** Backend API to generate the actual token.

Here is an example implementation using Node.js and Express:

```javascript theme={null}
app.post('/auth/createConnectToken', async (req, res) => {
    try {
        // 1. Get access token from the Authorization header
        const authHeader = req.headers['authorization'];
        const accessToken = authHeader && authHeader.split(' ')[1];
        if (accessToken == null) {
            return res.sendStatus(401);
        }

        // 2. Verify the access token
        const user = await yourAuthSystem.verifyAccessToken(accessToken);
        if (!user) {
            return res.status(401).json({ error: 'Invalid access token' });
        }

        // 3. Request connect token from Corbado Backend API
        const response = await fetch('https://backendapi.cloud.corbado.io/v2/connectTokens', {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${CORBADO_BACKEND_API_KEY}`,
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                type: req.body.connectTokenType, // e.g., 'passkey-append'
                data: {
                    displayName: user.displayName,
                    identifier: user.email,
                }
            })
        });
        if (!response.ok) {
            console.error('Failed to get connect token:', await response.text());

            return res.status(500).json({ error: 'Failed to get connect token' });
        }

        const data = await response.json();
        
        // 3. Return the connect token to the frontend
        res.json({
            connectToken: data.secret
        });
    } catch (error) {
        console.error('Error getting connect token:', error);
        res.status(500).json({ error: 'Internal server error' });
    }
});
```

### 3.2 Complete Login

After a user successfully authenticates with a passkey, **Corbado Connect** generates [signedPasskeyData](/corbado-connect/concepts/signed-passkey-data).

To complete the login, your backend needs to verify this [signedPasskeyData](/corbado-connect/concepts/signed-passkey-data) to confirm the user's identity before creating an **access token** in your system.

The verification request requires `signedPasskeyData` and `username`. Here, `username` must match the JWT's `webauthnId` claim. Decoding the JWT does not verify it: trust its claims only after the Backend API returns `verificationResult: "success"`. The JWT's `sub` is Corbado's internal user ID. Resolve the verified `webauthnId` to your existing account and use that account's ID when creating your session.

In this example, `extractUserInfo` decodes the JWT payload and `yourAuthSystem.findUserByWebAuthnId` represents your application's identity lookup. Implement that lookup using the identifier mapping configured for your Connect integration.

Here's an example of an endpoint that verifies the [signedPasskeyData](/corbado-connect/concepts/signed-passkey-data) and creates an **access token** using Node.js and Express:

```typescript theme={null}
app.post('/auth/completeLogin', async (req, res) => {
    try {
        const { signedPasskeyData } = req.body;

        // Decode only to prepare verification. These claims are not trusted yet.
        const userInfo = extractUserInfo(signedPasskeyData);
        if (typeof userInfo?.webauthnId !== 'string' || !userInfo.webauthnId) {
            return res.status(401).json({ success: false, error: 'Invalid signedPasskeyData' });
        }

        // 1. Verify signedPasskeyData with Corbado Backend API
        const verifyResult = await fetch('https://backendapi.cloud.corbado.io/v2/passkey/verifySignedData', {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${CORBADO_BACKEND_API_KEY}`,
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ signedPasskeyData, username: userInfo.webauthnId })
        });

        const data = await verifyResult.json();
        if (!verifyResult.ok || data.verificationResult !== "success") {
            return res.status(401).json({ success: false, error: 'Invalid signedPasskeyData' });
        }

        // 2. Resolve the verified identity to an existing account
        const user = await yourAuthSystem.findUserByWebAuthnId(userInfo.webauthnId);
        if (!user) {
            return res.status(401).json({ success: false, error: 'Unknown user' });
        }
        // Apply your existing account eligibility and MFA policies before issuing a session.

        // 3. Create an access token
        const accessToken = await yourAuthSystem.createAccessToken({
            userId: user.id,
        });

        // 4. Send the access token to the client
        res.json({
            success: true,
            accessToken: accessToken,
        });
    } catch (error) {
        console.error('Verification failed:', error);
        res.status(500).json({ success: false, error: 'Verification failed' });
    }
});
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.