> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fexco.com/llms.txt
> Use this file to discover all available pages before exploring further.

# JWT authentication (Rate Service Connect API)

> Build and sign a JSON Web Token (JWT) to authenticate every Rate Service Connect API request.

Every Rate Service Connect API request must include a JSON Web Token(JWT) signed with your private key
and embedding your Fexco-issued client certificate. You construct a new
JWT for every request, it is not reused like an OAuth access token.

## Before you start

Make sure you have the following before proceeding:

* **Client certificate**: issued by Fexco after you submit a CSR. See [Set up a client certificate](https://docs.fexco.com/dcc/api-reference/authentication/client-certificate.md).
* **`openssl`**: required to generate a JWT from the command line. Pre-installed on macOS and most Linux distributions. On Windows, install it via [OpenSSL for Windows](https://slproweb.com/products/Win32OpenSSL.html) or use Windows Subsystem for Linux (WSL).

## Component of a JWT

A JWT consists of three Base64URL-encoded parts:

* The header
* The payload
* A signature joined by dots.

```
JWT = base64url(Header) . base64url(Payload) . base64url(Signature)
```

In production, use a JWT library for your language (such as `jsonwebtoken`
for Node.js, `PyJWT` for Python, or `java-jwt` for Java) to handle
signing and assembly.

### JWT header

The header identifies the signing algorithm and embeds your client
certificate so Fexco can verify the token.

```json theme={null}
{
  "alg": "RS256",
  "typ": "JWT",
  "version": "v1",
  "kid": "0bfde993-90d4-4244-b1d4-104544298385",
  "x5c": [
    "MIIDqTCCApGgAwIBAgIJAKNsmL7QbNMzMA0GCSqGSIb3DQEBCwUAMGsx..."
  ]
}
```

| Field | Description |
| :- | :- |
| `alg` | Signing algorithm. Always `RS256` (RSA with SHA-256). |
| `typ` | Token type. Always `JWT`. |
| `version` | API version identifier. |
| `kid` | Key identifier for the signing certificate. Optional. |
| `x5c` | X.509 certificate chain as an array of Base64-encoded DER certificates. The first entry is the Fexco-issued end-entity certificate (the `.pem` file returned to you after submitting your CSR). Subsequent entries are intermediate CA certificates. Do not include the root CA certificate. |

### JWT payload

The payload carries request metadata and a digest of the request body
that Fexco uses to detect tampering in transit.

```json theme={null}
{
  "iss": "client-system-identifier",
  "sub": "api-client-id",
  "aud": "dcc-dhs-api",
  "iat": 1745839403,
  "exp": 1745839703,
  "digest": "tP7hDajF4f6q0ysBQCHgef5K/PBq8iMASvlEARp8tl8=",
  "digestAlgorithm": "SHA-256"
}
```

| Field | Description |
| :- | :- |
| `iss` | Issuer. Identifies the client system making the request. Optional — used for troubleshooting. |
| `sub` | Subject. The API client identifier. Optional — used for troubleshooting. |
| `aud` | Audience. Always `dcc-dhs-api`. |
| `iat` | Issued-at time as a Unix timestamp. |
| `exp` | Expiration time as a Unix timestamp. Must be no more than 5 minutes after `iat`. |
| `digest` | Base64-encoded SHA-256 hash of the JSON request body: `Base64(SHA256(request_body))`. Ensures the request body has not been altered in transit. |
| `digestAlgorithm` | Hash algorithm used for the body digest. Always `SHA-256`. |

### JWT signature

The signature is computed by signing the Base64URL-encoded header and payload, joined by a dot, with your private key using RS256 (RSA with SHA-256). This is the same
private key you generated when creating your certificate signing request (CSR). See [Set up a client certificate](https://docs.fexco.com/dcc/api-reference/authentication/client-certificate.md).

```
Signature = RS256(base64url(Header) + "." + base64url(Payload), private_key)
```

Fexco verifies the signature using the public key in the certificate you
embedded in the `x5c` header field. If the signature does not match, the
request is rejected.

### Build the JWT generation script

The following steps walk through each part of the script. A complete,
ready-to-run version is available in the [Bash example](#bash-example) below.

**1. Define a base64url helper**

JWT encoding requires Base64URL. Standard Base64 with `+` and `/` replaced
and padding stripped. Define this helper once at the top of your script.

```bash theme={null}
base64url() {
  openssl base64 -e -A | tr '+/' '-_' | tr -d '='
}
```

**2. Encode the client certificate**

The certificate must be Base64-encoded before it can be embedded in the JWT
header. Replace `YOUR_ACQUIRER_ID` with your three-letter acquirer ID.

```bash theme={null}
cert_base64=$(openssl base64 -e -A -in client_YOUR_ACQUIRER_ID.pem)
```

**3. Build the JWT header**

Assemble the header JSON with the signing algorithm, token type, API
version, key ID, and encoded certificate.

```bash theme={null}
jwt_header='{"alg":"RS256","typ":"JWT","version":"v1","kid":"YOUR_KEY_ID","x5c":["'"$cert_base64"'"]}'
```

**4. Hash the request body**

Compute a SHA-256 digest of the exact JSON body you will send. This lets
Fexco detect any tampering in transit.

> [!NOTE]
>
> The `request_body` defined here is used only to compute the digest — the
> script does not make an API call. You must pass the exact same JSON as the
> request body in your API call. If the two differ, the digest will not match
> and the request will be rejected.

```bash theme={null}
request_body='{"transactionType":"PURCHASE","paymentDetails":{"bin":"409019"},"orderDetails":{"amount":"100.00","currency":"EUR"}}'
digest=$(echo -n "$request_body" | openssl dgst -sha256 -binary | openssl base64 -e -A)
```

**5. Build the JWT payload**

Assemble the payload with your client identifiers, timestamps, and the body
digest. The token expires 5 minutes after issue.

```bash theme={null}
iat=$(date +%s)
exp=$((iat + 300))
jwt_payload='{"iss":"YOUR_CLIENT_SYSTEM","sub":"YOUR_CLIENT_ID","aud":"dcc-dhs-api","iat":'"$iat"',"exp":'"$exp"',"digest":"'"$digest"'","digestAlgorithm":"SHA-256"}'
```

**6. Base64url-encode the header and payload**

Encode both parts so they are safe to use in the token string.

```bash theme={null}
header_b64=$(echo -n "$jwt_header" | base64url)
payload_b64=$(echo -n "$jwt_payload" | base64url)
```

**7. Sign the token**

Sign `header.payload` with your private key using RS256. Replace
`YOUR_ACQUIRER_ID` with your acquirer ID.

```bash theme={null}
signature=$(echo -n "${header_b64}.${payload_b64}" | openssl dgst -sha256 -sign client_YOUR_ACQUIRER_ID.key | base64url)
```

**8. Assemble the JWT**

Join the three parts with dots. The resulting `$jwt_token` is ready to use
as a Bearer token.

```bash theme={null}
jwt_token="${header_b64}.${payload_b64}.${signature}"
```

## Bash example

The script below generates a complete JWT and is useful for manual testing and
debugging. Before running it, replace the following placeholders with your own values:

* `YOUR_ACQUIRER_ID`: your three-letter acquirer ID (for example, `TST`)
* `YOUR_KEY_ID`: the key identifier for your signing certificate, provided by Fexco
* `YOUR_CLIENT_SYSTEM`: a label identifying your system, used for troubleshooting
* `YOUR_CLIENT_ID`: your API client identifier, provided by Fexco

Also replace the sample `request_body` value with the exact JSON body you will
send in your API request, the script uses it to compute the digest embedded in
the JWT. The script requires `openssl`, which is pre-installed on macOS and most
Linux distributions.

```bash theme={null}
# Define a base64url encoding function
base64url() {
  openssl base64 -e -A | tr '+/' '-_' | tr -d '='
}

# Encode the client certificate
cert_base64=$(openssl base64 -e -A -in client_YOUR_ACQUIRER_ID.pem)

# Build the JWT header
jwt_header='{"alg":"RS256","typ":"JWT","version":"v1","kid":"YOUR_KEY_ID","x5c":["'"$cert_base64"'"]}'

# Compute the SHA-256 digest of the request body
request_body='{"transactionType":"PURCHASE","paymentDetails":{"bin":"409019"},"orderDetails":{"amount":"100.00","currency":"EUR"}}'
digest=$(echo -n "$request_body" | openssl dgst -sha256 -binary | openssl base64 -e -A)

# Build the JWT payload
iat=$(date +%s)
exp=$((iat + 300))
jwt_payload='{"iss":"YOUR_CLIENT_SYSTEM","sub":"YOUR_CLIENT_ID","aud":"dcc-dhs-api","iat":'"$iat"',"exp":'"$exp"',"digest":"'"$digest"'","digestAlgorithm":"SHA-256"}'

# Base64url-encode the header and payload
header_b64=$(echo -n "$jwt_header" | base64url)
payload_b64=$(echo -n "$jwt_payload" | base64url)

# Sign the token
signature=$(echo -n "${header_b64}.${payload_b64}" | openssl dgst -sha256 -sign client_YOUR_ACQUIRER_ID.key | base64url)

# Assemble the complete JWT
jwt_token="${header_b64}.${payload_b64}.${signature}"
```

> [!TIP]
>
> To verify your token is correctly structured, paste it into
> [jwt.io](https://jwt.io). The debugger decodes the header and payload into
> readable JSON so you can confirm all required fields are present before making
> a request. Note that jwt.io cannot verify the RS256 signature without your
> public key.

## Make an authenticated request

Once you have a JWT, pass it as a `Bearer` token in the `Authorization` header
of your API request. For a full walkthrough of request parameters and response
handling, see [Request a DCC offer (Rate Service Connect)](https://docs.fexco.com/dcc/guides/rate-service/dcc-offer-rate-service-connect.md).
For the complete list of request headers and endpoint details, see the
[API reference](https://docs.fexco.com/dcc/api-reference/rest-ng/post-dcc-offers).

## Security best practices

* **Keep token expiry short.** Set `exp` to no more than 5 minutes after `iat`. Each request uses a new JWT, so long-lived tokens are unnecessary and increase risk.
* **Always include the request body digest.** The `digest` claim ensures
  the request body has not been tampered with in transit.
* **Use HTTPS.** All communication with the Rate Service Connect API must use HTTPS
  with [TLS 1.2 or higher](https://docs.fexco.com/dcc/guides/getting-started/security-setup.md#transport-encryption-tls).

## Next steps

* [Request a DCC offer (Rate Service Connect)](https://docs.fexco.com/dcc/guides/rate-service/dcc-offer-rate-service-connect.md):
  make your first authenticated rate request using the Rate Service Connect API.
* [Error handling](https://docs.fexco.com/dcc/api-reference/error-handling.md): error response
  format and authentication-related error codes.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.