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

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. 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:
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': `Basic ${CORBADO_API_SECRET}`,
                '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. To complete the login, your backend needs to verify this signedPasskeyData to confirm the user’s identity before creating an access token in your system. Here’s an example of an endpoint that verifies the signedPasskeyData and creates an access token using Node.js and Express:
app.post('/auth/completeLogin', async (req, res) => {
    try {
        const { signedPasskeyData } = req.body;

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

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

        // 2. Extract user information
        const userInfo = extractUserInfo(signedPasskeyData);

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

        // 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' });
    }
});