WebAuthn and PRF
Introductory reading on WebAuthn jobs in this suite and how the PRF extension unlocks a vault without sending secrets to the server.
WebAuthn and PRF
Extended reading on why this suite leans on WebAuthn and the prf extension — not a shipping checklist. Integration steps live in Passkeys and PRF. Primitive inventory: Crypto primitives. Design and upstream rationale: Crypto and package rationale.
Stage
Packages are pre-1.0. Prefer the security model before production use. Security is hard; this is an open foundation.
The product idea
Keys that open user data stay on the device. The server stores wraps, claims, and sealed envelopes. WebAuthn is how a passkey proves presence for login or step-up. When the authenticator supports PRF, the same credential family can also derive a site-bound secret for daily vault unlock — without sending that secret to your backend.
Two WebAuthn jobs
Mixing these jobs in UX is fine. Mixing their crypto outputs is not.
| Job | Layer | What the authenticator proves | What the server learns |
|---|---|---|---|
| Login / elevate | L0 / L1 | User presence + credential binding | Assertion material for session or step-up claim |
| Vault unlock (PRF) | L2 | Same credential family, plus PRF output in the browser | Only wrap ciphertext / metadata after unlock |
Elevate never receives the storage PRF secret. Vault unlock never grants admin routes. That separation is L0 · L1 · L2 — see How it works.
Hardened composition lives in @railman/auth-zero-access-passkey (enhancePasskey): frozen RP/origin posture, user verification, and a counter guard shared with passkey step-up when you enable it. Package guide: Passkey.
What PRF is (and is not)
PRF here means the WebAuthn prf extension: the authenticator derives bytes bound to your RP and a salt/info you choose in the browser. Those bytes stay in the page. They become a KEK that unwraps the MEK.
| PRF is | PRF is not |
|---|---|
| A daily unlock factor when the authenticator supports it | A replacement for TLS |
Domain-separated with purpose + userId (and resource id) | Something the blind store is allowed to see |
| Degradable — missing PRF → password or recovery UX | Magically available on every device |
Tenant salt and suite helpers live in the browser via defineZeroAccessPasskeyCryptoConfig. Ceremony: extensions → navigator.credentials.get → extractPrfSecret → unlock — documented in Passkeys and PRF. Glossary: Background.
Domain separation (why it matters)
PRF/HKDF info strings bind purpose and user:
prefix | purpose | userId | [resourceId] | [suffix]userId is required. Purposes such as storage_kek and elevate-related derivations must not be interchangeable. Cross-user mixups must fail closed.