auth-yes/tasks/complete/2026-0824.01.jul.feat.webauthn.prf-extension-1200.md

4.1 KiB

TASK METADATA

  • Target Files: server/main.ts, server/db.ts, ui/public/auth-client.js, ui/components/RegisterPage.tsx, ui/components/LoginPage.tsx
  • Core Objective: Implement WebAuthn PRF extension support for progressive feature detection and Key Encryption Key (KEK) derivation during registration and login, with graceful fallback.
  • Dependencies: WebCrypto API natively in Deno/browser, SimpleWebAuthn v13 for passing PRF extension options.
  • Additional Important Notes: This task establishes the PRF derivation pipeline. SSS multi-share reconstruction will integrate in a future story (3.3). If PRF is unsupported, registration/login must proceed normally without breaking standard WebAuthn flows.

Architectural Considerations & Risks

  • Risks:
    • Authenticator Compatibility: Not all authenticators support the WebAuthn PRF extension. A hard failure when PRF is missing would lock users out. The progressive fallback design is critical to prevent regressions in standard authentication.
    • Extension Types & SDK Mapping: Passing the exact extension payloads for PRF (eval.first, eval.second) in generateRegistrationOptions and generateAuthenticationOptions might require careful type mapping if SimpleWebAuthn types are strict.
    • Database Migrations: Modifying the passkeys table to include prf_enabled and prf_salt must maintain compatibility with existing passkey rows (which will default to false/null).
  • Alternatives:
    • Traditional server-side wrapping (HSM/KMS) or user passwords could be used for key derivation. However, the WebAuthn PRF extension natively binds the encryption key material to the hardware authenticator itself, preserving Auth-Yes's passwordless UX and zero-trust properties without transmitting raw secrets.

Proposed Implementation

1. Database Schema Updates (server/db.ts)

  • Modify the passkeys table schema to include a prf_enabled BOOLEAN DEFAULT FALSE column.
  • Add a prf_salt column (binary or hex string) to store the 32-byte cryptographic salt generated during registration.

2. Registration Flow (Server & Client)

  • Server (server/main.ts): In the /api/register/challenge endpoint, ensure the prf: {} extension is requested via generateRegistrationOptions.
  • Client (ui/public/auth-client.js): Execute navigator.credentials.create() through the client SDK. Extract getClientExtensionResults()?.prf.
  • Server (server/main.ts): In the /api/register/verify endpoint, inspect the extension results to check if PRF is enabled (prf.enabled === true). If supported, generate a 32-byte secure random salt (prf_salt). Store prf_enabled: true and the prf_salt alongside the new passkey record.

3. Login Flow (Server & Client)

  • Server (server/main.ts): In the /api/login/challenge endpoint, retrieve the user's prf_salt if their passkey has prf_enabled. Include the prf: { eval: { first: <prf_salt> } } extension payload in generateAuthenticationOptions.
  • Client (ui/public/auth-client.js):
    • Execute navigator.credentials.get() with the provided PRF evaluation salt.
    • Check getClientExtensionResults()?.prf?.results?.first for the PRF output.
  • Client-Side KEK Derivation:
    • If PRF output exists, use it as Input Keying Material (IKM) for WebCrypto HKDF to derive a 256-bit AES-GCM Key Encryption Key (KEK).
    • HKDF Parameters:
      • Hash: SHA-256 (RFC 5869)
      • Salt: 32-byte cryptographic salt (stored with passkey record)
      • Info: new TextEncoder().encode("auth-yes:prf:device-share:v1")
  • Progressive Fallback:
    • If getClientExtensionResults()?.prf is missing or fails, gracefully bypass the KEK derivation step and continue standard signature-only WebAuthn login.

4. UI Integration (ui/components/RegisterPage.tsx, ui/components/LoginPage.tsx)

  • (Optional but recommended) Include minor, non-blocking UI indicators or debug logs to signify when advanced hardware encryption (PRF) is successfully negotiated, aiding in development and progressive feature adoption.