[Design Discussion] Client-Initiated Backchannel Authentication (CIBA) #2740
Replies: 6 comments 2 replies
-
Revised Implementation Approach — What We Can Actually Build TodayAfter investigating the current ThunderID architecture, here is a revised and concrete implementation approach for CIBA. Key Architecture FindingThe original proposal suggested a flow-based grant executor pattern ( But — the flow engine is not tied to HTTP or browser context. It operates on Implementation: Hybrid ApproachCIBA will be implemented as a procedural grant handler that internally triggers a system flow for the notification + user authentication journey. 1. Procedural Layer (Grant Handler + Endpoints)
2. Internal System Flow (Notification + Async User Auth)The notification dispatch and user authentication will run as an internal flow — a standard ThunderID flow graph initiated programmatically by the backchannel endpoint, not by a browser. This is an internal system flow for now, not exposed in the flow builder UI. A new flow type Flow graph: How it executes: 3. Architecture DiagramWhat Needs to Be Built
What This Approach Preserves
Scope for Initial Implementation
|
Beta Was this translation helpful? Give feedback.
-
|
Our original architecture keeps flow engine/ execution separate from the protocol specific implementations. There will be pre and post layers which handles the protocol specific details. For example current OAuth integration is like this. When we support other protocols, ex: SAML, we can plug in the SAML pre-post layers without changing the internal flow execution layer. Adding a new CIBA executor or CIBA flow type brings the protocol specific implementation to the flow engine and execution. This doesn't align with the original design. CIBA is about decoupling the authentication and consumption into two devices right? The core authentication mechanism is applicable for native flow execution as well. CIBA is protocol specific which is only for OAuth authentication. Aligning with our original design, we should implement native executors that can be used in any flow which are capable of above authentication mechanism, but not following the protocol specific pattern. Then these can be engaged for a CIBA flow as well. However this can be flagged be reinventing the wheel where we would implement a CIBA similar pattern using native flow execution. Hence better to have a discussion on this and come to a conclusion.
IMO this is not the correct approach. CIBA should fall under the existing AUTHENTICATION flow type |
Beta Was this translation helpful? Give feedback.
-
|
I agree with the direction in the follow-up: keep CIBA protocol handling outside the flow engine as much as possible. The clean boundary seems to be: That keeps the flow engine protocol-neutral while still reusing it for the user interaction. I would avoid Instead, the reusable flow capability could be something like: Those can be used by CIBA now, but later also by non-OAuth flows: admin approval, device pairing, transaction approval, step-up auth, etc. For and store: {
"client_id": "...",
"user_id": "...",
"scope": "...",
"binding_message_hash": "...",
"flow_execution_id": "...",
"status": "pending",
"expires_at": "...",
"last_poll_at": "...",
"poll_interval": 5
}For defaults: So the implementation I would favor is: procedural CIBA grant/endpoints, existing |
Beta Was this translation helpful? Give feedback.
-
Converged approach - Notification Dispatch as a reusable utility flow (procedurally orchestrated for v1)Following an internal discussion (with @ThaminduDilshan), we've refined the design to address the concern about protocol specifics leaking into the flow engine. Here's where we landed. 1. No CIBA flow type - Notification Dispatch becomes a standalone utility flowDropping
This keeps the engine protocol-neutral and respects the pre-processing → flow execution → post-processing boundary @yudin-s described: protocol logic stays in the CIBA pre/post layers; the flow engine only runs protocol-neutral notification + authentication. 2. How the utility flow is invoked - two optionsThere are two ways to wire "dispatch the notification, then authenticate":
3. Why we're going with Option B for v1Option A depends on the CALL node (#2639), which is still an open design, the flow-type-category question, the per-frame context split, and the phase-2 same-type/sub-flow fork are all unresolved. Building CIBA on top of it would couple CIBA's delivery to a second in-flight design and put it on #2639's critical path. Option B avoids that:
4. Execution walkthrough (v1, poll mode)The two flows start at two different times: notification-dispatch at the backchannel request, the authentication flow only when the user actually engages: Key points:
5. Future - when the CALL node landsOnce #2639 is agreed and implemented, we migrate CIBA from Option B to Option A:
So CIBA ships now without blocking on #2639, and adopts the cleaner in-graph composition the moment that mechanism is available. |
Beta Was this translation helpful? Give feedback.
-
CIBA Phase 1 Implementation — What Was DoneRelated PR: The goal of Phase 1 was to bring full end-to-end support for the CIBA (Client-Initiated Backchannel Authentication) poll mode into ThunderID so that a client application can initiate an authentication request from the consumption device, and then poll for the result. The backchannel authentication endpoint ( On the user's device, they follow the notification link into the Gate UI, authenticate with their password, go through a consent step that shows which permissions are being requested, and complete the flow. The flow assertion is then posted back to a shared callback endpoint ( The client polls the token endpoint using the The notification channel is fully driven by the flow definition. The default flows use email and SMS, and both support a A dedicated The The |
Beta Was this translation helpful? Give feedback.
-
CIBA Phase 2 —
|
| Condition | CIBA error |
|---|---|
id_token_hint is malformed (not a valid JWT) |
invalid_request |
iss does not match Thunder's issuer |
invalid_request |
aud does not contain the requesting client_id |
invalid_request |
sub missing or empty |
invalid_request |
| Signature invalid (key found but verification fails) | invalid_request |
| Signature key not found (rotated key) | Signature check skipped; proceed |
exp expired |
Ignored |
Entity not found for the resolved sub (flow error) |
unknown_user_id |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Related Feature Issue
#2739
Problem Summary
ThunderID needs to support CIBA (OpenID Connect Client-Initiated Backchannel Authentication Flow - Core 1.0) to enable decoupled authentication scenarios. CIBA allows a client to initiate authentication of a user without browser redirects — the AS notifies the user on a separate device/channel, the user authenticates there, and the client retrieves tokens via polling. This is essential for call centers, POS, IoT, ambient agent authentication, and emerging agent-to-agent workflows.
High-Level Approach
/oauth2/bc-authorize) that accepts client-initiated auth requestsClientValidator → IdentifyingExecutor (Grant Identifier) → CIBAGrantExecutor— this is new to ThunderID; existing grants are procedural and will adopt this pattern in future/tokenwithauth_req_id, AS respondsauthorization_pendinguntil user completesauth_req_idlinks the client-side request to the user-side authentication, with TTL-based expirygrant_type=urn:openid:params:grant-type:cibaArchitecture Overview
Flow Pattern (CIBA-specific — existing grants do not follow this pattern yet):
Components:
login_hint/login_hint_token/id_token_hint/oauth2/bc-authorize) — new endpoint accepting CIBA requestsgrant_type=urn:openid:params:grant-type:ciba, read async state byauth_req_idauth_req_id→ pending/approved/denied state with TTLAsync State Lifecycle:
Security Considerations
auth_req_identropy — must be cryptographically random, unguessable, and single-userequested_expiry, capped by AS policy)intervalparameter; returnslow_downerror if client polls too fastImpacted Areas
backend/internal/oauth2/) — new backchannel endpoint, token endpoint extension for CIBA grant typebackend/internal/flow/) — async flow execution support (pause/resume on external signal)backend/internal/oauth2/dcr/) — CIBA client metadata fields/.well-known/openid-configuration) — advertise CIBA support, poll mode, supported auth methodsAlternatives Considered
Alternative 1: Implement CIBA as standalone procedural code (like existing grants)
Alternative 2: Wait for "OAuth grants as pluggable flows" before implementing CIBA
Alternative 3: Support all three modes (poll, ping, push) from the start
Questions for Community Input
requested_expiryhave an AS-enforced maximum, and if so, what's a sensible default? (CIBA spec suggests 120-300 seconds)Beta Was this translation helpful? Give feedback.
All reactions