Files
gamertan bfe6cfd29e
verify / verify (push) Successful in 3m40s
auth: publish passkey foundations preview
2026-08-21 17:33:00 -04:00

56 lines
2.6 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.
`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.