api-signature
Version:
Express/Restify middleware to authenticate HTTP requests based on api key and signature
78 lines (70 loc) • 2.82 kB
JavaScript
/**
* @author Michael Piper <hello@zeroant.co>
* MIT Licensed
*/
const { availableAlgorithms } = require('./algorithm');
const parser = require('./parser');
const verify = require('./verify');
const { BadSignatureError, UnauthorizedError } = require('./errors');
const Signer = require('./signer');
/**
* @function
* @public
* @description Create the middleware for api key based authentication
* @param {Object} options An object with options.
* @param {Function} options.getSecret The function to get the secret
* @param {String} [options.requestProperty='credentials'] The request property's name used to attach credentials
* @param {String[]} [options.requiredHeaders=[]] The required HTTP header of a request
* @param {Number|null} [options.requestLifetime=300] The lifetime of a request in second (set to null to disable it)
* @return {Function} The middleware function
* @throws {Error} The method "getSecret" must be defined
*/
function apiSignature(options) {
if (!options || !options.getSecret) {
throw new Error('The method "getSecret" must be defined');
}
/* Check if "requiredHeaders" param not exists use the date HTTP header by default */
if (typeof options.requiredHeaders!=="undefined" && !Array.isArray(options.requiredHeaders)) {
throw new Error('The object "requiredHeaders" must be a type of string[]');
}
const { getSecret, requestLifetime = 300, requestProperty = 'credentials', requiredHeaders=['date'] } = options;
const middleware = async function middleware(req, res, next) {
/* Don't check the signature for preflight request */
if (req.method === 'OPTIONS' && req.headers['access-control-request-headers']) {
const hasAuthInAccessControl =
req.headers['access-control-request-headers']
.split(',')
.map(header => header.trim().toLowerCase())
.indexOf('authorization') !== -1;
if (hasAuthInAccessControl) {
return next();
}
}
let signatureParams = null;
signatureParams = parser.parseRequest(req, {
algorithms: availableAlgorithms,
requestLifetime,
requiredHeaders
});
return await getSecret(signatureParams.keyid, (err, secret, credentials) => {
if (err) {
throw new UnauthorizedError(err.message);
}
if (!secret) {
throw new Error('The method "getSecret" must return the secret key through the callback function');
}
if (!verify.verifySignature(signatureParams, secret)) {
throw new BadSignatureError();
}
req[requestProperty] = credentials;
return next();
});
};
return middleware;
}
/**
* @module apiSignature
* @description The middleware for api signature based authentication
*/
module.exports = apiSignature;
module.exports.Signer = Signer;