ZitadelDocs

Verify a factor proof

Submits a proof (credential, code, assertion) to verify a factor challenge. The proof format depends on the challenge method. For example: - `password` method: { password: "…" } - `totp` method: { totp: { code: "123456" } } - `passkey` method: { passkey: { assertion: "…" } } - `recovery_code` method: { recovery_code: "…" } On successful verification, the factor is written to the auth attempt. The attempt moves to the next pending challenge or completes if all required factors are verified.

POST
/auth_attempts/{attempt_id}/challenges/{challenge_id}/verify

Submits a proof (credential, code, assertion) to verify a factor challenge.

The proof format depends on the challenge method. For example:

  • password method: { password: "…" }
  • totp method: { totp: { code: "123456" } }
  • passkey method: { passkey: { assertion: "…" } }
  • recovery_code method: { recovery_code: "…" }

On successful verification, the factor is written to the auth attempt. The attempt moves to the next pending challenge or completes if all required factors are verified.

Authorization

oauth2 auth_attempt.write
headerAuthorizationBearer <token>

Scope: auth_attempt.write

Path Parameters

attempt_id*string

The unique identifier of the authentication attempt

Match^att_[a-zA-Z0-9_-]+$
challenge_id*string

The unique identifier of this challenge.

Match^ch_[a-zA-Z0-9_-]+$

Header Parameters

Idempotency-Key?string

Optional retry key for safe retries of the verification request. If a request with the same key has already been processed, the original response is returned. This prevents duplicate processing of the same proof in case of network issues or client retries.

Request Body

application/json
  1. body

Proof for identifier method.

The login name is resolved against the designated identifier (x-identifier) of every user schema in the project, and must identify exactly one user; none, or users of more than one schema, rejects the proof. The caller does not name the property: which property identifies users is the project's decision, made in its user schemas (ADR 058 §5).

login_name*string

The value being identified, as the user typed it.

Response Body

Proof verified successfully, factor recorded on the auth attempt; it is materialized into a session only when the auth flow is completed

application/json
  1. response

The current state of an authentication attempt.

An attempt is an ephemeral state machine (15-minute TTL) that drives a single authentication round. It accepts factor challenges, verifies proofs, and completes into a handoff token. Clients drive the attempt by:

  1. Issuing challenges: POST /auth_attempts/{attempt_id}/challenges
  2. Verifying proofs: POST /auth_attempts/{attempt_id}/challenges/{challenge_id}/verify
  3. Completing: POST /auth_attempts/{attempt_id}/handoff
attempt_id*attempt-id

Unique identifier for this authentication attempt (dialect-minted prefixed opaque string).

Match^att_[a-zA-Z0-9_-]+$
project_id*project-id
Match^[a-zA-Z0-9_-]+$
state*string

Overall state of the authentication attempt:

  • in_progress: factors are still being collected; continue issuing challenges.
  • completed: all required factors have been verified; the attempt is ready for handoff.
  • failed: the attempt has failed (e.g. too many failed proofs) and cannot be continued.
  • expired: the 15-minute TTL has been exceeded.
Value in"in_progress""completed""failed""expired"
session_id?|

The session being authenticated into. Either newly created or passed in on POST. For step-up (re-auth), this is the existing session_id provided on creation.

user_id?|

The user ID, if known at this point in the flow. Null until an identifier challenge has been verified.

required_factors?array<>

The authentication factor methods that the project policy requires in order to complete this attempt. Computed by the server from the project's auth policy. Clients should use this to determine which challenges still need to be issued.

completed_factors?array<>

The factors that have been successfully verified in this attempt so far. Each item includes method-specific metadata in the payload field.

The flow engine uses verified_at timestamps to enforce time-based policies, such as requiring password verification within the last 5 minutes for step-up auth to access sensitive operations.

A factor appears here once its challenge reaches verified state.

challenges?array<>

All challenges issued within this attempt, in creation order. Use this to track the state of each factor challenge (pending, verified, failed, expired).

expires_at?string

When this attempt expires (15-minute TTL from creation)

Formatdate-time
created_at*string

When this attempt was created

Formatdate-time
curl -X POST 'https://example.com/auth_attempts/string/challenges/string/verify' \  -H 'Content-Type: application/json' \  -d '{  "login_name": "string"}'
{  "attempt_id": "string",  "project_id": "string",  "state": "in_progress",  "session_id": "string",  "user_id": "string",  "required_factors": [    "identifier"  ],  "completed_factors": [    {      "method": "identifier",      "verified_at": "2019-08-24T14:15:22Z",      "payload": {        "method": "identifier",        "user_id": "string"      }    }  ],  "challenges": [    {      "challenge_id": "string",      "method": "identifier",      "state": "pending",      "created_at": "2019-08-24T14:15:22Z",      "expires_at": "2019-08-24T14:15:22Z",      "payload": {}    }  ],  "expires_at": "2019-08-24T14:15:22Z",  "created_at": "2019-08-24T14:15:22Z"}