# Private Key JWT

We recommend using `private_key_jwt` as Client Authentication towards BankID.

As per the [standard](https://datatracker.ietf.org/doc/html/rfc7523#section-2.2) you must authenticate your application when calling:

- [Token](https://docs.stoe.no/bankid-oidc-provider/api/token.md) endpoint
- [PAR](https://docs.stoe.no/bankid-oidc-provider/api/par.md) endpoint

or, any other resource APIs (e.g. BankID Signing).

## Requirements

In order to use `private_key_jwt` you need to:

- generate a keypair
- build a JWKS according to spec with your public key(s)
- submit JWKS to BankID support (partners only)

> **Multiple keys per Client**
>
> A Client may have multiple keys associated with it (JWKS). They are each distinguished by the `kid` (Key ID).
>
> This is useful for key rotation.

### Generate JWKS

In order to register your public key you need to provide the key in JWKS format.

This format is represented as an array of [JWK](https://datatracker.ietf.org/doc/html/rfc7517)s under the `keys` field.

An example JWKS may look like this:

```json
{
  "keys":[
    {
      "kty":"EC",
      "crv":"P-256",
      "alg":"ES256",
      "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
      "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
      "use":"sig",
      "kid":"fpy9BfdmvVRubt5VN5Ct263YO5dpMi37nd1OKcJIzOQ"
    },
    {
      "kty":"RSA",
      "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx
4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMs
tn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2
QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbI
SD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqb
w0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
      "e":"AQAB",
      "alg":"RS256",
      "use":"sig",
      "kid":"-b1ua3CUopwJCcLjCGslrpJsLSAFiDVGKK2yLehXMaE"
    }
  ]
}
```

The requirements for the JWK are:

| Claim | Description                                                                      | Example                                                                                                    |
| ----- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `kty` | key type                                                                         | RSA, EC etc.                                                                                               |
| `alg` | algorithm                                                                        | ES256, RS256 etc. See `token_endpoint_auth_signing_alg_values_supported` for list of supported algorithms. |
| `use` | Usage. Must be `sig`                                                             | sig                                                                                                        |
| `kid` | Key ID. This you can generate as you wish, but it is important for key rotation. | RKylPNVyCN8wIO5L55LnIwV7IPh0sErQQzn--CD1xk8                                                                |

Then depending on key type you may have the actual key data encoded in fields, for example:

- `n` and `e` for RSA
- `crv`, `x` and `y` for EC

> **KID generation**
>
> One way to generate the KID could be the Base64Url encoded SHA-256 digest of the public key.

### Send us the JWKS

After building the JWKS, you can send it to us via a BankID Support case, or use the Stø Portal (depending on access and availability), if you are a partner with us.

> **We do not support URL to JWKS endpoint. You need to send us the JWKS for the Client.**

## Usage

### Build JWT assertion claim

When you are going to call the [Token](https://docs.stoe.no/bankid-oidc-provider/api/token.md) endpoint (or any other protected resource), you need to:

- build a [JWT](https://datatracker.ietf.org/doc/html/rfc7519) according to the table below.
- sign JWT using a private key.

The requirements for the JWT are:

| Claim | Required | Description                                                                                                                        |
| ----- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `aud` | yes      | The endpoint to call. E.g. token endpoint. E.g. <https://auth.current.bankid.no/auth/realms/current/protocol/openid-connect/token> |
| `sub` | yes      | The subject of this token, which is **your Client ID**. E.g. `some-client-bankid-current`                                          |
| `iss` | yes      | The issuer of this token, which is **your Client ID**. Same as subject. E.g `some-client-bankid-current`                           |
| `jti` | yes      | A unique token identifier. Cannot be used several times. E.g. `a2a39ffb-4899-43be-9938-560653778cd2`                               |
| `exp` | yes      | The token expiration time in seconds since Epoch (UNIX timestamp). E.g. `1759835872`                                               |
| `iat` | no       | When the token was issued in seconds since Epoch (UNIX timestamp). E.g. `1759815872`                                               |

Other **optional** claims are `x5u`, `x5c`, `x5t` / `x5t#S256` if you have a certificate issued for the keys.

#### Example decoded JWT with header and signature

```json
{
  "typ": "JWT",
  "kid": "-b1ua3CUopwJCcLjCGslrpJsLSAFiDVGKK2yLehXMaE",
  "alg": "RS256"
}
.
{
  "aud" : "https://auth.current.bankid.no/auth/realms/current/protocol/openid-connect/token",
  "iss" : "some-client-bankid-current",
  "sub" : "some-client-bankid-current",
  "jti" : "a3a2fc6e-29e3-4b4d-9284-615982c213c4",
  "exp" : "1759835872",
  "iat" : "1759815872"
}
.
9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGj...
```

> **Always add the `kid` of the key used in the JWT header.**

### Add client assertion parameters to request

| Parameter             | Description                                                                                                              |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| client_assertion      | The client assertion is the signed JWT in Base64Url encoding.                                                            |
| client_assertion_type | The type of client_assertion. This value is always url encoded `urn:ietf:params:oauth:client-assertion-type:jwt-bearer`. |

#### Example Token Request with `private_key_jwt`

```http
POST /auth/realms/current/protocol/openid-connect/token HTTP/1.1
Host: auth.current.bankid.no
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=Gw30fMKJBHkcOBSde5awLrMm4ahvgCNM2cFSTUOUflY
&redirect_uri=https://example.org/callback
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
```

### Key rotation

We support multiple keys per Client (JWKS) which makes it trivial to rotate the keys when needed.

If you want to add a new key for the Client, just contact BankID support (or your BankID Partner) with the new key as part of the JWKS. Then update it again to remove the old key after minimum 24 hours.

If you need to revoke a key, just contact BankID support (or your BankID Partner) with the revoked key gone, and new key added as part of the JWKS. Mark the ticket as high priority and add REVOKE to the subject.
