Table of Contents

Client-initiated backchannel authentication (CIBA), poll mode

A client asks for a user to be authenticated without a browser in the flow at all — a call-centre agent with the customer on the phone, a payment terminal. The server reaches the user on a device it already knows how to reach, shows them the client's binding message, and the client polls the token endpoint until they answer. The client is the demo host's ciba-client; the host's DemoBackChannelAuthentication resolves login_hint as a username and "notifies" the user by logging a link to the /ciba page.

Follows: OpenID Connect CIBA Core 1.0.

Recorded from the demo host (src/Fdyay.IdentityServer.Host) by the CibaFlow test on every CI run. A value in braces, such as {access_token_1}, is one that differs on every run; the same placeholder is the same value wherever it appears. See how these pages are made.

sequenceDiagram
    participant Client
    participant AuthorizationServer as Authorization Server
    participant User
    Client->>AuthorizationServer: POST /connect/ciba
    AuthorizationServer-->>Client: 200 OK
    Client->>AuthorizationServer: POST /connect/token
    AuthorizationServer-->>Client: 400 Bad Request, authorization_pending
    Note over User,AuthorizationServer: The user, notified on their own device, signs in,<br/>sees the binding message and approves (the host's<br/>/ciba page#59; not shown).
    Client->>AuthorizationServer: POST /connect/token
    AuthorizationServer-->>Client: 200 OK

1. The client starts the request

Client-authenticated, naming the user with login_hint and saying what is being approved in binding_message. The answer is an auth_req_id to poll with, and how often to poll.

Client → Authorization Server

POST /connect/ciba HTTP/1.1
Authorization: Basic Y2liYS1jbGllbnQ6c2VjcmV0
Content-Type: application/x-www-form-urlencoded

scope=openid+profile+api
&login_hint=alice
&binding_message=Pay+42.00+EUR+to+Demo+Shop

Authorization Server → Client

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Cache-Control: no-store, no-cache, max-age=0
Pragma: no-cache

{
  "auth_req_id": "{auth_req_id_1}",
  "expires_in": 300,
  "interval": 5
}

2. The client polls before the user has answered

It is told to keep waiting.

Client → Authorization Server

POST /connect/token HTTP/1.1
Authorization: Basic Y2liYS1jbGllbnQ6c2VjcmV0
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aopenid%3Aparams%3Agrant-type%3Aciba
&auth_req_id={auth_req_id_1}

Authorization Server → Client

HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=UTF-8
Cache-Control: no-store, no-cache, max-age=0
Pragma: no-cache

{
  "error": "authorization_pending"
}

The user, notified on their own device, signs in, sees the binding message and approves (the host's /ciba page; not shown).

3. The client polls again

The user has approved, so the tokens are released.

Client → Authorization Server

POST /connect/token HTTP/1.1
Authorization: Basic Y2liYS1jbGllbnQ6c2VjcmV0
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aopenid%3Aparams%3Agrant-type%3Aciba
&auth_req_id={auth_req_id_1}

Authorization Server → Client

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Cache-Control: no-store, no-cache, max-age=0
Pragma: no-cache

{
  "access_token": "{access_token_1}",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "openid profile api",
  "id_token": "{id_token_1}"
}

{access_token_1} decoded — the header, then the payload; the signature is not shown:

{
  "alg": "RS256",
  "kid": "{key_id_1}",
  "typ": "at+jwt"
}
{
  "aud": [
    "api-resource"
  ],
  "iss": "http://localhost",
  "iat": {iat},
  "nbf": {iat},
  "exp": {iat + 3600},
  "jti": "{jti_1}",
  "client_id": "ciba-client",
  "scope": "api openid profile",
  "sub": "alice",
  "auth_time": {auth_time},
  "idp": "local",
  "amr": [
    "pwd"
  ],
  "sid": "{session_id_1}"
}

{id_token_1} decoded — the header, then the payload; the signature is not shown:

{
  "alg": "RS256",
  "kid": "{key_id_1}",
  "typ": "JWT"
}
{
  "aud": [
    "ciba-client"
  ],
  "iss": "http://localhost",
  "iat": {iat},
  "nbf": {iat},
  "exp": {iat + 300},
  "jti": "{jti_2}",
  "azp": "ciba-client",
  "sub": "alice",
  "auth_time": {auth_time},
  "idp": "local",
  "amr": [
    "pwd"
  ],
  "name": "Alice Smith",
  "sid": "{session_id_1}",
  "at_hash": "{at_hash_1}"
}