Handler.keyManager
Creates a server handler that manages WebAuthn credential public keys. This handler stores and retrieves the public keys associated with passkey credentials, enabling users to access their accounts from any device.
Usage
import { Handler, Kv } from 'tempo.ts/server'
const handler = Handler.keyManager({
kv: Kv.memory(),
path: '/keys',
rp: 'example.com',
})Then plug handler into your server framework of choice. For example:
createServer(handler.listener) // Node.js
Bun.serve(handler) // Bun
Deno.serve(handler) // Deno
app.all('*', c => handler.fetch(c.request)) // Elysia
app.use(handler.listener) // Express
app.use(c => handler.fetch(c.req.raw)) // Hono
export const GET = handler.fetch // Next.js
export const POST = handler.fetch // Next.jsHow It Works
The key manager handler exposes three endpoints:
GET /{path}/challenge
Generates a random challenge for WebAuthn credential creation. The challenge is stored temporarily (with a 5-minute expiration) to be verified later during credential registration.
GET /{path}/:credentialId
Retrieves the public key for a given credential ID. This is used when a user authenticates with an existing passkey.
POST /{path}/:credentialId
Stores a public key for a new credential. This endpoint:
- Verifies the challenge was previously issued and is still valid
- Validates the credential's
clientDataJSON.typeis'webauthn.create' - Verifies the origin matches the configured RP ID (if set)
- Extracts and stores the public key from the authenticator data
Parameters
kv
- Type:
Kv - Required: Yes
The key-value store to use for persisting challenges and public keys. tempo.ts provides adapters for common KV stores:
import { Kv } from 'tempo.ts/server'
// In-memory store (for development/testing)
const kv = Kv.memory()
// Cloudflare KV
import { env } from 'cloudflare:workers'
const kv = Kv.cloudflare(env.KEY_STORE)path
- Type:
string
The base path where the handler will listen for requests. All three endpoints (/challenge, /:credentialId) will be mounted under this path.
import { Handler } from 'tempo.ts/server'
const handler = Handler.keyManager({
// ... other options
path: '/api/keys',
})
// Endpoints will be:
// GET /api/keys/challenge
// GET /api/keys/:credentialId
// POST /api/keys/:credentialIdrp
- Type:
string | { id: string, name?: string }
The Relying Party (RP) identifier and name. This is used to:
- Include RP information in the challenge response
- Verify the origin of credential registration requests
import { Handler } from 'tempo.ts/server'
// Simple string (uses as both id and name)
const handler = Handler.keyManager({
// ... other options
rp: 'example.com',
})
// Object with custom name
const handler2 = Handler.keyManager({
// ... other options
rp: {
id: 'example.com',
name: 'Example App',
},
})Storage Schema
The handler uses the following key patterns in the KV store:
challenge:{hex}- Stores issued challengescredential:{credentialId}- Stores public keys