unleash-server
Version:
Unleash is an enterprise ready feature flag service. It provides different strategies for handling feature flags.
83 lines • 3.17 kB
JavaScript
import { createHash, createHmac } from 'node:crypto';
import { timingSafeEqual } from 'crypto';
const REQUEST_LIFETIME = 5 * 60 * 1000;
export const hmacSignatureVerifyTokenRequest = (edgeService) => {
return async (req, res, next) => {
try {
const auth = req.headers.authorization || '';
const m = auth.match(/^HMAC ([^:]+):(.+)$/);
if (!m) {
res.status(401).json({
error: 'Missing HMAC authorization header',
});
return;
}
const clientId = m[1];
const providedSig = m[2];
const timestamp = req.headers['x-timestamp'];
const nonce = req.headers['x-nonce'];
const bodyHash = req.headers['content-sha256'];
if (!timestamp || !nonce || !bodyHash) {
res.status(401).json({ error: 'Missing content headers' });
return;
}
const now = Date.now();
const ts = Date.parse(timestamp);
if (Math.abs(now - ts) > REQUEST_LIFETIME) {
res.status(401).json({ error: 'Stale request' });
return;
}
const safeToUse = await edgeService.notSeenBefore({
clientId,
nonce,
expiresAt: new Date(now + 5 * 60 * 1000),
});
if (!safeToUse) {
res.status(401).json({ error: 'Replay detected' });
return;
}
const client = await edgeService.loadClient(clientId);
if (!client) {
res.status(401).json({ error: 'Unknown client' });
return;
}
const bodyAsString = JSON.stringify(req.body || '');
const computedHash = createHash('sha256')
.update(bodyAsString)
.digest('hex');
if (computedHash !== bodyHash) {
res.status(401).json({ error: 'Body tampering detected' });
return;
}
const canonical = req.method.toUpperCase() +
'\n' +
'/edge/issue-token' +
'\n' +
timestamp +
'\n' +
nonce +
'\n' +
bodyHash;
const secret = edgeService
.decryptedClientSecret(client)
.toString('utf-8');
const expectedSig = createHmac('sha256', Buffer.from(secret, 'base64url'))
.update(canonical)
.digest('base64url');
if (!timingSafeEqual(Buffer.from(expectedSig), Buffer.from(providedSig))) {
res.status(401).json({ error: 'Signature mismatch' });
return;
}
res.locals.clientId = clientId;
next();
}
catch (e) {
console.error('Error authenticating HMAC request:', e);
res.status(401).json({
error: 'Authenticating request failed',
});
return;
}
};
};
//# sourceMappingURL=edge-hmac-verifier.js.map