Skip to content

Instantly share code, notes, and snippets.

@aeros281
Created December 8, 2020 13:42
Show Gist options
  • Save aeros281/5679157e34efae42151a8fa10042bd0d to your computer and use it in GitHub Desktop.
Save aeros281/5679157e34efae42151a8fa10042bd0d to your computer and use it in GitHub Desktop.
var jwt = require('jsonwebtoken');
var UnauthorizedError = require('./errors/UnauthorizedError');
var unless = require('express-unless');
var async = require('async');
var set = require('lodash.set');
var DEFAULT_REVOKED_FUNCTION = function(_, __, cb) { return cb(null, false); };
module.exports = function(options) {
if (!options || !options.secret) throw new Error('secret should be set');
if (!options.algorithms) throw new Error('algorithms should be set');
if (!Array.isArray(options.algorithms)) throw new Error('algorithms must be an array');
var secret = options.secret;
var isRevokedCallback = options.isRevoked || DEFAULT_REVOKED_FUNCTION;
var _requestProperty = options.userProperty || options.requestProperty || 'user';
var _resultProperty = options.resultProperty;
var credentialsRequired = typeof options.credentialsRequired === 'undefined' ? true : options.credentialsRequired;
var result = function(req, res, next) {
var token;
if (req.method === 'OPTIONS' && req.headers.hasOwnProperty('access-control-request-headers')) {
var isValid = !!~req.headers['access-control-request-headers']
.split(',').map(function (header) {
return header.trim();
}).indexOf('authorization');
if (isValid) {
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 = jwt.decode(token, { complete: true }) || {};
} catch (err) {
return next(new UnauthorizedError('invalid_token', err));
}
async.waterfall([
function getSecret(callback){
callback(null, secret);
},
function verifyToken(secret, callback) {
jwt.verify(token, secret, options, function(err, decoded) {
if (err) {
callback(new UnauthorizedError('invalid_token', err));
} else {
callback(null, decoded);
}
});
},
function checkRevoked(decoded, callback) {
isRevokedCallback(req, dtoken.payload, 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 (_resultProperty) {
set(res, _resultProperty, result);
} else {
set(req, _requestProperty, result);
}
next();
});
};
result.unless = unless;
result.UnauthorizedError = UnauthorizedError;
return result;
};
module.exports.UnauthorizedError = UnauthorizedError;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment