Default endpoint contract¶
The Ktor adapter defines four POST endpoints under /webauthn. It is intentionally thin: route payloads adapt the default JSON contract to the JVM ceremony services, which perform protocol validation.
| Endpoint | Purpose | Important input |
|---|---|---|
/webauthn/registration/start |
Create registration options and ceremony state | User identity, RP ID, origin, optional resident-key policy |
/webauthn/registration/finish |
Validate and store a new credential | Raw registration credential response |
/webauthn/authentication/start |
Create assertion options and state | RP ID, origin, optional user name |
/webauthn/authentication/finish |
Validate assertion and credential ownership | Raw authentication credential response |
Identified and discoverable authentication¶
When userName is present at authentication start, the service creates an identified-account flow and populates allowCredentials. When it is absent or null, the service creates a discoverable flow with an empty allow-list and resolves the account from the returned credential.
Do not add a client-supplied userHandle to the start request as an account authority. Credential ownership and any returned authenticator user handle must be resolved and checked server-side.
Signed-data boundary¶
Finish requests carry a byte-preserving raw credential response. The service decodes the exact clientDataJSON bytes from that response, then validates the embedded ceremony type, challenge, and origin. This prevents a transport layer from presenting one value for policy validation while the authenticator signed another.
When to replace the default contract¶
Use a custom client/backend adapter when your API requires different envelopes, authorization, error shapes, endpoint paths, or continuation tokens. Preserve these invariants:
- options are generated by the authoritative backend;
- continuation state is opaque to the client and bound to the ceremony;
- the finish endpoint accepts the raw platform response without lossy conversion;
- signed client data is decoded once from that raw response;
- the state is bounded, one-time, and consumed atomically;
- product session creation happens only after successful validation.
See custom codecs and transport for the library seams.