Files
web/docs/PASSKEYS.md
T
2026-08-27 10:37:38 -04:00

64 lines
3.1 KiB
Markdown

<!-- SPDX-License-Identifier: MPL-2.0 -->
# Passkey integration
`authwebauthn` is a passkey ceremony service, not a login page or account
policy. An application supplies its exact relying-party identity, routes,
authorization decisions, session cookie, HTML, and local recovery command.
## Fixed security policy
- Use an exact HTTPS origin whose hostname equals the relying-party ID.
- Reject cross-origin ceremonies.
- Require discoverable credentials and user verification.
- Request no attestation conveyance.
- Permit ES256 only until another algorithm has explicit interoperability and
security evidence.
- Store random challenges and verifier session data only behind opaque,
single-use ceremony tokens.
- Treat clone warnings as audit signals rather than automatic lockout for
synchronized passkeys.
The service uses a random WebAuthn challenge for every ceremony. A sensitive
application operation is bound separately by storing the SHA-256 digest of its
canonical payload with that ceremony. Never substitute an operation hash,
timestamp, UUID, or counter for the random challenge.
## Application flow
1. A local command calls `Bootstrap` or `Recover` and writes the returned
enrollment token once to a newly created mode-`0600` file.
2. A server-rendered enrollment page calls `BeginEnrollment`; the browser uses
`navigator.credentials.create` with the returned `public_key` value.
3. The browser posts the credential and opaque ceremony token to a bounded JSON
endpoint; `FinishRegistration` verifies and stores the public credential.
4. Login uses `BeginLogin`, `navigator.credentials.get`, and `FinishLogin`.
The successful result contains an ordinary opaque `auth` session token.
5. Sensitive operations call `BeginApproval` with a canonical application
payload and `FinishApproval` with those exact same bytes. Any drift fails.
For an existing password-backed account, call `BeginPasswordMigration` only
from an authenticated account session and finish with
`FinishPasswordMigration`. The registration ceremony is bound to that user.
Successful completion stores the passkey, removes the password credential,
clears the password-change flag, revokes every session and pending ceremony,
and appends the audit event atomically. The application must clear the current
session cookie and return the user to passkey login after success.
`authhttp.WritePasskeyBegin` and `authhttp.ReadPasskeyFinish` provide bounded
JSON framing only. They do not register routes, authorize requests, serve
JavaScript, or set sessions automatically.
## Recovery and credential lifecycle
Recovery is deliberately host-local and should never be reachable through an
HTTP handler. It revokes all user sessions and pending ceremonies, replaces
prior enrollment tokens, appends a secret-free audit event, and returns one
15-minute token. It does not delete existing passkeys. After enrolling a
replacement, the operator reviews credential labels and removes lost keys with
a fresh passkey-bound removal ceremony. The final passkey cannot be removed
remotely.
Before enabling production mutations, applications should require at least two
independent passkeys and complete a local recovery drill.