Skip to content
LogoLogo

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 { ,  } from 'tempo.ts/server'
 
const  = .({
  : .(),
  : '/keys',
  : '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.js

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

  1. Verifies the challenge was previously issued and is still valid
  2. Validates the credential's clientDataJSON.type is 'webauthn.create'
  3. Verifies the origin matches the configured RP ID (if set)
  4. 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 {  } from 'tempo.ts/server'
 
// In-memory store (for development/testing)
const  = .()
 
// Cloudflare KV
import {  } from 'cloudflare:workers'
const  = .(.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 {  } from 'tempo.ts/server'
 
const  = .({
  // ... other options
  : '/api/keys', 
})
 
// Endpoints will be:
// GET  /api/keys/challenge
// GET  /api/keys/:credentialId
// POST /api/keys/:credentialId

rp

  • 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 {  } from 'tempo.ts/server'
 
// Simple string (uses as both id and name)
const  = .({
  // ... other options
  : 'example.com', 
})
 
// Object with custom name
const  = .({
  // ... other options
  : { 
    : 'example.com', 
    : 'Example App', 
  }, 
})

Storage Schema

The handler uses the following key patterns in the KV store:

  • challenge:{hex} - Stores issued challenges
  • credential:{credentialId} - Stores public keys