API Authentication

AI Tools

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:

  1. Generate a JWT token using a Yoti private key (.pem file)

  2. Acquire an OAuth Access Token using this JWT Token

  3. 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.

Before you start

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);
using System; using System.IO; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using Microsoft.IdentityModel.Tokens; using System.Security.Cryptography; using System.Text; class Program { static void Main() { string pemPath = "PATH_TO_PEM.pem"; string sdkId = "YOUR_SDK_ID"; string authURL = "https://auth.api.yoti.com/v1/oauth/token"; string token = BuildJWT(pemPath, sdkId, authURL); Console.WriteLine("Generated JWT: " + token); } static string BuildJWT(string pemPath, string sdkId, string authURL) { // Read PEM private key string pem = File.ReadAllText(pemPath); var rsa = RSA.Create(); rsa.ImportFromPem(pem.ToCharArray()); var securityKey = new RsaSecurityKey(rsa); var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.RsaSsaPssSha384); var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); var claims = new[] { new Claim(JwtRegisteredClaimNames.Iss, "sdk:" + sdkId), new Claim(JwtRegisteredClaimNames.Sub, "sdk:" + sdkId), new Claim(JwtRegisteredClaimNames.Aud, authURL), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(JwtRegisteredClaimNames.Exp, (now + 30 * 60).ToString()), new Claim(JwtRegisteredClaimNames.Iat, now.ToString()) }; var tokenDescriptor = new SecurityTokenDescriptor { Issuer = "sdk:" + sdkId, Subject = new ClaimsIdentity(claims), Audience = authURL, Expires = DateTimeOffset.FromUnixTimeSeconds(now + 30 * 60).UtcDateTime, SigningCredentials = credentials }; var handler = new JwtSecurityTokenHandler(); var securityToken = handler.CreateJwtSecurityToken(tokenDescriptor); return handler.WriteToken(securityToken); } }
// Click to edit code
// Click to edit code

Response

eyJhbGciOiJQUzM4NCIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzZGs6OWVlM2RiNmUtM2UwYy00ZTg1LWIyYTMtOTliZTMxNmY5MDJhIiwic3ViIjoic2RrOjllZTNkYjZlLTNlMGMtNGU4NS1iMmEzLTk5YmUzMTZmOTAyYSIsImF1ZCI6Imh0dHBzOi8vYXV0aC5hcGkueW90aS5jb20vdjEvb2F1dGhvdG9rZW4iLCJqdGkiOiI2N2RlMjM0OC0xOWYyLTRmODctOWE4YS01YTEzZjM5ZWIyNTEiLCJleHAiOjE3NzA3Mjc3MjAsImlhdCI6MTc3MDcyNTkyMH0.lgCJJ57OeV_mJGw91DR71ZRHgOWLbzSK1PYhqC59333PB4fBzxcz4chWHo5Go7vL_vGNYpJQJI-UuuBbZNNCF5OuDnWSQNHIcB_QW7OsYa6al8PcbC21oZrbdBSTbJdk_7S7ARo5noXKf4tH_cnbUkL7I50lMPnJngZBfpLb6ATAjbSWhsKhrT--GlWKs-NB58z8V-PDmG6c93PF7krA6oB964OtIMEHtFiXfbQB61xiYOUyR94G-pLUbftDnvXhyxmT_MZXaZRSIfIy8l75scBoHv96ylSH1yqBcw5vdQoye-Cd6YXGlJLS5QM9fqFm9JYIeOh7DP7Vo4PRTIFlcQ

OAuth 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/token

The 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); } }
using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main() { string token = "YOUR_JWT_TOKEN" var formData = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("grant_type", "client_credentials"), new KeyValuePair<string, string>("scope", "poa:verify"), new KeyValuePair<string, string>("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"), new KeyValuePair<string, string>("client_assertion", token) }); using var httpClient = new HttpClient(); try { var response = await httpClient.PostAsync("https://auth.api.yoti.com/v1/oauth/token", formData); var responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine("OAuth token response: " + responseBody); } catch (Exception ex) { Console.Error.WriteLine("Error requesting OAuth token: " + ex); } } }
COMING SOON
COMING SOON

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 here
// Click to edit code
// Click to edit code
// Click to edit code
// Click to edit code