Zum Hauptinhalt springen

Challenge lifecycle

Every authentication goes through the same short state machine. Understanding it saves you time when a challenge behaves unexpectedly.

States​

Three terminal states - approved, denied, expired - are sticky. Once a challenge lands there, subsequent polls return the same value until the sweeper eventually removes it.

Timings​

ParameterValueNotes
Challenge TTL5 minTime between create and expired.
JWT lifetime5 minTime between mint and exp.
JWKS cache in SDK5 minTime between refetches.
Clock skew (HMAC + JWT)±5 min (HMAC) / ±30 s (JWT)On the RP side.

The 5-minute challenge TTL is the reason poll_challenge defaults its timeout to 4 min 30 s - so the client returns ChallengeTimeoutError before the server would return expired, giving you cleaner control-flow.

Rotation isn't a problem​

Rotating an application's HMAC secret does not invalidate in-flight challenges. A challenge is bound to its id + origin + user; nothing about it depends on the RP's secret. The RP fleet may briefly see INVALID_SIGNATURE while a new secret propagates, but existing challenges keep their state.

Idempotency and safety​

  • Creating a challenge is not idempotent. Two calls in a row send two pushes to the user's phone. Debounce on the RP side if your users click Login twice.
  • Polling status is idempotent and cheap. It's a single indexed lookup on the in-memory challenge store, not a DB read.
  • Approving is single-shot. A user can't approve the same challenge twice - the first tap flips it to approved and the server ignores subsequent taps.

Failure modes and what they mean​

SymptomCause
404 on create_challengeThe user isn't registered for this application's tenant.
403 UNREGISTERED_ORIGIN on create_challengeOrigin wasn't previously POSTed to /v1/origins.
Status stays pending forever, then expiredPush didn't reach the user's phone (Internet? DND?).
INVALID_SIGNATURE on create_challenge onlySecret drift between your app and ztxbas. Rotate + redeploy.
TIMESTAMP_EXPIRED on every callRP host clock is off. Run NTP.