| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98 |
1×
1×
21×
21×
19×
21×
1×
9×
1×
32×
32×
32×
32×
32×
32×
32×
34×
34×
2×
32×
2×
30×
30×
4×
26×
4×
26×
16×
1×
16×
4×
26×
4×
26×
| 'use strict';
var _ = require('underscore'),
jwt = require('jwt-simple'),
Class = require('class.extend'),
APIError = require('./APIError'),
BEARER_REGEX = /^Bearer /,
INVALID_TOKEN_MSG = 'Invalid authorization token';
function createError(title, detail, headerFieldName) {
var err = new APIError(title, detail);
if (headerFieldName) {
err.addSource(APIError.LOC_HEADER, headerFieldName);
}
return err;
}
function invalidFieldResp(field, headerFieldName) {
return createError(INVALID_TOKEN_MSG, 'Invalid "' + field + '" value in the token.', headerFieldName);
}
module.exports = Class.extend({
init: function(publicKey) {
this._publicKey = publicKey;
},
issuer: function(issuer) {
this._issuer = issuer;
return this;
},
audience: function(audience) {
this._audience = audience;
return this;
},
revocation: function(revokedIDs) {
this._revokedIDs = revokedIDs;
return this;
},
validate: function(rawTokenString, isBearerFormat, headerFieldName) {
var errors = [],
tokenString = (isBearerFormat ? (rawTokenString || '').replace(BEARER_REGEX, '') : rawTokenString),
token;
// These first several types of errors must stop the flow of the rest of the
// validation ... they can not be compounded because they result in no token to
// actually validate.
if (_.isEmpty(tokenString)) {
return {
errors: [ createError('No token supplied', undefined, headerFieldName) ],
};
}
if (isBearerFormat && !BEARER_REGEX.test(rawTokenString)) {
return {
// eslint-disable-next-line max-len
errors: [ createError((headerFieldName ? (headerFieldName + ' header') : 'Bearer token') + ' not in correct format', undefined, headerFieldName) ],
};
}
try {
token = jwt.decode(tokenString, this._publicKey, false, 'RS256');
} catch(err) {
return {
errors: [ createError(INVALID_TOKEN_MSG, err.message, headerFieldName) ],
};
}
// The rest of these errors can be built up into an array of errors so that you can
// report all the errors that were found with the decoded token.
if (!_.isEmpty(this._issuer) && token.iss !== this._issuer) {
errors.push(invalidFieldResp('iss', headerFieldName));
}
if (!_.isEmpty(this._audience)) {
if (_.isArray(token.aud) && !_.contains(token.aud, this._audience)) {
errors.push(invalidFieldResp('aud', headerFieldName));
}
if (!_.isArray(token.aud) && token.aud !== this._audience) {
errors.push(invalidFieldResp('aud', headerFieldName));
}
}
if (!_.isEmpty(this._revokedIDs) && _.contains(this._revokedIDs, token.jti)) {
errors.push(createError(INVALID_TOKEN_MSG, 'Token has been revoked', headerFieldName));
}
return { errors: errors, token: token };
},
});
|