# OpenID Connect Client-Initiated Backchannel Authentication (CIBA)

BankID with biometrics supports [CIBA](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html) for certain use cases. Get in touch to discuss your use case and enable
CIBA for your client.

> **All permissions needs to be registered upfront**
>
> When authenticating with CIBA the permission needs to be registered upfront. This is explained in the steps below.

## Steps

\[TOC]

## 1. Register a client with BankID OIDC

If you don't have a client registered with [BankID OIDC](https://docs.stoe.no/bankid-oidc-provider/introduction.md)
yet, you'll need to register one. Follow the instructions
[here](https://docs.stoe.no/bankid-oidc-provider/resources/provisioning.md) to register your
application and obtain the necessary [client credentials](https://tools.ietf.org/html/rfc6749#section-1.3.4) (`client_id` and `client_secret`).
You will need two clients, one for the test environment and one for the production environment.

Ensure that your client has the `permissions/client` and `permissions/ciba` scopes available.

For testing the CIBA flow in the test environment, you will need to register a test user and get access to the BankID preprod app.
Detailed instructions on how to do this can be found in the [testing document](https://docs.stoe.no/bankid-with-biometrics/testing.md).

## 2. Get the BankID OpenID Connect Configuration

To ensure seamless integration with BankID OIDC and avoid hardcoding specific endpoints in you application, follow these
steps to dynamically fetch the [OpenID Connect configuration](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig):

1. Determine the appropriate configuration URL based on the environment:

   | Environment                       | Url                                                                                   |
   | --------------------------------- | ------------------------------------------------------------------------------------- |
   | Production                        | <https://auth.bankid.no/auth/realms/prod/.well-known/openid-configuration>            |
   | Current (public test environment) | <https://auth.current.bankid.no/auth/realms/current/.well-known/openid-configuration> |

2. Send an HTTP GET request to the respective configuration URL using your preferred programming language or API tool.

3. Capture the response which will contain a JSON document with the configuration. By dynamically fetching the OIDC configuration, your application remains flexible,
   allowing for future changes or updates to the endpoints without requiring modifications to your code.

4. To enhance the reliability and performance of your integration, we strongly advise implementing response caching for the OIDC configuration.

> **Example response from Current (relevant fields only)**

```json
{
  ...
  "token_endpoint": "https://auth.current.bankid.no/auth/realms/current/protocol/openid-connect/token",
  ...
}
```

_This is a sample response. The actual response may differ._

## 3. Get an access token from BankID OIDC

To obtain an access token from OIDC, follow these steps:

1. Retrieve the token endpoint URL `token_endpoint` dynamically from the OIDC configuration.

2. Call the token endpoint URL with your client credentials.

   You can find more detailed information about acquiring access tokens in the [BankID OIDC documentation](https://docs.stoe.no/bankid-oidc-provider/api/token.md).

3. In the scope field, include the following: `permissions/client`, and `permissions/ciba`. This field specifies the scope of access granted to the token.

4. To enhance the reliability and performance of your integration, we strongly advise implementing response caching for the access token. For example based on the expires_in property in the token response.

Here's an example of a valid request in the `current` environment:

```http
POST /auth/realms/current/protocol/openid-connect/token HTTP/1.1
Host: auth.current.bankid.no
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <Base64-encoded client id:client secret>

grant_type=client_credentials&scope=permissions%2Fclient%20permissions%2Fciba
```

Make sure to replace the `<Base64-encoded client id:client secret>` placeholder in the Authorization header with your actual client credentials.

## 4. Get the BankID with biometrics OIDC configuration

To ensure seamless integration and avoid hardcoding specific endpoints in you application, follow these
steps to dynamically fetch the [OpenID Connect configuration](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig):

1. Determine the appropriate configuration URL based on the environment:

   | Environment                       | Url                                                                        |
   | --------------------------------- | -------------------------------------------------------------------------- |
   | Production                        | <https://app.bankid.no/.well-known/openid-configuration>                   |
   | Current (public test environment) | <https://current.aletheia-test.idtech.no/.well-known/openid-configuration> |

2. Send an HTTP GET request to the respective configuration URL using your preferred programming language or API tool.

3. Capture the response which will contain a JSON document with the configuration. By dynamically fetching the OIDC configuration, your application remains flexible,
   allowing for future changes or updates to the endpoints without requiring modifications to your code.

4. To enhance the reliability and performance of your integration, we strongly advise implementing response caching for
   the OIDC configuration.

## 5. Check if user is able to authenticate with CIBA

To be able to authenticate with CIBA, the user must be registered with a sufficiently up-to-date BankID App to support app-required flows. To check whether the user meets these requirements, make a request to the [user-exists API](https://docs.stoe.no/bankid-with-biometrics/apis/permissions-api.md).

## 6. Register the permission upfront

To register a permission, follow these steps:

1. Construct a _permission statement_. See the [permissions API](https://docs.stoe.no/bankid-with-biometrics/apis/permissions-api.md) for detailed information of how to construct the different permissions.
   Encode the permission statement using URL safe Base64 encoding. Below is an example of a permission statement for a payment:

   ```javascript
   const permissionStatement = {
       "nonce": "dW5pcXVlIHZhbHVl", // Must be unique
       "id": "YmFza2V0IGlk", // Your reference
       "payments": [
           {
               "paymentId": "cGF5bWVudCBpZA", // Your reference
               "amount": "123.45",
               "currency": "NOK",
               "creditorName": "Scrooge McDuck"
           }
       ]
   }

   // Example implementation of a b64 URL Safe Encoder
   function b64URLSafeEncode(stringData) {
       return btoa(stringData)
           .replace(/\+/g, "-")
           .replace(/\//g, "_")
           .replace(/=/g, "");
   }

   b64URLSafeEncode(
       JSON.stringify(
           permissionStatement
       )
   );
   // -> "eyJub25jZSI6ImRXNXBjWFZsSUhaaGJIVmwiLCJpZCI6IlltRnphMlYwSUdsayIsInBheW1lbnRzIjpbeyJwYXltZW50SWQiOiJjR0Y1YldWdWRDQnBaQSIsImFtb3VudCI6IjEyMy40NSIsImN1cnJlbmN5IjoiTk9LIiwiY3JlZGl0b3JOYW1lIjoiU2Nyb29nZSBNY0R1Y2sifV19"
   ```

   > **Note**
   >
   > - The `nonce` must be a unique value for each permission you register.
   > - Use `id` (basket ID) and `paymentId` as references to the payment(s) in your systems. These will not be shown to the end user.
   > - The `amount` is a decimal amount represented as a string and must use the dot as the decimal separator.

2. Send a POST request to the [permissions endpoint](https://docs.stoe.no/bankid-with-biometrics/apis/permissions-api.md), providing the access token received in the first step as a bearer token.
   Include the permission, which is the URL safe Base64 encoding of the permission statement object.

   ```http
   POST /permissions/v1/ HTTP/1.1
   Host: api.current.aletheia-test.idtech.no
   Content-Type: application/json
   Authorization: Bearer <access token from step 1>

   {
     "type": "payment.v1",
     "iat": 1617091752,
     "exp": 1617092652,
     "permission": < URL safe b64 encoded permission statement from step 2a >,
     "intents": [
       "ciba"
     ],
     "loginHint": [
       {
         "scheme": "nnin",
         "value": "<nnin_as_string>"
       }
     ]
   }
   ```

   > **Note**
   >
   > - `iat` and `exp` are timestamps in seconds since the UNIX epoch. `iat` can be at most 1 minute
   >   into the past. `exp` sets the maximum time by which the permission must be granted and should
   >   normally be set just a few minutes into the future.
   > - When using `intents : ["ciba"]` it is mandatory to include `loginHint` in the request.

3. On a successful request, you will receive a response body containing a permission ID, a permission token and a binding message:

   ```json
   {
     "id": "1.BY.MEKrA-00GVtmvWOCDYQko_fSg93LO5n2rBnlj-X4MQg.BiDEl_CgfakqahcXjLrFet_GGpEkR_W8D-RK4hORBO0",
     "permissionToken": "eyJhbGciOiJIUzI1NiIsImtpZCI6IjB5Z3hDX3UzQlpPWXlKZ0Y3eDJ0eWtQaTRCTml2cG9KMDBmYjFzTGliUVUiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2MTcwOTU3NDQsImlhdCI6MTYxNzA5MjE0NCwicyI6IjA6LTEjOTIzIiwicCI6IkJpREVsX0NnZmFrcWFoY1hqTHJGZXRfR0dwRWtSX1c4RC1SSzRoT1JCTzAiLCJuYmYiOjE2MTcwOTIxNDh9.39GWvNjNMSKKhKOF4t4HcDnZGNJmLmPT3f612z1VKko",
     "bindingMessage":"Farlig frakk"
   }
   ```

## 7. Initiate backchannel request

Make a POST request to the [bc-authorize](https://docs.stoe.no/bankid-with-biometrics/apis/oidc-api.md) endpoint, including the the `permissionToken` from step 3 as the `login_hint_token` to the bc-authorize endpoint.

Expand the below list below on which scopes to include (space-separated).

> **Scopes**
>
> The `openid` scope is always required.
>
> ## `profile`: Gets the user's profile information
>
> Returned Claims:
>
> - `name`
> - `given_name`
> - `family_name`
> - `birthdate`
> - `updated_at`
>
> ## `sub_nnin`: Gets the user's national identity number (NNIN).
>
> _The `sub_nnin` scope is only available for clients with explicit access!_
>
> Returned claims:
>
> - `sub_nnin`
>
> ## `sub_bankid`: Gets the the user's BankID PID.
>
> Returned claims:
>
> - `sub_bankid`

Passing access token as Bearer token:

```http
POST /oidc/v1/bc-authorize HTTP/1.1
Host: oidc.current.aletheia-test.idtech.no
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer <access token from client credentials exchange>

login_hint_token=<permission token from step 3>
&binding_message=<binding message from step 3>
&scope=openid%20sub_bankid

```

Passing access token as `client_assertion` form value:

```http
POST /oidc/v1/bc-authorize HTTP/1.1
Host: oidc.current.aletheia-test.idtech.no
Content-Type: application/x-www-form-urlencoded

client_assertion=<access token from client credentials exchange>
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&login_hint_token=<permission token from step 3>
&binding_message=<binding message from step 3>
&scope=openid%20sub_bankid

```

Note: Replace string with the actual `login_hint_token` and provide appropriate values for other fields.

After calling the bc-authorize endpoint, you will receive the following response:

```json
{
  "auth_req_id": "stringstring",
  "expires_in": 0,
  "interval": 0
}
```

The bc-authorize endpoint returns `auth_req_id` which you will use when posting a request to the token endpoint afterwards.

## 8. Poll for the result

1. Make a POST request to the [token](https://docs.stoe.no/bankid-with-biometrics/apis/oidc-api.md) endpoint using the `CibaTokenRequest` schema.
   You may call call this endpoint every `interval` seconds, as specified in the response from the previous step.

   Passing access token as Bearer token:

   ```http
   POST /oidc/v1/token HTTP/1.1
   Host: oidc.current.aletheia-test.idtech.no
   Content-Type: application/x-www-form-urlencoded
   Authorization: Bearer <access token from client credentials exchange>

   grant_type=urn%3Aopenid%3Aparams%3Agrant-type%3Aciba
   &auth_req_id=<auth_req_id from step 7>

   ```

   Passing access token as `client_assertion` form value:

   ```http
   POST /oidc/v1/token HTTP/1.1
   Host: oidc.current.aletheia-test.idtech.no
   Content-Type: application/x-www-form-urlencoded

   client_assertion=<access token from client credentials exchange>
   &client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
   &grant_type=urn%3Aopenid%3Aparams%3Agrant-type%3Aciba
   &auth_req_id=<auth_req_id from step 7>

   ```

   Note: Replace string with the actual values, including the auth_req_id.

2. While the user haven't completed the authentication yet the token endpoint will respond with a 400 `authorization_pending` error.

   ```json
   {
     "error": "authorization_pending",
     "error_description": "User has not yet granted authorization"
   }
   ```

3. Keep polling this endpoint until you get a different error with a 400 response, or a 200 response with the `id_token` in the response body.

   Here's an example of a successful response:

   ```json
   {
     "token_type": "string",
     "expires_in": 0,
     "id_token": "string"
   }
   ```

   And here's an example of an errored response:

   ```json
   {
     "error": "access_denied",
     "error_description": "Authorization was denied by the user"
   }
   ```

## 9. Validate the ID token

To ensure the authenticity and integrity of the received ID token, you must perform the following verification:

- Verify that the algorithm used to sign the ID token is found in the BankID with biometrics OIDC configuration under `id_token_signing_alg_values_supported`.
- Verify that the token is signed by a key from our [JWKS (JSON Web Key Set)](https://datatracker.ietf.org/doc/html/rfc7517)
  Document. The URI for the JWKS document can be found under the `jwks_uri` key in the BankID with biometrics OIDC configuration
  document you retrieved in [step 4](https://docs.stoe.no/bankid-with-biometrics/flows/ciba.md#4-get-the-bankid-with-biometrics-oidc-configuration). We recommend caching the JWKS for 30 minutes, and also fetching a new JWKS if the `kid` in the ID token does not match any key in the cached JWKS.

We recommend verifying these additional properties:

- `iss` (issuer) - The issuer of the token must match the issuer in the BankID with biometrics OIDC configuration.
- `aud` (audience) - The audience of the token must match your client ID.
- `exp` (expiration time) - The token must not be expired.
- `iat` (issued at) - The token must not be issued in the future.
- `acr` (authentication context class reference) is only relevant if you've asked for [forced step up](https://docs.stoe.no/bankid-with-biometrics/advanced-topics/forcing-step-up.md). If relevant must be verified to be the expected value.

Further information on how to validate the ID token can be found in the [OpenID Connect Core specification](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation).

## 10. Get the signed permission (optional)

To get the signed permission you can send in a GET request to the `permissionId/grant` endpoint. The `permissionId` you use is the one that you
got from your [permissions](https://docs.stoe.no/bankid-with-biometrics/apis/permissions-api.md) POST request. Be aware that it can return a 404 error code
(permission not yet granted) but as mentioned in the api specification you can set up a polling request on the `permissionId/grant`
endpoint until your initial permission has been signed.

If your permission has been successfully granted you will receive this response:

```json
{
  "gv": 1,
  "pt": "string",
  "type": "string",
  "rpId": "bankid-app",
  "sv": "2020-10-30@deadbeef",
  "iat": 1800000000,
  "iss": "string",
  "nonce": "string",
  "proofKeyId": "string",
  "sub": "nnin:010112345",
  "permissionId": "string",
  "digest": "string"
}
```

The grant is a JWT that can be validated as follows:

- In the BankID with biometrics OIDC configuration (as mentioned [here](https://docs.stoe.no/bankid-with-biometrics/flows/ciba.md#4-get-the-bankid-with-biometrics-oidc-configuration)) get the Grant JWK set from the URI pointed to by the `jwks_uri_grants` property in this document.
- Check that the JWT is signed by one of the keys in the Grant JWK set. You can find a suitable library for performing this verification at <https://jwt.io/libraries>.
- Check that the `nonce` claim inside the token payload is equal to the `nonce` in the permission statement created in step 2.
