Skip to content

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.