CIBA
Client-Initiated Backchannel Authentication — decoupled, no-browser sign-in where SenseCrypt emails the user a sign-in QR, they approve on their phone, and your backend collects the tokens.
SenseCrypt supports CIBA (OpenID Connect Client-Initiated Backchannel Authentication). CIBA is a decoupled flow: your backend starts authentication with only a hint about the user — there is no browser and no redirect. The user approves on their phone, and your backend collects the tokens afterward.
This fits scenarios like call-center verification, kiosk or point-of-sale confirmation, or any server-initiated step-up where the person is not at a browser you control.
How it fits SenseCrypt
When your backend calls the backchannel endpoint, SenseCrypt emails the sign-in QR to the resolved user. The user scans it with the Authenticator app and completes the same on-device face ceremony as an interactive sign-in. Receiving the emailed QR is itself the email-possession proof, and the face match is the biometric factor — so a CIBA sign-in carries the same assurance as a browser one.
The overall shape is: start the request (get an auth_req_id), then collect the tokens (poll, ping, or push).
The full decoupled flow — start on the consumption device, approve on the phone, collect the tokens — looks like this:
1. Start the request
Your backend authenticates as a CIBA-enabled client and POSTs to the backchannel authentication endpoint (resolve it from discovery as backchannel_authentication_endpoint; conventionally /v1/idp/oidc/bc-authorize):
POST {backchannel_authentication_endpoint}
scope=openid ...
login_hint={user_email}
binding_message={short_message} # optional, ≤ 512 chars, shown to the user
client_notification_token={token} # required for ping/push
# + your client authenticationRules for the request:
- The
scopemust includeopenid, and every scope must be one your client is allowed to use. - Provide exactly one user hint:
login_hint(an email),login_hint_token, orid_token_hint. Supplying zero or more than one is an error. binding_message(optional) is a short string shown to the user so they can confirm the request matches what they initiated. It is capped at 512 characters.client_notification_tokenis required forpingandpushdelivery (it is how SenseCrypt calls you back); it is ignored forpoll.requested_expiry(optional) can shorten/lengthen the request TTL, clamped to the server's bounds.user_codeis not used. SenseCrypt is passwordless, sobackchannel_user_code_parameter_supportedisfalseand any strayuser_codeis ignored.- You may request an API audience with
resource/audience, exactly as in the code flow (see Authorization).
On success you receive:
{
"auth_req_id": "…",
"expires_in": 300,
"interval": 5
}auth_req_id— the opaque handle you present when polling. It is single-use.expires_in— seconds until the request expires (default 300 s for an enrolled-user sign-in; longer when the flow also involves self-signup).interval— minimum seconds between polls (default 5 s; omitted forpush).
See the bc-authorize API reference.
2. Collect the tokens
Poll (and ping) delivery
Poll the token endpoint with the CIBA grant, no faster than interval:
POST {token_endpoint}
grant_type=urn:openid:params:grant-type:ciba
auth_req_id={auth_req_id}
# + your client authenticationHandle these responses:
| Response | Meaning | What to do |
|---|---|---|
200 with tokens | The user approved. | Done — the auth_req_id is now spent. |
400 authorization_pending | Not approved yet. | Keep polling at interval. |
400 slow_down | You polled too fast. | Back off, then continue polling. |
400 expired_token | The request expired. | Start a new request. |
400 access_denied | The user declined (or the ceremony failed). | Stop. |
The success body is a standard token response (access_token, id_token, token_type: "Bearer", expires_in, scope, and refresh_token if offline_access was granted). All the token semantics apply.
With ping delivery, SenseCrypt notifies your client_notification_token endpoint when the request is ready, and you then collect the tokens at the token endpoint exactly as with poll.
Push delivery
With push delivery, SenseCrypt delivers the tokens directly to your notification endpoint out-of-band. Push clients do not poll the token endpoint — a push client that tries to poll is rejected.
Delivery modes
SenseCrypt advertises poll, ping, and push in its discovery metadata (backchannel_token_delivery_modes_supported: ["poll", "ping", "push"]). Your client is registered for exactly one mode, and that registration — not a request parameter — decides the delivery behavior.
| Mode | You poll? | Callback | client_notification_token |
|---|---|---|---|
poll | Yes, at interval | none | not used |
ping | Yes, after the callback | notify-only | required |
push | No | tokens delivered out-of-band | required |
Edge cases and gotchas
- Respect
interval. Polling faster earns aslow_down; keep to the advertised cadence (default 5 s). auth_req_idis single-use and time-boxed. After a successful200, or afterexpires_inelapses, it's spent — start a fresh request rather than retrying.- Exactly one hint. Zero or two of
login_hint/login_hint_token/id_token_hintis a request error. - Ping/push need a notification token. Without
client_notification_token, a ping/push request is rejected. binding_messageis bounded. Over 512 characters is refused (it would otherwise bloat the dispatched email); keep it short and human-readable.- CIBA requests are rate-limited per client and per client+target-email QR dispatch, so a runaway loop can't spray sign-in emails at a user.
- The user must be enrolled. The hint has to resolve to a user who can complete the face ceremony; a non-enrollable target fails closed like any other gate.
Set it up
See the CIBA backchannel integration guide for a concrete request-and-poll example, and OIDC & OAuth 2.0 for client authentication.
Related
- Tokens & sessions — the tokens you collect and how to validate them.
- Authorization — the access gate and requesting an API audience.
SCIM 2.0
Provision users and groups into a SenseCrypt tenant from your identity provider using the SCIM 2.0 protocol, with pending-shell enrollment for biometrics, filtering, bulk, and eager revocation.
Multi-tenancy & issuers
Each SenseCrypt tenant is its own OIDC issuer and SAML IdP on its own hostname, with its own signing keys and a hard isolation boundary. Resolve every endpoint from discovery.