Authenticate using mutual TLS

Leveraging the mutual Transport Layer Security (mTLS) encrypted protocol for client authentication ensures a secure and seamless identity experience that meets FAPI 2.0 (Financial-grade API) requirements. This approach overcomes the vulnerability of compromising a client identity by mutually validating client and server with X.509 certificates on the network level and preventing token theft through certificate-based binding. For more information, refer to RFC 8705 (OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens).

Note

This guide describes the integration steps for a simple client authentication flow, for example when a client needs to obtain a client access token to authorize subsequent API calls. For more use cases, see Next steps.

How it works

In user authentication and client authentication scenarios, mutual client and Mosaic certificate authentication occurs during the TLS handshake before any data is sent over HTTPS. During the handshake, the client presents a self-signed certificate which is then forwarded to Mosaic and is compared against the JWKS that was provided. The JWKS includes information about the key type, key usage, etc. For details on JWKS structure, see RFC 7517.

For example, a client requests a token (Step 3) using mTLS authentication. Upon the receiving the client access token, your app validates it (Step 4) and then uses this token to authorize the call.

ClientMosaicNetwork level: TLS handshakeService level (HTTPS)Client certificateValidate certificateMosaic CertificateValidate certificateget client token: /oidc/tokentokenvalidate tokenrequest, authorized with tokenreturn resultClientMosaic

Step 1: Create JWKS

Use strong cryptography to secure the client authentication process. Start by implementing cryptographic key pair and certificate generation. Then, convert the public certificate generated into the JWK format, for example, using the Node.js jose library. The keys are generated and converted once during initial configuration but later you can reissue new keys if necessary.

Below is an example of JWKS:

Copy
Copied
{
  "keys": [
    {
      "kty": "RSA",
      "use": "sig",
      "kid": "CpTM4iGliMoklgafvDXA4TbclcynyD_wMgjOfhiCUUE",
      "x5c": ["MIIDazCCAlOgAwIBAgIURndmlRmyo9snXN45B..."],
      "alg": "RS256",
      "e": "AQAB",
      "n": "vXoSLHWtv_t7f78rvKGPkLDuc-9MkzvLiWf-iUfQm..."
    }
  ]
}
Field Description
kty Key type (RSA)
use Signature use
kid Key ID
x5c X.509 cert(s)
alg Signing algorithm
n, e RSA key details

Step 2: Submit certificate to Mosaic

Configure your Mosaic client to use mTLS as an authentication method and provide the JWKS you've generated in Step 1. This will be used to prove the client identity to the server.

  • For OIDC implementations : from the Admin Portal under Applications , click your application and proceed to the OIDC client settings to update the authentication method to Self-signed mTLS and submit JWKS. If you don't already have an application, you'll need to create one first (see Create application ).
FAPI 2.0 compliance

Consider enabling "Enforce FAPI 2.0 compliance" when creating a client. See Manage clients

  • If using SSO Service : from the Admin Portal under SSO Service , navigate to Service Definition > Client groups and proceed to the OIDC client settings to update the authentication method to self-signed mTLS and submit JWKS.
Tip

You can rotate keys whenever needed. Update JWKS in the OIDC client configuration in the Admin Portal.

Step 3: Authenticate client

Authenticate a client and obtain a client access token by sending a POST request like the one below to the /oidc/token endpoint, along with the parameters listed below.

Field Description
cert A self-signed certificate file (e.g., client-cert.pem).
key A client private key file (e.g., client-key.pem).
client_id Client ID. Can be obtained from client settings in the Mosaic Admin Portal.
grant_type Should be set to client_credentials.
Copy
Copied
import https from 'https';
import fs from 'fs';
import fetch from 'node-fetch';

// Load mTLS credentials
const agent = new https.Agent({
  cert: fs.readFileSync('CLIENT_CERTIFICATE_FILE'),
  key: fs.readFileSync('CLIENT_PRIVATE_KEY_FILE'),
  rejectUnauthorized: true
});

async function run() {
  const formData = new URLSearchParams({
    client_id: 'CLIENT_ID',
    grant_type: 'client_credentials'
  });

  const resp = await fetch(
    'https://api.transmitsecurity.io/cis/oidc/token',
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      body: formData.toString(),
      agent // Enables mTLS
    }
  );

  const data = await resp.text();
  console.log(data);
}

run();

Step 4: Validate tokens

The /oidc/token response includes a client access token. Tokens must be validated as described here. If you enable token binding, the generated token will be bound to a certificate (cnf claim). Validate the token signatures using the public key retrieved from the JWKS endpoint:

Copy
Copied
  https://api.transmitsecurity.io/cis/oidc/jwks
Note

Cache a response returned by /oidc/jwks for further reuse to avoid reaching API rate limits and prevent latency issues. Signing keys don't change often. Yet, if token validation fails due to a signature mismatch, try updating the cache first and then revalidating the token signature.

Next steps

When authentication with mTLS is enabled, a self-signed certificate and key should be used in all calls that typically leverage client secrets, including:

  • Obtaining client access tokens with /oidc/token
  • Obtaining user access tokens with /oidc/token
  • Initiating a PAR request with /oidc/request
  • Login a user with /oidc/auth
  • Initiating a backchannel flow with /oidc/backchannel
  • Initiating a device flow with /oidc/device/auth
  • Revoking a token /oidc/token/revocation
  • etc.
Note

For implementation details, see the respective guides or API reference. The steps mentioned in this guide remain relevant for these integrations as well.

For example, below are sample requests leveraging mTLS in the PAR and CIBA flows:

PAR with PKCECIBA
Copy
Copied
import https from 'https';
import fs from 'fs';
import fetch from 'node-fetch';

// Load mTLS credentials
const agent = new https.Agent({
  cert: fs.readFileSync('CLIENT_CERTIFICATE_FILE'),
  key: fs.readFileSync('CLIENT_PRIVATE_KEY_FILE'),
  rejectUnauthorized: true
});

async function run() {
  const formData = {
    client_id: 'CLIENT_ID',
    redirect_uri: 'REDIRECT_URI',
    response_type: 'code',
    scope: 'openid',
    code_challenge: 'HASHED_CODE_VERIFIER',
    code_challenge_method: 'S256'
  };

  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/oidc/request`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      body: new URLSearchParams(formData).toString(),
      agent // Enables mTLS
    }
  );

  const data = await resp.text();
  console.log(data);
}

run();
Copy
Copied
import https from 'https';
import fs from 'fs';
import fetch from 'node-fetch';

// Load mTLS credentials
const agent = new https.Agent({
  cert: fs.readFileSync('CLIENT_CERTIFICATE_FILE'),
  key: fs.readFileSync('CLIENT_PRIVATE_KEY_FILE'),
  rejectUnauthorized: true
});

async function run() {
  const formData = {
    client_id: 'CLIENT_ID',
    scope: 'openid',
    login_hint: 'LOGIN HINT',
    binding_message: 'MESSAGE'
  };

  const resp = await fetch(
    `https://api.transmitsecurity.io/cis/oidc/backchannel`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      body: new URLSearchParams(formData).toString(),
      agent // Enables mTLS
    }
  );

  const data = await resp.text();
  console.log(data);
}

run();