Title
Create new category
Edit page index title
Edit category
Edit link
API Authentication
This document will cover the steps required to send API requests using Yoti’s new Central Auth.
All API calls will be made using an OAuth Access Token as a bearer token.
The required steps to generate this Access Token and make API calls to Yoti are as follows:
- Generate a JWT token using a Yoti private key (.pem file)
- Acquire an OAuth Access Token using this JWT Token
- Send an API request to Yoti
A token can be reused for multiple requests - you must NOT use a new token for every request. There is a limit of 200 active tokens per service/SDK ID. We advise that you use one key for all calls within a scope and obtain a new token every 30 minutes.
You will need a Verified Yoti Organisation account and service set up with a Private PEM key and SDK ID.
Set this up by following this guide
Generating a JWT Token
We use the private_key_JWT client authentication method from OIDC Core. The JWT will be signed by the RSA private key of the service/SDK ID that you are authenticating as.
| Header | Value | Description |
|---|---|---|
| alg | PS384 | Algorithm. We require the algorithm to be PS384. No other algorithms are accepted. |
| typ | JWT | Type. Must be the string value JWT. |
The following claims must be present in the payload:
| Claim | Value | Description |
|---|---|---|
| iss | sdk: <YOUR_SDK_ID> | Issuer. Must be set to the string “sdk:" || SDK ID, e.g. sdk:67d60fe2-5576-49ae-9ac9-ad76b232c5e1. |
| sub | sdk: <YOUR_SDK_ID> | Subject. Must be set to the same value as iss |
| aud | https://api.example.com | Audience. This must be the full URL for the OAuth client credentials grant endpoint. |
| jti | UUID string | JWT ID. We require a valid UTF-8 string of at least 16 bytes and at most 128 bytes (not characters) in length. Each JWT that is issued must use a different jti value; the authorisation server will remember iss || jti |
| exp | 1751700000 | Expiry time. The authorisation server will refuse to grant client credentials if a request is processed after this time. The expiry time can be a maximum of 30 minutes in the future. |
| iat | 1751700000 | OPTIONAL. Issued at. The authorisation server will refuse to grant client credentials if this value is unreasonably far in the past. We have a threshold of 30 minutes |
| nbf | 1751700000 | OPTIONAL. Not before. The authorisation server will refuse to grant client credentials if the request arrives before the “not before” time. |
Examples
const fs = require("fs");const jwt = require("jsonwebtoken");const uuid = require("uuid");const pem = fs.readFileSync("PATH_TO_PEM.pem");const sdkId = "YOUR_SDK_ID";const authURL = "https://auth.api.yoti.com/v1/oauth/token";const issuedAt = Math.floor(Date.now() / 1000);const expiry = issuedAt + 30 * 60;function buildJWT() { const claims = { iss: "sdk:" + sdkId, sub: "sdk:" + sdkId, aud: authURL, jti: uuid.v4(), exp: expiry, iat: issuedAt, }; const headers = { alg: "PS384", typ: "JWT", }; try { const token = jwt.sign(claims, pem, { algorithm: "PS384", header: headers, }); return token; } catch (error) { console.error("Error building JWT:", error); }}const token = buildJWT();console.log("Generated JWT successfully");console.log(token);Response
eyJhbGciOiJQUzM4NCIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzZGs6OWVlM2RiNmUtM2UwYy00ZTg1LWIyYTMtOTliZTMxNmY5MDJhIiwic3ViIjoic2RrOjllZTNkYjZlLTNlMGMtNGU4NS1iMmEzLTk5YmUzMTZmOTAyYSIsImF1ZCI6Imh0dHBzOi8vYXV0aC5hcGkueW90aS5jb20vdjEvb2F1dGhvdG9rZW4iLCJqdGkiOiI2N2RlMjM0OC0xOWYyLTRmODctOWE4YS01YTEzZjM5ZWIyNTEiLCJleHAiOjE3NzA3Mjc3MjAsImlhdCI6MTc3MDcyNTkyMH0.lgCJJ57OeV_mJGw91DR71ZRHgOWLbzSK1PYhqC59333PB4fBzxcz4chWHo5Go7vL_vGNYpJQJI-UuuBbZNNCF5OuDnWSQNHIcB_QW7OsYa6al8PcbC21oZrbdBSTbJdk_7S7ARo5noXKf4tH_cnbUkL7I50lMPnJngZBfpLb6ATAjbSWhsKhrT--GlWKs-NB58z8V-PDmG6c93PF7krA6oB964OtIMEHtFiXfbQB61xiYOUyR94G-pLUbftDnvXhyxmT_MZXaZRSIfIy8l75scBoHv96ylSH1yqBcw5vdQoye-Cd6YXGlJLS5QM9fqFm9JYIeOh7DP7Vo4PRTIFlcQOAuth Token Grant
Once you have generated a JWT token, this can be used to grant an OAuth Access Token to be used for Yoti API calls.
You must request this from the Yoti authorisation server. The request method is POST, and the body from the client must be encoded with the application/x-www-form-urlencoded content type
POST https://auth.api.yoti.com/v1/oauth/tokenThe request must include the following form values:
| Header | Value | Description |
|---|---|---|
| grant_type | client_credentials | OAuth grant type |
| scope | poa:verify | A space-separated list of one or more scopes that the token will grant access for. |
| client_assertion_type | urn:ietf:params:oauth:client-assertion-type:jwt-bearer | The OAuth client assertion type |
| client_assertion | JWT value | The value of the JWT token |
| comment | “production_key” | Must be valid UTF-8, limited to 128 characters, and comprise only Unicode printable characters. |
Examples
const axios = require("axios");const token = "YOUR_JWT_TOKEN"async function requestOAuthToken() { const formData = new URLSearchParams({ grant_type: "client_credentials", scope: "poa:verify", client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", client_assertion: token, }); try { axios .post("https://auth.api.yoti.com/v1/oauth/token", formData, { headers: { "Content-Type": "application/x-www-form-urlencoded", }, }) .then((response) => { console.log("OAuth token response:", response.data); }); } catch (error) { console.error("Error requesting OAuth token:", error); }}Response
{ access_token: 'yta_NZuDqGkNOFp4DgwYbdR4TY_X15IEZsuWArACxprJJwp93MCG', token_type: 'bearer', expires_in: 2700, scope: 'poa:verify'}Errors
Due to the OAuth RFC, we will only return 400 or 403 error codes. Details of the error will be found in the response.
| Error Code | Details |
|---|---|
| 400 | Bad Request |
| 403 | Forbidden |
{ "error": 'invalid_scope', "error_description": 'empty list or unrecognised scope name'}SDKs
Methods have been added to the Yoti SDKs to generate the Access Tokens instead of the steps above.
use Yoti\Auth\AuthenticationTokenGenerator;$generator = AuthenticationTokenGenerator::builder() ->withSdkId('YOUR_SDK_ID') ->withPemFilePath('PATH_TO_YOUR_PEM') ->build();$response = $generator->generate(['poa:verify']); // must specify the scope hereGot a question? Contact us here.