Tools for server ↔ client WebAuthn ceremony orchestration.
Quickprompt · Install · Usage Walkthrough · API Reference · License
Prompt your agent:
Add passkey authentication to my app using https://raw.githubusercontent.com/wevm/webauthx/refs/heads/main/SKILL.md, and add it to my skills.
npm i webauthx
pnpm i webauthx
bun i webauthx
Register a new passkey credential for a user. The server generates a challenge, the client creates the credential, and the server verifies it.
import { Registration } from 'webauthx/server' app.post('/register/options', async (request) => { const { name } = await request.json() // Generate a challenge and WebAuthn options for the client. const { challenge, options } = Registration.getOptions({ name, rp: { id: 'example.com', name: 'Example' }, }) // Persist challenge for verification in the next step. await store.storeChallenge(challenge) // Send WebAuthn options to the client. return Response.json({ options }) })
import { Registration } from 'webauthx/client' // Fetch WebAuthn options from the server. const { options } = await fetch('/register/options', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'alice' }), }).then((r) => r.json()) // Prompt the user to create a passkey. const credential = await Registration.create({ options }) // Send the credential to the server for verification. await fetch('/register/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ credential }), })
import { Registration } from 'webauthx/server' app.post('/register/verify', async (request) => { const { credential } = await request.json() // Consume the stored challenge (single-use). const challenge = await store.consumeChallenge(request) // Verify attestation & extract the public key. const result = Registration.verify(credential, { challenge, origin: 'https://example.com', rpId: 'example.com', }) // Persist the credential for future authentication. await store.storeCredential({ ...result.credential, aaguid: result.aaguid, }) })
import { Aaguid } from 'webauthx/server' const info = await Aaguid.lookup({ id: storedCredential.aaguid }) // => { name: '1Password', iconLight?: 'data:image/...', iconDark?: 'data:image/...' } // => null
Authenticate a returning user with their existing passkey. The server generates a challenge, the client signs it, and the server verifies the signature.
import { Authentication } from 'webauthx/server' app.post('/auth/options', async (request) => { const { credentialId } = await request.json() // Generate a challenge and WebAuthn options for the client. const { challenge, options } = Authentication.getOptions({ credentialId, rpId: 'example.com', }) // Persist challenge for verification in the next step. await store.storeChallenge(challenge) // Send WebAuthn options to the client. return Response.json({ options }) })
import { Authentication } from 'webauthx/client' // Fetch WebAuthn options from the server. const { options } = await fetch('/auth/options', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ credentialId }), }).then((r) => r.json()) // Prompt the user to sign the challenge with their passkey. const response = await Authentication.sign({ options }) // Send the response to the server for verification. await fetch('/auth/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ response }), })
import { Authentication } from 'webauthx/server' app.post('/auth/verify', async (request) => { const { response } = await request.json() // Consume the stored challenge (single-use). const challenge = await store.consumeChallenge(request) // Look up the stored public key for this credential. const credential = await store.getCredential(response.id) // Verify the P-256 signature. const valid = Authentication.verify(response, { challenge, publicKey: credential.publicKey, origin: 'https://example.com', rpId: 'example.com', }) })
Signs a challenge via navigator.credentials.get. Accepts WebAuthn options from the server and returns a response ready to send back.
import { Authentication } from 'webauthx/client' const response = await Authentication.sign({ options })
| Parameter | Type | Description |
|---|---|---|
getFn |
function |
Custom credential request function (for testing). |
options |
CredentialRequestOptions |
WebAuthn options from Authentication.getOptions. |
Promise<Authentication.Response>
Response to send to the server.
Creates a new WebAuthn credential via navigator.credentials.create. Accepts WebAuthn options from the server and returns a credential ready to send back.
import { Registration } from 'webauthx/client' const credential = await Registration.create({ options })
| Parameter | Type | Description |
|---|---|---|
createFn |
function |
Custom credential creation function (for testing). |
options |
CredentialCreationOptions |
WebAuthn options from Registration.getOptions. |
Promise<Registration.Credential>
Credential to send to the server.
Generates PublicKeyCredentialRequestOptions for authentication. A random 32-byte challenge is generated if one isn't provided.
import { Authentication } from 'webauthx/server' const { challenge, options } = Authentication.getOptions({ credentialId: storedCredential.id, rpId: 'example.com', })
| Parameter | Type | Description |
|---|---|---|
challenge |
Hex |
Optional challenge. A random 32-byte hex value is generated if omitted. |
credentialId |
string | string[] |
Credential ID(s) to allow. |
rpId |
string |
Relying party ID. |
timeout |
number |
Timeout in milliseconds. |
userVerification |
string |
User verification requirement. |
{ challenge: Hex; options: CredentialRequestOptions }
Challenge to store and the WebAuthn options to send to the client.
Verifies an authentication response from the client. Validates the rpIdHash, origin, challenge, and P-256 signature.
import { Authentication } from 'webauthx/server' const valid = Authentication.verify(response, { challenge, publicKey: credential.publicKey, origin: 'https://example.com', rpId: 'example.com', })
| Parameter | Type | Description |
|---|---|---|
options.challenge |
Hex |
Expected challenge. |
options.origin |
string | string[] |
Expected origin(s). |
options.publicKey |
Hex |
The stored P-256 public key (hex). |
options.rpId |
string |
Expected relying party ID. |
response |
Authentication.Response |
The authentication response from the client. |
boolean
true if the signature is valid.
Generates PublicKeyCredentialCreationOptions for registration. A random 32-byte challenge is generated if one isn't provided.
import { Registration } from 'webauthx/server' const { challenge, options } = Registration.getOptions({ name: 'alice', rp: { id: 'example.com', name: 'Example' }, })
| Parameter | Type | Description |
|---|---|---|
attestation |
string |
Attestation conveyance preference. |
authenticatorSelection |
object |
Authenticator selection criteria. |
challenge |
Hex |
Optional challenge. A random 32-byte hex value is generated if omitted. |
excludeCredentialIds |
string[] |
Credential IDs to exclude (prevent re-registration). |
name |
string |
Display name for the credential (shorthand for user.name). |
rp |
{ id: string; name: string } |
Relying party identifier and display name. |
timeout |
number |
Timeout in milliseconds. |
user |
{ name: string; displayName?: string; id?: BufferSource } |
User account descriptor. Alternative to name. |
{ challenge: Hex; options: CredentialCreationOptions }
Challenge to store and the WebAuthn options to send to the client.
Verifies a registration credential from the client. Validates the attestation, rpIdHash, challenge, and origin, and extracts the P-256 public key.
import { Registration } from 'webauthx/server' const result = Registration.verify(credential, { challenge, origin: 'https://example.com', rpId: 'example.com', })
| Parameter | Type | Description |
|---|---|---|
credential |
Registration.Credential |
The credential from the client. |
options.challenge |
Hex | Uint8Array | ((challenge: string) => boolean) |
Expected challenge value or async validator function. |
options.origin |
string | string[] |
Expected origin(s) (e.g. "https://example.com"). |
options.rpId |
string |
Relying party ID (e.g. "example.com"). |
options.userVerification |
string |
User verification requirement. Default: 'required'. |
Registration.Response & { aaguid: string }
Includes the verified credential, signature counter, and the credential's authenticator AAGUID.
Looks up friendly authenticator metadata from the community-maintained AAGUID registry. By default, the registry is fetched from the upstream combined JSON file and cached in-memory for subsequent lookups.
import { Aaguid } from 'webauthx/server' const info = await Aaguid.lookup({ id: '08987058-cadc-4b81-b6e1-30de50dcbe96', })
Extracts the AAGUID from a serialized registration credential.
const aaguid = Aaguid.extract(credential)
Returns string | undefined.
Looks up authenticator metadata by AAGUID.
const info = await Aaguid.lookup({ id: aaguid })
Returns Aaguid.Info | null.
Parameters:
| Parameter | Type | Description |
|---|---|---|
cache |
boolean |
Reuse an in-memory cache for the selected remoteList. Default: true. |
fetchFn |
typeof fetch |
Custom fetch implementation. |
id |
string |
AAGUID to resolve. |
remoteList |
string |
Override the remote registry URL. Defaults to Aaguid.remoteList. |
type Info = { name: string iconLight?: string | undefined iconDark?: string | undefined }
MIT