@villedemontreal/jwt-validator 5.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +313 -0
- package/dist/scripts/index.d.ts +6 -0
- package/dist/scripts/index.js +16 -0
- package/dist/scripts/index.js.map +1 -0
- package/dist/scripts/lint.d.ts +6 -0
- package/dist/scripts/lint.js +18 -0
- package/dist/scripts/lint.js.map +1 -0
- package/dist/scripts/lintFix.d.ts +6 -0
- package/dist/scripts/lintFix.js +21 -0
- package/dist/scripts/lintFix.js.map +1 -0
- package/dist/scripts/showCoverage.d.ts +13 -0
- package/dist/scripts/showCoverage.js +40 -0
- package/dist/scripts/showCoverage.js.map +1 -0
- package/dist/scripts/test.d.ts +13 -0
- package/dist/scripts/test.js +29 -0
- package/dist/scripts/test.js.map +1 -0
- package/dist/scripts/testUnits.d.ts +15 -0
- package/dist/scripts/testUnits.js +95 -0
- package/dist/scripts/testUnits.js.map +1 -0
- package/dist/scripts/watch.d.ts +14 -0
- package/dist/scripts/watch.js +96 -0
- package/dist/scripts/watch.js.map +1 -0
- package/dist/src/config/configs.d.ts +88 -0
- package/dist/src/config/configs.js +123 -0
- package/dist/src/config/configs.js.map +1 -0
- package/dist/src/config/constants.d.ts +56 -0
- package/dist/src/config/constants.js +66 -0
- package/dist/src/config/constants.js.map +1 -0
- package/dist/src/config/init.d.ts +15 -0
- package/dist/src/config/init.js +48 -0
- package/dist/src/config/init.js.map +1 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.js +32 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/jwtValidator.d.ts +21 -0
- package/dist/src/jwtValidator.js +129 -0
- package/dist/src/jwtValidator.js.map +1 -0
- package/dist/src/jwtValidator.test.d.ts +1 -0
- package/dist/src/jwtValidator.test.js +500 -0
- package/dist/src/jwtValidator.test.js.map +1 -0
- package/dist/src/middleware/jwtMiddleware.d.ts +7 -0
- package/dist/src/middleware/jwtMiddleware.js +27 -0
- package/dist/src/middleware/jwtMiddleware.js.map +1 -0
- package/dist/src/models/customError.d.ts +11 -0
- package/dist/src/models/customError.js +38 -0
- package/dist/src/models/customError.js.map +1 -0
- package/dist/src/models/expressRequest.d.ts +15 -0
- package/dist/src/models/expressRequest.js +17 -0
- package/dist/src/models/expressRequest.js.map +1 -0
- package/dist/src/models/gluuUserType.d.ts +9 -0
- package/dist/src/models/gluuUserType.js +14 -0
- package/dist/src/models/gluuUserType.js.map +1 -0
- package/dist/src/models/jwtPayload.d.ts +30 -0
- package/dist/src/models/jwtPayload.js +19 -0
- package/dist/src/models/jwtPayload.js.map +1 -0
- package/dist/src/models/pagination.d.ts +16 -0
- package/dist/src/models/pagination.js +16 -0
- package/dist/src/models/pagination.js.map +1 -0
- package/dist/src/models/publicKey.d.ts +29 -0
- package/dist/src/models/publicKey.js +13 -0
- package/dist/src/models/publicKey.js.map +1 -0
- package/dist/src/repositories/cachedPublicKeyRepository.d.ts +53 -0
- package/dist/src/repositories/cachedPublicKeyRepository.js +102 -0
- package/dist/src/repositories/cachedPublicKeyRepository.js.map +1 -0
- package/dist/src/repositories/publicKeyRepository.d.ts +19 -0
- package/dist/src/repositories/publicKeyRepository.js +44 -0
- package/dist/src/repositories/publicKeyRepository.js.map +1 -0
- package/dist/src/userValidator.d.ts +30 -0
- package/dist/src/userValidator.js +35 -0
- package/dist/src/userValidator.js.map +1 -0
- package/dist/src/userValidator.test.d.ts +1 -0
- package/dist/src/userValidator.test.js +251 -0
- package/dist/src/userValidator.test.js.map +1 -0
- package/dist/src/utils/jwtMock.d.ts +31 -0
- package/dist/src/utils/jwtMock.js +221 -0
- package/dist/src/utils/jwtMock.js.map +1 -0
- package/dist/src/utils/logger.d.ts +11 -0
- package/dist/src/utils/logger.js +54 -0
- package/dist/src/utils/logger.js.map +1 -0
- package/dist/src/utils/testingConfigurations.d.ts +7 -0
- package/dist/src/utils/testingConfigurations.js +16 -0
- package/dist/src/utils/testingConfigurations.js.map +1 -0
- package/package.json +82 -0
- package/src/config/configs.ts +145 -0
- package/src/config/constants.ts +83 -0
- package/src/config/init.ts +58 -0
- package/src/index.ts +15 -0
- package/src/jwtValidator.test.ts +607 -0
- package/src/jwtValidator.ts +162 -0
- package/src/middleware/jwtMiddleware.ts +33 -0
- package/src/models/customError.ts +37 -0
- package/src/models/expressRequest.ts +27 -0
- package/src/models/gluuUserType.ts +9 -0
- package/src/models/jwtPayload.ts +58 -0
- package/src/models/pagination.ts +26 -0
- package/src/models/publicKey.ts +33 -0
- package/src/repositories/cachedPublicKeyRepository.ts +121 -0
- package/src/repositories/publicKeyRepository.ts +75 -0
- package/src/userValidator.test.ts +279 -0
- package/src/userValidator.ts +54 -0
- package/src/utils/jwtMock.ts +243 -0
- package/src/utils/logger.ts +60 -0
- package/src/utils/testingConfigurations.ts +12 -0
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ApiErrorAndInfo,
|
|
3
|
+
createInvalidParameterError,
|
|
4
|
+
createServerError,
|
|
5
|
+
} from '@villedemontreal/general-utils/dist/src';
|
|
6
|
+
import { assert } from 'chai';
|
|
7
|
+
import * as express from 'express';
|
|
8
|
+
import * as jwt from 'jsonwebtoken';
|
|
9
|
+
import * as nock from 'nock';
|
|
10
|
+
import * as validator from 'validator';
|
|
11
|
+
import { configs } from './config/configs';
|
|
12
|
+
import { constants } from './config/constants';
|
|
13
|
+
import { jwtValidator } from './jwtValidator';
|
|
14
|
+
import { isRequestWithJwt } from './models/expressRequest';
|
|
15
|
+
import { IPublicKeys } from './models/publicKey';
|
|
16
|
+
import { cachedPublicKeyRepository } from './repositories/cachedPublicKeyRepository';
|
|
17
|
+
import { jwtMock } from './utils/jwtMock';
|
|
18
|
+
import { setTestingConfigurations } from './utils/testingConfigurations';
|
|
19
|
+
const httpMocks = require('node-mocks-http');
|
|
20
|
+
|
|
21
|
+
// ==========================================
|
|
22
|
+
// Set Testing configurations
|
|
23
|
+
// ==========================================
|
|
24
|
+
setTestingConfigurations();
|
|
25
|
+
|
|
26
|
+
// ==========================================
|
|
27
|
+
// JWT Validator
|
|
28
|
+
// ==========================================
|
|
29
|
+
let date;
|
|
30
|
+
let publicKeys: IPublicKeys;
|
|
31
|
+
|
|
32
|
+
function createPathRegex() {
|
|
33
|
+
const regExpEscape = (s: any) => {
|
|
34
|
+
return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return new RegExp(`${regExpEscape(configs.getEndpoint())}(.*)`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
before('JWT Validator - init app & get jwt public key', async () => {
|
|
41
|
+
nock.cleanAll();
|
|
42
|
+
|
|
43
|
+
// Use mock keys
|
|
44
|
+
await jwtMock.mockPublicKeys();
|
|
45
|
+
|
|
46
|
+
publicKeys = await cachedPublicKeyRepository.getAll();
|
|
47
|
+
|
|
48
|
+
assert.match(publicKeys[1].publicKey, /^-----BEGIN PUBLIC KEY-----\n/m);
|
|
49
|
+
assert.match(publicKeys[1].publicKey, /^-----BEGIN PUBLIC KEY-----\n/m);
|
|
50
|
+
assert.match(publicKeys[1].publicKey, /\n-----END PUBLIC KEY-----$/m);
|
|
51
|
+
|
|
52
|
+
const key: string = publicKeys[1].publicKey
|
|
53
|
+
.replace(/^-----BEGIN PUBLIC KEY-----\n/m, '')
|
|
54
|
+
.replace(/\n-----END PUBLIC KEY-----$/m, '')
|
|
55
|
+
.split('\n')
|
|
56
|
+
.join('');
|
|
57
|
+
assert.isTrue(validator.default.isBase64(key));
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('JWT Validator - verifyHeader - should reject null header', async () => {
|
|
61
|
+
const response = await jwtValidator.verifyAuthorizationHeader(null).catch((err) => {
|
|
62
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_AUTHORIZATION_HEADER);
|
|
63
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
64
|
+
assert.strictEqual(err.error.message, 'Invalid Authorization header');
|
|
65
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.NULL_VALUE);
|
|
66
|
+
assert.strictEqual(err.error.details[0].target, 'Authorization header');
|
|
67
|
+
assert.strictEqual(err.error.details[0].message, 'Empty Authorization header');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
assert.isUndefined(response);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('JWT Validator - verifyHeader - should reject empty header', async () => {
|
|
74
|
+
const response = await jwtValidator.verifyAuthorizationHeader('').catch((err) => {
|
|
75
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_AUTHORIZATION_HEADER);
|
|
76
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
77
|
+
assert.strictEqual(err.error.message, 'Invalid Authorization header');
|
|
78
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.NULL_VALUE);
|
|
79
|
+
assert.strictEqual(err.error.details[0].target, 'Authorization header');
|
|
80
|
+
assert.strictEqual(err.error.details[0].message, 'Empty Authorization header');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
assert.isUndefined(response);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('JWT Validator - verifyHeader - should reject unknow authentication scheme', async () => {
|
|
87
|
+
const response = await jwtValidator.verifyAuthorizationHeader('Unknow JWT').catch((err) => {
|
|
88
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_AUTHORIZATION_HEADER);
|
|
89
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
90
|
+
assert.strictEqual(err.error.message, 'Invalid Authorization header');
|
|
91
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.INVALID_VALUE);
|
|
92
|
+
assert.strictEqual(err.error.details[0].target, 'Authorization header');
|
|
93
|
+
assert.strictEqual(
|
|
94
|
+
err.error.details[0].message,
|
|
95
|
+
'Bad authentication scheme, "Bearer" required'
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
assert.isUndefined(response);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('JWT Validator - verifyHeader - should reject bad token', async () => {
|
|
103
|
+
const response = await jwtValidator.verifyAuthorizationHeader('Bearer JWT').catch((err) => {
|
|
104
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_JWT);
|
|
105
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
106
|
+
assert.strictEqual(err.error.message, 'Invalid JWT');
|
|
107
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.INVALID_VALUE);
|
|
108
|
+
assert.strictEqual(err.error.details[0].target, 'jwt');
|
|
109
|
+
assert.strictEqual(err.error.details[0].message, 'jwt malformed');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
assert.isUndefined(response);
|
|
113
|
+
});
|
|
114
|
+
it('JWT Validator - verifyHeader - should accept good token', async () => {
|
|
115
|
+
date = new Date(publicKeys[5].createdAt);
|
|
116
|
+
date.setHours(date.getHours() + 24);
|
|
117
|
+
const payload: any = {
|
|
118
|
+
accessToken: 'c9ba5a95-d7f9-41f9-9a24-a7e41882f7ef',
|
|
119
|
+
iss: 'Issuer',
|
|
120
|
+
inum: 'MyInum',
|
|
121
|
+
iat: date.getTime() / 1000,
|
|
122
|
+
exp: date.getTime() / 1000 + 3600,
|
|
123
|
+
sub: '@!4025.CA62.9BB6.16C5!0001!2212.0010!0000!0000.0001',
|
|
124
|
+
keyId: 5,
|
|
125
|
+
};
|
|
126
|
+
const token: string = jwt.sign(JSON.stringify(payload), jwtMock.getPrivateKey(), {
|
|
127
|
+
algorithm: 'RS256',
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const response = await jwtValidator.verifyAuthorizationHeader('Bearer ' + token);
|
|
131
|
+
|
|
132
|
+
assert.deepEqual(response, payload);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('JWT Validator - verify - should reject bad token', async () => {
|
|
136
|
+
const response = await jwtValidator.verifyToken('JWT').catch((err) => {
|
|
137
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_JWT);
|
|
138
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
139
|
+
assert.strictEqual(err.error.message, 'Invalid JWT');
|
|
140
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.INVALID_VALUE);
|
|
141
|
+
assert.strictEqual(err.error.details[0].target, 'jwt');
|
|
142
|
+
assert.strictEqual(err.error.details[0].message, 'jwt malformed');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
assert.isUndefined(response);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('JWT Validator - verify - should reject invalid token: missing signature', async () => {
|
|
149
|
+
let token = '';
|
|
150
|
+
token += Buffer.from('{}', 'base64');
|
|
151
|
+
token += '.';
|
|
152
|
+
token += Buffer.from('{}', 'base64');
|
|
153
|
+
token += '.';
|
|
154
|
+
|
|
155
|
+
const response = await jwtValidator.verifyToken(token).catch((err) => {
|
|
156
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_JWT);
|
|
157
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
158
|
+
assert.strictEqual(err.error.message, 'Invalid JWT');
|
|
159
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.INVALID_VALUE);
|
|
160
|
+
assert.strictEqual(err.error.details[0].target, 'jwt');
|
|
161
|
+
assert.strictEqual(err.error.details[0].message, 'jwt malformed');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
assert.isUndefined(response);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('JWT Validator - verify - should reject invalid token: empty JSON', async () => {
|
|
168
|
+
let token = '';
|
|
169
|
+
token += Buffer.from('{}', 'base64');
|
|
170
|
+
token += '.';
|
|
171
|
+
token += Buffer.from('{}', 'base64');
|
|
172
|
+
token += '.';
|
|
173
|
+
token += 'TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ';
|
|
174
|
+
|
|
175
|
+
const response = await jwtValidator.verifyToken(token).catch((err) => {
|
|
176
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_JWT);
|
|
177
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
178
|
+
assert.strictEqual(err.error.message, 'Invalid JWT');
|
|
179
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.INVALID_VALUE);
|
|
180
|
+
assert.strictEqual(err.error.details[0].target, 'jwt');
|
|
181
|
+
assert.strictEqual(err.error.details[0].message, 'jwt malformed');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
assert.isUndefined(response);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('JWT Validator - verify - should reject invalid token: missing public key ID', async () => {
|
|
188
|
+
const token: string = jwt.sign('{"a":"a"}', 'key', { algorithm: 'HS256' });
|
|
189
|
+
|
|
190
|
+
const response = await jwtValidator.verifyToken(token).catch((err) => {
|
|
191
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_JWT);
|
|
192
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
193
|
+
assert.strictEqual(err.error.message, 'Invalid JWT');
|
|
194
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.INVALID_VALUE);
|
|
195
|
+
assert.strictEqual(err.error.details[0].target, 'jwt');
|
|
196
|
+
assert.strictEqual(err.error.details[0].message, 'missing public key ID');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
assert.isUndefined(response);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('JWT Validator - verify - should reject invalid token: keyId is no longer active', async () => {
|
|
203
|
+
const token: string = jwt.sign('{"a":"a", "keyId": "1"}', 'key', { algorithm: 'HS256' });
|
|
204
|
+
|
|
205
|
+
const response = await jwtValidator.verifyToken(token).catch((err) => {
|
|
206
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_JWT);
|
|
207
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
208
|
+
assert.strictEqual(err.error.message, 'Invalid JWT');
|
|
209
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.INVALID_VALUE);
|
|
210
|
+
assert.strictEqual(err.error.details[0].target, 'jwt');
|
|
211
|
+
assert.strictEqual(err.error.details[0].message, 'this keyId is no longer active');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
assert.isUndefined(response);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('JWT Validator - verify - should reject invalid token: keyId not found', async () => {
|
|
218
|
+
const pathRegex = createPathRegex();
|
|
219
|
+
|
|
220
|
+
// Intercept request
|
|
221
|
+
nock(configs.getHost()).get(pathRegex).reply(404);
|
|
222
|
+
|
|
223
|
+
const token: string = jwt.sign('{"a":"a", "keyId": "25"}', 'key', { algorithm: 'HS256' });
|
|
224
|
+
|
|
225
|
+
const response = await jwtValidator.verifyToken(token).catch((err) => {
|
|
226
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_JWT);
|
|
227
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
228
|
+
assert.strictEqual(err.error.message, 'Invalid JWT');
|
|
229
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.INVALID_VALUE);
|
|
230
|
+
assert.strictEqual(err.error.details[0].target, 'jwt');
|
|
231
|
+
assert.strictEqual(err.error.details[0].message, 'this keyId is no longer active');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
assert.isUndefined(response);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('JWT Validator - verify - should reject invalid algorithm: bad signature', async () => {
|
|
238
|
+
let token = '';
|
|
239
|
+
token += Buffer.from('{"alg":"HS256","typ":"JWT"}', 'base64');
|
|
240
|
+
token += '.';
|
|
241
|
+
token += Buffer.from('{"a":"a", "keyId": 5}', 'base64');
|
|
242
|
+
token += '.';
|
|
243
|
+
token += 'TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ';
|
|
244
|
+
|
|
245
|
+
const response = await jwtValidator.verifyToken(token).catch((err) => {
|
|
246
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_JWT);
|
|
247
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
248
|
+
assert.strictEqual(err.error.message, 'Invalid JWT');
|
|
249
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.INVALID_VALUE);
|
|
250
|
+
assert.strictEqual(err.error.details[0].target, 'jwt');
|
|
251
|
+
assert.strictEqual(err.error.details[0].message, 'jwt malformed');
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
assert.isUndefined(response);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('JWT Validator - verify - should reject invalid algorithm: bad signature (algorithm)', async () => {
|
|
258
|
+
const token: string = jwt.sign('{"a":"a", "keyId": 5}', 'key', { algorithm: 'HS256' });
|
|
259
|
+
|
|
260
|
+
const response = await jwtValidator.verifyToken(token).catch((err) => {
|
|
261
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_JWT);
|
|
262
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
263
|
+
assert.strictEqual(err.error.message, 'Invalid JWT');
|
|
264
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.INVALID_VALUE);
|
|
265
|
+
assert.strictEqual(err.error.details[0].target, 'jwt');
|
|
266
|
+
assert.strictEqual(err.error.details[0].message, 'invalid algorithm');
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
assert.isUndefined(response);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('JWT Validator - verify - should reject expired token', async () => {
|
|
273
|
+
date = new Date(publicKeys[5].createdAt);
|
|
274
|
+
date.setHours(date.getHours() + 24);
|
|
275
|
+
const payload: any = {
|
|
276
|
+
b: 'b',
|
|
277
|
+
iat: date.getTime() / 1000,
|
|
278
|
+
exp: 1,
|
|
279
|
+
keyId: 5,
|
|
280
|
+
};
|
|
281
|
+
const token: string = jwt.sign(JSON.stringify(payload), jwtMock.getPrivateKey(), {
|
|
282
|
+
algorithm: 'RS256',
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
const response = await jwtValidator.verifyToken(token).catch((err) => {
|
|
286
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_JWT);
|
|
287
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
288
|
+
assert.strictEqual(err.error.message, 'Invalid JWT');
|
|
289
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.INVALID_VALUE);
|
|
290
|
+
assert.strictEqual(err.error.details[0].target, 'jwt');
|
|
291
|
+
assert.strictEqual(err.error.details[0].message, 'jwt expired');
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
assert.isUndefined(response);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('JWT Validator - verify - should reject expired public key', async () => {
|
|
298
|
+
const payload: any = {
|
|
299
|
+
b: 'b',
|
|
300
|
+
keyId: 2,
|
|
301
|
+
};
|
|
302
|
+
const token: string = jwt.sign(JSON.stringify(payload), jwtMock.getPrivateKey(), {
|
|
303
|
+
algorithm: 'RS256',
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const response = await jwtValidator.verifyToken(token).catch((err) => {
|
|
307
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_JWT);
|
|
308
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
309
|
+
assert.strictEqual(err.error.message, 'Invalid JWT');
|
|
310
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.INVALID_VALUE);
|
|
311
|
+
assert.strictEqual(err.error.details[0].target, 'jwt');
|
|
312
|
+
assert.strictEqual(err.error.details[0].message, 'this keyId is expired');
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
assert.isUndefined(response);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('JWT Validator - verify - should reject expired public key (by state)', async () => {
|
|
319
|
+
const payload: any = {
|
|
320
|
+
b: 'b',
|
|
321
|
+
keyId: 1,
|
|
322
|
+
};
|
|
323
|
+
const token: string = jwt.sign(JSON.stringify(payload), jwtMock.getPrivateKey(), {
|
|
324
|
+
algorithm: 'RS256',
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
const response = await jwtValidator.verifyToken(token).catch((err) => {
|
|
328
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_JWT);
|
|
329
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
330
|
+
assert.strictEqual(err.error.message, 'Invalid JWT');
|
|
331
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.INVALID_VALUE);
|
|
332
|
+
assert.strictEqual(err.error.details[0].target, 'jwt');
|
|
333
|
+
assert.strictEqual(err.error.details[0].message, 'this keyId is no longer active');
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
assert.isUndefined(response);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('JWT Validator - verify - should reject revoked public key', async () => {
|
|
340
|
+
const payload: any = {
|
|
341
|
+
b: 'b',
|
|
342
|
+
keyId: 3,
|
|
343
|
+
};
|
|
344
|
+
const token: string = jwt.sign(JSON.stringify(payload), jwtMock.getPrivateKey(), {
|
|
345
|
+
algorithm: 'RS256',
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const response = await jwtValidator.verifyToken(token).catch((err) => {
|
|
349
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_JWT);
|
|
350
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
351
|
+
assert.strictEqual(err.error.message, 'Invalid JWT');
|
|
352
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.INVALID_VALUE);
|
|
353
|
+
assert.strictEqual(err.error.details[0].target, 'jwt');
|
|
354
|
+
assert.strictEqual(err.error.details[0].message, 'this keyId is no longer active');
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
assert.isUndefined(response);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('JWT Validator - verify - should reject jwt created after the expiration date of the key', async () => {
|
|
361
|
+
date = new Date(publicKeys[4].expiresAt);
|
|
362
|
+
date.setHours(date.getHours() + 24);
|
|
363
|
+
const payload: any = {
|
|
364
|
+
b: 'b',
|
|
365
|
+
iat: date.getTime() / 1000,
|
|
366
|
+
keyId: 4,
|
|
367
|
+
};
|
|
368
|
+
const token: string = jwt.sign(JSON.stringify(payload), jwtMock.getPrivateKey(), {
|
|
369
|
+
algorithm: 'RS256',
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
const response = await jwtValidator.verifyToken(token).catch((err) => {
|
|
373
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_JWT);
|
|
374
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
375
|
+
assert.strictEqual(err.error.message, 'Invalid JWT');
|
|
376
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.INVALID_VALUE);
|
|
377
|
+
assert.strictEqual(err.error.details[0].target, 'jwt');
|
|
378
|
+
assert.strictEqual(
|
|
379
|
+
err.error.details[0].message,
|
|
380
|
+
"this jwt can't be created after the expiration of the public key"
|
|
381
|
+
);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
assert.isUndefined(response);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('JWT Validator - verify - should reject jwt created before the creation date of the key', async () => {
|
|
388
|
+
date = new Date(publicKeys[4].createdAt);
|
|
389
|
+
date.setHours(date.getHours() - 48);
|
|
390
|
+
const payload: any = {
|
|
391
|
+
b: 'b',
|
|
392
|
+
iat: date.getTime() / 1000,
|
|
393
|
+
keyId: 4,
|
|
394
|
+
};
|
|
395
|
+
const token: string = jwt.sign(JSON.stringify(payload), jwtMock.getPrivateKey(), {
|
|
396
|
+
algorithm: 'RS256',
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
const response = await jwtValidator.verifyToken(token).catch((err) => {
|
|
400
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_JWT);
|
|
401
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
402
|
+
assert.strictEqual(err.error.message, 'Invalid JWT');
|
|
403
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.INVALID_VALUE);
|
|
404
|
+
assert.strictEqual(err.error.details[0].target, 'jwt');
|
|
405
|
+
assert.strictEqual(
|
|
406
|
+
err.error.details[0].message,
|
|
407
|
+
"this jwt can't be created before the public key"
|
|
408
|
+
);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
assert.isUndefined(response);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it('JWT Validator - verify - should accept good token', async () => {
|
|
415
|
+
date = new Date();
|
|
416
|
+
const payload: any = {
|
|
417
|
+
accessToken: 'c9ba5a95-d7f9-41f9-9a24-a7e41882f7ef',
|
|
418
|
+
iss: 'Issuer',
|
|
419
|
+
inum: 'MyInum',
|
|
420
|
+
iat: date.getTime() / 1000,
|
|
421
|
+
exp: date.getTime() / 1000 + 3600,
|
|
422
|
+
sub: '@!4025.CA62.9BB6.16C5!0001!2212.0010!0000!0000.0001',
|
|
423
|
+
keyId: 4,
|
|
424
|
+
};
|
|
425
|
+
const token: string = jwt.sign(JSON.stringify(payload), jwtMock.getPrivateKey(), {
|
|
426
|
+
algorithm: 'RS256',
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
const response = await jwtValidator.verifyToken(token);
|
|
430
|
+
|
|
431
|
+
assert.deepEqual(response, payload);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it('isRequestWithJwt', async () => {
|
|
435
|
+
const req: express.Request = httpMocks.createRequest({ method: 'GET', url: '/' });
|
|
436
|
+
assert.isFalse(isRequestWithJwt(req));
|
|
437
|
+
|
|
438
|
+
req[constants.requestExtraVariables.JWT] = 'Bonjour la police';
|
|
439
|
+
assert.isTrue(isRequestWithJwt(req));
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it('JWT Validator - unable to get public key', async () => {
|
|
443
|
+
const pathRegex = createPathRegex();
|
|
444
|
+
|
|
445
|
+
// Intercept request
|
|
446
|
+
nock(configs.getHost()).get(pathRegex).reply(500);
|
|
447
|
+
|
|
448
|
+
const token: string = jwt.sign('{"a":"a", "keyId": "25"}', 'key', { algorithm: 'HS256' });
|
|
449
|
+
|
|
450
|
+
const response = await jwtValidator.verifyToken(token).catch((err) => {
|
|
451
|
+
assert.strictEqual(err.error.code, constants.errors.codes.UNABLE_TO_GET_PUBLIC_KEY);
|
|
452
|
+
assert.strictEqual(err.httpStatus, 500);
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
assert.isUndefined(response);
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it('JWT Validator - network error - should accept good token if cached', async () => {
|
|
459
|
+
// Invalidate cache
|
|
460
|
+
const currentNextUpdate = (cachedPublicKeyRepository as any)._nextUpdate;
|
|
461
|
+
(cachedPublicKeyRepository as any)._nextUpdate = undefined;
|
|
462
|
+
|
|
463
|
+
const pathRegex = createPathRegex();
|
|
464
|
+
|
|
465
|
+
// Intercept request
|
|
466
|
+
nock(configs.getHost()).get(pathRegex).replyWithError({ code: 'ABORTED' });
|
|
467
|
+
|
|
468
|
+
date = new Date();
|
|
469
|
+
const payload: any = {
|
|
470
|
+
accessToken: 'c9ba5a95-d7f9-41f9-9a24-a7e41882f7ef',
|
|
471
|
+
iss: 'Issuer',
|
|
472
|
+
inum: 'MyInum',
|
|
473
|
+
iat: date.getTime() / 1000,
|
|
474
|
+
exp: date.getTime() / 1000 + 3600,
|
|
475
|
+
sub: '@!4025.CA62.9BB6.16C5!0001!2212.0010!0000!0000.0001',
|
|
476
|
+
keyId: 4,
|
|
477
|
+
};
|
|
478
|
+
const token: string = jwt.sign(JSON.stringify(payload), jwtMock.getPrivateKey(), {
|
|
479
|
+
algorithm: 'RS256',
|
|
480
|
+
});
|
|
481
|
+
const response = await jwtValidator.verifyToken(token);
|
|
482
|
+
assert.deepEqual(response, payload);
|
|
483
|
+
|
|
484
|
+
(cachedPublicKeyRepository as any)._nextUpdate = currentNextUpdate;
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it('JWT Validator - 500 error from api - should accept good token if cached', async () => {
|
|
488
|
+
// Invalidate cache
|
|
489
|
+
const currentNextUpdate = (cachedPublicKeyRepository as any)._nextUpdate;
|
|
490
|
+
(cachedPublicKeyRepository as any)._nextUpdate = undefined;
|
|
491
|
+
|
|
492
|
+
const pathRegex = createPathRegex();
|
|
493
|
+
|
|
494
|
+
// Error from api
|
|
495
|
+
// Intercept request
|
|
496
|
+
nock(configs.getHost())
|
|
497
|
+
.get(pathRegex)
|
|
498
|
+
.reply(500, createServerError('Error while sending request'));
|
|
499
|
+
|
|
500
|
+
date = new Date();
|
|
501
|
+
const payload: any = {
|
|
502
|
+
accessToken: 'c9ba5a95-d7f9-41f9-9a24-a7e41882f7ef',
|
|
503
|
+
iss: 'Issuer',
|
|
504
|
+
inum: 'MyInum',
|
|
505
|
+
iat: date.getTime() / 1000,
|
|
506
|
+
exp: date.getTime() / 1000 + 3600,
|
|
507
|
+
sub: '@!4025.CA62.9BB6.16C5!0001!2212.0010!0000!0000.0001',
|
|
508
|
+
keyId: 4,
|
|
509
|
+
};
|
|
510
|
+
const token: string = jwt.sign(JSON.stringify(payload), jwtMock.getPrivateKey(), {
|
|
511
|
+
algorithm: 'RS256',
|
|
512
|
+
});
|
|
513
|
+
const response = await jwtValidator.verifyToken(token);
|
|
514
|
+
assert.deepEqual(response, payload);
|
|
515
|
+
|
|
516
|
+
(cachedPublicKeyRepository as any)._nextUpdate = currentNextUpdate;
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it('JWT Validator - 429 error from api - should accept good token if cached', async () => {
|
|
520
|
+
// Invalidate cache
|
|
521
|
+
const currentNextUpdate = (cachedPublicKeyRepository as any)._nextUpdate;
|
|
522
|
+
(cachedPublicKeyRepository as any)._nextUpdate = undefined;
|
|
523
|
+
|
|
524
|
+
const pathRegex = createPathRegex();
|
|
525
|
+
|
|
526
|
+
// Error from api
|
|
527
|
+
// Intercept request
|
|
528
|
+
nock(configs.getHost()).get(pathRegex).reply(429);
|
|
529
|
+
|
|
530
|
+
date = new Date();
|
|
531
|
+
const payload: any = {
|
|
532
|
+
accessToken: 'c9ba5a95-d7f9-41f9-9a24-a7e41882f7ef',
|
|
533
|
+
iss: 'Issuer',
|
|
534
|
+
inum: 'MyInum',
|
|
535
|
+
iat: date.getTime() / 1000,
|
|
536
|
+
exp: date.getTime() / 1000 + 3600,
|
|
537
|
+
sub: '@!4025.CA62.9BB6.16C5!0001!2212.0010!0000!0000.0001',
|
|
538
|
+
keyId: 4,
|
|
539
|
+
};
|
|
540
|
+
const token: string = jwt.sign(JSON.stringify(payload), jwtMock.getPrivateKey(), {
|
|
541
|
+
algorithm: 'RS256',
|
|
542
|
+
});
|
|
543
|
+
const response = await jwtValidator.verifyToken(token);
|
|
544
|
+
assert.deepEqual(response, payload);
|
|
545
|
+
|
|
546
|
+
(cachedPublicKeyRepository as any)._nextUpdate = currentNextUpdate;
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
it('JWT Validator - 400 error from api - should reject', async () => {
|
|
550
|
+
// Invalidate cache
|
|
551
|
+
const currentNextUpdate = (cachedPublicKeyRepository as any)._nextUpdate;
|
|
552
|
+
(cachedPublicKeyRepository as any)._nextUpdate = undefined;
|
|
553
|
+
|
|
554
|
+
const pathRegex = createPathRegex();
|
|
555
|
+
|
|
556
|
+
// Error from api
|
|
557
|
+
// Intercept request
|
|
558
|
+
nock(configs.getHost())
|
|
559
|
+
.get(pathRegex)
|
|
560
|
+
.reply(400, createInvalidParameterError('Something is wrong'));
|
|
561
|
+
|
|
562
|
+
date = new Date();
|
|
563
|
+
const payload: any = {
|
|
564
|
+
accessToken: 'c9ba5a95-d7f9-41f9-9a24-a7e41882f7ef',
|
|
565
|
+
iss: 'Issuer',
|
|
566
|
+
inum: 'MyInum',
|
|
567
|
+
iat: date.getTime() / 1000,
|
|
568
|
+
exp: date.getTime() / 1000 + 3600,
|
|
569
|
+
sub: '@!4025.CA62.9BB6.16C5!0001!2212.0010!0000!0000.0001',
|
|
570
|
+
keyId: 4,
|
|
571
|
+
};
|
|
572
|
+
const token: string = jwt.sign(JSON.stringify(payload), jwtMock.getPrivateKey(), {
|
|
573
|
+
algorithm: 'RS256',
|
|
574
|
+
});
|
|
575
|
+
let error;
|
|
576
|
+
try {
|
|
577
|
+
await jwtValidator.verifyToken(token);
|
|
578
|
+
} catch (e) {
|
|
579
|
+
error = e;
|
|
580
|
+
}
|
|
581
|
+
assert.isDefined(error);
|
|
582
|
+
assert.instanceOf(error, ApiErrorAndInfo);
|
|
583
|
+
(cachedPublicKeyRepository as any)._nextUpdate = currentNextUpdate;
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
it('JWT Validator - network error - should reject bad token anyway', async () => {
|
|
587
|
+
// Invalidate cache
|
|
588
|
+
const currentNextUpdate = (cachedPublicKeyRepository as any)._nextUpdate;
|
|
589
|
+
(cachedPublicKeyRepository as any)._nextUpdate = undefined;
|
|
590
|
+
|
|
591
|
+
const pathRegex = createPathRegex();
|
|
592
|
+
|
|
593
|
+
// Intercept request
|
|
594
|
+
nock(configs.getHost()).get(pathRegex).replyWithError({ code: 'ABORTED' });
|
|
595
|
+
|
|
596
|
+
const response = await jwtValidator.verifyToken('JWT').catch((err) => {
|
|
597
|
+
assert.strictEqual(err.error.code, constants.errors.codes.INVALID_JWT);
|
|
598
|
+
assert.strictEqual(err.error.target, 'Authorization header');
|
|
599
|
+
assert.strictEqual(err.error.message, 'Invalid JWT');
|
|
600
|
+
assert.strictEqual(err.error.details[0].code, constants.errors.codes.INVALID_VALUE);
|
|
601
|
+
assert.strictEqual(err.error.details[0].target, 'jwt');
|
|
602
|
+
assert.strictEqual(err.error.details[0].message, 'jwt malformed');
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
assert.isUndefined(response);
|
|
606
|
+
(cachedPublicKeyRepository as any)._nextUpdate = currentNextUpdate;
|
|
607
|
+
});
|