A comprehensive TypeScript library providing both client-side and server-side WebAuthn functionality for implementing passwordless authentication with passkeys and other WebAuthn-compatible authenticators
npx @tessl/cli install tessl/npm-simplewebauthn@13.1.0SimpleWebAuthn is a comprehensive TypeScript library providing both client-side and server-side WebAuthn functionality for implementing passwordless authentication with passkeys and other WebAuthn-compatible authenticators. It consists of two complementary packages that work together to provide a complete WebAuthn implementation.
npm install @simplewebauthn/browser or deno add jsr:@simplewebauthn/browsernpm install @simplewebauthn/server or deno add jsr:@simplewebauthn/serverBrowser Package:
import {
startRegistration,
startAuthentication,
browserSupportsWebAuthn,
platformAuthenticatorIsAvailable,
browserSupportsWebAuthnAutofill,
type RegistrationResponseJSON,
type AuthenticationResponseJSON,
} from "@simplewebauthn/browser";Server Package:
import {
generateRegistrationOptions,
verifyRegistrationResponse,
generateAuthenticationOptions,
verifyAuthenticationResponse,
MetadataService,
SettingsService,
} from "@simplewebauthn/server";
// Server helpers for advanced use cases
import {
generateChallenge,
generateUserID,
convertAAGUIDToString,
decodeAttestationObject,
parseAuthenticatorData,
isoBase64URL,
isoUint8Array,
} from "@simplewebauthn/server/helpers";Complete Registration and Authentication Flow:
// === CLIENT SIDE (Browser) ===
import {
startRegistration,
startAuthentication,
browserSupportsWebAuthn
} from "@simplewebauthn/browser";
// Check WebAuthn support
if (!browserSupportsWebAuthn()) {
console.log("WebAuthn not supported");
return;
}
// Registration
const registrationOptions = await fetch("/webauthn/register/begin").then(r => r.json());
const registrationResponse = await startRegistration({
optionsJSON: registrationOptions,
});
await fetch("/webauthn/register/finish", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(registrationResponse),
});
// Authentication
const authOptions = await fetch("/webauthn/authenticate/begin").then(r => r.json());
const authResponse = await startAuthentication({
optionsJSON: authOptions,
});
await fetch("/webauthn/authenticate/finish", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(authResponse),
});
// === SERVER SIDE ===
import {
generateRegistrationOptions,
verifyRegistrationResponse,
generateAuthenticationOptions,
verifyAuthenticationResponse,
} from "@simplewebauthn/server";
// Registration endpoints
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,
});
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",
});
if (verification.verified) {
// Save credential to database
res.json({ verified: true });
}
});
// Authentication endpoints
app.post('/webauthn/authenticate/begin', async (req, res) => {
const options = await generateAuthenticationOptions({
rpID: "example.com",
});
req.session.challenge = options.challenge;
res.json(options);
});
app.post('/webauthn/authenticate/finish', async (req, res) => {
const verification = await verifyAuthenticationResponse({
response: req.body,
expectedChallenge: req.session.challenge,
expectedOrigin: "https://example.com",
credential: storedCredential,
});
if (verification.verified) {
// User authenticated
res.json({ verified: true });
}
});SimpleWebAuthn is built around several key components:
Browser Package:
startRegistration and startAuthentication functionsWebAuthnErrorServer Package:
Handles the complete WebAuthn registration flow on the client side.
function startRegistration(options: {
optionsJSON: PublicKeyCredentialCreationOptionsJSON;
useAutoRegister?: boolean;
}): Promise<RegistrationResponseJSON>;Handles the complete WebAuthn authentication flow with support for conditional UI.
function startAuthentication(options: {
optionsJSON: PublicKeyCredentialRequestOptionsJSON;
useBrowserAutofill?: boolean;
verifyBrowserAutofillInput?: boolean;
}): Promise<AuthenticationResponseJSON>;Functions to detect WebAuthn capabilities and browser support.
function browserSupportsWebAuthn(): boolean;
function platformAuthenticatorIsAvailable(): Promise<boolean>;
function browserSupportsWebAuthnAutofill(): Promise<boolean>;Utilities for converting between Base64URL strings and ArrayBuffers.
function base64URLStringToBuffer(base64URLString: string): ArrayBuffer;
function bufferToBase64URLString(buffer: ArrayBuffer): string;Comprehensive error handling and abort service for managing operations.
class WebAuthnError extends Error {
code: WebAuthnErrorCode;
constructor(options: {
message: string;
code: WebAuthnErrorCode;
cause: Error;
name?: string;
});
}
interface WebAuthnAbortService {
createNewAbortSignal(): AbortSignal;
cancelCeremony(): void;
}Server-side functions for generating WebAuthn registration options.
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>;Server-side functions for verifying WebAuthn registration responses.
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>;Server-side functions for generating WebAuthn authentication options.
function generateAuthenticationOptions(options: {
rpID: string;
allowCredentials?: {
id: Base64URLString;
transports?: AuthenticatorTransportFuture[];
}[];
challenge?: string | Uint8Array;
timeout?: number;
userVerification?: 'required' | 'preferred' | 'discouraged';
extensions?: AuthenticationExtensionsClientInputs;
}): Promise<PublicKeyCredentialRequestOptionsJSON>;Server-side functions for verifying WebAuthn authentication responses.
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>;Services for managing authenticator metadata and root certificates.
interface MetadataService {
initialize(options?: {
verificationMode?: VerificationMode;
mdsServers?: string[];
statements?: MetadataStatement[];
}): Promise<void>;
getStatement(aaguid: string): Promise<MetadataStatement | undefined>;
getStatements(): Promise<MetadataStatement[]>;
}
interface SettingsService {
setRootCertificates(opts: {
identifier: RootCertIdentifier;
certificates: (Uint8Array | string)[];
}): void;
getRootCertificates(opts: {
identifier: RootCertIdentifier;
}): string[];
}interface RegistrationResponseJSON {
id: Base64URLString;
rawId: Base64URLString;
response: AuthenticatorAttestationResponseJSON;
authenticatorAttachment?: AuthenticatorAttachment;
clientExtensionResults: AuthenticationExtensionsClientOutputs;
type: PublicKeyCredentialType;
}
interface AuthenticationResponseJSON {
id: Base64URLString;
rawId: Base64URLString;
response: AuthenticatorAssertionResponseJSON;
authenticatorAttachment?: AuthenticatorAttachment;
clientExtensionResults: AuthenticationExtensionsClientOutputs;
type: PublicKeyCredentialType;
}interface PublicKeyCredentialCreationOptionsJSON {
rp: PublicKeyCredentialRpEntity;
user: PublicKeyCredentialUserEntityJSON;
challenge: Base64URLString;
pubKeyCredParams: PublicKeyCredentialParameters[];
timeout?: number;
excludeCredentials?: PublicKeyCredentialDescriptorJSON[];
authenticatorSelection?: AuthenticatorSelectionCriteria;
hints?: PublicKeyCredentialHint[];
attestation?: AttestationConveyancePreference;
attestationFormats?: AttestationFormat[];
extensions?: AuthenticationExtensionsClientInputs;
}
interface PublicKeyCredentialRequestOptionsJSON {
challenge: Base64URLString;
timeout?: number;
rpId?: string;
allowCredentials?: PublicKeyCredentialDescriptorJSON[];
userVerification?: UserVerificationRequirement;
hints?: PublicKeyCredentialHint[];
extensions?: AuthenticationExtensionsClientInputs;
}interface VerifiedRegistrationResponse {
verified: boolean;
registrationInfo?: {
fmt: AttestationFormat;
counter: number;
aaguid: string;
credentialID: Uint8Array;
credentialPublicKey: Uint8Array;
userVerified: boolean;
credentialDeviceType: CredentialDeviceType;
credentialBackedUp: boolean;
};
}
interface VerifiedAuthenticationResponse {
verified: boolean;
authenticationInfo?: {
newCounter: number;
userVerified: boolean;
credentialDeviceType: CredentialDeviceType;
credentialBackedUp: boolean;
};
}type Base64URLString = string;
type AuthenticatorTransportFuture =
| 'ble'
| 'cable'
| 'hybrid'
| 'internal'
| 'nfc'
| 'smart-card'
| 'usb';
type WebAuthnErrorCode =
| 'ERROR_CEREMONY_ABORTED'
| 'ERROR_INVALID_DOMAIN'
| 'ERROR_INVALID_RP_ID'
| 'ERROR_INVALID_USER_ID_LENGTH'
| 'ERROR_MALFORMED_PUBKEYCREDPARAMS'
| 'ERROR_AUTHENTICATOR_GENERAL_ERROR'
| 'ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT'
| 'ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT'
| 'ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED'
| 'ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG'
| 'ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE'
| 'ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY';
type CredentialDeviceType = 'singleDevice' | 'multiDevice';
type PublicKeyCredentialHint = 'hybrid' | 'security-key' | 'client-device';
type AttestationFormat =
| 'fido-u2f'
| 'packed'
| 'android-safetynet'
| 'android-key'
| 'tpm'
| 'apple'
| 'none';
type VerificationMode = 'permissive' | 'strict';
type RootCertIdentifier = AttestationFormat | 'mds';