Signature Validation and Authentication

Overview

This document outlines the purpose, functionality, and implementation of a signature validation service written in Node.js using TypeScript. The service ensures secure communication by verifying the authenticity and integrity of incoming requests using a cryptographic signature scheme.

Features

The service performs the following key tasks:

  1. Canonicalization - Orders and structures the HTTP request’s method, URI, query parameters, headers, and body payload in a consistent manner to create a canonical string.

  2. Hashing - Hashes the ordered request body (payload) using SHA-256.

  3. Signature Generation - Generates a signature by combining the canonical string, a secret key, and a timestamp using the HMAC-SHA256 algorithm.

  4. Signature Validation - Compares the provided signature (from the incoming request) with the generated signature using a timing-safe comparison to mitigate timing attacks.

  5. Error Handling - Throws an error if the signatures do not match or if other issues (e.g., timestamp expiration) are detected.


Key Functions and Methods

1. orderJsonByKeysAscending(jsonObj: Record<string, any>): Record<string, any>

  • Purpose: Recursively orders all keys of a JSON object and sorts elements in arrays in ascending order.

  • Objects: Both the top-level keys and the keys of any nested objects are sorted alphabetically.

  • Arrays:

    • If the array contains primitive values (e.g., strings, numbers), the elements are sorted in ascending order.

    • If the array contains objects, the elements are sorted based on the lexicographical order of their JSON string representations, after their keys have been sorted.

    • Arrays containing mixed types (both primitives and objects) or complex structures are returned as is without sorting their elements.

  • Input: Any JavaScript value, including:

  • Primitive types: string, number, boolean, null, undefined .

  • Objects: Objects with key-value pairs, including nested objects.

  • Arrays: Arrays containing any type of elements (primitives, objects, or mixed).

  • Output: Returns a new JSON object or value with the following modifications:

  • Objects: All keys are sorted alphabetically at every level of nesting.

  • Arrays of primitives: Elements are sorted in ascending order.

  • Arrays of objects: Elements are sorted based on the lexicographical order of their JSON string representations.

  • Mixed-type arrays: Arrays containing mixed types are returned as is without sorting their elements.

If the input is invalid (e.g., null, undefined, or not an object/array), the function returns the input as is without modification.

Example Usage:

Example Output:

  1. createCanonicalQueryString(queryParams: { [key: string]: string }): string

  • Purpose: Sorts query parameters by key and concatenates them into a canonical string.

  • Input: An object containing query parameters.

  • Output: A query string with sorted keys.

Example Usage:

  1. createCanonicalHeadersString(headers: { [key: string]: string }): string

  • Purpose: Sorts specific headers (like x-authorization-api-key and x-authorization-timestamp) alphabetically and formats them.

  • Input: An object containing headers.

  • Output: A string with sorted headers.

Example Usage:

  1. hashPayload(payload: Record<string, any>): string

  • Purpose: Serializes and hashes a sorted payload using SHA-256.

  • Input: A JSON object (payload).

  • Output: A SHA-256 hash string

Example Usage:

  1. createCanonicalString(method: string, uri: string, queryParams: { [key: string]: string }, headers: { [key: string]: string }, body: Record<string, any>): string

  • Purpose: Creates a canonical string that combines the http method, URI, query parameters, headers, and hashed payload.

  • Input: HTTP method, URI, query parameters, headers, and body payload.

  • Output: A canonical string for signing with format ${method}\n${uri}\n${canonicalQueryString}\n${canonicalHeaders}\n${hashedPayload}

Example Usage:

  1. generateCanonicalSignature(secretKey: string, stringToSign: string, timestamp: number): string

  • Purpose: Generates a HMAC-SHA256 signature using a secret key, the string to sign, and a timestamp.

  • Input: Secret key, canonical string, and timestamp.

  • Output: A cryptographic signature string

Example Usage:

Conclusion

This service provides a robust solution for validating incoming HTTP requests based on cryptographic signatures. It ensures the integrity and authenticity of the request by comparing a provided signature with a generated one, preventing tampering and unauthorized access.

Was this helpful?