express-ramses-auth
Version:
Express implementation of RAMSES - Robust Access Model for Securing Exposed Services
157 lines (130 loc) • 4.18 kB
JavaScript
const UnauthorizedError = require('./errors/UnauthorizedError');
const ramses = require('ramses-auth');
const set = require('lodash.set');
const unless = require('express-unless');
const async = require('async');
var DEFAULT_REVOKED_FUNCTION = function (_, __, cb) {
return cb(null, false);
};
function isFunction(object) {
return Object.prototype.toString.call(object) === '[object Function]';
}
function wrapStaticKeyInCallback(secret) {
return function (_, __, cb) {
return cb(null, secret);
};
}
const ramsesMiddleware = function (options) {
if (!options || !options.key) {
throw new Error('verification key must be set');
}
var keyCallback = options.key;
if (!isFunction(keyCallback)) {
keyCallback = wrapStaticKeyInCallback(keyCallback);
}
var isRevokedCallback = options.isRevoked || DEFAULT_REVOKED_FUNCTION;
var credentialsRequired = typeof options.credentialsRequired === 'undefined' ? true : options.credentialsRequired;
var _requestProperty = options.requestProperty || 'user';
var _resultProperty = options.resultProperty;
var attachFullToken = options.attachFullToken || false;
const middleware = function (req, res, next) {
var token;
if (req.method === 'OPTIONS' && req.headers.hasOwnProperty('access-control-request-headers')) {
var hasAuthInAccessControl = !!~req.headers['access-control-request-headers']
.split(',').map(function (header) {
return header.trim();
}).indexOf('authorization');
if (hasAuthInAccessControl) {
return next();
}
}
if (options.getToken && typeof options.getToken === 'function') {
try {
token = options.getToken(req);
} catch (e) {
return next(e);
}
} else if (req.headers && req.headers.authorization) {
var parts = req.headers.authorization.split(' ');
if (parts.length == 2) {
var scheme = parts[0];
var credentials = parts[1];
if (/^Bearer$/i.test(scheme)) {
token = credentials;
} else {
if (credentialsRequired) {
return next(new UnauthorizedError('credentials_bad_scheme', {
message: 'Format is Authorization: Bearer [token]'
}));
} else {
return next();
}
}
} else {
return next(new UnauthorizedError('credentials_bad_format', {
message: 'Format is Authorization: Bearer [token]'
}));
}
}
if (!token) {
if (credentialsRequired) {
return next(new UnauthorizedError('credentials_required', {
message: 'No authorization token was found'
}));
} else {
return next();
}
}
var dtoken;
try {
dtoken = ramses.decode(token, options);
} catch (err) {
return next(new UnauthorizedError('invalid_token', err));
}
async.waterfall([
function getKey(callback) {
keyCallback(req, dtoken, callback);
},
function verifyToken(key, callback) {
ramses.verify(token, key, options, function (err, dtoken) {
if (err) {
callback(err);
} else {
callback(null, dtoken);
}
});
},
function checkRevoked(decoded, callback) {
isRevokedCallback(req, dtoken, function (err, revoked) {
if (err) {
callback(err);
} else if (revoked) {
callback(new UnauthorizedError('revoked_token', {
message: 'The token has been revoked.'
}));
} else {
callback(null, decoded);
}
});
}
], function (err, result) {
if (err) {
return next(err);
}
if (attachFullToken) {
var attachment = dtoken;
} else {
var attachment = dtoken.payload;
}
if (_resultProperty) {
set(res, _resultProperty, attachment);
}
set(req, _requestProperty, attachment);
next();
});
};
middleware.unless = unless;
middleware.UnauthorizedError = UnauthorizedError;
return middleware;
}
module.exports = ramsesMiddleware;