UNPKG

4.07 kBJavaScriptView Raw
1var jwt = require('jsonwebtoken');
2var UnauthorizedError = require('./errors/UnauthorizedError');
3var unless = require('express-unless');
4var async = require('async');
5var set = require('lodash.set');
6
7var DEFAULT_REVOKED_FUNCTION = function(_, __, cb) { return cb(null, false); };
8
9function isFunction(object) {
10 return Object.prototype.toString.call(object) === '[object Function]';
11}
12
13function wrapStaticSecretInCallback(secret){
14 return function(_, __, cb){
15 return cb(null, secret);
16 };
17}
18
19module.exports = function(options) {
20 if (!options || !options.secret) throw new Error('secret should be set');
21
22 var secretCallback = options.secret;
23
24 if (!isFunction(secretCallback)){
25 secretCallback = wrapStaticSecretInCallback(secretCallback);
26 }
27
28 var isRevokedCallback = options.isRevoked || DEFAULT_REVOKED_FUNCTION;
29
30 var _requestProperty = options.userProperty || options.requestProperty || 'user';
31 var credentialsRequired = typeof options.credentialsRequired === 'undefined' ? true : options.credentialsRequired;
32
33 var middleware = function(req, res, next) {
34 var token;
35
36 if (req.method === 'OPTIONS' && req.headers.hasOwnProperty('access-control-request-headers')) {
37 var hasAuthInAccessControl = !!~req.headers['access-control-request-headers']
38 .split(',').map(function (header) {
39 return header.trim();
40 }).indexOf('authorization');
41
42 if (hasAuthInAccessControl) {
43 return next();
44 }
45 }
46
47 if (options.getToken && typeof options.getToken === 'function') {
48 try {
49 token = options.getToken(req);
50 } catch (e) {
51 return next(e);
52 }
53 } else if (req.headers && req.headers.authorization) {
54 var parts = req.headers.authorization.split(' ');
55 if (parts.length == 2) {
56 var scheme = parts[0];
57 var credentials = parts[1];
58
59 if (/^Bearer$/i.test(scheme)) {
60 token = credentials;
61 } else {
62 if (credentialsRequired) {
63 return next(new UnauthorizedError('credentials_bad_scheme', { message: 'Format is Authorization: Bearer [token]' }));
64 } else {
65 return next();
66 }
67 }
68 } else {
69 return next(new UnauthorizedError('credentials_bad_format', { message: 'Format is Authorization: Bearer [token]' }));
70 }
71 }
72
73 if (!token) {
74 if (credentialsRequired) {
75 return next(new UnauthorizedError('credentials_required', { message: 'No authorization token was found' }));
76 } else {
77 return next();
78 }
79 }
80
81 var dtoken;
82
83 try {
84 dtoken = jwt.decode(token, { complete: true }) || {};
85 } catch (err) {
86 return next(new UnauthorizedError('invalid_token', err));
87 }
88
89 async.waterfall([
90 function getSecret(callback){
91 var arity = secretCallback.length;
92 if (arity == 4) {
93 secretCallback(req, dtoken.header, dtoken.payload, callback);
94 } else { // arity == 3
95 secretCallback(req, dtoken.payload, callback);
96 }
97 },
98 function verifyToken(secret, callback) {
99 jwt.verify(token, secret, options, function(err, decoded) {
100 if (err) {
101 callback(new UnauthorizedError('invalid_token', err));
102 } else {
103 callback(null, decoded);
104 }
105 });
106 },
107 function checkRevoked(decoded, callback) {
108 isRevokedCallback(req, dtoken.payload, function (err, revoked) {
109 if (err) {
110 callback(err);
111 }
112 else if (revoked) {
113 callback(new UnauthorizedError('revoked_token', {message: 'The token has been revoked.'}));
114 } else {
115 callback(null, decoded);
116 }
117 });
118 }
119
120 ], function (err, result){
121 if (err) { return next(err); }
122 set(req, _requestProperty, result);
123 next();
124 });
125 };
126
127 middleware.unless = unless;
128 middleware.UnauthorizedError = UnauthorizedError;
129
130 return middleware;
131};