Simple HMAC-based authentication library for Node.js with replay attack prevention
npm install @mkvisuals/hmac-simpleSimple HMAC-based authentication library with built-in replay attack prevention.
- HMAC-based request signing and verification
- Works in both Node.js and browsers
- Replay attack prevention with nonce tracking
- Timestamp validation
- Constant-time signature comparison to prevent timing attacks
- Automatic cleanup of expired nonces
- TypeScript support with full type definitions
- Both ESM and CommonJS support
- Tiny bundle size (~4.5KB ESM, ~3KB CJS)
``bash`
npm install @mkvisuals/hmac-simple
`typescript
import { HmacSimple, HMAC_HEADERS } from '@mkvisuals/hmac-simple';
// Initialize with a shared secret key
const hmac = new HmacSimple({
secretKey: 'your-secret-key-here',
algorithm: 'sha256', // optional, default: 'sha256'
timestampToleranceMs: 5 60 1000, // optional, default: 5 minutes
nonceMaxAgeMs: 30 60 1000, // optional, default: 30 minutes
});
// Client: Create headers for an outgoing request (async)
const clientId = 'client-123';
const requestBody = JSON.stringify({ data: 'example' });
const headers = await hmac.createHeaders(clientId, requestBody);
console.log(headers);
// {
// 'x-hmac-id': 'client-123',
// 'x-hmac-timestamp': '1234567890',
// 'x-hmac-nonce': 'abc123...',
// 'x-hmac-signature': 'def456...'
// }
// Server: Verify headers from an incoming request (async)
const result = await hmac.verifyHeaders(
headers[HMAC_HEADERS.id],
headers[HMAC_HEADERS.ts],
headers[HMAC_HEADERS.nonce],
headers[HMAC_HEADERS.sig],
requestBody
);
if (result.valid) {
console.log('Request authenticated successfully');
} else {
console.error('Authentication failed:', result.error);
}
`
#### Constructor
`typescript`
new HmacSimple(config: HmacConfig)
Config Options:
- secretKey (string, required): The shared secret key used for HMAC signature generationalgorithm
- (string, optional): Hash algorithm to use (default: 'sha256')timestampToleranceMs
- (number, optional): Maximum allowed time difference in milliseconds (default: 300000 - 5 minutes)nonceMaxAgeMs
- (number, optional): How long to track nonces in milliseconds (default: 1800000 - 30 minutes)
#### Methods
##### async createHeaders(clientId: string, payload?: string): Promise
Creates HMAC authentication headers for a request.
Parameters:
- clientId: Unique identifier for the clientpayload
- : Optional request payload (typically JSON stringified body)
Returns: Promise resolving to an object containing HMAC headers
##### async verifyHeaders(clientId: string, timestamp: string, nonce: string, signature: string, payload?: string): Promise
Verifies HMAC authentication headers from a request.
Parameters:
- clientId: Client identifier from the requesttimestamp
- : Timestamp from the request headernonce
- : Nonce from the request headersignature
- : Signature from the request headerpayload
- : Optional request payload to verify
Returns: Promise resolving to a verification result object with valid boolean and optional error message
##### getNonceCount(): number
Returns the current number of tracked nonces (useful for monitoring).
##### destroy(): void
Cleans up resources by clearing all tracked nonces.
Object containing header name constants:
`typescript`
{
id: 'x-hmac-id',
ts: 'x-hmac-timestamp',
nonce: 'x-hmac-nonce',
sig: 'x-hmac-signature',
}
`typescript
import { HmacSimple } from '@mkvisuals/hmac-simple';
const hmac = new HmacSimple({ secretKey: 'shared-secret' });
const clientId = 'client-123';
async function makeAuthenticatedRequest() {
const body = JSON.stringify({ action: 'getData' });
const headers = await hmac.createHeaders(clientId, body);
const response = await fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...headers,
},
body,
});
return response.json();
}
`
`typescript
import express from 'express';
import { HmacSimple, HMAC_HEADERS } from '@mkvisuals/hmac-simple';
const app = express();
const hmac = new HmacSimple({ secretKey: 'shared-secret' });
app.use(express.json());
app.post('/data', async (req, res) => {
const clientId = req.headers[HMAC_HEADERS.id];
const timestamp = req.headers[HMAC_HEADERS.ts];
const nonce = req.headers[HMAC_HEADERS.nonce];
const signature = req.headers[HMAC_HEADERS.sig];
const payload = JSON.stringify(req.body);
const result = await hmac.verifyHeaders(clientId, timestamp, nonce, signature, payload);
if (!result.valid) {
return res.status(401).json({ error: result.error });
}
// Request is authenticated
res.json({ success: true, data: 'your data here' });
});
app.listen(3000);
``
- Secret Key: Keep your secret key secure and never commit it to version control
- HTTPS: Always use HTTPS in production to prevent man-in-the-middle attacks
- Payload Verification: Always include the request payload in the signature to prevent tampering
- Time Synchronization: Ensure server clocks are synchronized (NTP) for timestamp validation
- Nonce Storage: For distributed systems, consider using a shared cache (Redis) for nonce tracking
MIT