Start a passkey registration

POST /fapi/v1/me/passkeys/registration-options · called from the environment's sign-in host

Opens a WebAuthn registration for the authenticated user from the account portal and returns the options to pass to navigator.credentials.create. Finish it with POST /fapi/v1/me/passkeys. The registration is valid for 5 minutes, can be finished once, and only by the session that started it. Requires the authenticated user's __Host-idp_session cookie; if the cookie is missing, expired, or revoked, returns 401 unauthenticated. A first-party app on another origin can instead send its front API access token (Authorization: DPoP <token> with a DPoP proof; see Front API access token).

The session must have been verified in the last 10 minutes for a passkey, or in the last 5 minutes for a security key. If the user already has a second factor, the session must also have been opened with a second factor (urn:idp:acr:mfa): a new passkey cannot raise the assurance of the session that adds it.

Request

Headers:

Header Required Description
Idp-Client yes SDK identifier.
Origin yes Must be exactly https://<sign-in host>.
Content-Type yes application/json.

Body:

Field Type Required Description
role string yes passkey: a credential that signs in on its own (needs passkey sign-in to be on in the environment). security_key: a credential used only as a second factor (needs the security key second factor to be on).

Response

200 OK:

{
  "ceremony_id": "CQAAAAAAAAAAAAAAAAAAAA",
  "options": {"publicKey": {"challenge": "…", "rp": {"id": "example.keenauth.app", "name": "Example"}, "user": {"id": "…", "name": "ada@example.org", "displayName": "ada@example.org"}, "pubKeyCredParams": [], "timeout": 300000, "excludeCredentials": [], "authenticatorSelection": {}, "attestation": "none", "extensions": {"credProps": true}}}
}

Pass options.publicKey to PublicKeyCredential.parseCreationOptionsFromJSON and the result to navigator.credentials.create. excludeCredentials lists the user's credentials for this relying party, so the same authenticator is not registered twice.

Errors

Status Code When
400 malformed_request The body is not valid JSON.
400 unknown_field The body has a field other than role.
401 unauthenticated No valid session cookie.
401 step_up_required The session is too old or a second factor is required; re-verify with the max_age (and if applicable, acr_values) in the WWW-Authenticate header.
403 origin_not_allowed Origin is missing or wrong.
403 no_allowed_method The environment does not allow this role.
409 credential_limit_reached The user already has the maximum number of passkeys and security keys.
413 payload_too_large The body is too large.
415 unsupported_media_type Content-Type is not application/json.
422 validation_failed role is missing or not passkey or security_key.
422 aaguid_not_allowed The environment's authenticator allowlist is invalid; registration is blocked until it is fixed.

Example

curl -X POST https://example.keenauth.app/fapi/v1/me/passkeys/registration-options \
  -H "Idp-Client: my-app/1.0.0 (node/22.0.0)" \
  -H "Origin: https://example.keenauth.app" \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{"role": "passkey"}'

Personal data

This endpoint carries personal data: credentials. Personal data in the KeenAuth APIs describes what is stored, how long it is kept and how it is deleted. Do not send special categories of personal data (health, biometric data, religion, political opinions, sexual orientation) in any field.