Skip to content

Instantly share code, notes, and snippets.

@The-Don-Himself
Last active March 10, 2025 11:24
Show Gist options
  • Save The-Don-Himself/e24f3608f11bc71d041efbc37670978e to your computer and use it in GitHub Desktop.
Save The-Don-Himself/e24f3608f11bc71d041efbc37670978e to your computer and use it in GitHub Desktop.
While working on the Griffin Bank API for my fintech, I came across the requirement for Signed Requests, this is my Node implementation that works, no dependencies required.
import env from './config/environment'
import crypto from 'crypto';
const { private_key, keyid, api_key } = env.GRIFFIN
public getMessageSignaturesHeaders = async (
request_method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
request_path: string,
request_body?: object,
request_query?: string,
) => {
// Step 1: Compute content-digest
const requestBody = request_body ? JSON.stringify(request_body) : '';
const hash = crypto.createHash('sha512').update(requestBody).digest('base64');
const contentDigest = `sha-512=:${hash}:`;
// Step 2: Construct signature-input
const created = Math.floor(Date.now() / 1000);
const nonce = crypto.randomUUID();
const method = request_method.toUpperCase();
const authority = 'api.griffin.com';
const date = new Date().toUTCString();
const path = request_path;
const query = request_query || '?';
// Include @query in the signature parameters if query parameters are present
const signatureParams = `("@method" "@authority" "@path" "content-type" "content-length" "date" "content-digest" "@query");created=${created};keyid="${keyid}";nonce="${nonce}"`
const signatureInput = `sig1=${signatureParams}`;
// Step 3: Construct signing string
const signingStringLines = [
`"@method": ${method}`,
`"@authority": ${authority}`,
`"@path": ${path}`,
`"content-type": application/json`,
`"content-length": ${Buffer.byteLength(requestBody)}`,
`"date": ${date}`,
`"content-digest": ${contentDigest}`,
`"@query": ${query}`,
`"@signature-params": ${signatureParams}`
];
// Join lines with newline characters
const signingString = signingStringLines.join('\n');
// Step 4: Load Ed25519 private key
const privateKey = crypto.createPrivateKey({
key: private_key, // Ensure this is securely stored
format: 'pem',
type: 'pkcs8',
});
// Step 5: Sign the string using Ed25519
const signature = crypto.sign(null, Buffer.from(signingString, 'utf-8'), privateKey);
// Step 6: Encode signature in Base64
const signatureBase64 = `sig1=:${signature.toString('base64')}:`;
// Step 7: Attach headers to the request
const headers = {
Host: authority,
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `GriffinAPIKey ${api_key}`, // Ensure this is securely stored
'Content-Digest': contentDigest,
'Content-Length': Buffer.byteLength(requestBody).toString(),
Signature: signatureBase64,
'Signature-Input': signatureInput,
Date: date,
};
return headers;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment