CIBA
CIBA is an authentication flow where the client can initiate an authentication request for a user without the user being present at the client device. The authentication happens on a separate device (e.g., mobile phone) via a backchannel.
Overview
- Spec: OpenID Connect CIBA Flow Core 1.0
- Status: Fully Implemented
- Endpoint:
POST /bc-authorize - Delivery Modes: Poll, Ping, Push
Why Use CIBA?
Use Cases
- IoT Devices: Authenticate users on devices without input capabilities
- Call Centers: Agent initiates authentication for customer on their mobile device
- Banking: Transaction approval via mobile app while using web application
- Smart TVs: User authenticates on their phone instead of typing on TV remote
Comparison with Device Flow
| Feature | CIBA | Device Flow |
|---|---|---|
| User Identification | Client provides user hint | User enters code |
| Initiation | Client-initiated | User-initiated |
| User Discovery | Server knows the user | User self-identifies |
| Best For | Known user, backchannel | Unknown user, self-service |
How CIBA Works
Flow Overview
- Client initiates request: Sends
POST /bc-authorizewithlogin_hint - Server sends notification: Notifies user on their device (push, SMS, email)
- User approves: User approves the request on their device
- Client polls for tokens: Client polls
/tokenendpoint (poll mode) - Server returns tokens: After user approval, returns tokens
Token Delivery Modes
1. Poll Mode (Default)
Client polls the token endpoint with auth_req_id:
POST /tokengrant_type=urn:openid:params:grant-type:ciba&auth_req_id=1c266114-a1be-4252-8ad1-04986c5b9ac1&client_id=my_client_id&client_secret=my_client_secret2. Ping Mode
Server sends HTTP POST notification when user approves, then client fetches tokens:
POST https://client.example.com/ciba/notifyContent-Type: application/json
{ "auth_req_id": "1c266114-a1be-4252-8ad1-04986c5b9ac1"}3. Push Mode
Server sends tokens directly in the notification:
POST https://client.example.com/ciba/notifyContent-Type: application/json
{ "auth_req_id": "1c266114-a1be-4252-8ad1-04986c5b9ac1", "access_token": "eyJhbGc...", "token_type": "Bearer", "expires_in": 3600, "id_token": "eyJhbGc..."}API Reference
Backchannel Authentication Endpoint
POST /bc-authorize
Request
POST /bc-authorize HTTP/1.1Content-Type: application/x-www-form-urlencoded
scope=openid+profile+email&client_id=my_client_id&client_secret=my_client_secret&binding_message=Transaction+ID:+12345Request Parameters
| Parameter | Required | Description |
|---|---|---|
scope | Yes | OAuth scopes (must include openid) |
client_id | Yes | Client identifier |
login_hint | One of* | User identifier (email, phone, username) |
login_hint_token | One of* | JWT containing login hint |
id_token_hint | One of* | Previously issued ID token |
binding_message | No | Human-readable message (max 140 chars) |
user_code | No | User verification code |
requested_expiry | No | Request expiry in seconds |
client_notification_token | Cond. | Required for ping/push modes |
*One of login_hint, login_hint_token, or id_token_hint is required
Response
{ "auth_req_id": "1c266114-a1be-4252-8ad1-04986c5b9ac1", "expires_in": 300, "interval": 5}Token Endpoint (Poll Mode)
POST /token
POST /token HTTP/1.1Content-Type: application/x-www-form-urlencoded
grant_type=urn:openid:params:grant-type:ciba&client_id=my_client_id&client_secret=my_client_secret&auth_req_id=1c266114-a1be-4252-8ad1-04986c5b9ac1Response States
Pending:
{ "error": "authorization_pending", "error_description": "User has not yet authorized the authentication request"}Slow Down:
{ "error": "slow_down", "error_description": "You are polling too frequently. Please slow down."}Denied:
{ "error": "access_denied", "error_description": "User denied the authentication request"}Success:
{ "access_token": "eyJhbGc...", "token_type": "Bearer", "expires_in": 3600, "id_token": "eyJhbGc...", "refresh_token": "eyJhbGc...", "scope": "openid profile email"}Login Hint Formats
The login_hint parameter supports multiple formats:
Phone (E.164)
login_hint=+14155552671login_hint=tel:+14155552671Subject Identifier
login_hint=sub:user123Username
login_hint=johndoeBinding Message
The binding_message is displayed to the user during approval:
binding_message=Sign in to Banking Appbinding_message=Transaction ID: 12345binding_message=Approve payment of $100.00Constraints:
- Maximum 140 characters
- Unicode letters, numbers, punctuation, and spaces allowed
- Displayed prominently in user approval UI
Usage Example
// 1. Initiate CIBA requestconst cibaResponse = await fetch('https://auth.example.com/bc-authorize', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ scope: 'openid profile email', client_id: 'my_client_id', client_secret: 'my_client_secret', binding_message: 'Sign in to My App', }),});
const { auth_req_id, interval } = await cibaResponse.json();
// 2. Poll for tokenslet tokens;while (!tokens) { await new Promise(resolve => setTimeout(resolve, interval * 1000));
const tokenResponse = await fetch('https://auth.example.com/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'urn:openid:params:grant-type:ciba', client_id: 'my_client_id', client_secret: 'my_client_secret', auth_req_id, }), });
if (tokenResponse.ok) { tokens = await tokenResponse.json(); }}
console.log('Authenticated:', tokens);Security Considerations
Login Hint Validation
- Validate login hints are properly formatted
- Verify user exists before initiating notification
- Rate limit per login hint to prevent spam
Binding Message Injection
- Sanitize binding messages to prevent phishing
- Display raw text only, no HTML or scripts
- Limit length to prevent UI issues
Replay Protection
auth_req_idis single-use only- Requests expire after configured timeout (default 5 minutes)
- Token issuance is atomic and tracked
Client Authentication
- Always require client authentication for CIBA
- Use client_secret or mutual TLS
- Validate client is authorized for CIBA grant type
Rate Limiting
- bc-authorize: 10 requests per minute per client
- Token endpoint (polling): Respects
intervalparameter - Notification delivery: 5 notifications per minute per user
Client Registration
Register a client with CIBA support:
{ "client_name": "My CIBA App", "grant_types": ["urn:openid:params:grant-type:ciba"], "backchannel_token_delivery_mode": "poll", "backchannel_client_notification_endpoint": "https://app.example.com/ciba/notify", "backchannel_user_code_parameter": true}Discovery Metadata
CIBA support is advertised in the discovery document:
{ "backchannel_authentication_endpoint": "https://auth.example.com/bc-authorize", "backchannel_token_delivery_modes_supported": ["poll", "ping", "push"], "backchannel_user_code_parameter_supported": true, "grant_types_supported": [ "authorization_code", "refresh_token", "urn:openid:params:grant-type:ciba" ]}Error Codes
| Error | HTTP Status | Description |
|---|---|---|
invalid_request | 400 | Missing required parameter or invalid format |
unauthorized_client | 401 | Client not registered for CIBA |
expired_token | 400 | Auth request expired before approval |
authorization_pending | 400 | User hasn’t approved yet |
slow_down | 400 | Client polling too frequently |
access_denied | 403 | User denied the request |