# Work with JSON Web Tokens

This short guide shows how to create a [JSON Web Token](https://datatracker.ietf.org/doc/html/rfc7519) when working with our [Open Banking API](/docs/api/open-banking).

JSON Web Tokens (JWTs) are required when authorising [Open Banking consents](/docs/api/open-banking#create-an-account-access-consent).

They are composed of a base64url-encoded [header](#header), [payload](#payload) and cryptographic [signature](#signature), separated by a dot `.`, and as such, are similar to [JSON Web Signatures (JWSs)](/docs/guides/build-banking-apps/tutorials/work-with-json-web-signatures).

## Prerequisites

To work with JWT, you must first prepare:

- A signing key and signing certificate pair
- A JWKs URL at which your JSON Web Key (JWK) is publicly available

  :::tip [Verify your JWK]
  [Verify](/docs/guides/build-banking-apps/register-your-application-using-dcr/get-the-jwks-url#verify-that-your-jwks-url-is-publicly-accessible) that your JWKs URL is publicly accessible, and that the content is properly encoded.
  :::

- A JWT library to cryptographically create a JSON Web Token

To learn how to obtain them for testing purposes, see [Get Started: Prepare your Sandbox environment](/docs/guides/build-banking-apps/get-started/prepare-sandbox-environment).

## Header

The header section of the JWT contains mandatory parameters to validate the signature of payment requests.

You must provide the `kid` parameter of your signing certificate.

| Field                           | Description                                                       |
| ------------------------------- | ----------------------------------------------------------------- |
| `alg`                           | Algorithm used for signature, always `PS256`                      |
| `kid`                           | The `kid` parameter of your signing key / certificate             |

- ![Template]

  This is a template JWT header that you can copy and fill with your own `kid` value:

  ```json
  {
      "alg": "PS256",
      "kid": "<kid parameter of your signing certificate>"
  }
  ```

- ![Example]

  This is a sample JWT header:

  ```json
  {
      "alg": "PS256",
      "kid": "abc123"
  }
  ```

## Payload

In the case of JWT, the payload will contain a JSON object with information about the consent.

| Field           | Description                                                                                                                                   | Required                      |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
| `response_type` | Always set to `code id_token`.                                                                                                                | yes                           |
| `client_id`     | The client ID for your application.                                                                                                           | yes                           |
| `aud`           | The intended audience for the JWT. This must contain `https://oba-auth.revolut.com` or `https://sandbox-oba-auth.revolut.com` for Sandbox.    | yes                           |
| `redirect_uri`  | One of the redirect URIs that you defined when you created the application.                                                                   | yes                           |
| `scope`         | The scope that you are requesting, for example, `accounts` or `payments`.                                                                     | yes                           |
| `state`         | OAuth parameter that lets you restore the state of the session after redirection. If provided, this value is returned in the redirect URI.    | no                            |
| `nbf`           | A Unix timestamp, in seconds, before which the JWT is not valid. This value cannot be more than 60 minutes in the past.                       | yes                           |
| `exp`           | A Unix timestamp, in seconds, after which the JWT is no longer valid. This value must not be more than 60 minutes after the value in `nbf`.   | yes                           |
| `claims`        | In its `id_token.openbanking_intent_id.value` field, it must contain the ID of the consent which will be approved by the user.                | yes                           |

:::note [Unix timestamp recommendations]
In order to accommodate for minor time synchronisation differences, we recommend using an `nbf` value of 5 minutes into the past, and an `exp` value which is 55 minutes after `nbf`.
:::

For demonstration purposes, in this guide we are using the following JSON payload.
In this sample payload, the JWT was only valid on `29 Jan 2025`, between `13:50:53` and `14:45:53` (UTC).

```json
{
  "response_type": "code id_token",
  "client_id": "d363951f-0f70-43e9-80e6-db9b0c578061",
  "redirect_uri": "https://example.com",
  "aud": "https://oba-auth.revolut.com",
  "scope": "payments",
  "state": "86413c89-a69f-449f-97cc-a6ca949338f6",
  "nbf": 1738158653,
  "exp": 1738161953,
  "claims": {
    "id_token": {
      "openbanking_intent_id": {
        "value": "43e56447-4088-4111-a6e8-1eef585645a7"
      }
    }
  }
}
```

## Signature

To compute the signature, you can use any library that is compatible with your environment. 

For example, in JavaScript, a library such as [jsrsasign](https://github.com/kjur/jsrsasign) can be used.

```javascript
private_key = "---abc---";

header = {
  "alg": "PS256",
  "kid": "abc123"
};

payload = {
  "response_type": "code id_token",
  "client_id": "d363951f-0f70-43e9-80e6-db9b0c578061",
  "redirect_uri": "https://example.com",
  "aud": "https://oba-auth.revolut.com",
  "scope": "payments",
  "state": "86413c89-a69f-449f-97cc-a6ca949338f6",
  "nbf": 1738158653,
  "exp": 1738161953,
  "claims": {
    "id_token": {
      "openbanking_intent_id": {
        "value": "43e56447-4088-4111-a6e8-1eef585645a7"
      }
    }
  }
};

sJWT = KJUR.jws.JWS.sign("PS256", header, payload, private_key);
```

## Full JWT

The final JWT for the above header and payload would result in:
`eyJhbGciOiJQUzI1NiIsImtpZCI6ImFiYzEyMyJ9.**eyJyZXNwb25zZV90eXBlIjoiY29kZSBpZF90b2tlbiIsImNsaWVudF9pZCI6ImQzNjM5NTFmLTBmNzAtNDNlOS04MGU2LWRiOWIwYzU3ODA2MSIsInJlZGlyZWN0X3VyaSI6Imh0dHBzOi8vZXhhbXBsZS5jb20iLCJhdWQiOiJodHRwczovL29iYS1hdXRoLnJldm9sdXQuY29tIiwic2NvcGUiOiJwYXltZW50cyIsInN0YXRlIjoiODY0MTNjODktYTY5Zi00NDlmLTk3Y2MtYTZjYTk0OTMzOGY2IiwibmJmIjoxNzM4MTU4NjUzLCJleHAiOjE3MzgxNjE5NTMsImNsYWltcyI6eyJpZF90b2tlbiI6eyJvcGVuYmFua2luZ19pbnRlbnRfaWQiOnsidmFsdWUiOiI0M2U1NjQ0Ny00MDg4LTQxMTEtYTZlOC0xZWVmNTg1NjQ1YTcifX19fQ**.jVM_DvUkBkN5HVtsG1GyEjC--------shNdHoAphHInCxU72eC4UOfzS4rUoBJDw`

The payload section above was bolded to highlight the three sections composing a JWT and facilitate reading.

:::note
For the signature section of the JWT, we have used an arbitrary string.
:::

## What's next
- [Initiate your first payment](/docs/guides/build-banking-apps/tutorials/initiate-your-first-payment)
- Test a [payment consent with Postman](/docs/guides/build-banking-apps/tutorials/test-with-postman/get-a-payment-consent)
- Check out API reference for [Domestic payments](/docs/api/open-banking#create-a-domestic-payment)