@villedemontreal/jwt-validator 5.7.12 → 5.8.1
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/README.md +24 -0
- package/dist/src/config/tokenTransformationMiddlewareConfig.d.ts +26 -0
- package/dist/src/config/tokenTransformationMiddlewareConfig.js +3 -0
- package/dist/src/config/tokenTransformationMiddlewareConfig.js.map +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/jwtValidator.js +2 -2
- package/dist/src/jwtValidator.js.map +1 -1
- package/dist/src/middleware/tokenTransformationMiddleware.d.ts +9 -0
- package/dist/src/middleware/tokenTransformationMiddleware.js +75 -0
- package/dist/src/middleware/tokenTransformationMiddleware.js.map +1 -0
- package/dist/src/models/publicKey.d.ts +3 -0
- package/dist/src/models/publicKey.js.map +1 -1
- package/dist/src/repositories/publicKeyRepository.js +12 -4
- package/dist/src/repositories/publicKeyRepository.js.map +1 -1
- package/package.json +10 -10
- package/src/config/tokenTransformationMiddlewareConfig.ts +28 -0
- package/src/index.ts +1 -0
- package/src/jwtValidator.ts +3 -3
- package/src/middleware/tokenTransformationMiddleware.ts +82 -0
- package/src/models/publicKey.ts +3 -0
- package/src/repositories/publicKeyRepository.ts +12 -4
package/README.md
CHANGED
|
@@ -155,6 +155,30 @@ let jwt: string = '...';
|
|
|
155
155
|
let jwtPayload: IJWTPayload = await jwtValidator.verifyToken(jwt);
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
+
### Token transformation middleware
|
|
159
|
+
|
|
160
|
+
When working locally, you do not have all the infrastructure required to simulate the translation between an access token and a JWT (ex: Kong).
|
|
161
|
+
|
|
162
|
+
Prior to usage of the `jwtValidationMiddleware`, you can use the `token transformation middleware` which do an API call to exchange your access token for an extended jwt and update the authorization header.
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
export async function createApp(apiRoutes: IHandlerRoute[]): Promise<express.Express> {
|
|
166
|
+
// ...
|
|
167
|
+
|
|
168
|
+
if (configs.security.jwt.enable) {
|
|
169
|
+
if (configs.environment.isLocal) {
|
|
170
|
+
// Required prior to the jwtValidationMiddleware
|
|
171
|
+
app.use(tokenTransformationMiddleware(config));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ...
|
|
175
|
+
app.use(jwtValidationMiddleware())
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ...
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
158
182
|
## Usage For Tests
|
|
159
183
|
|
|
160
184
|
This library provides a mock tool to generate your own JWT with a signature which it will be correcly validated.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for the token transformation
|
|
3
|
+
* middleware (UTTM).
|
|
4
|
+
*/
|
|
5
|
+
export interface ITokenTtransformationMiddlewareConfig {
|
|
6
|
+
service: ITokenTtransformationMiddlewareServiceConfig;
|
|
7
|
+
extensions: {
|
|
8
|
+
jwt: {
|
|
9
|
+
customDataProvider: ITokenTtransformationMiddlewareCustomDataProviderConfig;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Configuration specific for the token transformation service.
|
|
15
|
+
*/
|
|
16
|
+
export interface ITokenTtransformationMiddlewareServiceConfig {
|
|
17
|
+
uri: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Configuration specific for the custom data provider.
|
|
21
|
+
*/
|
|
22
|
+
export interface ITokenTtransformationMiddlewareCustomDataProviderConfig {
|
|
23
|
+
uri: string;
|
|
24
|
+
method: string;
|
|
25
|
+
options: any;
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenTransformationMiddlewareConfig.js","sourceRoot":"","sources":["../../../src/config/tokenTransformationMiddlewareConfig.ts"],"names":[],"mappings":""}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from './config/constants';
|
|
|
2
2
|
export * from './config/init';
|
|
3
3
|
export * from './jwtValidator';
|
|
4
4
|
export * from './middleware/jwtMiddleware';
|
|
5
|
+
export * from './middleware/tokenTransformationMiddleware';
|
|
5
6
|
export * from './models/expressRequest';
|
|
6
7
|
export * from './models/gluuUserType';
|
|
7
8
|
export * from './models/jwtPayload';
|
package/dist/src/index.js
CHANGED
|
@@ -23,6 +23,7 @@ __exportStar(require("./config/constants"), exports);
|
|
|
23
23
|
__exportStar(require("./config/init"), exports);
|
|
24
24
|
__exportStar(require("./jwtValidator"), exports);
|
|
25
25
|
__exportStar(require("./middleware/jwtMiddleware"), exports);
|
|
26
|
+
__exportStar(require("./middleware/tokenTransformationMiddleware"), exports);
|
|
26
27
|
__exportStar(require("./models/expressRequest"), exports);
|
|
27
28
|
__exportStar(require("./models/gluuUserType"), exports);
|
|
28
29
|
__exportStar(require("./models/jwtPayload"), exports);
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,qDAAmC;AACnC,6CAA6C;AAC7C,gDAAgD;AAChD,6CAA6C;AAC7C,qCAAqC;AACrC,6CAA6C;AAC7C,gDAA8B;AAC9B,iDAA+B;AAC/B,6DAA2C;AAC3C,0DAAwC;AACxC,wDAAsC;AACtC,sDAAoC;AACpC,qDAAmC;AACnC,kDAAgC;AAChC,kDAAgC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,qDAAmC;AACnC,6CAA6C;AAC7C,gDAAgD;AAChD,6CAA6C;AAC7C,qCAAqC;AACrC,6CAA6C;AAC7C,gDAA8B;AAC9B,iDAA+B;AAC/B,6DAA2C;AAC3C,6EAA2D;AAC3D,0DAAwC;AACxC,wDAAsC;AACtC,sDAAoC;AACpC,qDAAmC;AACnC,kDAAgC;AAChC,kDAAgC"}
|
package/dist/src/jwtValidator.js
CHANGED
|
@@ -36,7 +36,7 @@ class JwtValidator {
|
|
|
36
36
|
const key = await this.getJwtPublicKey(payload);
|
|
37
37
|
this.validateJwtCreationTimestamp(payload, key);
|
|
38
38
|
this.validateJwtExpirationTimestamp(payload, key);
|
|
39
|
-
return this.verifyJwt(token, key
|
|
39
|
+
return this.verifyJwt(token, key);
|
|
40
40
|
}
|
|
41
41
|
parseJwt(token) {
|
|
42
42
|
const payload = jwt.decode(token);
|
|
@@ -52,7 +52,7 @@ class JwtValidator {
|
|
|
52
52
|
verifyJwt(token, publicKey) {
|
|
53
53
|
let payload;
|
|
54
54
|
try {
|
|
55
|
-
payload = jwt.verify(token, publicKey);
|
|
55
|
+
payload = jwt.verify(token, publicKey.parsedPublicKey ?? publicKey.publicKey);
|
|
56
56
|
}
|
|
57
57
|
catch (err) {
|
|
58
58
|
throw (0, customError_1.createInvalidJwtError)({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwtValidator.js","sourceRoot":"","sources":["../../src/jwtValidator.ts"],"names":[],"mappings":";;;AAAA,kEAAuD;AACvD,oCAAoC;AACpC,iCAAiC;AACjC,kDAA+C;AAC/C,sDAA2F;AAC3F,oDAAgE;AAChE,kDAAgE;AAChE,wFAAqF;AAuBrF;;GAEG;AACH,MAAM,YAAY;IACT,KAAK,CAAC,yBAAyB,CAAC,MAAc;QACnD,IAAI,qBAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;YACzB,MAAM,IAAA,0CAA4B,EAAC;gBACjC,IAAI,EAAE,qBAAS,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU;gBACvC,MAAM,EAAE,sBAAsB;gBAC9B,OAAO,EAAE,4BAA4B;aACtC,CAAC,CAAC;SACJ;QAED,MAAM,KAAK,GAAa,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACjD,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE;YACzB,MAAM,IAAA,0CAA4B,EAAC;gBACjC,IAAI,EAAE,qBAAS,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa;gBAC1C,MAAM,EAAE,sBAAsB;gBAC9B,OAAO,EAAE,8CAA8C;aACxD,CAAC,CAAC;SACJ;QAED,OAAO,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;IAEM,KAAK,CAAC,WAAW,CAAC,KAAa;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAErC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAEhD,IAAI,CAAC,4BAA4B,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAChD,IAAI,CAAC,8BAA8B,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAElD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"jwtValidator.js","sourceRoot":"","sources":["../../src/jwtValidator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as express from 'express';
|
|
2
|
+
import { ITokenTtransformationMiddlewareConfig } from '../config/tokenTransformationMiddlewareConfig';
|
|
3
|
+
/**
|
|
4
|
+
* Token transformation Middleware. It will generate extended jwt
|
|
5
|
+
* in exchange for an access token.
|
|
6
|
+
*
|
|
7
|
+
* @param {boolean} config Configuration of the middleware.
|
|
8
|
+
*/
|
|
9
|
+
export declare const tokenTransformationMiddleware: (config?: ITokenTtransformationMiddlewareConfig) => (req: express.Request, res: express.Response, next: express.NextFunction) => void;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.tokenTransformationMiddleware = void 0;
|
|
4
|
+
const general_utils_1 = require("@villedemontreal/general-utils");
|
|
5
|
+
const http_header_fields_typed_1 = require("http-header-fields-typed");
|
|
6
|
+
const constants_1 = require("../config/constants");
|
|
7
|
+
const customError_1 = require("../models/customError");
|
|
8
|
+
const logger_1 = require("../utils/logger");
|
|
9
|
+
const superagent = require("superagent");
|
|
10
|
+
const _regexAccessToken = /([a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12})/;
|
|
11
|
+
const logger = (0, logger_1.createLogger)('Token transformation middleware');
|
|
12
|
+
/**
|
|
13
|
+
* Token transformation Middleware. It will generate extended jwt
|
|
14
|
+
* in exchange for an access token.
|
|
15
|
+
*
|
|
16
|
+
* @param {boolean} config Configuration of the middleware.
|
|
17
|
+
*/
|
|
18
|
+
const tokenTransformationMiddleware = (config) => {
|
|
19
|
+
return (req, res, next) => {
|
|
20
|
+
try {
|
|
21
|
+
// Validate the authorization header
|
|
22
|
+
const authHeader = req.get(http_header_fields_typed_1.default.AUTHORIZATION);
|
|
23
|
+
if (general_utils_1.utils.isBlank(authHeader)) {
|
|
24
|
+
throw (0, customError_1.createInvalidAuthHeaderError)({
|
|
25
|
+
code: constants_1.constants.errors.codes.INVALID_VALUE,
|
|
26
|
+
target: 'authorization_header',
|
|
27
|
+
message: 'authorization header is empty',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
// Extract the access token value from the authorization header
|
|
31
|
+
const accessTokenRegExpArray = _regexAccessToken.exec(authHeader);
|
|
32
|
+
if (accessTokenRegExpArray.length <= 1) {
|
|
33
|
+
throw (0, customError_1.createInvalidAuthHeaderError)({
|
|
34
|
+
code: constants_1.constants.errors.codes.INVALID_VALUE,
|
|
35
|
+
target: 'access_token',
|
|
36
|
+
message: 'could not find a valid access token from the authorization header',
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const accessToken = accessTokenRegExpArray[1];
|
|
40
|
+
// Call the service endpoint to exchange the access token for a extended jwt
|
|
41
|
+
superagent
|
|
42
|
+
.post(config.service.uri)
|
|
43
|
+
.send({ accessToken, extensions: config.extensions })
|
|
44
|
+
.then((response) => {
|
|
45
|
+
const extendedJwt = response.body.jwts?.extended;
|
|
46
|
+
logger.debug(extendedJwt, 'Extended jwt content.');
|
|
47
|
+
const basicJwt = response.body.jwts?.basic;
|
|
48
|
+
logger.debug(basicJwt, 'Basic jwt content.');
|
|
49
|
+
// Get the extended jwt. If not available, fallback to basic jwt.
|
|
50
|
+
const jwt = extendedJwt ?? basicJwt;
|
|
51
|
+
if (jwt) {
|
|
52
|
+
// Warning: Headers are all in lowercase. To be sure to replace
|
|
53
|
+
// the authorization header instead of duplicate it, must use lower case property name.
|
|
54
|
+
req.headers[http_header_fields_typed_1.default.AUTHORIZATION.toLowerCase()] = `Bearer ${jwt}`;
|
|
55
|
+
logger.debug(req.headers, 'Request headers');
|
|
56
|
+
next();
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
const err = (0, customError_1.createInvalidJwtError)({
|
|
60
|
+
code: constants_1.constants.errors.codes.NULL_VALUE,
|
|
61
|
+
target: 'jwt',
|
|
62
|
+
message: 'could not get a valid jwt from token translation service',
|
|
63
|
+
});
|
|
64
|
+
next(err);
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
.catch((err) => next(err));
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
next(err);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
exports.tokenTransformationMiddleware = tokenTransformationMiddleware;
|
|
75
|
+
//# sourceMappingURL=tokenTransformationMiddleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenTransformationMiddleware.js","sourceRoot":"","sources":["../../../src/middleware/tokenTransformationMiddleware.ts"],"names":[],"mappings":";;;AAAA,kEAAuD;AAEvD,uEAA6D;AAC7D,mDAAgD;AAEhD,uDAA4F;AAC5F,4CAA+C;AAC/C,yCAA0C;AAE1C,MAAM,iBAAiB,GAAG,gEAAgE,CAAC;AAE3F,MAAM,MAAM,GAAG,IAAA,qBAAY,EAAC,iCAAiC,CAAC,CAAC;AAE/D;;;;;GAKG;AACI,MAAM,6BAA6B,GAE+C,CACvF,MAAM,EACN,EAAE;IACF,OAAO,CAAC,GAAoB,EAAE,GAAqB,EAAE,IAA0B,EAAQ,EAAE;QACvF,IAAI;YACF,oCAAoC;YACpC,MAAM,UAAU,GAAW,GAAG,CAAC,GAAG,CAAC,kCAAqB,CAAC,aAAa,CAAC,CAAC;YACxE,IAAI,qBAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE;gBAC7B,MAAM,IAAA,0CAA4B,EAAC;oBACjC,IAAI,EAAE,qBAAS,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa;oBAC1C,MAAM,EAAE,sBAAsB;oBAC9B,OAAO,EAAE,+BAA+B;iBACzC,CAAC,CAAC;aACJ;YAED,+DAA+D;YAC/D,MAAM,sBAAsB,GAAG,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAClE,IAAI,sBAAsB,CAAC,MAAM,IAAI,CAAC,EAAE;gBACtC,MAAM,IAAA,0CAA4B,EAAC;oBACjC,IAAI,EAAE,qBAAS,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa;oBAC1C,MAAM,EAAE,cAAc;oBACtB,OAAO,EAAE,mEAAmE;iBAC7E,CAAC,CAAC;aACJ;YACD,MAAM,WAAW,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAAC;YAE9C,4EAA4E;YAC5E,UAAU;iBACP,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC;iBACxB,IAAI,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC;iBACpD,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE;gBACjB,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC;gBACjD,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,uBAAuB,CAAC,CAAC;gBAEnD,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC;gBAC3C,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;gBAE7C,iEAAiE;gBACjE,MAAM,GAAG,GAAG,WAAW,IAAI,QAAQ,CAAC;gBAEpC,IAAI,GAAG,EAAE;oBACP,+DAA+D;oBAC/D,uFAAuF;oBACvF,GAAG,CAAC,OAAO,CAAC,kCAAqB,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC,GAAG,UAAU,GAAG,EAAE,CAAC;oBACjF,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;oBAC7C,IAAI,EAAE,CAAC;iBACR;qBAAM;oBACL,MAAM,GAAG,GAAG,IAAA,mCAAqB,EAAC;wBAChC,IAAI,EAAE,qBAAS,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU;wBACvC,MAAM,EAAE,KAAK;wBACb,OAAO,EAAE,0DAA0D;qBACpE,CAAC,CAAC;oBACH,IAAI,CAAC,GAAG,CAAC,CAAC;iBACX;YACH,CAAC,CAAC;iBACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;SAC9B;QAAC,OAAO,GAAG,EAAE;YACZ,IAAI,CAAC,GAAG,CAAC,CAAC;SACX;IACH,CAAC,CAAC;AACJ,CAAC,CAAC;AA9DW,QAAA,6BAA6B,iCA8DxC"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import * as crypto from 'crypto';
|
|
1
3
|
/**
|
|
2
4
|
* The content of public key
|
|
3
5
|
*/
|
|
@@ -5,6 +7,7 @@ export interface IPublicKey {
|
|
|
5
7
|
id: number;
|
|
6
8
|
algorithm: string;
|
|
7
9
|
publicKey: string;
|
|
10
|
+
parsedPublicKey?: crypto.KeyObject;
|
|
8
11
|
state: PublicKeyState;
|
|
9
12
|
createdAt?: string;
|
|
10
13
|
expiresAt?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publicKey.js","sourceRoot":"","sources":["../../../src/models/publicKey.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"publicKey.js","sourceRoot":"","sources":["../../../src/models/publicKey.ts"],"names":[],"mappings":";;;AA4BA;;GAEG;AACH,IAAY,cAIX;AAJD,WAAY,cAAc;IACxB,mCAAiB,CAAA;IACjB,qCAAmB,CAAA;IACnB,qCAAmB,CAAA;AACrB,CAAC,EAJW,cAAc,GAAd,sBAAc,KAAd,sBAAc,QAIzB"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.publicKeyRepository = void 0;
|
|
4
4
|
const http_request_1 = require("@villedemontreal/http-request");
|
|
5
|
+
const crypto = require("crypto");
|
|
5
6
|
const lodash_1 = require("lodash");
|
|
6
7
|
const superagent = require("superagent");
|
|
7
8
|
const general_utils_1 = require("@villedemontreal/general-utils");
|
|
@@ -13,11 +14,15 @@ class PublicKeyRepository {
|
|
|
13
14
|
const request = superagent.get(url);
|
|
14
15
|
const response = await http_request_1.httpUtils.send(request);
|
|
15
16
|
if (!response.ok) {
|
|
16
|
-
throw new Error(`An error
|
|
17
|
+
throw new Error(`An error occurred calling ${url} : ${response.error}`);
|
|
17
18
|
}
|
|
18
19
|
const data = response.body;
|
|
19
20
|
if (data && data.items && (0, lodash_1.isArray)(data.items)) {
|
|
20
|
-
const
|
|
21
|
+
const items = data.items.map((key) => ({
|
|
22
|
+
...key,
|
|
23
|
+
parsedPublicKey: crypto.createPublicKey(key.publicKey),
|
|
24
|
+
}));
|
|
25
|
+
const newKeys = (0, lodash_1.keyBy)(items, (item) => item.id);
|
|
21
26
|
return newKeys;
|
|
22
27
|
}
|
|
23
28
|
return null;
|
|
@@ -33,11 +38,14 @@ class PublicKeyRepository {
|
|
|
33
38
|
if (response.status === 404) {
|
|
34
39
|
return null;
|
|
35
40
|
}
|
|
36
|
-
throw (0, general_utils_1.createError)(constants_1.constants.errors.codes.UNABLE_TO_GET_PUBLIC_KEY, `An error
|
|
41
|
+
throw (0, general_utils_1.createError)(constants_1.constants.errors.codes.UNABLE_TO_GET_PUBLIC_KEY, `An error occurred calling ${url} : ${response.error}`)
|
|
37
42
|
.httpStatus(response.status)
|
|
38
43
|
.build();
|
|
39
44
|
}
|
|
40
|
-
return
|
|
45
|
+
return {
|
|
46
|
+
...response.body,
|
|
47
|
+
parsedPublicKey: crypto.createPublicKey(response.body.publicKey),
|
|
48
|
+
};
|
|
41
49
|
}
|
|
42
50
|
}
|
|
43
51
|
exports.publicKeyRepository = new PublicKeyRepository();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publicKeyRepository.js","sourceRoot":"","sources":["../../../src/repositories/publicKeyRepository.ts"],"names":[],"mappings":";;;AAAA,gEAA0D;AAC1D,mCAAwC;AACxC,yCAAyC;AAEzC,kEAA6D;AAC7D,+CAA4C;AAC5C,mDAAgD;AAuBhD,MAAM,mBAAmB;IAChB,KAAK,CAAC,MAAM;QACjB,MAAM,GAAG,GAAG,GAAG,wBAAS,CAAC,OAAO,CAC9B,iBAAO,CAAC,OAAO,EAAE,EACjB,iBAAO,CAAC,WAAW,EAAE,CACtB,IAAI,iBAAO,CAAC,sBAAsB,EAAE,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEpC,MAAM,QAAQ,GAAG,MAAM,wBAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC/C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE;YAChB,MAAM,IAAI,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"publicKeyRepository.js","sourceRoot":"","sources":["../../../src/repositories/publicKeyRepository.ts"],"names":[],"mappings":";;;AAAA,gEAA0D;AAC1D,iCAAiC;AACjC,mCAAwC;AACxC,yCAAyC;AAEzC,kEAA6D;AAC7D,+CAA4C;AAC5C,mDAAgD;AAuBhD,MAAM,mBAAmB;IAChB,KAAK,CAAC,MAAM;QACjB,MAAM,GAAG,GAAG,GAAG,wBAAS,CAAC,OAAO,CAC9B,iBAAO,CAAC,OAAO,EAAE,EACjB,iBAAO,CAAC,WAAW,EAAE,CACtB,IAAI,iBAAO,CAAC,sBAAsB,EAAE,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEpC,MAAM,QAAQ,GAAG,MAAM,wBAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC/C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE;YAChB,MAAM,IAAI,KAAK,CAAC,6BAA6B,GAAG,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;SACzE;QAED,MAAM,IAAI,GAAiC,QAAQ,CAAC,IAAI,CAAC;QACzD,IAAI,IAAI,IAAI,IAAI,CAAC,KAAK,IAAI,IAAA,gBAAO,EAAC,IAAI,CAAC,KAAK,CAAC,EAAE;YAC7C,MAAM,KAAK,GAAiB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;gBACnD,GAAG,GAAG;gBACN,eAAe,EAAE,MAAM,CAAC,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC;aACvD,CAAC,CAAC,CAAC;YACJ,MAAM,OAAO,GAAgB,IAAA,cAAK,EAAC,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC7D,OAAO,OAAO,CAAC;SAChB;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,KAAa;QAC/B,MAAM,GAAG,GAAG,wBAAS,CAAC,OAAO,CAAC,iBAAO,CAAC,OAAO,EAAE,EAAE,iBAAO,CAAC,WAAW,EAAE,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC1F,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEpC,MAAM,QAAQ,GAAG,MAAM,wBAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC/C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE;YAChB,6CAA6C;YAC7C,4BAA4B;YAC5B,6CAA6C;YAC7C,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE;gBAC3B,OAAO,IAAI,CAAC;aACb;YAED,MAAM,IAAA,2BAAW,EACf,qBAAS,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAC/C,6BAA6B,GAAG,MAAM,QAAQ,CAAC,KAAK,EAAE,CACvD;iBACE,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC;iBAC3B,KAAK,EAAE,CAAC;SACZ;QACD,OAAO;YACL,GAAG,QAAQ,CAAC,IAAI;YAChB,eAAe,EAAE,MAAM,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC;SACjE,CAAC;IACJ,CAAC;CACF;AAEY,QAAA,mBAAmB,GAAyB,IAAI,mBAAmB,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@villedemontreal/jwt-validator",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.8.1",
|
|
4
4
|
"description": "Module to validate JWT (JSON Web Tokens)",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"typings": "dist/src",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"@types/express": "4.17.17",
|
|
51
51
|
"@types/http-status-codes": "1.2.0",
|
|
52
52
|
"@types/jsonwebtoken": "9.0.1",
|
|
53
|
-
"@types/lodash": "4.14.
|
|
54
|
-
"@types/node": "18.15.
|
|
53
|
+
"@types/lodash": "4.14.192",
|
|
54
|
+
"@types/node": "18.15.11",
|
|
55
55
|
"@types/request": "2.48.8",
|
|
56
56
|
"@types/request-promise-native": "1.0.18",
|
|
57
57
|
"@types/superagent": "4.1.16",
|
|
@@ -60,23 +60,23 @@
|
|
|
60
60
|
"@types/mocha": "10.0.1",
|
|
61
61
|
"@types/sinon": "10.0.13",
|
|
62
62
|
"@types/validator": "13.7.14",
|
|
63
|
-
"@typescript-eslint/eslint-plugin": "5.
|
|
64
|
-
"@typescript-eslint/parser": "5.
|
|
63
|
+
"@typescript-eslint/eslint-plugin": "5.57.1",
|
|
64
|
+
"@typescript-eslint/parser": "5.57.1",
|
|
65
65
|
"@villedemontreal/scripting": "2.1.6",
|
|
66
66
|
"chai": "4.3.7",
|
|
67
|
-
"eslint": "8.
|
|
68
|
-
"eslint-config-prettier": "8.
|
|
67
|
+
"eslint": "8.38.0",
|
|
68
|
+
"eslint-config-prettier": "8.8.0",
|
|
69
69
|
"eslint-plugin-prettier": "4.2.1",
|
|
70
|
-
"fs-extra": "11.1.
|
|
70
|
+
"fs-extra": "11.1.1",
|
|
71
71
|
"mocha": "10.2.0",
|
|
72
72
|
"mocha-jenkins-reporter": "0.4.8",
|
|
73
73
|
"mock-express-request": "0.2.2",
|
|
74
74
|
"node-mocks-http": "1.12.2",
|
|
75
75
|
"node-notifier": "10.0.1",
|
|
76
76
|
"nyc": "15.1.0",
|
|
77
|
-
"sinon": "15.0.
|
|
77
|
+
"sinon": "15.0.3",
|
|
78
78
|
"superagent-mocker": "0.5.2",
|
|
79
|
-
"typescript": "5.0.
|
|
79
|
+
"typescript": "5.0.4",
|
|
80
80
|
"validator": "13.9.0"
|
|
81
81
|
}
|
|
82
82
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for the token transformation
|
|
3
|
+
* middleware (UTTM).
|
|
4
|
+
*/
|
|
5
|
+
export interface ITokenTtransformationMiddlewareConfig {
|
|
6
|
+
service: ITokenTtransformationMiddlewareServiceConfig;
|
|
7
|
+
extensions: {
|
|
8
|
+
jwt: {
|
|
9
|
+
customDataProvider: ITokenTtransformationMiddlewareCustomDataProviderConfig;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Configuration specific for the token transformation service.
|
|
16
|
+
*/
|
|
17
|
+
export interface ITokenTtransformationMiddlewareServiceConfig {
|
|
18
|
+
uri: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Configuration specific for the custom data provider.
|
|
23
|
+
*/
|
|
24
|
+
export interface ITokenTtransformationMiddlewareCustomDataProviderConfig {
|
|
25
|
+
uri: string;
|
|
26
|
+
method: string;
|
|
27
|
+
options: any;
|
|
28
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from './config/constants';
|
|
|
7
7
|
export * from './config/init';
|
|
8
8
|
export * from './jwtValidator';
|
|
9
9
|
export * from './middleware/jwtMiddleware';
|
|
10
|
+
export * from './middleware/tokenTransformationMiddleware';
|
|
10
11
|
export * from './models/expressRequest';
|
|
11
12
|
export * from './models/gluuUserType';
|
|
12
13
|
export * from './models/jwtPayload';
|
package/src/jwtValidator.ts
CHANGED
|
@@ -61,7 +61,7 @@ class JwtValidator implements IJwtValidator {
|
|
|
61
61
|
this.validateJwtCreationTimestamp(payload, key);
|
|
62
62
|
this.validateJwtExpirationTimestamp(payload, key);
|
|
63
63
|
|
|
64
|
-
return this.verifyJwt(token, key
|
|
64
|
+
return this.verifyJwt(token, key);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
private parseJwt(token: string): IJWTPayload {
|
|
@@ -76,10 +76,10 @@ class JwtValidator implements IJwtValidator {
|
|
|
76
76
|
return payload;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
private verifyJwt(token: string, publicKey:
|
|
79
|
+
private verifyJwt(token: string, publicKey: IPublicKey): IJWTPayload {
|
|
80
80
|
let payload: any;
|
|
81
81
|
try {
|
|
82
|
-
payload = jwt.verify(token, publicKey);
|
|
82
|
+
payload = jwt.verify(token, publicKey.parsedPublicKey ?? publicKey.publicKey);
|
|
83
83
|
} catch (err) {
|
|
84
84
|
throw createInvalidJwtError({
|
|
85
85
|
code: constants.errors.codes.INVALID_VALUE,
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { utils } from '@villedemontreal/general-utils';
|
|
2
|
+
import * as express from 'express';
|
|
3
|
+
import httpHeaderFieldsTyped from 'http-header-fields-typed';
|
|
4
|
+
import { constants } from '../config/constants';
|
|
5
|
+
import { ITokenTtransformationMiddlewareConfig } from '../config/tokenTransformationMiddlewareConfig';
|
|
6
|
+
import { createInvalidAuthHeaderError, createInvalidJwtError } from '../models/customError';
|
|
7
|
+
import { createLogger } from '../utils/logger';
|
|
8
|
+
import superagent = require('superagent');
|
|
9
|
+
|
|
10
|
+
const _regexAccessToken = /([a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12})/;
|
|
11
|
+
|
|
12
|
+
const logger = createLogger('Token transformation middleware');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Token transformation Middleware. It will generate extended jwt
|
|
16
|
+
* in exchange for an access token.
|
|
17
|
+
*
|
|
18
|
+
* @param {boolean} config Configuration of the middleware.
|
|
19
|
+
*/
|
|
20
|
+
export const tokenTransformationMiddleware: (
|
|
21
|
+
config?: ITokenTtransformationMiddlewareConfig
|
|
22
|
+
) => (req: express.Request, res: express.Response, next: express.NextFunction) => void = (
|
|
23
|
+
config
|
|
24
|
+
) => {
|
|
25
|
+
return (req: express.Request, res: express.Response, next: express.NextFunction): void => {
|
|
26
|
+
try {
|
|
27
|
+
// Validate the authorization header
|
|
28
|
+
const authHeader: string = req.get(httpHeaderFieldsTyped.AUTHORIZATION);
|
|
29
|
+
if (utils.isBlank(authHeader)) {
|
|
30
|
+
throw createInvalidAuthHeaderError({
|
|
31
|
+
code: constants.errors.codes.INVALID_VALUE,
|
|
32
|
+
target: 'authorization_header',
|
|
33
|
+
message: 'authorization header is empty',
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Extract the access token value from the authorization header
|
|
38
|
+
const accessTokenRegExpArray = _regexAccessToken.exec(authHeader);
|
|
39
|
+
if (accessTokenRegExpArray.length <= 1) {
|
|
40
|
+
throw createInvalidAuthHeaderError({
|
|
41
|
+
code: constants.errors.codes.INVALID_VALUE,
|
|
42
|
+
target: 'access_token',
|
|
43
|
+
message: 'could not find a valid access token from the authorization header',
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const accessToken = accessTokenRegExpArray[1];
|
|
47
|
+
|
|
48
|
+
// Call the service endpoint to exchange the access token for a extended jwt
|
|
49
|
+
superagent
|
|
50
|
+
.post(config.service.uri)
|
|
51
|
+
.send({ accessToken, extensions: config.extensions })
|
|
52
|
+
.then((response) => {
|
|
53
|
+
const extendedJwt = response.body.jwts?.extended;
|
|
54
|
+
logger.debug(extendedJwt, 'Extended jwt content.');
|
|
55
|
+
|
|
56
|
+
const basicJwt = response.body.jwts?.basic;
|
|
57
|
+
logger.debug(basicJwt, 'Basic jwt content.');
|
|
58
|
+
|
|
59
|
+
// Get the extended jwt. If not available, fallback to basic jwt.
|
|
60
|
+
const jwt = extendedJwt ?? basicJwt;
|
|
61
|
+
|
|
62
|
+
if (jwt) {
|
|
63
|
+
// Warning: Headers are all in lowercase. To be sure to replace
|
|
64
|
+
// the authorization header instead of duplicate it, must use lower case property name.
|
|
65
|
+
req.headers[httpHeaderFieldsTyped.AUTHORIZATION.toLowerCase()] = `Bearer ${jwt}`;
|
|
66
|
+
logger.debug(req.headers, 'Request headers');
|
|
67
|
+
next();
|
|
68
|
+
} else {
|
|
69
|
+
const err = createInvalidJwtError({
|
|
70
|
+
code: constants.errors.codes.NULL_VALUE,
|
|
71
|
+
target: 'jwt',
|
|
72
|
+
message: 'could not get a valid jwt from token translation service',
|
|
73
|
+
});
|
|
74
|
+
next(err);
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
.catch((err) => next(err));
|
|
78
|
+
} catch (err) {
|
|
79
|
+
next(err);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
};
|
package/src/models/publicKey.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as crypto from 'crypto';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* The content of public key
|
|
3
5
|
*/
|
|
@@ -5,6 +7,7 @@ export interface IPublicKey {
|
|
|
5
7
|
id: number;
|
|
6
8
|
algorithm: string;
|
|
7
9
|
publicKey: string;
|
|
10
|
+
parsedPublicKey?: crypto.KeyObject;
|
|
8
11
|
state: PublicKeyState;
|
|
9
12
|
createdAt?: string;
|
|
10
13
|
expiresAt?: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { httpUtils } from '@villedemontreal/http-request';
|
|
2
|
+
import * as crypto from 'crypto';
|
|
2
3
|
import { isArray, keyBy } from 'lodash';
|
|
3
4
|
import * as superagent from 'superagent';
|
|
4
5
|
|
|
@@ -37,12 +38,16 @@ class PublicKeyRepository implements IPublicKeyRepository {
|
|
|
37
38
|
|
|
38
39
|
const response = await httpUtils.send(request);
|
|
39
40
|
if (!response.ok) {
|
|
40
|
-
throw new Error(`An error
|
|
41
|
+
throw new Error(`An error occurred calling ${url} : ${response.error}`);
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
const data: IPaginatedResult<IPublicKey> = response.body;
|
|
44
45
|
if (data && data.items && isArray(data.items)) {
|
|
45
|
-
const
|
|
46
|
+
const items: IPublicKey[] = data.items.map((key) => ({
|
|
47
|
+
...key,
|
|
48
|
+
parsedPublicKey: crypto.createPublicKey(key.publicKey),
|
|
49
|
+
}));
|
|
50
|
+
const newKeys: IPublicKeys = keyBy(items, (item) => item.id);
|
|
46
51
|
return newKeys;
|
|
47
52
|
}
|
|
48
53
|
return null;
|
|
@@ -63,12 +68,15 @@ class PublicKeyRepository implements IPublicKeyRepository {
|
|
|
63
68
|
|
|
64
69
|
throw createError(
|
|
65
70
|
constants.errors.codes.UNABLE_TO_GET_PUBLIC_KEY,
|
|
66
|
-
`An error
|
|
71
|
+
`An error occurred calling ${url} : ${response.error}`
|
|
67
72
|
)
|
|
68
73
|
.httpStatus(response.status)
|
|
69
74
|
.build();
|
|
70
75
|
}
|
|
71
|
-
return
|
|
76
|
+
return {
|
|
77
|
+
...response.body,
|
|
78
|
+
parsedPublicKey: crypto.createPublicKey(response.body.publicKey),
|
|
79
|
+
};
|
|
72
80
|
}
|
|
73
81
|
}
|
|
74
82
|
|