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

# Authentication

> The Ebury authentication scheme is based on OpenID Connect 1.0, using the Authorization Code flow.

The Ebury authentication scheme is based on <a href="http://openid.net/specs/openid-connect-core-1_0.html" target="_blank">OpenID Connect 1.0</a>, which builds on OAuth 2.0 to make it easier to verify the identity of end users. We've chosen OpenID Connect as we believe it offers our consumers a good mix of security and flexibility and implemented that Authorization Code flow for OpenID Connect: The steps required to complete this flow are detailed in the following sections.

The diagram below shows an overview of the process when Second Factor Authentication (2FA) is disabled:

<img src="https://mintcdn.com/ebury/hyecsfNgRYJYRIcg/images/openid_overview.png?fit=max&auto=format&n=hyecsfNgRYJYRIcg&q=85&s=3fadaf50fa3e9d199ee56cc88f917629" alt="OpenID flow overview" width="666" height="458" data-path="images/openid_overview.png" />

When 2FA in enabled the process is slightly different as can be seen on the following diagram:

<img src="https://mintcdn.com/ebury/hyecsfNgRYJYRIcg/images/openid_overview_2fa.png?fit=max&auto=format&n=hyecsfNgRYJYRIcg&q=85&s=d558269b59d7fd14ebddc45b90f53820" alt="OpenID flow overview" width="806" height="582" data-path="images/openid_overview_2fa.png" />

## Acquire an access token

Acquiring an access token is a three-step process:

1. [Redirect the user](#redirect-the-user-to-ebury) to Ebury to authorise your app
2. The user [authenticates](#user-authentication) with Ebury
3. If 2FA is enabled, the user provides the verification code on the [2FA screen](#second-factor-authentication)
4. [Ebury redirects the user](#ebury-redirects-back-to-your-app) back to your app with an authorization code
5. [Exchange](#exchange-the-authorization-code) the authorization code for an access token

### Redirect the user to Ebury

```shell theme={null}
"https://auth.ebury.io/authenticate?
    scope=openid&
    response_type=code&
    client_id=$client_id&
    state=$state&
    redirect_uri=$redirect_uri"
```

To start the authentication process a request needs to be made in a browser to our authorization server to identify the application attempting to access the user's profile. This may be implemented in a mobile or web application but the access tokens can be used for server-based applications as well.

<Note>
  For server-based applications, once you have authenticated for the first time you'll be able to use refresh tokens to reauthorize access (more on this below).
</Note>

**Query Parameters**

<ParamField query="client_id" type="string" required>
  Your client identifier
</ParamField>

<ParamField query="scope" type="string" required>
  Must be `openid`; no other values are supported
</ParamField>

<ParamField query="response_type" type="string" required>
  Must be `code`
</ParamField>

<ParamField query="state" type="string" required>
  A random, per request value used to maintain state between request and callbacks and protect against <a href="https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)">cross-site request forgery attacks</a>
</ParamField>

<ParamField query="redirect_uri" type="string" required>
  The redirect URL that is registered for your application, this **must** match the value we hold
</ParamField>

### User Authentication

If all parameters are successfully validated the Authorization Server will present an Ebury login screen. The user will be required to enter their Ebury Online email address and password, as shown below.

<img src="https://mintcdn.com/ebury/hyecsfNgRYJYRIcg/images/login_screen.png?fit=max&auto=format&n=hyecsfNgRYJYRIcg&q=85&s=f96f5924d524e72552c1db75bfc124d4" alt="Login Screen" width="2784" height="1770" data-path="images/login_screen.png" />

When the user clicks the "Login" button after entering their credentials, an authentication process occurs behind the scenes with the following request parameters in the body.

```shell theme={null}
curl -X POST \
-H "Cache-Control: no-cache" \
-H "Content-Type: application/x-www-form-urlencoded" \
https://auth.ebury.io/authenticate \
--data 'email={{EBO_LOGIN_USER}}&password={{EBO_LOGIN_PASS}}&client_id={{CLIENT_AUTH_ID}}&state={{STATE}}'
```

**Request Parameters**

<ParamField body="email" type="string" required>
  The user's email address for authentication
</ParamField>

<ParamField body="password" type="string" required>
  The user's password for authentication
</ParamField>

<ParamField body="client_id" type="string" required>
  Your client identifier
</ParamField>

<ParamField body="state" type="string" required>
  A random, per request value used to maintain state between request and callbacks
</ParamField>

### Ebury redirects back to your app

The redirection process occurs after successful authentication. Ebury will redirect the user back to your registered `redirect_uri` with the authorization code:

```http theme={null}
HTTP/1.1 302 Found
Location: https://your.redirect.url/?code=$authorization_code&state=$state_token
```

```html theme={null}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: <a
        href="http://your.redirect.url?code=$authorization_code&amp;state=$state_token">http://your.redirect.url?code=$authorization_code&amp;$state_token</a>.
    If not click the link.
```

**Query Parameters**

<ParamField query="code" type="string" required>
  The authorization code returned by the authorization endpoint
</ParamField>

<ParamField query="state" type="string" required>
  The state parameter passed from your application in the original authorization request
</ParamField>

The example below shows the `login` request returning a `302 Found` response, with the `code` and `state` present in the redirect URL:

<img src="https://mintcdn.com/ebury/hyecsfNgRYJYRIcg/images/auth_login_code.png?fit=max&auto=format&n=hyecsfNgRYJYRIcg&q=85&s=d8c071a44d81965c084e73351ecfa17c" alt="Login request returning the authorization code" width="1479" height="446" data-path="images/auth_login_code.png" />

### No-redirect Response

Alternatively, if you prefer to handle authentication programmatically without browser redirects, you can use the `No-Redirect` header:

```shell theme={null}
curl -X POST \
-H "Cache-Control: no-cache" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "No-Redirect: application/json" \
https://auth.ebury.io/authenticate \
--data 'email={{EBO_LOGIN_USER}}&password={{EBO_LOGIN_PASS}}&client_id={{CLIENT_AUTH_ID}}&state={{STATE}}'
```

```http theme={null}
POST /authenticate HTTP/1.1
Host: auth.ebury.io
Cache-Control: no-cache
Content-Type: application/x-www-form-urlencoded
No-Redirect: application/json
```

**Request Headers**

<ParamField header="No-Redirect" type="string" required>
  Set to `application/json` to receive the authorization code in JSON format instead of being redirected
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Must be `application/x-www-form-urlencoded`
</ParamField>

<ParamField header="Cache-Control" type="string">
  Set to `no-cache` to prevent caching of the authentication request
</ParamField>

Response when the `No-Redirect` header is used:

```json Response theme={null}
{
    "code": "$authorization_code$"
}
```

When the `No-Redirect` header is included in the authentication request with the value `application/json`, instead of redirecting the user, the response will contain only the authorization code in JSON format returning a 200 OK response. This is useful for server-to-server authentication flows where you need programmatic access to the authorization code.

Once the credentials have been successfully entered, one of two things can happen:

* If the user does not have Second Factor Authentication (2FA) enabled, the authentication process is completed ([Authentication completed](#authentication-completed))
* If the user has 2FA enabled, go to [Second Factor Authentication](#second-factor-authentication)

### Second Factor Authentication

If the user has 2FA enabled, it will be redirected to the 2FA screen and will be issued a 2FA code (SMS/TOTP are supported for now).
The user will be required to enter this code, as shown below:

<img src="https://mintcdn.com/ebury/hyecsfNgRYJYRIcg/images/2fa_screen.png?fit=max&auto=format&n=hyecsfNgRYJYRIcg&q=85&s=a1478add9d6272f851f9f93940d8fd4c" alt="2FA Screen" width="1543" height="1031" data-path="images/2fa_screen.png" />

If the verification code was entered correctly then the authentication process is completed ([Authentication completed](#authentication-completed))

<Note>
  For some reason, if you lose the first verification code, you can request to resend the code (more on this below).
</Note>

**Query Parameters**

<ParamField query="code" type="string" required>
  Value returned by authorization endpoint
</ParamField>

<ParamField query="client_id" type="string" required>
  Your client identifier
</ParamField>

### Authentication completed

After the authentication process is completed, the user will be redirected to the `redirect_uri` registered for the application. The following querystring parameters will be included:

**Response Parameters**

<ResponseField name="code" type="string">
  A code that allows your application to call the Token Endpoint to complete the OpenID flow
</ResponseField>

<ResponseField name="state" type="string">
  Value passed from your application in the original authorization request
</ResponseField>

### Exchange the authorization code

```shell theme={null}
curl -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Authorization: Basic {{Credentials}}" \
https://auth.ebury.io/token \
--data 'grant_type=authorization_code&code=$code&redirect_uri=$redirect_uri'
```

Building the `Credentials` value for the `Authorization` header:

```javascript theme={null}
// `Credentials` is the base64 encoding of "client_id:client_secret".
// In Postman this is typically computed in a pre-request script and stored
// in the `Credentials` environment variable used by the Authorization header.
const credentials = Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64');
postman.setEnvironmentVariable("Credentials", credentials);

// The header then becomes: Authorization: Basic {{Credentials}}
```

```json Response theme={null}
{
  "token_type": "Bearer",
  "access_token": "XKtOK3hNzKpLkaom3J2MEPyKm7f7jZ",
  "refresh_token": "2E9KVBXgVzQSPTvoHjJB1Eu2eBjzup",
  "expires_in": 3600,
  "id_token": "eyJhbGciOiAiSFMyNTYifQ==.ewogICJhdWQiOiAiWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFgiLAogICJzdWIiOiAiWFhYWFhYWFhYWFgiLAogICJpc3MiOiAiaHR0cHM6Ly9hdXRoLmVidXJ5LmlvIiwKICAiaWF0IjogIjE0NjQyNjY2MzkiLAogICJjbGllbnRzIjogWwogICAgIlhYWFhYWFhYWFhYIgogIF0sCiAgImV4cCI6ICIxNDY0MzUzMDM5Igp9Cg==.bkieHxES1spJnVmDmhganElaP6LZfikKXZ8uphVQwUo"
}
```

The final step is for your application to exchange the authorisation code for an access token that will provide access to the API. This must happen within 10 minutes of receiving your authorisation code.

The request must be authenticated using HTTP Basic authentication. The `Authorization` header takes the form `Basic {{Credentials}}`, where `{{Credentials}}` is the base64 encoding of your `client_id` and `client_secret` joined by a colon (`client_id:client_secret`), as shown in the sample above.

The following parameters are passed to the token endpoint

**Query Parameters**

<ParamField query="grant_type" type="string" required>
  Must be set to `authorization_code`
</ParamField>

<ParamField query="code" type="string" required>
  Value returned by authorization endpoint
</ParamField>

<ParamField query="redirect_uri" type="string" required>
  The redirect URL that is registered for your application, this **must** match the value we hold
</ParamField>

If the all parameters are correct a response will be returned containing the following:

**Response Fields**

<ResponseField name="token_type" type="string">
  The Authorization header scheme to use when making requests, will be `Bearer`
</ResponseField>

<ResponseField name="access_token" type="string">
  An OAuth access token that can be used to call the API
</ResponseField>

<ResponseField name="refresh_token" type="string">
  An OAuth refresh token that can be used to get a new access token when when the last expires
</ResponseField>

<ResponseField name="expires_in" type="integer">
  Expiry period in seconds from time token returned, currently returns `3600` (1 hour)
</ResponseField>

<ResponseField name="id_token" type="string">
  A signed, base 64 encoded <a href="https://jwt.io/" target="_blank">JSON Web Token</a> that provides verification of the identity that authorized the request. The token includes the client identifier, which is a required parameter **on the majority of API calls**.
</ResponseField>

Decoded JSON Web Token (without signature):

```json theme={null}
{
  "alg": "HS256"
}
{
  "aud": "XXXXXXXXXXXXXXXXXXXXXXXXXX",
  "sub": "XXXXXXXXXXX",
  "iss": "https://auth.ebury.io",
  "iat": "1464266639",
  "clients": [{
      "client_id": "XXXXXXXXXXX",
      "client_name": "Example client name"
  }],
  "exp": "1464353039"
}
```

Sample code for extracting data from the JSON Web Token:

```javascript theme={null}
var jsonData = JSON.parse(responseBody);

token = jsonData["access_token"];
postman.setEnvironmentVariable("token", token);

idtoken = parseJwt(jsonData["id_token"]);
postman.setEnvironmentVariable("contact_id", idtoken["sub"]);

clients = idtoken["clients"];
postman.setEnvironmentVariable("client_id", clients[0].client_id);

/*function to decode JSON web token*/
function parseJwt(token) {
  var base64Url = token.split(".")[1];
  var base64 = base64Url.replace("-", "+").replace("_", "/");
  return JSON.parse(atob(base64));
}
```

<Note>
  As per the OpenID connect specification for MAC-based algorithms the JSON Web Token is signed with <strong>your</strong> client secret, allowing you to verify it using an appropriate JSON Web Signature library.
</Note>

By decoding this token we can get the value for `client_id` needed for future requests.

An array of `client_id` and `client_name` lists the potential clients that the contact can act on behalf of. The majority of customers will only have one client identifier, but some may have multiple client accounts and thus multiple identifiers that the contact can act on

The `client_id` is required in query params of requests post authentication in order to identify the client to which the request is on behalf of. Your application may have to allow a contact to select the correct `client_id` to use.

The example below shows the `token` request with the `Authorization: Basic {{Credentials}}` header set, returning the `access_token`, `refresh_token` and `id_token`:

<img src="https://mintcdn.com/ebury/hyecsfNgRYJYRIcg/images/auth_access_token.png?fit=max&auto=format&n=hyecsfNgRYJYRIcg&q=85&s=266b761b41a93e6abb9e1919306e7391" alt="Get Access Token request and response" width="1479" height="751" data-path="images/auth_access_token.png" />

## Refreshing access

```shell theme={null}
curl -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Authorization: Basic {{Credentials}}" \
https://auth.ebury.io/token \
--data 'grant_type=refresh_token&refresh_token=$refresh_token&scope=openid'
```

Your access token will expire according to the value set in the [Exchange the authorisation code](#exchange-the-authorization-code) response, but you can directly get a new access token by using your refresh token without needing to restart the authentication from the start.

The `Authorization` header for this request is **the same** as the one used to [exchange the authorisation code](#exchange-the-authorization-code): `Basic {{Credentials}}`, where `{{Credentials}}` is the base64 encoding of `client_id:client_secret`. Do **not** use your access token here.

The example below shows the refresh request using the same `Authorization: Basic {{Credentials}}` header, returning a new `access_token`:

<img src="https://mintcdn.com/ebury/hyecsfNgRYJYRIcg/images/auth_refresh_token.png?fit=max&auto=format&n=hyecsfNgRYJYRIcg&q=85&s=46760ef99560a7c3f47f1e0c9dc3be8f" alt="Get Refresh Token request and response" width="1479" height="751" data-path="images/auth_refresh_token.png" />

Our OpenID Provider conforms to the refresh token mechanism described <a href="http://openid.net/specs/openid-connect-core-1_0.html#RefreshTokens" target="_blank">here</a>.
The refresh token lifespan is configurable depending on the client's needs.

<Warning>
  Unused refresh tokens will expire after their lifespan ends.
</Warning>

**Request Fields**

<ParamField body="grant_type" type="string" required>
  Should be `refresh_token`
</ParamField>

<ParamField body="refresh_token" type="string" required>
  One of the last 10 refresh tokens, issued within the last 'n' days. Where 'n' is the refresh token lifespan agreed with the client (default value is 28 days).
</ParamField>

<ParamField body="scope" type="string" required>
  Should be `openid`
</ParamField>

If successful, the response will contain the same data as the original access token [response](#exchange-the-authorization-code).

## Getting an access token on behalf of a client

It is possible for financial institutions to get an access token on behalf of their clients by using the contact id of the client.

This functionality is not available per default, you need to contact Ebury to enable it for each contact you need an access token for.

This endpoint is only available using [mTLS](#trusted-authentication).

```shell theme={null}
curl -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Authorization: Basic {{Credentials}}" \
https://trusted.ebury.io/token/$contact_id \
--data 'grant_type=client_credentials'
--cert fullchain.pem \
--key privkey.pem
```

**Path Parameters**

<ParamField path="contact_id" type="string" required>
  The contact identifier
</ParamField>

**Request Fields**

<ParamField body="grant_type" type="string" required>
  Should be `client_credentials`
</ParamField>

If successful, the response will contain the access token [response](#exchange-the-authorization-code).

## Authenticating requests

```http theme={null}
GET /example-endpoint HTTP/1.1
Authorization: Bearer your-access-token
```

**All** requests must be authenticated with an access token.

**Almost all** requests will accept an optional Ebury user identifier, set in the `X-Contact-Id` header.

<Note>
  The <code>X-Contact-Id</code> header used to be a requirement, but you can safely drop it from your requests and you should do so if possible. If present, it will be checked for equality with the contact id that is making the request.
</Note>

**Request Headers**

<ParamField header="Authorization" type="string" required>
  Your access token, prefixed with the `Bearer` scheme
</ParamField>

<ParamField header="x-api-key" type="string">
  Your API key (Deprecated)
</ParamField>

<ParamField header="X-Contact-Id" type="string">
  Identifier for an Ebury user (Deprecated)
</ParamField>

## Trusted authentication

By default when a client connects to a server over HTTPS only the identity of the server is verified.

Ebury supports verifying the identity of the client as well by using a client certificate.

To use this functionality you need to connect using these URLs instead of the ones you usually use.

| Environment | Hostname |
| - | - |
| `Sandbox` | [https://trustedsandbox.ebury.io](https://trustedsandbox.ebury.io) |
| `Production` | [https://trusted.ebury.io](https://trusted.ebury.io) |

Also, note that you will use these endpoints for both authentication and API access. For example:

* To retrieve an access token in the sandbox environment you would call [https://trustedsandbox.ebury.io/token](https://trustedsandbox.ebury.io/token) instead of [https://auth.ebury.io/token](https://auth.ebury.io/token)
* To create a payment in the sandbox environment you would call [https://trustedsandbox.ebury.io/payments](https://trustedsandbox.ebury.io/payments) instead of [https://api.ebury.io/payments](https://api.ebury.io/payments)

In order to use this functionality you need to provide Ebury with the subject distinguished name of the certificate you will be using to connect because we will match the subject DN you provide with the subject DN of the client's certificate used during the connection.

<Warning>
  Self signed certificates are not supported
</Warning>

In order to check the validity of the client certificate you will be using to connect to and also to get the Subject DN of your certificate you can use the `check_cert` endpoint.

<CodeGroup>
  ```shell curl theme={null}
  curl --cert fullchain.pem --key privkey.pem https://trustedsandbox.ebury.io/cert_check
  ```

  ```go Go theme={null}
  package main

  import (
      "crypto/tls"
      "io/ioutil"
      "net/http"
      "log"
      "strings"
  )

  func main() {
  	cert, err := tls.LoadX509KeyPair("fullchain.pem",
  	                                 "privkey.pem")
  	if err != nil {
  		log.Fatal(err)
  	}

  	tlsConfig := &tls.Config{
  		Certificates:       []tls.Certificate{cert},
  		MinVersion:         tls.VersionTLS13,
  	}
  	tlsConfig.BuildNameToCertificate()
  	transport := &http.Transport{TLSClientConfig: tlsConfig}

  	req, err := http.NewRequest("GET", "https://trustedsandbox.ebury.io/cert_check", strings.NewReader(""))
  	if err != nil {
  		log.Fatal(err)
  	}

  	client := &http.Client{Transport: transport}
  	resp, err := client.Do(req)
  	if err != nil {
  		log.Fatal(err)
  	}

  	contents, err := ioutil.ReadAll(resp.Body)
  	log.Println(string(contents))
  }
  ```

  ```php PHP theme={null}
  #!/usr/bin/php
  <?php

  $ch = curl_init();
  curl_setopt($ch, CURLOPT_SSLCERT, 'fullchain.pem');
  curl_setopt($ch, CURLOPT_SSLKEY, 'privkey.pem');
  curl_setopt($ch, CURLOPT_VERBOSE, true);
  curl_setopt($ch, CURLOPT_URL,'https://trustedsandbox.ebury.io/cert_check');
  $result=curl_exec($ch);
  echo $result;

  ?>

  ```

  ```python Python theme={null}
  import requests

  response = requests.get('https://trustedsandbox.ebury.io/cert_check', cert=("fullchain.pem", "privkey.pem"))

  print(response.text)
  ```
</CodeGroup>

```text Response theme={null}
CN=81be95eb.ngrok.io
```

If everything goes fine you will get a 200 response and the content of the response will be the subject DN of the certificate.
You can now  share the subject DN of the certificate with Ebury and the trusted connection with the regular endpoints will work without any further changes.

On the other hand, if there is a problem with the certificate or connection you might get one of these errors.

| Status Code | Error |
| - | - |
| `495` | The certificate provided is invalid or the provided certificate's Subject DN does not matches the one for the client. |
| `496` | There was not certificate provided. |
| `497` | The connection was made using HTTP instead of HTTPS. |

You can check the next section in order to troubleshoot the issue.

### Troubleshooting

In order to use this functionality the client you are connecting with must support the TLS 1.3 protocol, we don't support older versions of the protocol.

You need at least OpenSSL 1.1.1 in order to be able to use TLS 1.3, to check your openssl version you can use:

`openssl version`

Also, the connection must be done by using any of the following ciphers:

* TLS\_AES\_128\_GCM\_SHA256
* TLS\_AES\_256\_GCM\_SHA384
* TLS\_CHACHA20\_POLY1305\_SHA256

You can check the ciphers supported by doing:

`openssl ciphers -v | grep TLSv1.3`

If you can run any of these commands and get a successfull verification you are good to go

`openssl s_client -tls1_3 -ciphersuites 'TLS_AES_128_GCM_SHA256' -connect tls13.cloudflare.com:443`

`openssl s_client -tls1_3 -ciphersuites 'TLS_AES_256_GCM_SHA384' -connect tls13.cloudflare.com:443`

`openssl s_client -tls1_3 -ciphersuites 'TLS_CHACHA20_POLY1305_SHA256' -connect tls13.cloudflare.com:443`
