# CIBA (OIDC extension)

# About CIBA

OpenID Connect CIBA (Client Initiated Backchannel Authentication (opens new window)) is an extension of the OpenID Connect protocol that allows the client application to initiate the authentication process and receive real-time updates on the authentication status.

# Differences between CIBA and the authorization code flow

The authorization code flow and the CIBA flow are two different authentication flows in the OpenID Connect protocol.

In the authorization code flow, the client application redirects the user to the authorisation server, where the user logs in and provides consent. After successful authentication, the authorisation server redirects the user back to the client application with an authorization code. The client application then exchanges this code for an access token and a refresh token by making a direct request to the authorization server's token endpoint. This flow is primarily designed for web applications and requires user interaction during the entire authentication process.

On the other hand, the CIBA flow introduces a more flexible and asynchronous approach to authentication. In this flow, the client application initiates the authentication process by making a backchannel authentication request to the authorization server. The server responds with a unique authentication reference. The client can then receive real-time updates on the authentication status by polling the token endpoint. This flow is particularly useful in scenarios where the client application may not have a user interface or needs to authenticate the user on a different device, or when it is desirable to create a completely customised login flow with complete control over the UI in a web application or a mobile app (similar to the Authentication REST API headless flow).

# Application flow

A successful CIBA flow will follow the following pattern:

  1. The client makes a call to the backchannel_authentication_endpoint, indicating in the acr_values parameter which ID method is being used and possibly passing other parameters applicable for that ID method.
  2. The client then proceeds to polling the token_endpoint which will answer with authorization_pending until the end-user has completed the authentication, at which point the usual token response is returned, containing id_token and access_token etc.
  3. The client may then optionally use the access_token to call the UserInfo endpoint if needed, or skip this step if the id_token already contains the required information.

More details in the OIDC official documentation

# Setting up an OIDC client for CIBA

In the Dashboard, select open OIDC clients > Add client and set Primary grant type to CIBA, and add the scopes you need.

Ciba OIDC client click-to-zoom

After that, you add a secret to your client and then perform any additional configuration you would like to do.

# ID methods that support CIBA

You can use the buttons below to explore the current list of ID methods that support CIBA.

# Example using Swedish BankID

# The backchannel endpoint

The URL of the backchannel_authentication_endpoint is found in the discovery metadata, so if your domain with Signicat is yourdomain then the metadata can be found at https://yourdomain.signicat.com/auth/open/.well-known/openid-configuration and the backchannel_authentication_endpoint will point to https://yourdomain.signicat.com/auth/open/ciba.

The call to the CIBA endpoint will be a HTTP POST with content-type application/x-www-form-urlencoded, and the call will be authenticated with Basic authentication and the following parameters

  • login_hint = endUserIp:127.0.0.1 flow:QR (127.0.0.1 is obviously an example and must be replaced with the actual public IP address of the end-user.)
  • acr_values = idp:sbid
  • scope = openid profile nin

In terms of HTTP, the request will look like this:

POST /auth/open/connect/ciba HTTP/1.1
Authorization: Basic ZGV2LW...CamdraTd2
Host: yourdomain.signicat.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 100
 
login_hint=endUserIp%3A127.0.0.1%20flow%3AQR&acr_values=idp%3Asbid&scope=openid%20profile%20nin

And the response will be (some headers omitted for brevity):

HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8

{
    "auth_req_id": "5ddc7357-abe0-6940-b4a3-fd6eb3277830",
    "expires_in": 120,
    "interval": 1
}

To obtain the autoStartToken for APP_LAUNCH or the data for the QR code flow, your application must request it from the token endpoint as described in the following section.

# The token endpoint

From this point on, the flow consists of polling the token endpoint. To do that, we perform HTTP POST calls to the token endpoint with the following parameters

  • auth_req_id = 5ddc7357-abe0-6940-b4a3-fd6eb3277830 (The auth_req_id received in the previous response.)
  • grant_type = urn:openid:params:grant-type:ciba

In terms of HTTP, the request will look like this:

POST /auth/open/connect/token HTTP/1.1
Authorization: Basic ZGV2LW...CamdraTd2
Host: yourdomain.signicat.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 101
 
auth_req_id=8c65e7e5-f5cc-1b4c-a3f5-9f81985ccc83&grant_type=urn%3Aopenid%3Aparams%3Agrant-type%3Aciba

And the response will be (some headers omitted for brevity):

HTTP/1.1 400 Bad Request
content-type: application/json; charset=UTF-8
 
{
    "error": "authorization_pending",
    "error_description": "bankid.584e5929-0d9e-4dc7-9890-921f...f252b",
    "error_uri": "https://yourdomain.signicat.com/auth/open/config/errors/authorization_pending"
}
  • In the APP_LAUNCH flow, the error_description contains an autoStartToken. You can use this to construct the app launch URL. The autoStartToken will remain the same for the duration of the session.
  • In the QR code flow, the error_description contains the data you need to generate a QR code for the user to scan.

Animated QR code

Swedish BankID uses an “animated QR code” security feature, which means that a QR code is only valid for a very short time (seconds). This means your application must generate a new QR code for each call to the token endpoint, and to continuously update the QR code displayed to the end-user. In such scenarios, a polling interval of 1-2 seconds is recommended.

Your client should continue to poll the endpoint as long as the response is an HTTP 400 with error equal to authorization_pending. The token endpoint will respond like this:

  • If the user cancels the process, the response will be an HTTP 400 with error access_denied.
  • If the user takes too long to complete the process, the response will be an HTTP 400 with error expired_token.
  • If the user has still not fully completed the authentication, the response will be an HTTP 400 with error authorization_pending.
  • And when the user has successfully completed the process, the response will be an HTTP 200 with the usual token response, including id_token and access_token.

# Code example

The following is a simplistic Python example of a function that will first call the backchannel endpoint and then make one call to the token endpoint, printing out both responses:

def ciba(end_user_ip, flow='QR'):    
    # First the backchannel call
    url = base_url + '/auth/open/connect/ciba'
    headers = {
        'Authorization': 'Basic ZGV2LWNs...amdraTd2',
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    data = {
        'login_hint': 'endUserIp:' + end_user_ip + ' flow:' + flow,
        'acr_values': 'idp:sbid',
        'scope': 'openid profile'
    }
    http_response = requests.post(url, headers=headers, data=data)
    ciba_response = json.loads(http_response.content)
    print(json.dumps(ciba_response, indent=4))

    # And then the token endpoint call
    url = base_url + '/auth/open/connect/token'
    data = {
        'auth_req_id': ciba_response['auth_req_id'],
        'grant_type': 'urn:openid:params:grant-type:ciba'
    }

    http_response = requests.post(url, headers=headers, data=data)
    token_response = json.loads(http_response.content)    
    print(json.dumps(token_response, indent=4))

Running the function produces the following output:

{
    "auth_req_id": "9761e8e8-a38b-2e40-a7bd-730805ef9e70",
    "expires_in": 120,
    "interval": 1
}

{
    "error": "authorization_pending",
    "error_description": "bankid.3a047f6a-5e7b-41...8faf1db5e892",
    "error_uri": "https://yourdomain.signicat.com/auth/open/config/errors/authorization_pending"
}

# A note about the flow options

In the example above, we exclusively used the QR flow. The other option is the APP_LAUNCH flow which will – instead of the BankID QR code data, return the autostarttoken from BankID that you can use to launch the BankID app in “same-device” scenarios. For more information, please see https://www.bankid.com/en/utvecklare/guider/teknisk-integrationsguide/programstart (opens new window).

Last updated: 24/11/2023 14:22 UTC