or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

authentication.mdbrowser-support.mddata-encoding.mderror-handling.mdindex.mdregistration.mdserver.md
tile.json

server.mddocs/

SimpleWebAuthn Server

Server-side WebAuthn functionality for Node.js, Deno, and other JavaScript runtimes. Provides functions to generate WebAuthn options, verify responses, and manage authenticator metadata.

Additional Imports

The server package also exports helper functions for advanced use cases:

import {
  generateChallenge,
  generateUserID,
  convertAAGUIDToString,
  decodeAttestationObject,
  decodeClientDataJSON,
  parseAuthenticatorData,
  toHash,
  convertCOSEtoPKCS,
  verifySignature,
  isoBase64URL,
  isoUint8Array,
} from "@simplewebauthn/server/helpers";

Capabilities

Registration Options Generation

Generates options for WebAuthn registration ceremonies.

/**
 * Prepare a value to pass into navigator.credentials.create(...) for authenticator registration
 */
function generateRegistrationOptions(options: {
  rpName: string;
  rpID: string;
  userName: string;
  userID?: Uint8Array;
  challenge?: string | Uint8Array;
  userDisplayName?: string;
  timeout?: number;
  attestationType?: 'direct' | 'enterprise' | 'none';
  excludeCredentials?: {
    id: Base64URLString;
    transports?: AuthenticatorTransportFuture[];
  }[];
  authenticatorSelection?: AuthenticatorSelectionCriteria;
  extensions?: AuthenticationExtensionsClientInputs;
  supportedAlgorithmIDs?: COSEAlgorithmIdentifier[];
  preferredAuthenticatorType?: 'securityKey' | 'localDevice' | 'remoteDevice';
}): Promise<PublicKeyCredentialCreationOptionsJSON>;

type GenerateRegistrationOptionsOpts = Parameters<typeof generateRegistrationOptions>[0];

Usage Examples:

import { generateRegistrationOptions } from "@simplewebauthn/server";

// Basic registration options
const registrationOptions = await generateRegistrationOptions({
  rpName: "My App",
  rpID: "example.com",
  userName: "user@example.com",
  userDisplayName: "John Doe",
});

// Advanced registration with exclusions and authenticator selection
const advancedOptions = await generateRegistrationOptions({
  rpName: "My App", 
  rpID: "example.com",
  userName: "user@example.com",
  userDisplayName: "John Doe",
  attestationType: "direct",
  excludeCredentials: existingCredentials.map(cred => ({
    id: cred.id,
    transports: cred.transports,
  })),
  authenticatorSelection: {
    authenticatorAttachment: "platform",
    requireResidentKey: true,
    userVerification: "required",
  },
  timeout: 120000,
});

Registration Response Verification

Verifies WebAuthn registration responses from the client.

/**
 * Verify that the user has legitimately completed the registration process
 */
function verifyRegistrationResponse(options: {
  response: RegistrationResponseJSON;
  expectedChallenge: string | ((challenge: string) => boolean | Promise<boolean>);
  expectedOrigin: string | string[];
  expectedRPID?: string | string[];
  expectedType?: string | string[];
  requireUserPresence?: boolean;
  requireUserVerification?: boolean;
  supportedAlgorithmIDs?: COSEAlgorithmIdentifier[];
}): Promise<VerifiedRegistrationResponse>;

interface VerifiedRegistrationResponse {
  verified: boolean;
  registrationInfo?: {
    fmt: AttestationFormat;
    counter: number;
    aaguid: string;
    credentialID: Uint8Array;
    credentialPublicKey: Uint8Array;
    userVerified: boolean;
    credentialDeviceType: CredentialDeviceType;
    credentialBackedUp: boolean;
  };
}

Usage Examples:

import { verifyRegistrationResponse } from "@simplewebauthn/server";

const verification = await verifyRegistrationResponse({
  response: registrationResponseFromClient,
  expectedChallenge: expectedChallenge,
  expectedOrigin: "https://example.com",
  expectedRPID: "example.com",
  requireUserVerification: true,
});

if (verification.verified && verification.registrationInfo) {
  // Store credential in database
  await saveCredential({
    id: verification.registrationInfo.credentialID,
    publicKey: verification.registrationInfo.credentialPublicKey,
    counter: verification.registrationInfo.counter,
    deviceType: verification.registrationInfo.credentialDeviceType,
    backedUp: verification.registrationInfo.credentialBackedUp,
  });
}

Authentication Options Generation

Generates options for WebAuthn authentication ceremonies.

/**
 * Prepare a value to pass into navigator.credentials.get(...) for authenticator authentication
 */
function generateAuthenticationOptions(options: {
  rpID: string;
  allowCredentials?: {
    id: Base64URLString;
    transports?: AuthenticatorTransportFuture[];
  }[];
  challenge?: string | Uint8Array;
  timeout?: number;
  userVerification?: 'required' | 'preferred' | 'discouraged';
  extensions?: AuthenticationExtensionsClientInputs;
}): Promise<PublicKeyCredentialRequestOptionsJSON>;

type GenerateAuthenticationOptionsOpts = Parameters<typeof generateAuthenticationOptions>[0];

Usage Examples:

import { generateAuthenticationOptions } from "@simplewebauthn/server";

// Basic authentication options (passwordless)
const authOptions = await generateAuthenticationOptions({
  rpID: "example.com",
  userVerification: "preferred",
});

// Authentication with specific credentials (second factor)
const specificAuthOptions = await generateAuthenticationOptions({
  rpID: "example.com",
  allowCredentials: userCredentials.map(cred => ({
    id: cred.id,
    transports: cred.transports,
  })),
  userVerification: "required",
  timeout: 60000,
});

Authentication Response Verification

Verifies WebAuthn authentication responses from the client.

/**
 * Verify that the user has legitimately completed the authentication process
 */ 
function verifyAuthenticationResponse(options: {
  response: AuthenticationResponseJSON;
  expectedChallenge: string | ((challenge: string) => boolean | Promise<boolean>);
  expectedOrigin: string | string[];
  expectedRPID?: string | string[];
  credential: {
    id: Base64URLString;
    publicKey: Uint8Array;
    counter: number;
    transports?: AuthenticatorTransportFuture[];
  };
  requireUserPresence?: boolean;
  requireUserVerification?: boolean;
  advancedFIDOConfig?: {
    userVerification?: 'required' | 'preferred' | 'discouraged';
  };
}): Promise<VerifiedAuthenticationResponse>;

interface VerifiedAuthenticationResponse {
  verified: boolean;
  authenticationInfo?: {
    newCounter: number;
    userVerified: boolean;
    credentialDeviceType: CredentialDeviceType;
    credentialBackedUp: boolean;
  };
}

Usage Examples:

import { verifyAuthenticationResponse } from "@simplewebauthn/server";

const credential = await getCredentialFromDB(credentialID);

const verification = await verifyAuthenticationResponse({
  response: authenticationResponseFromClient,
  expectedChallenge: expectedChallenge,
  expectedOrigin: "https://example.com",
  expectedRPID: "example.com",
  credential: {
    id: credential.id,
    publicKey: credential.publicKey,
    counter: credential.counter,
    transports: credential.transports,
  },
  requireUserVerification: true,
});

if (verification.verified && verification.authenticationInfo) {
  // Update counter in database
  await updateCredentialCounter(
    credentialID, 
    verification.authenticationInfo.newCounter
  );
  
  // User is authenticated
  return loginUser(userID);
}

Metadata Service

Service for managing FIDO Metadata Service (MDS) data to validate authenticators.

/**
 * Global service for interacting with the FIDO Metadata Service
 */
interface MetadataService {
  /**
   * Prepare the service to handle remote MDS servers and/or cache local metadata statements
   */
  initialize(options?: {
    verificationMode?: VerificationMode;
    mdsServers?: string[];
    statements?: MetadataStatement[];
  }): Promise<void>;
  
  /**
   * Get a metadata statement for a given AAGUID
   */
  getStatement(aaguid: string): Promise<MetadataStatement | undefined>;
  
  /**
   * Get all cached metadata statements
   */
  getStatements(): Promise<MetadataStatement[]>;
}

type VerificationMode = 'permissive' | 'strict';

// Exported singleton instance
const MetadataService: MetadataService;

Usage Examples:

import { MetadataService } from "@simplewebauthn/server";

// Initialize with default FIDO MDS
await MetadataService.initialize();

// Initialize with custom verification mode
await MetadataService.initialize({
  verificationMode: 'strict', // Only allow registered AAGUIDs
  mdsServers: ['https://mds.fidoalliance.org/'],
});

// Get metadata for a specific authenticator
const statement = await MetadataService.getStatement(aaguid);
if (statement) {
  console.log("Authenticator:", statement.description);
  console.log("Attestation types:", statement.attestationTypes);
}

Settings Service

Service for managing root certificates and attestation format settings.

/**
 * Global service for managing attestation format settings
 */
interface SettingsService {
  /**
   * Set potential root certificates for attestation formats that use them
   */
  setRootCertificates(opts: {
    identifier: RootCertIdentifier;
    certificates: (Uint8Array | string)[];
  }): void;
  
  /**
   * Get any registered root certificates for the specified attestation format
   */
  getRootCertificates(opts: {
    identifier: RootCertIdentifier;
  }): string[];
}

type RootCertIdentifier = AttestationFormat | 'mds';

// Exported singleton instance  
const SettingsService: SettingsService;

Usage Examples:

import { SettingsService } from "@simplewebauthn/server";

// Add custom root certificates for Android attestation
SettingsService.setRootCertificates({
  identifier: 'android-key',
  certificates: [customRootCertPEM],
});

// Get root certificates for a format
const certs = SettingsService.getRootCertificates({
  identifier: 'apple',
});

Supported Algorithm Identifiers

/**
 * Supported crypto algo identifiers
 * See https://w3c.github.io/webauthn/#sctn-alg-identifier
 * and https://www.iana.org/assignments/cose/cose.xhtml#algorithms
 */
const supportedCOSEAlgorithmIdentifiers: COSEAlgorithmIdentifier[] = [
  -8,    // EdDSA
  -7,    // ECDSA w/ SHA-256
  -36,   // ECDSA w/ SHA-512
  -37,   // RSASSA-PSS w/ SHA-256
  -38,   // RSASSA-PSS w/ SHA-384
  -39,   // RSASSA-PSS w/ SHA-512
  -257,  // RSASSA-PKCS1-v1_5 w/ SHA-256
  -258,  // RSASSA-PKCS1-v1_5 w/ SHA-384
  -259,  // RSASSA-PKCS1-v1_5 w/ SHA-512
  -65535 // RSASSA-PKCS1-v1_5 w/ SHA-1 (Deprecated)
];

Complete Server Workflow

import {
  generateRegistrationOptions,
  verifyRegistrationResponse,
  generateAuthenticationOptions,
  verifyAuthenticationResponse,
  MetadataService,
} from "@simplewebauthn/server";

// Initialize metadata service
await MetadataService.initialize();

// Registration flow
app.post('/webauthn/register/begin', async (req, res) => {
  const options = await generateRegistrationOptions({
    rpName: "My App",
    rpID: "example.com", 
    userName: req.user.email,
    userDisplayName: req.user.name,
    excludeCredentials: req.user.credentials?.map(cred => ({
      id: cred.id,
      transports: cred.transports,
    })),
  });
  
  req.session.challenge = options.challenge;
  res.json(options);
});

app.post('/webauthn/register/finish', async (req, res) => {
  const verification = await verifyRegistrationResponse({
    response: req.body,
    expectedChallenge: req.session.challenge,
    expectedOrigin: "https://example.com",
    expectedRPID: "example.com",
  });
  
  if (verification.verified && verification.registrationInfo) {
    // Save credential to database
    await saveCredential(req.user.id, verification.registrationInfo);
    res.json({ verified: true });
  } else {
    res.status(400).json({ error: "Registration failed" });
  }
});

// Authentication flow
app.post('/webauthn/authenticate/begin', async (req, res) => {
  const options = await generateAuthenticationOptions({
    rpID: "example.com",
    allowCredentials: req.user.credentials?.map(cred => ({
      id: cred.id,
      transports: cred.transports,
    })),
  });
  
  req.session.challenge = options.challenge;
  res.json(options);
});

app.post('/webauthn/authenticate/finish', async (req, res) => {
  const credential = await getCredential(req.body.id);
  
  const verification = await verifyAuthenticationResponse({
    response: req.body,
    expectedChallenge: req.session.challenge,
    expectedOrigin: "https://example.com",
    expectedRPID: "example.com",
    credential: credential,
  });
  
  if (verification.verified) {
    // Update counter and log in user
    await updateCredentialCounter(credential.id, verification.authenticationInfo.newCounter);
    req.session.loggedIn = true;
    res.json({ verified: true });
  } else {
    res.status(400).json({ error: "Authentication failed" });
  }
});