Skip to content

Session resumption

Payer API 0.14 adds optional resume_session_id to POST /v1/payer/unlock-challenges. An ordinary unlock still creates a new session. Resumption restores the original session needed by an existing transfer after the native app has cleared its credentials on background lock. It requires IDENTITY_SESSION_RESUMPTION_ENABLED=true; the option defaults off.

The payer API runs only in the local sandbox: the hosted sandbox does not serve it yet.

Send the enrolled device_id, the current six-digit Futa passcode, the original session UUID and a preparation Idempotency-Key. A resumption challenge has device_challenge.binding.purpose: RESUME_SESSION and echoes resume_session_id. Before signing, verify the exact requested target, enrolled key, payer, challenge ID, purpose, canonical signing bytes and deadline. Its opaque server digest binds the target and passcode. A normal UNLOCK proof cannot be substituted. The target is not checked for existence during preparation; a challenge does not establish that a session can be resumed.

Complete through the existing /v1/payer/unlock-challenges/{id}/complete route with the current passcode, device signature and a stable completion key. Only after fresh device proof and passcode verification does identity check exact original payer/device ownership, revocation and original idle/absolute deadlines. Unknown, foreign, signed-out or expired target sessions are rejected. A revoked caller device is also rejected. Ordinary unlock and resumption share preparation/proof budgets and passcode lockouts; resumption is not a lockout bypass.

A successful response uses the existing PayerSessionIssued shape. Its session_id and protected credential are the original ones, with a fresh DPoP nonce. Resumption creates no session, changes no last-activity date, extends no original deadline and transfers no ownership. The client must validate that the returned session is the requested original and still within its original lifetime before using it. Resumption does not approve, execute or retry a transfer. Original transfer consent and issuer deadlines remain separate and unchanged.

After a lost response, retry the exact completion key/body with its original signature. A confirmed completion can be replayed after service restart only while the original session and replay window remain valid. A different completion key cannot reuse a successfully consumed challenge. Disabling the feature also blocks resumption completion/replay. Unavailable protected storage returns a sanitized error with no credential; do not silently fall back to creating or executing a replacement transfer.

Native apps may retain the safe original session/request references needed to offer recovery, but must continue to clear credentials and pending private challenges on lock. Resuming always requires fresh user/device proof; it is not automatic background unlocking. Actual native wiring is a separate implementation step. SDK users can pass resumeSessionId to preparePayerUnlock, validate the UnlockChallenge, then call completePayerUnlock. Ordinary requests omit the field. Generated types and round trips preserve both forms.