A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://developers.google.com/identity/passkeys/developer-guides/server-authentication below:

Server-side passkey authentication | Web guides

Skip to main content Server-side passkey authentication

Stay organized with collections Save and categorize content based on your preferences.

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 and Server-side passkey 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 for the web, or Sign in your user with Credential Manager for an Android app.
Overview

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

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.

The following sections dive into the specifics of each step.

Note: While it's possible to implement server-side passkeys functionality from scratch, we recommend that you rely on a library instead.

In the examples throughout this article, we'll demonstrate using a JavaScript and TypeScript FIDO server-side library called SimpleWebAuthn.


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 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 object.

// Example challenge, base64URL-encoded
weMLPOSx1VfSnMV6uPwDKbjGdKRMaUDGxeDEUTT5VN8
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. Out of precaution, SimpleWebAuthn uses 32-byte challenges which are more secure than the 16-bytes recommendation.

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 in Node.js, or secrets in Python. Per the specification, 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.

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: 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.

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

Once you've created publicKeyCredentialRequestOptions, send it to the client.

Options sent by the server. challenge decoding happens client-side. Example code: create credential request options

We're using the SimpleWebAuthn library in our examples. Here, we hand over the creation of credential request options to its generateAuthenticationOptions function.

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.
import {
  generateRegistrationOptions,
  verifyRegistrationResponse,
  generateAuthenticationOptions,
  verifyAuthenticationResponse
} 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 object.

navigator.credentials.get returns a PublicKeyCredential.

The response is an 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:

Send the PublicKeyCredential object to the server.

Note: The PublicKeyCredential object includes fields that are ArrayBuffers, so they aren't supported by JSON.stringify(). You must manually encode them using base64URL before you can be send them to the server over HTTPS.

On the server, do the following:

Suggested database schema. Learn more about this design in Server-side passkey registration. Example code: verify and sign in the user

We're using the SimpleWebAuthn library in our examples. Here, we hand over verification of the authentication response to its verifyAuthenticationResponse function.

Note: SimpleWebAuthn relies on the SubtleCrypto interface's verify() method. SubtleCrypto is an interface of the Web Crypto API that provides low-level cryptographic functionality.
import {
  generateRegistrationOptions,
  verifyRegistrationResponse,
  generateAuthenticationOptions,
  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.');
    }
    // Base64URL decode some values
    const authenticator = {
      credentialPublicKey: isoBase64URL.toBuffer(cred.publicKey),
      credentialID: isoBase64URL.toBuffer(cred.id),
      transports: cred.transports,
    };

    // Verify the credential
    const { verified, authenticationInfo } = await verifyAuthenticationResponse({
      response,
      expectedChallenge,
      expectedOrigin,
      expectedRPID,
      authenticator,
      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:

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 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 or dive into the complete list of verifications in the specification.

Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2025-05-19 UTC.

[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2025-05-19 UTC."],[[["Passkeys are authenticated server-side by generating unique challenges, creating credential request options, and verifying user sign-in attempts."],["Servers use libraries like SimpleWebAuthn to generate challenges, create credential request options, and verify authentication assertions."],["The verification process ensures the request's validity and the user's identity before granting access and updating session information."],["Successful authentication using passkeys relies on matching RP IDs, origins, and verifying client data, authenticator data, and signatures."]]],[]]


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4