๐Ÿ” Authentication and 2FA Management

How to implement authentication quickly and securely with the AUDD Mint.

When connecting to our API, security is our top priority. To protect sensitive data and ensure only trusted clients gain access, we use a layered authentication model combining IP whitelisting, mutual TLS, credentials, and two-factor authentication.

๐Ÿšง

IP Whitelisting Required

Before you can begin interacting with the AUDD Mint, your IP address(es) will need to be whitelisted within our system. Reach out to our team via your dedicated slack channel to complete this step.

๐Ÿงฉ mTLS

We require you to create your own root CA and provide its certificate to us. You will then use a client certificate signed by that CA.

You will then be able to use the client.key and client.pem in your https clients. For example, using nodejs:

const axios = require('axios');
const https = require('https');
const fs = require('fs');

// Read the key and cert files
const key = fs.readFileSync('client.key');
const cert = fs.readFileSync('client.pem');

const httpsAgent = new https.Agent({
  key: key,
  cert: cert,
  rejectUnauthorized: true,
});

axios.get('https://distributor-api-staging.audd.digital/...', {
  httpsAgent: httpsAgent,
})

๐Ÿšช Gaining Access

When your account is set up, youโ€™ll receive an email at your nominated address with a link to create your password. Your username and password let our system recognise you and give you access.

Alongside your login details, weโ€™ll also generate an API key for you. This key is used during the initial authentication step to securely connect to our system. Once youโ€™ve authenticated, youโ€™ll receive a JWT (JSON Web Token) - from that point on, youโ€™ll use the JWT as a bearer token for ongoing access instead of the API key.

๐Ÿ”‘ 2FA Setup

Even with IP whitelisting, mTLS, and passwords, 2FA adds protection against stolen credentials or certificates by requiring a possession factor tied to the user or system. This ensures stronger identity assurance for both humans and machines and meets compliance expectations for strong authentication.

To set up 2FA via the API, you will first need to generate a shared secret by making a GET request to /login/two-factor/secret

curl --cert client.crt --key client.key https://distributor-api-staging.audd.digital/login/two-factor/secret -H "Authorization: 5pv515CM2uL4VAEXu5zEVDf..."

This will return a json object of the form

{"secret":"2Qs5WzZ3TzEvBhEIcIov","secretBase32Encoded":"3EFTSWZWO5HTCLYGCEEHBCRP"}

You will use the secretBase32Encoded value as the secret for TOTP generation. As an example, this code can be generated using the otpauth npm package with the following code:

import otpauth from "otpauth";
const totp = new otpauth.TOTP({
  secret: "3EFTSWZWO5HTCLYGCEEHBCRP",
  issuer: "AUDD",
  label: "[email protected]",
});
const code = totp.generate();

Now that you are able to generate TOTP codes using the shared secret, you need to register that shared secret as an MFA method in our systems. This is done by making a POST request to /mfa/enable:

This endpoint registers your MFA secret and returns a list of recovery codes.

๐Ÿ“˜

How to use the recovery codes

The recovery codes can be used in place of the TOTP when logging into the Portal or via the API.

๐Ÿ‘ค Logging In

You are now set up to log in and begin using the rest of our API. To login, make a POST request to /mfa/login

This endpoint returns a JWT (token) and refresh token. You will then be able to authenticate to the rest of our API by using the JWT as a bearer token in the Authorization header: Authorization: Bearer ${token}.

The JWT expires in 5 minutes and can be refreshed using the refresh token which expires in 15 minutes.

To refresh your JWT, make a POST request to /login/jwt/refresh

This endpoint returns a new token and refresh token that you can use to continue accessing our API.

Typescript Example

Below is a comprehensive example showing programmatic MFA setup and login.

/**
 * Example script demonstrating how to authenticate with the AUDD Digital API.
 * Uses mutual TLS (mTLS) and TOTP-based multi-factor authentication (MFA).
 *
 * - Loads credentials and certificates from environment variables
 * - Generates and registers a new MFA secret via /login/two-factor/secret
 * - Enables MFA with /mfa/enable and supports login via /mfa/login
 * - Demonstrates access token usage and refresh token flow
 *
 * Intended for developers integrating with the AUDD API sandbox to test secure
 * authentication workflows.
 */

import https from "https";
import fs from "fs";
import axios from "axios";
import { TOTP } from "otpauth";

// -----------------------------------------------------------------------------
// Configuration and environment variables
// -----------------------------------------------------------------------------

// Load paths to client key/cert for mTLS authentication
const clientKeyFile = process.env.CLIENT_KEY_FILE!;
const clientCertFile = process.env.CLIENT_CERT_FILE!;

// Load credentials and secrets from environment variables
const apiKey = process.env.API_KEY!; // API key used to authorize setup calls
const mfaSecret = process.env.MFA_SECRET!; // Pre-registered MFA secret for login
const username = process.env.USERNAME!; // User account name
const password = process.env.PASSWORD!; // User password

// Sandbox API base URL (change to production as needed)
const url = "https://api.sandbox.audd.digital";

// -----------------------------------------------------------------------------
// HTTPS agent setup for mutual TLS (mTLS)
// -----------------------------------------------------------------------------

// Create an HTTPS agent using client certificate and key for secure communication
const agent = new https.Agent({
  key: fs.readFileSync(clientKeyFile), // private key
  cert: fs.readFileSync(clientCertFile), // client certificate
});

// Create an Axios HTTP client pre-configured with the mTLS agent and base URL
const client = axios.create({
  baseURL: url,
  httpsAgent: agent,
});

// -----------------------------------------------------------------------------
// Helper: Generate a TOTP (Time-based One-Time Password)
// -----------------------------------------------------------------------------

// Generates a 6-digit TOTP using the shared MFA secret
const generateTotp = (secret: string) => {
  const generator = new TOTP({ secret });
  return generator.generate();
};

// -----------------------------------------------------------------------------
// Step 1: Set up Multi-Factor Authentication (MFA)
// -----------------------------------------------------------------------------

const setUpMfa = async () => {
  // Request a new MFA secret from the API (only needed once per user)
  let res = await client.get("/login/two-factor/secret", {
    headers: { Authorization: apiKey },
  });

  // Extract the secret (Base32-encoded) from the response
  const secret = res.data.secretBase32Encoded;

  // Display the secret so the user can store it (e.g., in a password manager or env var)
  console.log(`MFA secret: ${secret}`);

  // Construct the registration payload
  const body = {
    username,
    password,
    mfaSecretB32: secret,
    mfaCode: generateTotp(secret), // Generate a code using the new secret
  };
  console.log(`Request body: `, body);

  // Send the MFA enablement request to register the secret
  res = await client.post("/mfa/enable", body, {
    headers: { Authorization: apiKey },
  });
};

// -----------------------------------------------------------------------------
// Step 2: Log in with MFA credentials
// -----------------------------------------------------------------------------

const loginMfa = async () => {
  // Send username, password, and a freshly generated TOTP code
  let res = await client.post(
    "/mfa/login",
    {
      username,
      password,
      mfaCode: generateTotp(mfaSecret),
    },
    {
      headers: {
        Authorization: apiKey,
      },
    }
  );

  // The response includes an access token, refresh token, and expiry info
  return {
    token: res.data.token,
    refreshToken: res.data.refreshToken,
    expiryInstant: res.data.tokenExpirationInstant,
  };
};

// -----------------------------------------------------------------------------
// Step 3: Refresh JWT access token
// -----------------------------------------------------------------------------

const refreshToken = async (tokens: {
  token: string;
  refreshToken: string;
}) => {
  // Exchange the expired/expiring access token for a new one
  let res = await client.post("/login/jwt/refresh", {
    accessToken: tokens.token,
    refreshToken: tokens.refreshToken,
  });

  // Return the refreshed tokens
  return {
    token: res.data.token,
    refreshToken: res.data.refreshToken,
  };
};

// -----------------------------------------------------------------------------
// Step 4: Example protected API request (fetch user data)
// -----------------------------------------------------------------------------

const getUsers = async (token: string) => {
  // Access a protected endpoint using the Bearer token
  const res = await client.get("/users", {
    headers: { Authorization: `Bearer ${token}` },
  });
  return res.data;
};

// -----------------------------------------------------------------------------
// End-to-end example request flow
// -----------------------------------------------------------------------------

const exampleRequestFlow = async () => {
  // 1. Log in with MFA to obtain tokens
  const tokenInfo = await loginMfa();
  console.log("initial login result:", tokenInfo);

  // 2. Make an authenticated API request
  const userRes = await getUsers(tokenInfo.token);
  console.log("user get data:", userRes);

  // 3. Refresh the access token using the refresh token
  const refreshRes = await refreshToken(tokenInfo);
  console.log("refresh result:", refreshRes);
};

// -----------------------------------------------------------------------------
// Execution
// -----------------------------------------------------------------------------

// Run MFA setup only once per user to register the secret
// setUpMfa();

// Run the example authentication and request flow
exampleRequestFlow();