Skip to main content

Overview

This tutorial shows you how to authenticate to the Issuing API using Node.js by generating signed JSON Web Tokens (JWTs).

In This Recipe

  1. Install Dependencies
  2. Create signJWT function
  3. Usage - GET Request
  4. Usage - POST Request

Prerequisites

  • Node.js 16 or higher
  • Your Issuing API credentials (Access Key and RSA key pair)

Step 1: Install Dependencies

Install the required npm packages:
npm install jsonwebtoken

Step 2: Create signJWT Function

Create a utility function to handle JWT signing with SHA256 body hashing:
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
const fs = require('fs');

/**
 * Sign a JWT for Issuing API authentication
 * @param {string} privateKeyPath - Path to the private key file
 * @param {string} accessKey - Your Access Key (UUID)
 * @param {string} uri - The API endpoint URI (e.g., '/v1/ping')
 * @param {string} method - HTTP method (GET, POST, etc.)
 * @param {string} requestBody - The request body (empty string for GET requests)
 * @returns {string} Signed JWT token
 */
function signJWT(privateKeyPath, accessKey, uri, method, requestBody) {
  // Read private key
  const privateKey = fs.readFileSync(privateKeyPath, 'utf8');

  // Hash request body with SHA256
  const hashedBody = crypto
    .createHash('sha256')
    .update(requestBody)
    .digest('hex');

  const now = Math.floor(Date.now() / 1000);

  // Create JWT payload
  const payload = {
    sub: accessKey,
    iat: now,
    exp: now + 30, // 30 second expiration
    uri: uri,
    method: method,
    body: hashedBody,
  };

  // Sign the token
  const token = jwt.sign(payload, privateKey, {
    algorithm: 'RS256',
    header: {
      typ: 'JWT',
      alg: 'RS256',
    },
  });

  return token;
}

module.exports = { signJWT };

Important Notes:

  • Request body is hashed with SHA256 before signing
  • 30 second expiration for security
  • Private key format: PEM-encoded RSA private key
  • Empty body: Use empty string "" for GET requests

Step 3: Usage - GET Request

Make a simple GET request to test connectivity:
const { signJWT } = require('./auth'); // Assuming the signJWT function is in auth.js

const privateKeyPath = './utgl-access.private'; // Path to your private key
const accessKey = '6e33a078-99ed-4aa1-8e67-b0e19e9475fd'; // Replace with your access key

const uri = '/v1/ping';
const method = 'GET';
const requestBody = '';

async function makeGetRequest() {
  try {
    // Generate JWT
    const jwtToken = signJWT(privateKeyPath, accessKey, uri, method, requestBody);

    // Make HTTP request
    const response = await fetch(`https://sandbox.access.utgl.io${uri}`, {
      method: method,
      headers: {
        'Authorization': `Bearer ${jwtToken}`,
        'Content-Type': 'application/json',
      },
    });

    const data = await response.json();
    console.log(`${method} ${uri} ${response.status}`);
    console.log('Response:', data);
  } catch (error) {
    console.error('Error:', error.message);
  }
}

makeGetRequest();

Step 4: Usage - POST Request

Make a POST request with a request body:
const { signJWT } = require('./auth'); // Assuming the signJWT function is in auth.js

const privateKeyPath = './utgl-access.private'; // Path to your private key
const accessKey = '6e33a078-99ed-4aa1-8e67-b0e19e9475fd'; // Replace with your access key

const uri = '/v1/ping';
const method = 'POST';
const requestBody = JSON.stringify({ message: 'Hello, world!' });

async function makePostRequest() {
  try {
    // Generate JWT
    const jwtToken = signJWT(privateKeyPath, accessKey, uri, method, requestBody);

    // Make HTTP request
    const response = await fetch(`https://sandbox.access.utgl.io${uri}`, {
      method: method,
      headers: {
        'Authorization': `Bearer ${jwtToken}`,
        'Content-Type': 'application/json',
      },
      body: requestBody,
    });

    const data = await response.json();
    console.log(`${method} ${uri} ${response.status}`);
    console.log('Response:', data);
  } catch (error) {
    console.error('Error:', error.message);
  }
}

makePostRequest();

Using with Axios

If you prefer using Axios instead of fetch:
const axios = require('axios');
const { signJWT } = require('./auth');

const privateKeyPath = './utgl-access.private';
const accessKey = '6e33a078-99ed-4aa1-8e67-b0e19e9475fd';

async function makeRequest() {
  const uri = '/v1/accounts';
  const method = 'GET';
  const requestBody = '';

  const jwtToken = signJWT(privateKeyPath, accessKey, uri, method, requestBody);

  try {
    const response = await axios({
      method: method,
      url: `https://sandbox.access.utgl.io${uri}`,
      headers: {
        'Authorization': `Bearer ${jwtToken}`,
        'Content-Type': 'application/json',
      },
    });

    console.log(`${method} ${uri} ${response.status}`);
    console.log('Response:', response.data);
  } catch (error) {
    console.error('Error:', error.response?.data || error.message);
  }
}

makeRequest();

TypeScript Version

For TypeScript projects:
import jwt from 'jsonwebtoken';
import crypto from 'crypto';
import fs from 'fs';

interface JWTPayload {
  sub: string;
  iat: number;
  exp: number;
  uri: string;
  method: string;
  body: string;
}

export function signJWT(
  privateKeyPath: string,
  accessKey: string,
  uri: string,
  method: string,
  requestBody: string
): string {
  const privateKey = fs.readFileSync(privateKeyPath, 'utf8');

  const hashedBody = crypto
    .createHash('sha256')
    .update(requestBody)
    .digest('hex');

  const now = Math.floor(Date.now() / 1000);

  const payload: JWTPayload = {
    sub: accessKey,
    iat: now,
    exp: now + 30,
    uri: uri,
    method: method,
    body: hashedBody,
  };

  return jwt.sign(payload, privateKey, {
    algorithm: 'RS256',
    header: {
      typ: 'JWT',
      alg: 'RS256',
    },
  });
}

Common Pitfalls

Body Hashing is Critical! The signJWT function automatically hashes the request body with SHA256 before including it in the JWT claims. Make sure you pass the exact same request body to both signJWT() and your HTTP request.
Token Expiration: JWT tokens expire after 30 seconds. Generate a fresh token for each API request to avoid authentication errors.
JSON Stringify Consistency: When sending JSON bodies, use JSON.stringify() consistently. The exact string representation must match between signing and sending.

Next Steps