tru.ID uses three methods of authentication. Basic Authentication with a client_id
and a client_secret
is used for managing access tokens. Bearer (Token) Authentication is used to authenticate all other APIs. Signed HTTP Message authentication is used with HTTP callbacks and webhooks made from the tru.ID platform to your application servers to enable you to verify the request origin.
All API requests must be made over HTTPS and API calls made over HTTP will fail.
This guide covers:
- Basic Authentication: when managing OAuth2 Access Tokens
- Bearer (Token) Authentication: with all product API endpoints that require authentication
- Signed HTTP Messages: for callback and webhook signing
Basic Authentication
In the request the Authentication
HTTP header value should be composed of Basic
plus a Base64 value generated by concatenating a client_id
, a colon (":") and a client_secret
.
Authorization: Basic Base64Encode(concat({client_id}, ':', {client_secret}))
client_id
and client_secret
credentials are associated with either a Workspace (a container for usage and billing for one or more projects) or a Project (a container for configuration related to the usage of one or more products).
Workspace credentials are created upon creation of your tru.ID account and your default workspace (a workspace that is automatically created for you). They can be retrieved from the tru.ID Console]. The client_secret
is only stored in browser localStorage and will be lost if you logout of the Console. You should store the client_secret
securely. If required, you can reset your Workspace client_secret
from the Console.
Project credentials are created upon creation of a new Project. Again, tru.ID does not store the client_secret
so you should make sure you store it securely. You cannot reset your Project client_secret
. Therefore, if you lose a Project client_secret
you should create a new Project and associated credentials.
Basic client_id
and client_secret
authentication is used with:
Using Basic Authentication
The following example shows using Basic Authentication via a Base64 encoded header to create an access token.
Example
curl -H "Authorization: Basic Base64({client_id}:{client_secret})" \-X POST -F 'grant_type=client_credentials' \-F 'scope=phone_check' \https://eu.api.tru.id/oauth2/v1/token
Basic client_id
and client_secret
authentication is used with:
Bearer (Token) Authentication
The tru.ID APIs expect the bearer token to be a OAuth2 Access Token. The access token should be within the Authorization
HTTP header, prefixed with Bearer
.
Authorization: Bearer {access_token}
An access token is created using a client_id
and client_secret
and the OAuth2 /token
endpoint.
Access tokens are used with:
Creating Access Tokens
When creating an access token a grant_type
of client_credentials
should be used along with a scope
. The scope defines the resources the access token enables access to. An access token can have multiple scopes by supplying scopes separated by a space within the scope
parameter.
The scopes and associated resources are:
projects
:/console/{version}/projects*
workspaces
:/console/{version}/workspaces/*
phone_check
:/phone_check/{version}/*
Examples
Create an access token with a scope of phone_check
to access /phone_check/*
resources.
curl -H "Authorization: Basic Base64({client_id}:{client_secret})" \-X POST -F 'grant_type=client_credentials' \-F 'scope=phone_check' \https://eu.api.tru.id/oauth2/v1/token
Create an access token with a scope of projects
and phone_check
to access /console/{version}/projects*
and /phone_check/{version}/*
resources.
curl -H "Authorization: Basic Base64({client_id}:{client_secret})" \-X POST -F 'grant_type=client_credentials' \-F 'scope=projects phone_check' \https://eu.api.tru.id/oauth2/v1/token
Using Access Tokens
The following example shows using access token authentication with to create a PhoneCheck.
Example
curl -X POST \-H 'Authorization: Bearer IdyB_OyXbxf8-nsLfJtIYXvM5kY9-rjAejX8IvUisOo.H7McspB--9lRA5hdzzEM5GcsS0D87nTtvP6oGQtHFNI' \-H 'Content-Type: application/json' \-d '{"phone_number": "447700900000"}''https://eu.api.tru.id/phone_check/v1/checks'
Access tokens are used with:
Signed HTTP Messages for Callbacks & Webhooks
Callback and webhook requests are signed according to the http message signing RFC.
You verify if a request has been sent by the tru.ID platform ,and hasn't been tampered with, by validating a signature received in the request.
The signature verification process is as follows:
Signature Payload + Signature + Signing Key -> Verify Signature -> Valid | Invalid
- Signature Payload - The payload used to create the signature. According to the RFC, it can be a combination of several parts of the request. For example, headers, request path, etc.
- Signature - The result of signing the payload with the private part of the tru.ID signing key.
- Signing Key - The public part of the key tru.ID used to sign the request.
Verifying the Signature
Public Signing Key
In order to verify a request signature you need to access the tru.ID public signing keys. They are exposed via an endpoint as a JSON Web Key Set (JWKS):
https://{data-residency}.api.tru.id/.well-known/jwks.json
This way, you always have access to the most recent set of signing keys. The JWKS is structured as follows:
{"keys": [{"kty": "RSA","e": "AQAB","use": "sig","kid": "c05a90fb91000fe6b1b3b988127ac3d8756101ca","alg": "RS256","n": "ALyUy3g7f4Rt4-OxGOildEOj_FW5wjT7l0ZoumqbiCyWCsW5XC2J4QadAkcFaSFOGGwJCW_lGi-y8sCx67y5vG6t3l6pRQOxtZ7_4wZ69A4347fKe4vSekzQyZVBcBHEH12Ea2P5Juu_OQpVJoTcvdF6e1oyQXubJ70xNCkYdYvkUoMy8VViQpXZuKttP_z6gc6tck04sJKXqQG8WRPur2wpIu_jcfdUDOe9RRFiq_P90E9YnsweiszsHz2gthDbhDY9kZz-U4o8yLdwRPqBcJOp6ye6V5-9uZz9rFNMitHDuNOd_vtJCdaccJGQ0Re2lQplRWYtVOEDHmfCzmDigss="}]}
There can be multiple keys inside the keys
attribute. It is important to note the following attributes in a key:
kid
- the key's unique identifier. One of these will match thekeyId
of the signature metadata recieved in the inbound request.alg
- the key's algorithm used to sign or verify requests. In this example it is RSA 256.
You should convert the JSON Web Key (JWK) into a PEM format, which should then be used to verify digital signatures. There are many libraries available that support this conversion.
Signature and Signature Payload
As per the RFC, the signature and corresponding metadata are in the Authorization
header of the request. Here's an example of a signed PhoneCheck callback:
POST / HTTP/1.1Host: enpcxr60rbv5h.x.pipedream.netAuthorization: Signature keyId="c05a90fb91000fe6b1b3b988127ac3d8756101ca",algorithm="rsa-sha256",headers="(request-target) host date x-4auth-callback digest",signature="PSNUgy/M7S2r62O6agIVvgopFyqLZv5UwpDu6ZBIqE2Me6kMy3vb8o+w8wVt+yVrLs8ZfJVL7LNnr5PcfNTxMvrfOp1pLuigvya42KCnN18RmfFubP54wQOnYb7j2UqS8CzJheSVzow8rOMqzZSMcXMUFvj1KQXETuRB0fJENw9RCvWMi59jg8TUD3S9jEWV0uw7d21j53+fdnaC7bIBF038A4NncLenbdTXSQX8R72CD4UAqA5/r/9VISoue/a2T3gC0MOueUOLC8AjLH864BV/2cDvyXz/9oTcm0SJ2CRaKdLaiolDx4LhaMGE4bC8xOSYXs2sGJwlsGJOIFCGGQ=="Content-Type: application/jsonDate: Fri, 18 Sep 2020 14:52:03 GMTDigest: SHA-256=36206190f57d5a7dc5d8e2b9fa57f21ce0ecfd31f45eaaf200de2d5d6bffbc60X-4auth-Callback: phone_checkContent-Length: 169{"check_id":"c2b0ac55-9184-4bbe-9ce9-2147fcd9e63e","status":"COMPLETED","match":true,"charge_amount":1.0,"charge_currency":"API","created_at":"2020-09-18T14:51:54+0000"}
The signature metadata of this request, extracted from the Authorization
header:
Signature keyId="c05a90fb91000fe6b1b3b988127ac3d8756101ca",algorithm="rsa-sha256",headers="(request-target) host date x-4auth-callback digest",signature="PSNUgy/M7S2r62O6agIVvgopFyqLZv5UwpDu6ZBIqE2Me6kMy3vb8o+w8wVt+yVrLs8ZfJVL7LNnr5PcfNTxMvrfOp1pLuigvya42KCnN18RmfFubP54wQOnYb7j2UqS8CzJheSVzow8rOMqzZSMcXMUFvj1KQXETuRB0fJENw9RCvWMi59jg8TUD3S9jEWV0uw7d21j53+fdnaC7bIBF038A4NncLenbdTXSQX8R72CD4UAqA5/r/9VISoue/a2T3gC0MOueUOLC8AjLH864BV/2cDvyXz/9oTcm0SJ2CRaKdLaiolDx4LhaMGE4bC8xOSYXs2sGJwlsGJOIFCGGQ=="
And the signature payload, extracted from the signature
metadata field:
PSNUgy/M7S2r62O6agIVvgopFyqLZv5UwpDu6ZBIqE2Me6kMy3vb8o+w8wVt+yVrLs8ZfJVL7LNnr5PcfNTxMvrfOp1pLuigvya42KCnN18RmfFubP54wQOnYb7j2UqS8CzJheSVzow8rOMqzZSMcXMUFvj1KQXETuRB0fJENw9RCvWMi59jg8TUD3S9jEWV0uw7d21j53+fdnaC7bIBF038A4NncLenbdTXSQX8R72CD4UAqA5/r/9VISoue/a2T3gC0MOueUOLC8AjLH864BV/2cDvyXz/9oTcm0SJ2CRaKdLaiolDx4LhaMGE4bC8xOSYXs2sGJwlsGJOIFCGGQ==
In order to verify the signature, you first reconstruct the signature payload. The headers
metadata field
describes what the payload should look like. In our example it will have the following values:
(request-target)
- the request method and the path and query of the effective request URI (examples)host
- the value ofHost
headerdate
- the value ofDate
headerx-4auth-callback
- the value ofx-4auth-callback
headerdigest
- the value ofDigest
header. The value is the body contents hashed with the algoritm identified in theSignature
header byalgorithm={algoritm_value}
and then Base64 encoded. For example,digest: SHA-256=hex(SHA256(Body))
.
The reconstructed payload will look like this (the newlines are important):
(request-target): post /host: enpcxr60rbv5h.x.pipedream.netdate: Fri, 18 Sep 2020 14:52:03 GMTx-4auth-callback: phone_checkdigest: SHA-256=36206190f57d5a7dc5d8e2b9fa57f21ce0ecfd31f45eaaf200de2d5d6bffbc60
The next step is identifying the key used to sign the request. The keyId
metadata value should match a kid
in the JWKS.
The final step is identifying the algorithm used to sign the request. The algorithm
metadata value tells us it was rsa-sha256
so we can now fully verify the signature.
Example
The following Node.js example show how to handle the PhoneCheck callback, retrieve the tru.ID JWKS, parse the HTTP signature and verify that HTTP messages was signed, and thus the request made, by tru.
const express = require('express')const app = express()const bodyParser = require('body-parser')const util = require('util')app.use(bodyParser.json())const jwksClient = require('jwks-rsa')const getSigningKey = util.promisify(keyClient.getSigningKey)const httpSignature = require('http-signature')const keyClient = jwksClient({jwksUri: `${API_BASE_URL}/.well-known/jwks.json`,})app.post('/callback', async (req, res) => {console.log('received callback', req.headers, req.body)const parsed = httpSignature.parseRequest(req)const keyId = parsed.keyIdconst jwk = await getSigningKey(keyId)const verified = httpSignature.verifySignature(parsed, jwk.getPublicKey())if (!verified) {res.sendStatus(400)return}res.sendStatus(200)})app.listen(3000)
The libraries used in the above example are:
- HTTP Message Signing: node-http-signature
- JWKS: node-jwks-rsa
HTTP message signing and JWKS libraries are available in other programming languages.
HTTP message signing is used with: