> [!NOTE]
> **Note:** This article is part of a series on server-side passkey implementation. Explore the other articles in this series: [Introduction to server-side passkey implementation](https://developers.google.com/identity/passkeys/developer-guides/server-introduction) and [Server-side passkey registration](https://developers.google.com/identity/passkeys/developer-guides/server-registration).   
> This article focuses on server-side functionality. To learn about the overall passkey authentication flow including client-side steps, review [Sign in with a passkey](https://web.dev/articles/passkey-form-autofill) for the web, or [Sign in your user with Credential Manager](https://developer.android.com/training/sign-in/passkeys#sign-in) for an Android app.

## Overview

Here's a high-level overview of the key steps involved in passkey authentication:

![Passkey authentication flow](https://developers.google.com/identity/passkeys/developer-guides/images/server-authentication.png)

- Define the challenge and other options needed to authenticate with a passkey. Send them to the client, so you can pass them to your passkey authentication call (`navigator.credentials.get` on the web). After the user confirms passkey authentication, the passkey authentication call is resolved and returns a credential (`PublicKeyCredential`). The credential contains an *authentication assertion*.

> [!IMPORTANT]
> **Key Term:** The *authentication assertion* is a piece of data that includes the signed challenge, and other information you need as an RP to authenticate the user.

- Verify the authentication assertion.
- If the authentication assertion is valid, authenticate the user.

The following sections dive into the specifics of each step.

> [!NOTE]
> **Note:** While it's possible to implement server-side passkeys functionality from scratch, [we recommend that you rely on a library instead](https://developers.google.com/identity/passkeys/developer-guides/server-introduction#server-side_libraries).   
>
> In the examples throughout this article, we'll demonstrate using a JavaScript and TypeScript FIDO server-side library called [SimpleWebAuthn](https://simplewebauthn.dev/docs/packages/server).  
> While your specific server-side implementation may look slightly different depending on your chosen stack, the core concepts and key steps remain consistent. Refer to the documentation for your preferred library for detailed guidance.

### Create the challenge

> [!IMPORTANT]
> **Key Term:** The *challenge* is a piece of data that protects users from replay attacks. Once presented with a challenge, the passkey provider will sign it to prove that the user holds the passkey at the time of the authentication request. If an attacker intercepts an authentication request and tries to *replay* it to authenticate in place of the user, it will fail because the server will detect a challenge mismatch.

In practice, a challenge is an array of random bytes, represented as an [`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) object.

    // Example challenge, base64URL-encoded
    weMLPOSx1VfSnMV6uPwDKbjGdKRMaUDGxeDEUTT5VN8

> [!IMPORTANT]
> **Important:** You can rely on your FIDO server-side library for challenge creation. In SimpleWebAuthn, challenge creation takes place in `generateAuthenticationOptions`. See details in [Create credential request options](https://developers.google.com/identity/passkeys/developer-guides/server-authentication#create-credential-request-options). Out of precaution, SimpleWebAuthn uses [32-byte challenges](https://github.com/MasterKale/SimpleWebAuthn/blob/3c97a9ab050c05b552de99362c0bd12749c2aae5/packages/server/src/helpers/generateChallenge.ts#L15) which are more secure than the [16-bytes recommendation](https://w3c.github.io/webauthn/#sctn-cryptographic-challenges).

To ensure the challenge fulfills its purpose, you must:

1. **Ensure the same challenge is never used more than once.** Generate a new challenge on every sign-in attempt. Discard the challenge after every sign-in attempt, whether it succeeded or failed. Discard the challenge after a certain duration as well. Never accept the same challenge in a response more than once.
2. **Ensure the challenge is cryptographically secure** . A challenge should be practically impossible to guess**.** To create a cryptographically secure challenge server-side, it's best to rely on a FIDO server-side library you trust. If you create your own challenges instead, use the built-in cryptographic functionality available in your tech stack, or look for libraries that are designed for cryptographic use cases. Examples include [iso-crypto](https://www.npmjs.com/package/@pedrouid/iso-crypto) in Node.js, or [secrets](https://docs.python.org/3/library/secrets.html) in Python. Per the [specification](https://w3c.github.io/webauthn/#sctn-cryptographic-challenges), the challenge must be at least 16 bytes long in order to be considered secure.

Once you've created a challenge, save it in the user's session to verify it later.

### Create credential request options

Create credential request options as a `publicKeyCredentialRequestOptions` object.

To do so, rely on your FIDO server-side library. It will typically offer a utility function that can create these options for you. SimpleWebAuthn offers, for example, [`generateAuthenticationOptions`](https://simplewebauthn.dev/docs/packages/server#1-generate-authentication-options).

`publicKeyCredentialRequestOptions` should contain all the information needed for passkey authentication. Pass this information to the function in your FIDO server-side library that's responsible for creating the `publicKeyCredentialRequestOptions` object.

> [!NOTE]
> **Note:** In this article, we focus on the fields of `publicKeyCredentialRequestOptions` that require server-side work. Learn more about all the fields in [Sign in with a passkey](https://web.dev/articles/passkey-form-autofill).

Some of `publicKeyCredentialRequestOptions`' fields can be constants. Others should be dynamically defined on the server:

- **`rpId`** : Which RP ID you expect the credential to be associated with, for example `example.com`. Authentication will only succeed if the RP ID you provide here matches the RP ID associated with the credential. To populate RP ID, use the same value as the RP ID you set in `publicKeyCredentialCreationOptions` during credential registration.
- **`challenge`** : A piece of data that the passkey provider will sign to prove the user holds the passkey at the time of the authentication request. Review details in [Create the challenge](https://developers.google.com/identity/passkeys/developer-guides/server-authentication#create_the_challenge).
- **[`allowCredentials`](https://web.dev/articles/webauthn-discoverable-credentials#allow-credentials)** : An array of acceptable credentials for this authentication. Pass an empty array to let the user select an available passkey from a list shown by the browser. Review [Fetch a challenge from the RP server](https://web.dev/articles/passkey-form-autofill#fetch_a_challenge_from_the_rp_server) and [Discoverable credentials deep dive](https://web.dev/articles/webauthn-discoverable-credentials) for details.
- **[`userVerification`](https://developer.mozilla.org/docs/Web/API/CredentialsContainer/create#userverification)** : Indicates whether user verification using the device screen lock is "required", "preferred" or "discouraged". Review [Fetch a challenge from the RP server](https://web.dev/articles/passkey-form-autofill#fetch_a_challenge_from_the_rp_server).
- **`timeout`** : How long (in milliseconds) the user can take to complete authentication. It should be reasonably generous, and shorter than the lifetime of the `challenge`. The recommended default value is [5 minutes](https://w3c.github.io/webauthn/#sctn-timeout-recommended-range), but you can increase it --- up to 10 minutes, which is still within the [recommended range](https://w3c.github.io/webauthn/#sctn-timeout-recommended-range). Long timeouts make sense if you expect users to use the [hybrid workflow](https://developers.google.com/identity/passkeys/use-cases#sign-in-with-a-phone), which typically takes a bit longer. If the operation times out, a `NotAllowedError` will be thrown.

Once you've created `publicKeyCredentialRequestOptions`, send it to the client.
![publicKeyCredentialCreationOptions sent by the server](https://developers.google.com/identity/passkeys/developer-guides/images/auth-options.png) Options sent by the server. `challenge` decoding happens client-side.

### Example code: create credential request options

We're using the [SimpleWebAuthn library](https://developers.google.com/identity/passkeys/developer-guides/server-authentication#about_the_example_code) in our examples. Here, we hand over the creation of credential request options to its [`generateAuthenticationOptions`](https://simplewebauthn.dev/docs/packages/server#1-generate-authentication-options) function.

> [!NOTE]
> **Note:** Operations such as `challenge` creation are abstracted away, and live in SimpleWebAuthn's code. If you're curious, check SimpleWebAuthn's [source code for `generateAuthenticationOptions`](https://github.com/MasterKale/SimpleWebAuthn/blob/master/packages/server/src/authentication/generateAuthenticationOptions.ts).

    import {
      generateAuthenticationOptions,
    } from '@simplewebauthn/server';

    router.post('/signinRequest', csrfCheck, async (req, res) => {

      // Ensure you nest calls in try/catch blocks.
      // If something fails, throw an error with a descriptive error message.
      // Return that message with an appropriate error code to the client.
      try {
        // Use the generateAuthenticationOptions function from SimpleWebAuthn
        const options = await generateAuthenticationOptions({
          rpID: process.env.HOSTNAME,
          allowCredentials: [],
        });
        // Save the challenge in the user session
        req.session.challenge = options.challenge;

        return res.json(options);
      } catch (e) {
        console.error(e);
        return res.status(400).json({ error: e.message });
      }
    });

## Verify and sign in the user

When `navigator.credentials.get` resolves successfully on the client, it returns a [`PublicKeyCredential`](https://www.w3.org/TR/webauthn-3/#publickeycredential) object.
![PublicKeyCredential object sent by the server](https://developers.google.com/identity/passkeys/developer-guides/images/publicKeyCredential.png) `navigator.credentials.get` returns a `PublicKeyCredential`.

The `response` is an [`AuthenticatorAssertionResponse`](https://w3c.github.io/webauthn/#authenticatorassertionresponse). It represents the passkey provider's response to the client's instruction to create what's needed to try and authenticate with a passkey on the RP. It contains:

- `response.authenticatorData`and`response.clientDataJSON`, like at the [passkey registration](https://developers.google.com/identity/passkeys/developer-guides/server-registration) step.
- `response.signature` which contains a signature over these values.

Send the [`PublicKeyCredential`](https://www.w3.org/TR/webauthn-3/#iface-pkcredential) object to the server.

> [!NOTE]
> **Note:** In WebAuthn Level 3, [`PublicKeyCredential.prototype.toJSON()`](https://w3c.github.io/webauthn/#dom-publickeycredential-tojson) converts the credential response into a JSON-compatible object with Base64URL-encoded strings. When using `@simplewebauthn/browser`, `startAuthentication()` automatically returns this JSON object (`AuthenticationResponseJSON`) so it can be sent directly to the server without manual encoding.

On the server, do the following:
![Database schema](https://developers.google.com/identity/passkeys/developer-guides/images/database-schema-passkeys.png) Suggested database schema. Learn more about this design in [Server-side passkey registration](https://developers.google.com/identity/passkeys/developer-guides/server-registration).

- **Gather information you'll need to verify the assertion and authenticate the user:**
  - Get the expected challenge you stored in the session when you [generated the authentication options](https://developers.google.com/identity/passkeys/developer-guides/server-authentication#create_credential_request_options).
  - Get the expected [origin](https://developer.mozilla.org/docs/Web/API/AuthenticatorResponse/clientDataJSON#origin) and RP ID.
  - Find in your database who the user is. In the case of discoverable credentials, you don't know who is the user making an authentication request. To find out, you have two options:
    - Option 1: Use the `response.userHandle` in the `PublicKeyCredential` object. In the **Users** table, look for the `passkey_user_id` that matches `userHandle`.
    - Option 2: Use the credential `id` present in the `PublicKeyCredential` object. In the **Public key credentials** table, look for the credential `id` that matches the credential `id` present in the `PublicKeyCredential` object. Then look for the corresponding user using the foreign key `passkey_user_id` to your **Users** table.
  - Find in your database the public key credential information that matches the authentication assertion you've received. To do so, in the **Public key credentials** table, look for the credential `id` that matches the credential `id`present in the `PublicKeyCredential` object.
- **Verify the authentication assertion.** Hand over this verification step to your FIDO server-side library, which will typically offer a utility function for this purpose. SimpleWebAuthn offers, for example, [`verifyAuthenticationResponse`](https://simplewebauthn.dev/docs/packages/server#2-verify-authentication-response). Learn what's happening under the hood in [Appendix: verification of the authentication response](https://developers.google.com/identity/passkeys/developer-guides/server-authentication#appendix_verification_of_the_authentication_response).

- **Delete the challenge whether verification is successful or not**, to prevent replay attacks.

- **Sign in the user.** If the verification was successful, update session information to mark the user as signed-in. You may also want to return a `user` object to the client, so the frontend can use information associated with the newly signed-in user.

### Example code: verify and sign in the user

We're using the [SimpleWebAuthn library](https://developers.google.com/identity/passkeys/developer-guides/server-authentication#about_the_example_code) in our examples. Here, we hand over verification of the authentication response to its [`verifyAuthenticationResponse`](https://simplewebauthn.dev/docs/packages/server#2-verify-authentication-response) function.

> [!NOTE]
> **Note:** SimpleWebAuthn relies on the [SubtleCrypto interface's `verify()`](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/verify) method. SubtleCrypto is an interface of the Web Crypto API that provides low-level cryptographic functionality.

    import {
      verifyAuthenticationResponse,
    } from '@simplewebauthn/server';
    import { isoBase64URL } from '@simplewebauthn/server/helpers';

    router.post('/signinResponse', csrfCheck, async (req, res) => {
      const response = req.body;
      const expectedChallenge = req.session.challenge;
      const expectedOrigin = getOrigin(req.get('User-Agent'));
      const expectedRPID = process.env.HOSTNAME;

      // Ensure you nest verification function calls in try/catch blocks.
      // If something fails, throw an error with a descriptive error message.
      // Return that message with an appropriate error code to the client.
      try {
        // Find the credential stored to the database by the credential ID
        const cred = Credentials.findById(response.id);
        if (!cred) {
          throw new Error('Credential not found.');
        }
        // Find the user - Here alternatively we could look up the user directly
        // in the Users table via userHandle
        const user = Users.findByPasskeyUserId(cred.passkey_user_id);
        if (!user) {
          throw new Error('User not found.');
        }
        // Prepare credential information for verification
        const credential = {
          id: cred.id,
          publicKey: isoBase64URL.toBuffer(cred.publicKey),
          transports: cred.transports,
        };

        // Verify the credential
        const { verified, authenticationInfo } = await verifyAuthenticationResponse({
          response,
          expectedChallenge,
          expectedOrigin,
          expectedRPID,
          credential,
          requireUserVerification: false,
        });

        if (!verified) {
          throw new Error('User verification failed.');
        }

        // Kill the challenge for this session.
        delete req.session.challenge;

        req.session.username = user.username;
        req.session['signed-in'] = 'yes';

        return res.json(user);
      } catch (e) {
        delete req.session.challenge;

        console.error(e);
        return res.status(400).json({ error: e.message });
      }
    });

## Appendix: verification of the authentication response

Verifying the authentication response consists of the following checks:

- Ensure that the RP ID matches your site.
- Ensure that the request's origin matches your site's sign-in origin. For Android apps, review [Verify origin](https://developer.android.com/training/sign-in/passkeys#verify-origin).
- Check that the device was able to provide the challenge you gave it.
- Verify that during authentication, the user has followed the requirements you mandate as an RP. If you require user verification, ensure that the `uv` (user verified) flag in `authenticatorData` is `true`. Check that the `up` (user present) flag in `authenticatorData` is `true`, since user presence is [always required](https://w3c.github.io/webauthn/#sctn-op-make-cred) for passkeys.
- **Verify the signature.** To verify the signature, you need:
  - The signature, which is the signed challenge: `response.signature`
  - The public key, to verify the signature with.
  - The original signed data. This is the data whose signature is to be verified.
  - The cryptographic algorithm that was used to create the signature.

> [!NOTE]
> **Note:** You'll often read that the passkey provider signs the `challenge` to prove the user holds the passkey. This is a simplification. In reality, what the authenticator signs is a [concatenation](https://w3c.github.io/webauthn/#sctn-fido-u2f-sig-format-compat) of `authenticatorData`, and of a hash of `clientDataJSON` which contains the challenge.

To learn more about these steps, check SimpleWebAuthn's [source code for `verifyAuthenticationResponse`](https://github.com/MasterKale/SimpleWebAuthn/blob/master/packages/server/src/authentication/verifyAuthenticationResponse.ts) or dive into the complete list of verifications in the [specification](https://www.w3.org/TR/webauthn-3/#sctn-verifying-assertion).