# OpenID Connect Authorization Code Flow

BankID with biometrics supports
the [OpenID Connect Authorization Code Flow](https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth).

> **Authorizing a payment, card or account needs a pre-registered permission**
>
> If you are authorizing a payment, card or account you need to register this upfront. See [Authorization Code with upfront permissions](https://docs.stoe.no/bankid-with-biometrics/flows/code-flow-for-payments.md) for more details.

## Steps

\[TOC]

## The Authorization Code Flow

```mermaid
sequenceDiagram
    actor u as User
    participant b as App Backend
    participant bid as BankID OIDC

    u ->>+ b: Request login
    b ->>+ bid: /auth/realms/.../.well-known/openid-configuration
    bid -->>- b: OIDC Configuration
    b -->>- u: Redirect to the authorization endpoint
    u ->>+ bid: Initiate the BankID with biometrics authentication flow
    break Authentication failed
        bid -->> u: Redirect to the redirect URI with the error
    end
    bid -->>- u: Redirect to the redirect URI with the authorization code
    u ->>+ b: Invoke callback endpoint
    b ->>+ bid: Token exchange
    bid -->>- b: Access token and ID token
    Note right of b: The App Backend verifies the ID token
    b -->>- u: Store user session and mark the user as logged in
```

## 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`).

## 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. Implement the authorization code flow

To successfully implement the [OIDC Authorization Code Flow](https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth) in your application, follow the steps below:

The flow is specified [RFC 6749 §4.1](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1).

1. Retrieve the authorization endpoint URL dynamically from the OIDC configuration. Avoid hardcoding this URL in your
   application to allow for future changes as described in [step 2](https://docs.stoe.no/bankid-with-biometrics/flows/code.md#2-get-the-bankid-openid-connect-configuration).

2. Generate a random and [non-guessable](https://datatracker.ietf.org/doc/html/rfc6749#section-10.10) string, such as a UUID,
   and store it in a variable called `state`. The value is used to protecting against
   [cross-site request forgery (CSRF)](https://datatracker.ietf.org/doc/html/rfc6749#section-10.12) attacks.
   It will be sent back to your application in the callback.

3. Generate a cryptographically random and [non-guessable](https://datatracker.ietf.org/doc/html/rfc6749#section-10.10) string
   and store it in a variable called `nonce`. This value ensures the integrity of the ID token and mitigates replay attacks.

4. Store the `state` and `nonce` value in your application using your preferred session mechanism, so they can be retrieved later.
   These values should be stored together for proper verification.

5. (Optional, but **HIGHLY** recommended) Generate a cryptographically random and
   [non-guessable](https://datatracker.ietf.org/doc/html/rfc6749#section-10.10) string
   and store it in a variable called `code_verifier`. This value is used to mitigate against authorization code
   interception attacks through the use of [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636).

6. Build the authorization endpoint URI by adding the following parameters:
   - `client_id`: The client ID for your BankID OIDC client.
   - `scope`: Specifies the requested information and resources. In this example we use `openid` and `profile` and get a
     [regular ID token](https://docs.stoe.no/bankid-oidc-provider/core-concepts/id-token.md),
     and the `nnin_altsub` scope to get the Norwegian national identity number.
   - `response_type=code`: Indicates the usage of the Authorization Code Flow.
   - `redirect_uri`: The callback URI on your server where you want to receive the callback from BankID OIDC.
   - `acr_values=urn:bankid:bis`: Specifies the authentication context class reference (ACR) value. This value indicates
     that the user should be authenticated using BankID Substantial/BankID with biometrics.
   - `login_hint=:<nnin>`: Optionally supply the user's national identity number (nnin) to pre-fill the BankID login form.
   - `state`: Use the previously generated `state` value.
   - `nonce`: Use the previously generate `nonce` value.
   - `code_challenge`: Base64-URL encoded value of the SHA-256 hash of your generated value for `code_verifier`.
     Use the formula `code_challenge = BASE64-ENCODE(SHA256(code_verifier))`. See
     [RFC 7636 §4.2](https://datatracker.ietf.org/doc/html/rfc7636#section-4.2) for more details.
   - `code_challenge_method=S256`: Specify the code challenge method as SHA-256.

For more information about the parameters, read [here](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest)

Here is an example of a valid authorization request URI:

```properties
GET https://auth.current.bankid.no/auth/realms/current/precheck/auth
    ?client_id=bioexample-bankid-current
    &scope=openid profile nnin_altsub
    &response_type=code
    &redirect_uri=https://localhost:3000/api/auth/callback/bid
    &acr_values=urn:bankid:bis
    &state=4eJYruvMul0SOs5GGuJTjERgtVIovp7PFZt3Qw4EC94
    &nonce=6ZlR7ik_7YI740Ayt8DmMV75IoNfI2jLflVDfYexMQc
    &code_challenge=rMU4NcLC3_O_tsTZ3gQE1ONoXD6OMZph_2zRuobajhQ
    &code_challenge_method=S256
```

Make sure to set  `acr_values` to `urn:bankid:bis`. BIS is an [identity provider](https://docs.stoe.no/bankid-oidc-provider/core-concepts/identity-providers.md)
and this parameter specifies that the user should be authenticated using BankID with biometrics authentication.

By initializing the authorization code flow with the constructed URI, you will redirect the user to the authorization
endpoint. There, the user will authenticate with BankID with biometrics and consent to the requested scopes. For more information
on available scopes, read [here](https://docs.stoe.no/bankid-oidc-provider/core-concepts/scopes-and-claims.md).

## 4. Handle callback and exchange the auth code for tokens

After the user has authenticated, BankID OIDC will redirect the user back to the callback URI specified
in the `redirect_uri` parameter. See [RFC 6749 §4.1.2](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2) for more information about the authorization response.

```mermaid
graph TD
    classDef error stroke: #ef4444
    
    A[Callback Received] --> B[Verify State]
    B -->|Mismatch| C[Authentication failed]:::error
    B -->|Match| D[Check for error]
    D -->|Present| C
    D -->|Not present| E[Exchange code for token]
    E --> F[Verify the token]
    F -->|Fail| C
    F -->|OK| G[Authentication succeeded]
```

The application's server is responsible for performing the token exchange:

1. Retrieve the `code` parameter from the callback request.

2. Build the token request by including the following parameters:

   - `grant_type=authorization_code`: Indicates the usage of the Authorization Code Flow.
   - `client_id`: The client ID for your BankID OIDC client.
   - `client_secret`: The client secret for your BankID OIDC client.
   - `redirect_uri`: The same callback URI used in the authorization request.
   - `code`: The authorization code received in the callback.
   - `code_verifier`: If PKCE was used in the authorization request, include the `code_verifier` used.

   Obtain the `token_endpoint` from the [BankID OIDC provider configuration](https://docs.stoe.no/bankid-with-biometrics/flows/code.md#2-get-the-bankid-openid-connect-configuration).

3. Send a POST request to the token endpoint with the constructed parameters. The parameters should be encoded as
   `application/x-www-form-urlencoded`. Remember to include the `client_id` and `client_secret` as part of the basic authentication header in the request.
   See [RFC 6749 §4.1.3](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3) and [RFC 6749 §2.3.1](https://datatracker.ietf.org/doc/html/rfc6749#section-2.3)
   for more details on sending the request.

**Example token request:**

```properties
POST https://auth.current.bankid.no/auth/realms/current/protocol/openid-connect/token
    ?grant_type=authorization_code
    &redirect_uri=https://localhost:3000/api/auth/callback/bid
    &code=AUTHORIZATION_CODE
    &code_verifier=code_verifier

Authorization: Basic YmlvZXhhbXBsZS1iYW5raWQtY3VycmVudDpzZWNyZXQ=
```

_The example is formatted for readability._

For more detailed info see the
[BankID OIDC documentation](https://docs.stoe.no/bankid-oidc-provider/api/token.md).

## 5. Validate the ID token

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

- Verify that the `nonce` claim in the received ID token corresponds to the `nonce` value belonging to the same session (i.e. the `nonce` associated with the state).
- Verify that the `acr` claim include `LOA=3` (Level of Assurance 3). This indicating the desired level of trust in the authentication process.
- 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 the [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 OIDC configuration you retrieved in [step 2](https://docs.stoe.no/bankid-with-biometrics/flows/code.md#2-get-the-bankid-openid-connect-configuration). We recommend caching the JWKS for 30 minutes, and also fetching a new JWKS if the `kid` in the ID token header 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.

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).
