# API Basics
source: https://developer.mastercard.com/open-finance-data/documentation/api-basics/index.md

## Authentication {#authentication}

Mastercard uses [FAPI 2.0](https://openid.bitbucket.io/fapi/fapi-security-profile-2_0.html) to authenticate clients and provide access to Mastercard Open Finance APIs. FAPI 2.0 is an API security profile suitable for high-security applications based on the OAuth 2.0 Authorization Framework.

FAPI 2.0 is a security profile built on top of OAuth 2.0. The OpenID Foundation created it for financial services and other industries that need stronger security.

Mastercard implements the "FAPI2SP private key + DPoP" approach, which creates 2 layers of protection: only your application can get access tokens, and only your application can use those tokens.

Here is a simplified view of the authentication flow:
Diagram simple-auth-flow

The process uses the OAuth 2.0 client credentials flow for direct system-to-system communication (no user involved). Your application requests an access token from the Mastercard authorization server by signing a client assertion with your authentication key. You then use that access token to make requests to the API.

In addition to that, FAPI 2.0 binds access tokens to your application using DPoP (Demonstration of Proof-of-Possession). This means a stolen token cannot be used without your DPoP private key.

Before you begin, make sure you are familiar with:

* Mastercard Developers projects and credentials
* The OAuth 2.0 client credentials flow
* JSON Web Tokens (JWT)
* Public key cryptography and digital signatures
* The HTTP protocol

In the next sections, you will learn how FAPI 2.0 adds security layers to OAuth 2.0. You will also learn how to get credentials from Mastercard Developers, how to request access tokens, and how to make authenticated API calls.
Tip: If you prefer to start with code, jump to the [Step-by-Step Implementation](https://developer.mastercard.com/open-finance-data/documentation/api-basics/index.md#step-by-step-implementation).

#### Strong client authentication {#strong-client-authentication}

When your application requests an access token, it authenticates using a client assertion (`private_key_jwt`). You sign this JWT with your private key and send it to prove your identity. An attacker cannot impersonate your application without your private key.

#### Token binding {#token-binding}

Access tokens are bound to your application using DPoP (Demonstration of Proof-of-Possession). Each time you use an access token to call an API, you include a DPoP proof signed with the same private key. Even if someone intercepts your access token, they cannot create valid DPoP proofs without your private key.

#### Stronger algorithms {#stronger-algorithms}

FAPI 2.0 requires modern cryptographic algorithms (ES256 or PS256) that provide better security than older options.

The next sections walk you through each step of the authentication flow.

## Authentication Flow Overview {#authentication-flow-overview}

#### 1. Generate asymmetric cryptography keys {#1-generate-asymmetric-cryptography-keys}

As a pre-requisite, you need to generate an asymmetric cryptography public-private key pair and certificate.

When you authenticate on our authorization server, we want to make sure that only you can obtain access tokens for your clientId. For that we validate that your requests are signed with a private key that only you have.

At the moment we only support RSA keys. We recommend an RSA key of 4096 bits of length and valid for at least one year. In the future, you need to provide a new certificate to us before this expires, or face possible downtime in your applications.

##### Example {#example}

For testing, you can use [OpenSSL](https://openssl-library.org/) to generate these.

Write the following in the terminal:

    openssl req -x509 -sha256 -nodes -newkey rsa:4096 -keyout private.key -days 730 -out public.pem

This creates two files:

* private.key: The private key used for signing the JWT.
* public.pem: The public certificate that can be used to verify the JWT.

Note: **Do not share your Private Key.** Store the Private Key securely and use it to sign your JWT token before requesting an access token from us (find more details in the sections below). You need to share the public certificate with us.

In a production environment, we suggest generating the private key in an HSM or similar, exporting the public key and sharing that with us.

#### 2. Access Token Request {#2-access-token-request}

Then, your application requests an access token using a `private_key_jwt` and a DPoP proof:
Diagram access-token-request

|           Step           |               Action                |                                                                Description                                                                |
|--------------------------|-------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|
| ###### ❶ *** ** * ** *** | Generate DPoP key                   | Your application generates a key pair for DPoP (this is separate from your authentication key)                                            |
| ###### ❷ *** ** * ** *** | Create DPoP proof for token request | Your application creates a DPoP proof JWT, signed with your DPoP key                                                                      |
| ###### ❸ *** ** * ** *** | Create client assertion             | Your application creates a `private_key_jwt`, signed with your authentication key                                                         |
| ###### ❹ *** ** * ** *** | Request access token                | Your application sends the `private_key_jwt` and DPoP proof to the authorization server                                                   |
| ###### ❺ *** ** * ** *** | Create DPoP-bound access token      | The authorization server verifies your client assertion and creates an access token with a `cnf.jkt` claim that binds it to your DPoP key |
| ###### ❻ *** ** * ** *** | Return access token                 | The authorization server returns the access token to your application                                                                     |

#### 3. API Call {#3-api-call}

Finally, your application makes an authenticated request with the access token and a DPoP proof:
Diagram api-call

|           Step           |                Action                 |                                                       Description                                                        |
|--------------------------|---------------------------------------|--------------------------------------------------------------------------------------------------------------------------|
| ###### ❶ *** ** * ** *** | Create DPoP proof for resource access | Your application creates a DPoP proof JWT, signed with your DPoP key, and with an `ath` claim (hash of the access token) |
| ###### ❷ *** ** * ** *** | Make API call                         | Your application sends the access token and DPoP proof to the API                                                        |
| ###### ❸ *** ** * ** *** | Grant access                          | The API validates the access token and DPoP proof before granting access                                                 |
| ###### ❹ *** ** * ** *** | Return API response                   | The API returns the response with a `DPoP-Nonce` header containing the nonce to use for your next DPoP proof             |

## Step-by-Step Implementation {#step-by-step-implementation}

This section provides the exact values to use, JWTs to generate, and HTTP requests to make.

#### 1. Credential Setup {#1-credential-setup}

To access Mastercard APIs, you need the following details from your Mastercard Developers project:

* Your client ID, which identifies your application to Mastercard
* Your key ID, which tells the authorization server which public key to use when verifying your signatures
* Service scopes, which define which APIs and operations your application can access

Diagram credential-setup

For that:

##### ❶ Generate asymmetric cryptography keys {#-generate-asymmetric-cryptography-keys}

**EC key (recommended - ES256):**

```shell
openssl req -x509 -sha256 -nodes -newkey ec -pkeyopt ec_paramgen_curve:P-256 -keyout private.pem -days 730 -out public.pem
```

**RSA key (PS256 or RS256):**

```shell

openssl req -x509 -sha256 -nodes -newkey rsa:4096 -keyout private.pem -days 730 -out public.pem
```

Warning: Mastercard Open Finance's CSR‑based credential generation and OAuth 2.0 implementation both support RSA and EC keys. Choose the key type that best fits your cryptographic and compliance requirements.

This will create two files:

* private.pem: The private key used for signing the JWT.
* public.pem: The public certificate that can be used to verify the JWT.

Warning: **Do not share your Private Key.** Store the Private Key securely and use it to sign your JWT token before requesting an access token from us (find more details in the sections below).  
All credentials shown in this guide (client ID, key ID, authentication and DPoP key pairs) are examples. Replace them with your own.

In a production environment, we suggest generating the private key in an HSM or similar, exporting the public key and sharing that with us.

##### ❷ Share the Public Certificate with us {#-share-the-public-certificate-with-us}

Before you can start using the Mastercard Open Finance authentication, contact your onboarding specialist to add your Public Certificate to our list of trusted certificates.
Note: It is important to note that self-signed certificates are only supported in our Sandbox/MTF environment. When you are ready to move to Production, you will need to acquire a certificate from a supported Certificate Authority (CA). For more information, refer to [Moving to Production](https://stage.developer.mastercard.com/drafts/open-finance-data/staging/documentation/api-basics/authentication/#moving-to-production).

After that, you can call the `oauth2/token` endpoint to obtain an access token, necessary to use the Mastercard Open Finance Data API.

```Sandbox
https://mtf.auth.openfinance.mastercard.eu/oauth2/token
```

#### 2. Access Token Request {#2-access-token-request-1}

##### ❶ Generate DPoP key {#-generate-dpop-key}

Diagram gen-dpop-key

DPoP (Demonstration of Proof-of-Possession) requires a separate key pair from your authentication key. This key proves you own the access token when making API calls. You can use either ES256 (elliptic curve) or PS256 (RSA-PSS) algorithms. We recommend ES256 because it produces smaller signatures.

Example of code generating an ES256 key pair:

```java
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.spec.ECGenParameterSpec;

KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
keyGen.initialize(new ECGenParameterSpec("secp256r1"), new SecureRandom());
KeyPair keyPair = keyGen.generateKeyPair();
```

Corresponding DPoP JWK (JSON Web Key):

```json
{
  "kty": "EC",
  "crv": "P-256",
  "x": "cojbH-aPEUBxt2_uSx5P9UTUkl5X_CFbnncJ35-onlc",
  "y": "89bpkg2grnJC0rzo_I2c_BTLB0sXHBvbmu5jjSwyOv8",
  "d": "QSmrwP5VQoH7PzWJ3ZTKhG7S_Wr1zDP8ko6-z7SLTa8"
}
```

##### ❷ Create DPoP proof for token request {#-create-dpop-proof-for-token-request}

Diagram create-depop-proof

A DPoP proof is a JWT that proves you control the DPoP private key. For the token request, include the HTTP method (`POST`) and the token endpoint URL.
Warning: When you request an access token for the first time, you do not have a nonce yet. Send your DPoP proof without the `nonce` claim. The server will respond with a `use_dpop_nonce` error and provide a nonce in the `DPoP-Nonce` response header. Extract that nonce, include it in your next DPoP proof, and try again.

Decoded DPoP proof JWT header:

```json
{
  "alg": "ES256",
  "typ": "dpop+jwt",
  "jwk": {
    "kty": "EC",
    "crv": "P-256",
    "x": "l4Z_zOEP582iito_-hrGsgmdklKbGwpBfiRzn9zivpQ",
    "y": "63Y3FXat07NN8k65gKg7i-tTJ_04k8I_76dvokhZKJg",
    "kid": "0d486418-e771-47ae-bcfc-14767f303ca9"
  },
  "kid": "0d486418-e771-47ae-bcfc-14767f303ca9"
}
```

Decoded DPoP proof JWT payload:

```json
{
  "jti": "def0211c-877c-4037-a6ff-4fcc8a75cd4e",
  "htm": "POST",
  "htu": "https://mtf.auth.openfinance.mastercard.eu/oauth2/token",
  "iat": 1779276047,
  "exp": 1779276947,
  "nonce": "I0C3_CPO-G4497vHHGqI_ERBHv3nEl-WEPvfvOwSX9s"
}
```

* `jwk`: your public DPoP key
* `jti`: a unique identifier for this proof
* `htm`: the HTTP method (always `POST` for token requests)
* `htu`: the full token endpoint URL (scheme, host, and path only - no query parameters)
* `iat`: the timestamp when this proof was created (in seconds since Unix epoch)
* `exp`: the timestamp when this proof expires (in seconds since Unix epoch)
* `nonce`: the nonce from the previous `DPoP-Nonce` header, do not include `nonce` if you do not have it yet

Signed DPoP proof ([view on jwt.io](https://www.jwt.io/#token=eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Arand0IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiY29qYkgtYVBFVUJ4dDJfdVN4NVA5VVRVa2w1WF9DRmJubmNKMzUtb25sYyIsInkiOiI4OWJwa2cyZ3JuSkMwcnpvX0kyY19CVExCMHNYSEJ2Ym11NWpqU3d5T3Y4In19.eyJqdGkiOiJzWTVkX3J2QnNKS0NOSC15IiwiaHRtIjoiUE9TVCIsImh0dSI6Imh0dHBzOi8vc2FuZGJveC5hcGkubWFzdGVyY2FyZC5jb20vb2F1dGgvdG9rZW4iLCJpYXQiOjE3NjAwMjg2MDUsImV4cCI6MTc2MDAyODcyNSwibm9uY2UiOiI1ZTg5NzI1MTMzMjdmMGIzNjcwYjIxZjMwOGNmNWU4ZSJ9.f9I9RZuKIbCaOREGoqgcG46l4r39kZMZFFF_tFTPS8VlMGLPiOLxop5Fj_KnO578C5GTU9PK9wR_-3HJmGLn0g)):

    eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Arand0IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoibDRaX3pPRVA1ODJpaXRvXy1ockdzZ21ka2xLYkd3cEJmaVJ6bjl6aXZwUSIsInkiOiI2M1kzRlhhdDA3Tk44azY1Z0tnN2ktdFRKXzA0azhJXzc2ZHZva2haS0pnIiwia2lkIjoiMGQ0ODY0MTgtZTc3MS00N2FlLWJjZmMtMTQ3NjdmMzAzY2E5In0sImtpZCI6IjBkNDg2NDE4LWU3NzEtNDdhZS1iY2ZjLTE0NzY3ZjMwM2NhOSJ9.eyJqdGkiOiJkZWYwMjExYy04NzdjLTQwMzctYTZmZi00ZmNjOGE3NWNkNGUiLCJodG0iOiJQT1NUIiwiaHR1IjoiaHR0cHM6Ly9hcGlndy1nYXBpZ3ctZXV3MS1zdGcuaHMtZXV3MS1ucC1pLmZpbmkuY2l0eS9vYXV0aDIvaW50cm9zcGVjdCIsImlhdCI6MTc3OTI3NjA0NywiZXhwIjoxNzc5Mjc2OTQ3LCJub25jZSI6IkkwQzNfQ1BPLUc0NDk3dkhIR3FJX0VSQkh2M25FbC1XRVB2ZnZPd1NYOXMifQ.OrF8KHVBXcv6LDVMytl1ic1-hCH8KGddWgb6up2ygqBShyLws33BA-n-xa1hDnBapN0BExpkIiRznUqO83Agdw

##### ❸ Create client assertion {#-create-client-assertion}

Diagram create-client-assertion

The `private_key_jwt` authenticates your application to the authorization server. You sign this JWT with your authentication key (the one you downloaded from Mastercard Developers).

Decoded `private_key_jwt` header:

```json
{
  "alg": "PS256",
  "typ": "JWT",
  "kid": "535d5aa5-1214-4fae-9d18-19cdf3ba0bee"
}
```

Decoded `private_key_jwt` payload:

```json
{
  "jti": "e45db5f3-bf43-4c94-ae95-dc782247a541",
  "sub": "e317f3ca-bc92-413a-9360-b79690e111eb",
  "iss": "e317f3ca-bc92-413a-9360-b79690e111eb",
  "aud": "https://mtf.auth.openfinance.mastercard.eu",
  "iat": 1779276047,
  "exp": 1779276947
}
```

* `kid`: (Key ID) is a unique identifier for the public certificate that you shared with us. This is the SHA-256 thumbprint of the certificate in base64url encoding
* `jti`: a unique identifier for this assertion
* `iss`: your client ID (issuer)
* `sub`: your client ID (subject)
* `aud`: the authorization server's [issuer identifier value](https://sandbox.api.mastercard.com/.well-known/oauth-authorization-server)
* `iat`: the timestamp when this assertion was created (in seconds since Unix epoch)
* `exp`: the timestamp when this assertion expires (in seconds since Unix epoch)
* `nbf`: the timestamp before which this assertion is not valid (in seconds since Unix epoch)

Sample authentication key:

    ----BEGIN PRIVATE KEY-----
    MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCfcWgmoU8Kgr9K
    fS0HKld1ryZJaCEmRT+jUJa6JH0EM9dJQvJVHVUSWeFGvQW53kW3p2w1oivQ95qr
    +wVj6xlIrfaSamCxr1eCzmmp58oahUTM6YBvIuLJo5FqDHmgR11ogoZrMnwU/flJ
    HtzgmiJJwhInhjR5kJkdnnRaW3xCQ//Wymjdhz3U/X+K23cPaiC+f6NyBlU8fjRw
    ea7Hh5JnFCUCxPTDFkqRoKxGqNfSSg/MRe5hQKKOvROTV1sXEj8V7IBosHgF5Dg8
    tr8d7CdxmdUohn2RIHw09Qv3XL/k/ed6HJvqpqbTyJ+IGYiwqabfZzRnultcceQv
    Uvja0DaJAgMBAAECggEAAcWQlvdNacw8pG8CKFV53dKprgCkd6im1aGAX0anBfwu
    fP+rsjLuflL13COfE+rqoukPHIR2EK8jrpwOOgdDg2l9dzCL7QWKByfUiEENX6CR
    /GXu2a1NUFjLNZ64XkXUvaPPqxLHSrNsyPl5ElzcGy43Je3JmP04mlnnqJZiKdlT
    k0MdLE7r2rvz4bdbbr2nLu1m7P6xgiMcqI8pRWMvxSg6o7RcGdV2aIbFJr8RAtav
    XNqdzRqERM7JLELPxEvZOwuIGfu0UlH6ziGPiAqoFP03zBGNRhnWHls88tJVTTFt
    Xv9sPRjvO79gDfraIGkuM+rMUyf8pceaFOeWgqk5BQKBgQDgrsK2q8wiQiA3d9O2
    0lJoj/cjKDb7Cc14ofEKkfyGnY9MbPwsN4q7gdC8bHFJSx0iYB3TuJi7ZTXtnotc
    OtiBhQ1UGBZbz1YEkO708QuxvqKGDhg0xqs1/HaSv0aAD1xu+9ii6vMuWN/r5uYp
    jhEItisjWW7FHPdBdMxGgcWwPQKBgQC1qrtZdfthTBlwncy61+kUsXJ+kSDRVmp/
    /KWzv0TcYskGqJdfz0eg/llSXLukASyuT0A3lR9Knk3Ep43acHEYvKH0ri00jDjp
    jDCp4v1q1qDoiD+WKT5yW4V8GSQt5bh5IoBaMGomEh2Wl3qEvnqFshEB3ko/8S0s
    LHMKfmWYPQKBgCXKXye4Z6JPevm5Ztu2LnQ12ryIFA9PS8cBffhoK5A8yhBxuEx7
    nxMAt6oplzhbsO/KONoWj2HdmR95bjk6EJZgWaiOUJxqmC42bmq2bGAeD1n63ulp
    jPyMlSkDkQkTDFoCdCKa4AIooqkb8hSFN6o+ca0FatMulwvkaENnqmeFAoGAZky7
    bZhDs7mvxbwGstys00tNhzpgeaGpHWN/SCYYN+ak/0vm2jwWQPGaCQhRXfyPVaUp
    K2OdGF7muiwEdJLHDUAED44ZxLKFxfZ83N4HCKfOOsOm0v9su+cP1x8tQW9QH16v
    WppS3BdtdATfKGs/AZSRgTTVKQa+AlcRfUJKIZ0CgYAL9tG7FCeUQm0nB1SdyNer
    mLCTuiqDevwQgfzi/9d6yLJS1rhq9+X6V9/wBeLBCXo6/amWtWyzL0YcUCsrQcBx
    pl4wLyj/ieuH4iKkg5s8DwXCW6IHh4ObbPYmj/NgYUPNg7DrAaONjQTVEdnVUIz7
    idsyqRGmO11VjVz3z0SVIA==
    -----END PRIVATE KEY-----

    -----BEGIN PUBLIC KEY-----
    MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn3FoJqFPCoK/Sn0tBypX
    da8mSWghJkU/o1CWuiR9BDPXSULyVR1VElnhRr0Fud5Ft6dsNaIr0Peaq/sFY+sZ
    SK32kmpgsa9Xgs5pqefKGoVEzOmAbyLiyaORagx5oEddaIKGazJ8FP35SR7c4Joi
    ScISJ4Y0eZCZHZ50Wlt8QkP/1spo3Yc91P1/itt3D2ogvn+jcgZVPH40cHmux4eS
    ZxQlAsT0wxZKkaCsRqjX0koPzEXuYUCijr0Tk1dbFxI/FeyAaLB4BeQ4PLa/Hewn
    cZnVKIZ9kSB8NPUL91y/5P3nehyb6qam08ifiBmIsKmm32c0Z7pbXHHkL1L42tA2
    iQIDAQAB
    -----END PUBLIC KEY-----

Signed `private_key_jwt` ([view on jwt.io](https://www.jwt.io/#token=eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjMwMjQ0OTUyNWZhZDUzMDk4NzRiMTYyOThmM2NiYWFmMDAwMDAwMDAwMDAwMDAwMCJ9.eyJqdGkiOiI4cmRLek9HX0VoZ3BjcXJ1Iiwic3ViIjoiWnZUMHNrbFBzcXpUTmdLSklpZXg1X3dwcFh6MFRqMndsMzNMVVp0WG1DUUg4ZHJ5IiwiaXNzIjoiWnZUMHNrbFBzcXpUTmdLSklpZXg1X3dwcFh6MFRqMndsMzNMVVp0WG1DUUg4ZHJ5IiwiYXVkIjoiaHR0cHM6Ly9zYW5kYm94LmFwaS5tYXN0ZXJjYXJkLmNvbSIsImlhdCI6MTc2MDA4Nzk1NywiZXhwIjoxNzYwMDg4MDc3LCJuYmYiOjE3NjAwODc4Mzd9.bviW9Cm117ZrzRrH3f5wxMPMnViGBFUcXWecm--hSPkE7wM8Dz7RQrMAHyOR2-HwKGaLxprYWWxkDrDv4IHevxHnNmTimDZTEshgaQC_St3VlZ-FW5Px-cAsNY9t3dASc5rWcGvyukGZ5hxRvlSpI-M_P1Crp3iOosu0xVTK6jEp-QQfcJrsWzMht852E1OHlOIHI_R60UKNv4557oJtPgJqeL_F3sqvrPRUc2yvp2FG7XNKXQewuKlNPy9PSK9Njy6hXtzDV0hUIyHbC8mLmlxwVj2FpVZdEy_Tz6IHaXGr51nFnrOJwCdxPD7Ya4bclTndRAvEFUaCn86IsVq-1A)):

    eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjUzNWQ1YWE1LTEyMTQtNGZhZS05ZDE4LTE5Y2RmM2JhMGJlZSJ9.eyJpc3MiOiJlMzE3ZjNjYS1iYzkyLTQxM2EtOTM2MC1iNzk2OTBlMTExZWIiLCJzdWIiOiJlMzE3ZjNjYS1iYzkyLTQxM2EtOTM2MC1iNzk2OTBlMTExZWIiLCJhdWQiOiJodHRwczovL2FwaWd3LWdhcGlndy1ldXcxLXN0Zy5ocy1ldXcxLW5wLWkuZmluaS5jaXR5L29hdXRoMi9pbnRyb3NwZWN0IiwianRpIjoiZTQ1ZGI1ZjMtYmY0My00Yzk0LWFlOTUtZGM3ODIyNDdhNTQxIiwiaWF0IjoxNzc5Mjc2MDQ3LCJleHAiOjE3NzkyNzY5NDd9.oa-WqZ1L6CmHkSCcbKQe0FbwJoMxvFwIvt5uW5wT5K7c6JRLFb90oDGgbFPanLokNDgIaPIAoEjxK7FQrkTMSUXF99zlaybBMHB2TfLcYVAhEVpbanKnex-ksAn-v5gdmPwIQG_QPnsYUFQMNMhX5jxBiiiBlUwalDkuCPkFpYN3oimYwvFE5LDhQG2nbc39y5-7oSlqXmgtoCM_Xbt4KGyUaixu0PApwZFBOMu36hq83X8MvB0Lm9n5mYNr1tTkK8YuKAY-XNPGtIUSzeW-LxWnZOfaqZm_dfq4OgPw0UOUgzOEejf6AU0-42szNFpO4tTpNHyPFgwLTQ9gveCZ3w

##### ❹ Request access token {#-request-access-token}

Diagram request-access-token

Send a POST request to the token endpoint with your client assertion and DPoP proof. The request uses form-encoded data:

```bash
curl --request POST \
  --url https://mtf.auth.openfinance.mastercard.eu/oauth2/token \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --header 'DPoP: eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3...-n-xa1hDnBapN0BExpkIiRznUqO83Agdw' \
  --header 'X-Correlation-Id: 264e1909-3962-40a8-b0c4-f7fa2de923d2' \
  --data-urlencode 'client_id=e317f3ca-bc92-413a-9360-b79690e111eb' \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode 'scope=consent_create' \
  --data-urlencode 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
  --data-urlencode 'client_assertion=eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCIs...-42szNFpO4tTpNHyPFgwLTQ9gveCZ3w'
```

Optionally, for the Indirect Client, you can add the `X-On-Behalf-Of` header:

```bash
  --header 'X-On-Behalf-Of: 9efae653-af8b-4d54-b02a-010520d03e87' \
```

Parameters:

* `DPoP`: the DPoP proof
* `client_id`: your client ID
* `scope`: a space-separated list of scopes
* `client_assertion_type`: set to JWT Bearer Token assertion type
* `client_assertion`: your `private_key_jwt`

##### ❺ Create DPoP-bound access token {#-create-dpop-bound-access-token}

Diagram create-dpop-bound-token

The authorization server validates your client assertion and DPoP proof, then creates an access token bound to your DPoP key. This happens server-side. The authorization server:

1. Verifies your `private_key_jwt` signature using your public key
2. Checks the DPoP proof signature and claims
3. Extracts the public key from the DPoP proof (`jwk` claim)
4. Calculates a thumbprint of the DPoP public key
5. Creates an access token with a `cnf.jkt` (confirmation) claim containing the key thumbprint

##### ❻ Return access token {#-return-access-token}

Diagram return-access-token

The authorization server response includes the token, its type, expiration time, and scopes:

```json
{
  "access_token": "eyJraWQiOiJNQVNURVJDQVJEX09CX1BMQVRGT1JNLXBhcnRuZXItc2lnbi0xNzYzNzM3NTkyIiwidHlwIjoiSldUIiwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJlMzE3ZjNjYS1iYzkyLTQxM2EtOTM2MC1iNzk2OTBlMTExZWIiLCJhdWQiOiJodHRwczovL2FwaWd3LWdhcGlndy1ldXcxLXN0Zy5ocy1ldXcxLW5wLWkuZmluaS5jaXR5IiwibmJmIjoxNzc5Mjc1NDExLCJzY29wZSI6WyJjb25zZW50X2NyZWF0ZSJdLCJpc3MiOiJodHRwczovL2FwaWd3LWdhcGlndy1ldXcxLXN0Zy5ocy1ldXcxLW5wLWkuZmluaS5jaXR5IiwiY25mIjp7ImprdCI6IjdyaE5xRTJSeEdmQkd4SUpmbk13VTc3UFdUY2gwUHliSnA3WnFWc29wcFUifSwiZXhwIjoxNzc5Mjc2MzExLCJpYXQiOjE3NzkyNzU0MTEsImp0aSI6ImY4Yzc3NmJlLThjNTUtNDJkYy04MDhiLWVmZTFjNTQ2ZGI3MSJ9.CbGU4S2wsmHTjOYDHXrnlfK-Ed7yfU6eNxXD7coN2WP3t0S1Qk-uLNL7Hn3MV9K1Z-RoOsK3yNzhIOqQuXd4ag",
  "token_type": "DPoP",
  "expires_in": 899,
  "scope": "consent_create"
}
```

Response fields:

* `access_token`: the DPoP-bound access token
* `token_type`: always `DPoP` (not `Bearer`)
* `expires_in`: the token lifetime in seconds
* `scope`: the scopes granted to this token

#### 3. API Call {#3-api-call-1}

##### ❶ Create DPoP proof for resource access {#-create-dpop-proof-for-resource-access}

Diagram create-depop-proof

When you call the API, you need a new DPoP proof. This proof is similar to the previous one, but with two important differences: the `htu` and `htm` match the API endpoint you are calling, and you add an `ath` claim with the hash of your access token.

Example of code calculating the access token hash:

```java
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;

MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] accessTokenBytes = digest.digest("eyJraWQiOi...IiwidHlwIjoiSldUIiwiYWxnIjoiRVMyNTYifQ".getBytes(StandardCharsets.UTF_8));
String ath = Base64.getUrlEncoder().withoutPadding().encodeToString(accessTokenBytes);
```

Decoded DPoP proof JWT header:

```json
{
  "alg": "ES256",
  "typ": "dpop+jwt",
  "jwk": {
    "kty": "EC",
    "crv": "P-256",
    "x": "cojbH-aPEUBxt2_uSx5P9UTUkl5X_CFbnncJ35-onlc",
    "y": "89bpkg2grnJC0rzo_I2c_BTLB0sXHBvbmu5jjSwyOv8"
  }
}
```

Decoded DPoP proof JWT payload:

```json
{
  "jti": "47bs8_cGUYLgQmc9",
  "htm": "GET",
  "htu": "https://mtf.api.openfinance.mastercard.com/consents/<consent-id>",
  "iat": 1760099966,
  "exp": 1760100086,
  "ath": "Syre2WyO2hFtZbC8v4_LF41uuF4ysyAxqIA-J6UnxLc",
  "nonce": "5e8972513327f0b3670b21f308cf5e8e"
}
```

* `htm`: The HTTP method for the API call (`GET`, `POST`, `PUT`, and so on.)
* `htu`: The full API endpoint URL (scheme, host, and path only - no query parameters)
* `nonce`: The nonce from the previous `DPoP-Nonce` header
* `ath`: The SHA-256 hash of the access token (base64url encoded)

Signed DPoP proof ([view on jwt.io](https://www.jwt.io/#token=eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Arand0IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiY29qYkgtYVBFVUJ4dDJfdVN4NVA5VVRVa2w1WF9DRmJubmNKMzUtb25sYyIsInkiOiI4OWJwa2cyZ3JuSkMwcnpvX0kyY19CVExCMHNYSEJ2Ym11NWpqU3d5T3Y4In19.eyJqdGkiOiI0N2JzOF9jR1VZTGdRbWM5IiwiaHRtIjoiR0VUIiwiaHR1IjoiaHR0cHM6Ly9zYW5kYm94LmFwaS5tYXN0ZXJjYXJkLmNvbS9zZXJ2aWNlL3Jlc291cmNlLzg2ODY5ZjkyIiwiaWF0IjoxNzYwMDk5OTY2LCJleHAiOjE3NjAxMDAwODYsImF0aCI6IlN5cmUyV3lPMmhGdFpiQzh2NF9MRjQxdXVGNHlzeUF4cUlBLUo2VW54TGMiLCJub25jZSI6IjVlODk3MjUxMzMyN2YwYjM2NzBiMjFmMzA4Y2Y1ZThlIn0.gk2b6J1SMq7KTkfge71RZY8WHiBULCjUYNGwHqVJM-P-MByhz9Ogn9hOc8UBfT1d59jEch4FL9-R_vsyUAgSwQ)):

    eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Arand0IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiY29qYkgtYVBFVUJ4dDJfdVN4NVA5VVRVa2w1WF9DRmJubmNKMzUtb25sYyIsInkiOiI4OWJwa2cyZ3JuSkMwcnpvX0kyY19CVExCMHNYSEJ2Ym11NWpqU3d5T3Y4In19.eyJqdGkiOiI0N2JzOF9jR1VZTGdRbWM5IiwiaHRtIjoiR0VUIiwiaHR1IjoiaHR0cHM6Ly9zYW5kYm94LmFwaS5tYXN0ZXJjYXJkLmNvbS9zZXJ2aWNlL3Jlc291cmNlLzg2ODY5ZjkyIiwiaWF0IjoxNzYwMDk5OTY2LCJleHAiOjE3NjAxMDAwODYsImF0aCI6IlN5cmUyV3lPMmhGdFpiQzh2NF9MRjQxdXVGNHlzeUF4cUlBLUo2VW54TGMiLCJub25jZSI6IjVlODk3MjUxMzMyN2YwYjM2NzBiMjFmMzA4Y2Y1ZThlIn0.gk2b6J1SMq7KTkfge71RZY8WHiBULCjUYNGwHqVJM-P-MByhz9Ogn9hOc8UBfT1d59jEch4FL9-R_vsyUAgSwQ

##### ❷ Make API call {#-make-api-call}

Diagram make-api-call

Now you can call the Mastercard Open Finance API using your access token and DPoP proof. The token goes in the `Authorization` header with the `DPoP` scheme. The proof goes in the `DPoP` header.

```bash
    curl --request GET \
    --url https://mtf.api.openfinance.mastercard.com/consents/<consent-id> \
    --header 'Content-Type: application/json' \
    --header 'Authorization: DPoP <Access Token>' \
    --header 'DPoP: <DPoP Proof JWT>' \
    --header 'X-MC-Correlation-Id: aae4c399-9e93-48b1-ae04-ea3e0f6d82cb'
```

Parameters:

* `Authorization`: use the `DPoP` scheme followed by your access token
* `DPoP`: your signed DPoP proof

##### ❸ Grant access {#-grant-access}

Diagram grant-access

The API validates your request by checking three conditions:

1. The access token is valid and not expired.
2. The DPoP proof is signed with the private key matching the `cnf.jkt` claim in the access token.
3. The `ath` claim in the DPoP proof matches the hash of the access token.

If all checks pass, the API processes your request and returns the response.

##### ❹ Return API response {#-return-api-response}

Diagram return-api-response

If the request is successful, the API performs the requested operation. The response includes a new `DPoP-Nonce` header. Use this nonce in your next DPoP proof.

Sample response:

```json
{
  "id": "aae4c399-9e93-48b1-ae04-ea3e0f6d82cb"
}
```

Response headers:

    DPoP-Nonce: 7f6e5d4c3b2a1098fedcba9876543210

#### 4. Exchange Authorization Code - Obtain Consent Scoped Access Token {#4-exchange-authorization-code---obtain-consent-scoped-access-token}

Once you have obtained an Authorization Code via a callback redirect, you can exchange it for a Consent Scoped Access Token by calling the `oauth2/token` endpoint again, this time using the Authorization Code Grant flow.

```bash
    curl --request POST \
    --url https://mtf.auth.openfinance.mastercard.com/oauth2/token \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --header 'DPoP: <DPoP Proof JWT>' \
    --data client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
    --data client_id=<client-id> \
    --data code=<code> \
    --data grant_type=authorization_code
```

#### 5. Refresh Consent Scoped Access Token {#5-refresh-consent-scoped-access-token}

You can refresh a Consent Scoped Access Token by calling the `oauth2/token` endpoint again, this time using the Refresh Token Grant flow.

```bash
    curl --request POST \
    --url https://auth.openfinance.mastercard.com/oauth2/token \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --header 'DPoP: <DPoP Proof JWT>' \
    --header 'User-Agent: insomnia/11.2.0' \
    --data client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
    --data client_id=<client-id> \
    --data refresh_token=<Refresh Token> \
    --data grant_type=refresh_token
```

#### 6. Introspect a Partner Access Token {#6-introspect-a-partner-access-token}

You can introspect the validity of an Access Token by calling the introspection endpoint:

```bash
    curl --request POST \
    --url https://auth.openfinance.mastercard.com/oauth2/introspect \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --header 'DPoP: <DPoP Proof JWT>' \
    --header 'User-Agent: insomnia/11.2.0' \
    --header 'X-MC-Correlation-Id: aae4c399-9e93-48b1-ae04-ea3e0f6d82cb' \
    --data client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
    --data client_assertion=<Private Key JWT> \
    --data client_id=<client-id> \
    --data token=<Access Token>
```

#### 7. (Optional) Revoke Token {#7-optional-revoke-token}

You can revoke both Partner Access Tokens and Consent Scoped Access Tokens by calling the revocation endpoint:

```bash
    curl --request POST \
    --url https://auth.openfinance.mastercard.com/oauth2/revoke \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --header 'DPoP: <DPoP Proof JWT>' \
    --header 'User-Agent: insomnia/11.2.0' \
    --header 'X-MC-Correlation-Id: aae4c399-9e93-48b1-ae04-ea3e0f6d82cb' \
    --data client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
    --data client_assertion=<Private Key JWT> \
    --data client_id=<client-id> \
    --data token=<Access Token> \
    --data token_type_hint=access_token
```

## Handling Errors {#handling-errors}

Here are some common error codes and how to deal with them.
Note: For a complete list of error codes and their meanings, see [OAuth 2.0 Error Codes](https://developer.mastercard.com/platform/documentation/errors-and-troubleshooting/oauth2-error-codes/).

#### use_dpop_nonce {#use_dpop_nonce}

```json
{
  "error": "use_dpop_nonce"
}
```

This error occurs when the server requires you to include a nonce in your DPoP proof, but you did not include one or the nonce you provided is invalid. The response includes a `DPoP-Nonce` header with the nonce value you need to use: `DPoP-Nonce: 9a883cecdd6f6aa102e9a0e65d0b3643`.
Tip:   
How to fix it:

1. Extract the nonce from the `DPoP-Nonce` header
2. Add it to a new DPoP proof as the `nonce` claim
3. Send the request again with the updated DPoP proof

You do not have to wait for this error to update your nonce. You should get a `DPoP-Nonce` header with every successful response. If the nonce value changes, use the new one in your next DPoP proof. This helps you avoid errors from nonce rotation.

#### invalid_client {#invalid_client}

```json
{
  "error": "invalid_client"
}
```

This error means the authorization server could not validate your client assertion. This happens when your client assertion is malformed, expired, or signed with the wrong key.

Common causes:

* Your client assertion is expired (check the `exp` claim).
* You signed the client assertion with the wrong private key, or provided the wrong `kid`.
* The `iss` or `sub` claim does not match your client ID.
* The signature algorithm is not supported (use ES256 or PS256).

Tip: Check your client assertion JWT. Make sure all required claims are present and valid. Verify you are using the correct private key that matches the public certificate you shared with us, and that you are using a FAPI 2.0-compliant algorithm (ES256 or PS256).

#### invalid_dpop_proof {#invalid_dpop_proof}

```json
{
  "error": "invalid_dpop_proof"
}
```

This error means the DPoP proof you sent is invalid. This can happen for several reasons related to how you created or signed the DPoP proof.

Common causes:

* The DPoP proof is expired (check the `exp` claim).
* The `htu` claim does not match the request URI.
* The `htm` claim does not match the HTTP method (POST for token requests).
* The signature cannot be verified with the public key in the `jwk` claim.
* The signature algorithm is not supported (use ES256 or PS256).
* Some required claims are missing (`jti`, `htm`, `htu`, `iat`).
* The `ath` claim is missing when calling the API (required for all non-token-endpoint requests).

Tip: Double-check your DPoP proof JWT. Make sure the `htu` matches the exact URL you are calling (including scheme and host). The `htm` must match the HTTP method. All timestamps must be valid. When calling the API (not the token endpoint), you must include the `ath` claim with the base64url-encoded SHA-256 hash of your access token.

## Managing the Access Tokens {#managing-the-access-tokens}

The Access Tokens issued by Mastercard Open Finance have an expiration period. The expiration period is set to 1 hour. Note that this value may change in the future.

Always check the value in `expires_in` property of the received access token to understand the validity period. You will need to generate and sign a new JWT token and request a new Access Token when the current one expires.

## Updating your Keys {#updating-your-keys}

If your Public Certificate is due to expire, or your Private Key has been compromised, you will need to generate a new private-public key pair. For this go through the same steps as above.
Note: Begin signing your JWTs with your new Private Key only after confirmation that your new Public Certificate has been added to our trusted certificates.

## Moving to Production {#moving-to-production}

Once you are ready to make the move from testing in our Sandbox/MTF environment to our Production environment, it is essential that you acquire an SSL/TLS OV certificate from one of our Supported Certificate Authorities. Once you have received your certificate, you can contact your onboarding specialist for more information on how to proceed.

Supported Certificate Authorities:

* DigiCert
* GoDaddy
* NETS AS
* VeriSign

## Non-FAPI 2.0 Authentication (earlier integrations) {#non-fapi-20-authentication-earlier-integrations}

This section describes the authentication approach used prior to FAPI 2.0 authentication availability. This authentication is supported only for those who had already implemented to this approach.
Note: This API is currently available for Sandbox testing. [Contact us](mailto:openbankingeu_support@mastercard.com) for details of Production environment availability.

Earlier implementations used [OAuth 2.0 authorization](https://datatracker.ietf.org/doc/html/rfc6749#page-1) framework with [Client Credentials Grant](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4). In this flow, the Integrator needs to authenticate towards the Authorization Server to gain an Access Token that is used to access the API.

A description of the steps necessary to implement the authentication is detailed below.

### 1: Generate asymmetric cryptography keys {#1-generate-asymmetric-cryptography-keys-1}

As a pre-requisite, you need to generate an asymmetric cryptography public-private key pair and certificate.

When you authenticate on our authorization server, we want to make sure that only you can obtain access tokens for your clientId. For that we validate that your requests are signed with a private key that only you have.

At the moment we only support RSA keys. We recommend an RSA key of 4096 bits of length and valid for at least one year. In the future, you need to provide a new certificate to us before this expires, or face possible downtime in your applications.

#### Example {#example-1}

For testing, you can use [OpenSSL](https://openssl-library.org/) to generate these.

Write the following in the terminal:

    openssl req -x509 -sha256 -nodes -newkey rsa:4096 -keyout private.key -days 730 -out public.pem

This creates two files:

* private.key: The private key used for signing the JWT.
* public.pem: The public certificate that can be used to verify the JWT.

Note: **Do not share your Private Key.** Store the Private Key securely and use it to sign your JWT token before requesting an access token from us (find more details in the sections below).  
You need to share the public certificate with us (see below).

In a production environment, we suggest generating the private key in an HSM or similar, exporting the public key and sharing that with us.

### 2: Share the Public Certificate with us {#2-share-the-public-certificate-with-us}

Before you can start using the Mastercard Open Finance authentication, contact your onboarding specialist to add your Public Certificate to our list of trusted certificates.
Note: It is important to note that self-signed certificates are only supported in our Sandbox/MTF environment. When you are ready to move to Production, you need to acquire a certificate from a supported Certificate Authority (CA). For more information, refer to [Moving to Production](https://developer.mastercard.com/open-finance-data/documentation/api-basics/index.md#moving-to-production).

After that, you can call the `oauth2/token` endpoint to obtain an access token, necessary to use the Mastercard Open Finance Data API.
* Sandbox
* Production

```Sandbox
https://mtf.auth.openbanking.mastercard.eu/oauth2/token
```

```Production
https://auth.openbanking.mastercard.eu/oauth2/token
```

### 3: Generate and sign the JWT token {#3-generate-and-sign-the-jwt-token}

You need to populate the `client_assertion` field with a JWT used by the Auth Service for verifying the caller. The details necessary to generate and sign the JWT token are shown below.

The JSON Web Token (JWT) is an open standard ([RFC7519](https://datatracker.ietf.org/doc/html/rfc7519)) that enables transmitting security information between two parties in a compact way as a JSON object.

In this documentation, we cover how to generate a JWT with specific claims, sign it using a certificate generated by OpenSSL, and provide a C# example for the implementation.

### The JWT format {#the-jwt-format}

JWT consists of three parts: a header, a payload, and a signature.

#### The JWT header {#the-jwt-header}

The header specifies the type of token (JWT) and the signing algorithm (RS256) along with the kid of the certificate.

**Example**

```json
{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "<thumbprint-of-certificate>"
}
```

The `kid` (Key ID) is a unique identifier for the public certificate that you shared with us.
This is the SHA-256 thumbprint of the certificate in base64url encoding.

#### The JWT Payload {#the-jwt-payload}

The JWT Payload must have the following claims:

| **Field** |    **Name**     |    **Type**    |                                     **Description**                                      |
|-----------|-----------------|----------------|------------------------------------------------------------------------------------------|
| `sub`     | Subject         | string         | The ID of the client. This is the ID that you receive during onboarding your Integrator. |
| `iss`     | Issuer          | string         | The ID of the client (must be the same value as "Subject").                              |
| `exp`     | Expiration time | Unix timestamp | The expiration time of the JWT. Must be a date in future.                                |
| `aud`     | Audience        | string         | The audience of the JWT. Must be set to "auth.mastercard.com".                           |
| `jti`     | JWT ID          | string         | Must be a unique GUID per request.                                                       |

**Example**

```json
{
  "sub": "<client-id>",
  "iss": "<client-id>",
  "exp": 1723125616,
  "aud": "auth.mastercard.com",
  "jti": "27edbf3f-c5a1-460d-8248-0af06f93200b"
}
```

#### The JWT Signature {#the-jwt-signature}

To understand how this works, you can use [JWT.io](https://jwt.io/) and the pair of public certificate with private key in order to quickly sign a JWT. Make sure to use the right algorithm (RS256). To sign the JWT, you only need to provide the private key. If you want to validate that the signing went correctly, you can provide the public certificate as well, and you should see the "Signature verified" text.

### Example: Generate JWT and Sign with Private Key using C# {#example-generate-jwt-and-sign-with-private-key-using-c}

Below is a C# example for generating a JWT. The signed JWT should then be passed as the client_assertion to the oauth2/token endpoint.
Tip: This code serves to illustrate the core functionality. Make sure to add proper error handling, logging, validation, removing hardcoded values, and unit test it before putting it into production.

```C#
public string CreateJwtToken()
{
    // Create the RSA security key from the private key          
    var privateKey = File.ReadAllText("path/to/your/private.key");

    using var rsa = RSA.Create();
    rsa.ImportFromPem(privateKey);
    var rsaSecurityKey = new RsaSecurityKey(rsa)
    {
        KeyId = GetKidFromCertificate()
    };

    // Define token parameters
    var tokenHandler = new JwtSecurityTokenHandler();

    var tokenDescriptor = new SecurityTokenDescriptor
    {
        Subject = new ClaimsIdentity(new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub, "<client-id>"),
            new Claim(JwtRegisteredClaimNames.Iss, "<client-id>"),
            new Claim(JwtRegisteredClaimNames.Aud, "auth.mastercard.com"),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
        }),
        Expires = DateTime.UtcNow.AddHours(1),
        SigningCredentials = new SigningCredentials(rsaSecurityKey, SecurityAlgorithms.RsaSha256)
    };

    // Create the JWT token
    var token = tokenHandler.CreateToken(tokenDescriptor);
    return tokenHandler.WriteToken(token);
}
```

The above example calls a private method to add the key id. Mastercard Open Finance calculates the expected KID as follows, so your implementation should be functionally equivalent:

```C#
private static string GetKidFromCertificate()
{
    const string pemCertPath = "path/to/your/certificate.pem";
    var pemContent = File.ReadAllText(pemCertPath);

    // Remove PEM headers/footers and whitespace
    var cleanedPem = pemContent
        .Replace("-----BEGIN CERTIFICATE-----", "")
        .Replace("-----END CERTIFICATE-----", "")
        .Replace("\r", "")
        .Replace("\n", "")
        .Replace(" ", "")
        .Replace("\t", "");

    // Compute SHA-256 hash
    var certBytes = Convert.FromBase64String(cleanedPem);
    var hash = SHA256.HashData(certBytes);

    // Convert to base64url encoding
    var base64 = Convert.ToBase64String(hash);

    return base64.TrimEnd('=')
        .Replace('+', '-')
        .Replace('/', '_');
}
```

### 4: Request an access token {#4-request-an-access-token}

Once you have the JWT token signed, you can call Authentication Service in order to obtain a Mastercard Open Finance Access Token.

Note that you need to use the following URL for the Authentication Service endpoints:

`POST /oauth2/token`

Find below details on how to populate the fields:

|        **Field**        |                                                                                                                                 **Description**                                                                                                                                 |
|-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `grant_type`            | Set to client_credentials grant type.                                                                                                                                                                                                                                           |
| `client_assertion_type` | Set to JWT Bearer Token assertion type                                                                                                                                                                                                                                          |
| `client_assertion`      | Set to the signed JWT bearer token you created in previously.                                                                                                                                                                                                                   |
| `scope`                 | There are three possible values that you can set here - `ob_data`, `ob_providers`, and `ob_theming`. We also support setting multiple scopes. You can do this by passing an array indicating all of the scopes you want to use. Example: `"scope": ["ob_data", "ob_providers"]` |

Note: You cannot reuse a JWT after you already used it to obtain an Access Token.

In response, you receive the following:

|   **Field**    |                                                 **Description**                                                 |
|----------------|-----------------------------------------------------------------------------------------------------------------|
| `access_token` | Set to client_credentials grant type.                                                                           |
| `token_type`   | The type of the Access Token. This is always "bearer".                                                          |
| `expires_in`   | This is the Access Token that you need to use as a bearer token in all API requests to Mastercard Open Finance. |

### 5: Use the access token to access the API {#5-use-the-access-token-to-access-the-api}

Send the Access Token that you received above as a bearer token (Authorization header) in each API request to us to enable us to validate your access.

**Accessing the API example**

    GET /consents/d4ed1ee2-bf8e-4611-8507-55f4c410ce25 HTTP/1.1
    Host: mtf.api.openbanking.mastercard.com
    Content-Type: application/json
    Authorization: Bearer <Access Token>

### Managing the access tokens {#managing-the-access-tokens-1}

The Access Tokens issued by Mastercard Open Finance have an expiration period. The expiration period is set to 1 hour. Note that this value may change in the future.

Always check the value in `expires_in` property of the received access token to understand the validity period. You need to generate and sign a new JWT token and request a new Access Token when the current one expires.

### Updating your Keys {#updating-your-keys-1}

If your Public Certificate is due to expire, or your Private Key has been compromised, you need to generate a new private-public key pair. For this go through the same steps as above.
Note: Begin signing your JWTs with your new Private Key only after confirmation that your new Public Certificate has been added to our trusted certificates.

### Moving to Production {#moving-to-production-1}

Once you are ready to make the move from testing in our Sandbox/MTF environment to our Production environment, it is essential that you acquire an SSL/TLS OV certificate from one of our supported Certificate Authorities. Once you have received your certificate, contact your onboarding specialist for next steps.

#### Operational guidance (recommended) {#operational-guidance-recommended}

* Plan ahead: issuing an OV certificate can take 2--4 weeks.
* Validity: ensure the certificate has at least 9 months validity remaining at the time of submission.
* Renewal: provide replacement certificates 30--60 days before expiry to avoid service disruption.

#### Supported Certificate Authorities (SSL/TLS OV) {#supported-certificate-authorities-ssltls-ov}

* DigiCert
* GoDaddy
* NETS AS
* VeriSign

<br />

If you also require eIDAS qualified certificates (licensed / PSD2 scenarios):

* Some implementations (for example, operating under your own license) may also require eIDAS qualified certificates. eIDAS qualified certificates can only be issued by a Qualified Trust Service Provider (QTSP). Recommendation
* Prioritize QTSPs that can issue all certificates you need (ideally both SSL/TLS and the required eIDAS certificates) to reduce vendor management and renewal overhead.

#### Certificate Authority Notes {#certificate-authority-notes}

* DigiCert: commonly used for SSL/TLS; also offers eIDAS qualified certificate products.
* VeriSign: legacy brand now delivered under DigiCert's certificate portfolio.
* Sectigo: offers SSL/TLS and eIDAS qualified certificate products.
* NETS AS: used for SSL/TLS in some onboarding materials; verify eIDAS qualified issuance capability if required.
* GoDaddy: used for SSL/TLS; if you need eIDAS qualified certificates, verify QTSP issuance capability and scope.
