HMAC Authentication for API Requests

Implementing Secure API Authentication
Author: Taylor McNeil
Organization: NCR Corporation
Purpose: Internal & Public Developer Platform Documentation

Document Overview

This step-by-step tutorial was originally written for NCR's internal and public developer platform. It explains how to securely implement HMAC-based API authentication using sample code and integration instructions. It was used to onboard external developers and reduce API support tickets across multiple product teams.

Current Status: A version of this documentation exists today on the NCR DevEx portal.

What is HMAC Authentication?

Access Key authentication signs each HTTP request with a unique HMAC (Hash-based Message Authentication Code), adding an extra layer of security to your API calls.

🔑 Shared Key

Identifies the client making the request

🔐 Secret Key

Used to generate the HMAC signature

HMAC Authentication in Action

GET /provisioning/user-profiles HTTP/1.1 Accept: application/json Authorization: AccessKey e63ca6a9ca2e4db2bc13b741e7488437:Ysvt4LcqSnmIjvPbolVm2bS/zDXdqnYBtgtG+lWMlLI6uJp1MJiW34OVNtYrYA/B+6T/NDqhqFxbtlvuIFBliw== Date: Wed, 26 Jun 2019 17:38:30 GMT Host: gateway.ncrplatform.com
Authorization Header Format

The Authorization header uses the AccessKey scheme in the format:

Authorization: AccessKey {sharedKey}:{signature}

Why Use HMAC?

Production Environment Benefits:

Implementation

GitHub Repository

You can find this guide, along with example code, in the public ncr-bsp-hmac GitHub repository.

View Complete Code Examples

To generate the Authorization header, you'll need two essential components:

HMAC Generation Process Overview:

  1. 1Extract the date value from the Date HTTP header for signature inclusion
  2. 2Convert to ISO 8601 format for cross-system consistency
  3. 3Create a unique signing key by concatenating secret key and date
  4. 4Build signature payload from HTTP request details
  5. 5Generate HMAC by encrypting the payload with the signing key

Step 1: Extract Date Value

Pull the date value from the Date HTTP header (this will be used as part of the signature).

JavaScript - Date Extraction
// Extract current date for signature
let date = new Date();

Step 2: Convert to ISO 8601 Format

Convert the native date to ISO 8601 format. This ensures consistency across systems when signing and verifying the request.

JavaScript - ISO 8601 Conversion
// Convert to ISO 8601 format (UTC, to the millisecond)
let isoDate = date.toISOString().slice(0, 19) + ".000Z";

Step 3: Create Unique Signing Key

Create a unique key by concatenating your secret key and the date, separated by a colon:

JavaScript - Unique Key Generation
// Build signing key using secret key and ISO-formatted date
const uniqueKey = secretKey + isoDate;

Step 4: Build Signature Payload

Build the signature payload using details from the HTTP request. This is what gets signed. Include the following in order:

String Construction

Make sure each line is separated by a newline (\n) when constructing the signature string.

JavaScript - Signature Payload Construction
// Normalize URI: remove domain, encode path + query string
let uri = encodeURI(requestURL.replace(/^https?:\/\/[^/]+\//, "/"));

// Construct signature string from request components
let toSign = httpMethod + "\n" + uri;

if (contentType) {
    toSign += "\n" + contentType.trim();
}
if (contentMD5) {
    toSign += "\n" + contentMD5.trim();
}
if (nepApplicationKey) {
    toSign += "\n" + nepApplicationKey.trim();
}
if (nepCorrelationID) {
    toSign += "\n" + nepCorrelationID.trim();
}
if (nepOrganization) {
    toSign += "\n" + nepOrganization.trim();
}
if (nepServiceVersion) {
    toSign += "\n" + nepServiceVersion.trim();
}

Step 5: Generate HMAC Signature

Generate the HMAC by encrypting the signature payload using the key from Step 3.

JavaScript - HMAC Generation with crypto-js
// Generate HMAC using crypto-js library
const key = hmacSHA512(toSign, oneTimeSecret);
const hmacKey = Base64.stringify(key);

// Visit hmac.js for more implementation details

Complete JavaScript Implementation

Below is a complete function to generate an HMAC signature in Node.js using crypto-js. This function builds the signature string from key request elements, signs it with the user's secret key, and returns the properly formatted Authorization header value.

hmac-generator.js - Complete Implementation
const hmacSHA512 = require("crypto-js/hmac-sha512");
const Base64 = require("crypto-js/enc-base64");

/**
 * Generate HMAC Authorization header.
 *
 * @param {string} sharedKey - Your shared key
 * @param {string} secretKey - Your secret key
 * @param {Date} date - Native JS date object
 * @param {string} httpMethod - GET, POST, etc.
 * @param {string} requestURL - Full request URL
 * @param {string} [contentType=application/json] - Optional
 * @param {string} [contentMD5] - Optional
 * @param {string} [nepApplicationKey] - Optional
 * @param {string} [nepCorrelationID] - Optional
 * @param {string} [nepOrganization] - Optional
 * @param {string} [nepServiceVersion] - Optional
 * @returns {string} - Authorization header value in format `sharedKey:signature`
 */
module.exports = function ({
    sharedKey,
    secretKey,
    date,
    httpMethod,
    requestURL,
    contentType = "application/json",
    contentMD5,
    nepApplicationKey,
    nepCorrelationID,
    nepOrganization,
    nepServiceVersion,
}) {
    // Normalize URI: remove domain, encode path + query string
    let uri = encodeURI(requestURL.replace(/^https?:\/\/[^/]+\//, "/"));
    
    // Convert date to ISO 8601 format (UTC, to the millisecond)
    const isoDate = date.toISOString().slice(0, 19) + ".000Z";
    
    // Build signing key using secret key and ISO-formatted date
    const oneTimeSecret = secretKey + isoDate;
    
    // Construct signature string from request components
    let toSign = `${httpMethod}\n${uri}`;
    
    if (contentType) toSign += `\n${contentType.trim()}`;
    if (contentMD5) toSign += `\n${contentMD5.trim()}`;
    if (nepApplicationKey) toSign += `\n${nepApplicationKey.trim()}`;
    if (nepCorrelationID) toSign += `\n${nepCorrelationID.trim()}`;
    if (nepOrganization) toSign += `\n${nepOrganization.trim()}`;
    if (nepServiceVersion) toSign += `\n${nepServiceVersion.trim()}`;
    
    // Generate HMAC and return header value
    const key = hmacSHA512(toSign, oneTimeSecret);
    return `${sharedKey}:${Base64.stringify(key)}`;
};

Usage Example

Example Implementation
// Import the HMAC generator
const generateHMAC = require('./hmac-generator');

// Your API credentials
const sharedKey = 'e63ca6a9ca2e4db2bc13b741e7488437';
const secretKey = 'your-secret-key-here';

// Request details
const requestConfig = {
    sharedKey,
    secretKey,
    date: new Date(),
    httpMethod: 'GET',
    requestURL: 'https://gateway.ncrplatform.com/provisioning/user-profiles',
    contentType: 'application/json'
};

// Generate the authorization header
const authHeader = generateHMAC(requestConfig);

// Use in your HTTP request
const headers = {
    'Authorization': `AccessKey ${authHeader}`,
    'Accept': 'application/json',
    'Date': requestConfig.date.toUTCString(),
    'Host': 'gateway.ncrplatform.com'
};