@villedemontreal/jwt-validator 5.9.1 → 5.9.3
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/dist/src/config/configs.d.ts +15 -0
- package/dist/src/config/configs.js +11 -0
- package/dist/src/config/configs.js.map +1 -1
- package/dist/src/middleware/tokenTransformationMiddleware.js +49 -15
- package/dist/src/middleware/tokenTransformationMiddleware.js.map +1 -1
- package/dist/src/models/accessToken.d.ts +32 -0
- package/dist/src/models/accessToken.js +3 -0
- package/dist/src/models/accessToken.js.map +1 -0
- package/package.json +1 -1
- package/src/config/configs.ts +21 -0
- package/src/middleware/tokenTransformationMiddleware.ts +55 -18
- package/src/models/accessToken.ts +35 -0
|
@@ -21,6 +21,14 @@ export declare class Configs {
|
|
|
21
21
|
* Cache duration
|
|
22
22
|
*/
|
|
23
23
|
private _cacheDuration;
|
|
24
|
+
/**
|
|
25
|
+
* When this library is used as a dependency in a project, the source project name will be the property name in the package.json of this project
|
|
26
|
+
*
|
|
27
|
+
* @private
|
|
28
|
+
* @type {string}
|
|
29
|
+
* @memberof Configs
|
|
30
|
+
*/
|
|
31
|
+
private readonly _sourceProjectName;
|
|
24
32
|
private _loggerCreator;
|
|
25
33
|
private _correlationIdProvider;
|
|
26
34
|
constructor();
|
|
@@ -80,6 +88,13 @@ export declare class Configs {
|
|
|
80
88
|
* Sets the Correlation Id provider.
|
|
81
89
|
*/
|
|
82
90
|
setCorrelationIdProvider(correlationIdProvider: () => string): void;
|
|
91
|
+
/**
|
|
92
|
+
* Get the source project name where this library is imported
|
|
93
|
+
*
|
|
94
|
+
* @return {*} {string}
|
|
95
|
+
* @memberof Configs
|
|
96
|
+
*/
|
|
97
|
+
getSourceProjectName(): string;
|
|
83
98
|
/**
|
|
84
99
|
* The Correlation Id provider
|
|
85
100
|
*/
|
|
@@ -23,6 +23,8 @@ class Configs {
|
|
|
23
23
|
this._cacheDuration = constants_1.constants.default.cacheDuration;
|
|
24
24
|
this.libRoot = path.normalize(__dirname + '/../../..');
|
|
25
25
|
this.isWindows = os.platform() === 'win32';
|
|
26
|
+
const sourcePackageJson = require(`${constants_1.constants.appRoot}/package.json`);
|
|
27
|
+
this._sourceProjectName = sourcePackageJson?.name ? sourcePackageJson.name : '';
|
|
26
28
|
}
|
|
27
29
|
/**
|
|
28
30
|
* Get the host to query the public keys
|
|
@@ -108,6 +110,15 @@ class Configs {
|
|
|
108
110
|
setCorrelationIdProvider(correlationIdProvider) {
|
|
109
111
|
this._correlationIdProvider = correlationIdProvider;
|
|
110
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Get the source project name where this library is imported
|
|
115
|
+
*
|
|
116
|
+
* @return {*} {string}
|
|
117
|
+
* @memberof Configs
|
|
118
|
+
*/
|
|
119
|
+
getSourceProjectName() {
|
|
120
|
+
return this._sourceProjectName;
|
|
121
|
+
}
|
|
111
122
|
/**
|
|
112
123
|
* The Correlation Id provider
|
|
113
124
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configs.js","sourceRoot":"","sources":["../../../src/config/configs.ts"],"names":[],"mappings":";;;AACA,yBAAyB;AACzB,6BAA6B;AAC7B,2CAAwC;AAExC;;GAEG;AACH,MAAa,OAAO;
|
|
1
|
+
{"version":3,"file":"configs.js","sourceRoot":"","sources":["../../../src/config/configs.ts"],"names":[],"mappings":";;;AACA,yBAAyB;AACzB,6BAA6B;AAC7B,2CAAwC;AAExC;;GAEG;AACH,MAAa,OAAO;IAgClB;QAzBA;;WAEG;QACK,cAAS,GAAW,qBAAS,CAAC,OAAO,CAAC,QAAQ,CAAC;QACvD;;WAEG;QACK,yBAAoB,GAAW,qBAAS,CAAC,OAAO,CAAC,mBAAmB,CAAC;QAC7E;;WAEG;QACK,mBAAc,GAAW,qBAAS,CAAC,OAAO,CAAC,aAAa,CAAC;QAe/D,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,GAAG,WAAW,CAAC,CAAC;QACvD,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC,QAAQ,EAAE,KAAK,OAAO,CAAC;QAC3C,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,qBAAS,CAAC,OAAO,eAAe,CAAC,CAAC;QACvE,IAAI,CAAC,kBAAkB,GAAG,iBAAiB,EAAE,IAAI,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAClF,CAAC;IAED;;;OAGG;IACI,OAAO;QACZ,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE;YACf,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;SAC5C;QACD,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED;;;OAGG;IACI,WAAW;QAChB,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED;;;OAGG;IACI,sBAAsB;QAC3B,OAAO,IAAI,CAAC,oBAAoB,CAAC;IACnC,CAAC;IAED;;;OAGG;IACI,gBAAgB;QACrB,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;IAED;;;;OAIG;IACI,OAAO,CAAC,IAAY;QACzB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED;;;;OAIG;IACI,WAAW,CAAC,QAAgB;QACjC,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACI,sBAAsB,CAAC,mBAA2B;QACvD,IAAI,CAAC,oBAAoB,GAAG,mBAAmB,CAAC;IAClD,CAAC;IAED;;;;OAIG;IACI,gBAAgB,CAAC,aAAqB;QAC3C,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,IAAI,aAAa;QACf,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE;YACxB,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;SACxE;QACD,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;IAED;;OAEG;IACI,gBAAgB,CAAC,aAAwC;QAC9D,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC;IACtC,CAAC;IAED;;OAEG;IACI,wBAAwB,CAAC,qBAAmC;QACjE,IAAI,CAAC,sBAAsB,GAAG,qBAAqB,CAAC;IACtD,CAAC;IAED;;;;;OAKG;IACI,oBAAoB;QACzB,OAAO,IAAI,CAAC,kBAAkB,CAAC;IACjC,CAAC;IAED;;OAEG;IACH,IAAI,qBAAqB;QACvB,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE;YAChC,MAAM,IAAI,KAAK,CACb,yGAAyG,CAC1G,CAAC;SACH;QACD,OAAO,IAAI,CAAC,sBAAsB,CAAC;IACrC,CAAC;CACF;AA3JD,0BA2JC;AAEY,QAAA,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC"}
|
|
@@ -3,12 +3,41 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.tokenTransformationMiddleware = void 0;
|
|
4
4
|
const general_utils_1 = require("@villedemontreal/general-utils");
|
|
5
5
|
const http_header_fields_typed_1 = require("http-header-fields-typed");
|
|
6
|
+
const _ = require("lodash");
|
|
7
|
+
const configs_1 = require("../config/configs");
|
|
6
8
|
const constants_1 = require("../config/constants");
|
|
7
9
|
const customError_1 = require("../models/customError");
|
|
8
10
|
const logger_1 = require("../utils/logger");
|
|
9
11
|
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
12
|
const logger = (0, logger_1.createLogger)('Token transformation middleware');
|
|
13
|
+
/** Regex to test the UUID format of the Authorization header */
|
|
14
|
+
const _regexUuidAccessToken = /([a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12})/;
|
|
15
|
+
/** Regex to test the JWT format of the Authorization header */
|
|
16
|
+
const _regexJwtAccessToken = /([a-zA-Z0-9_=]+)\.([a-zA-Z0-9_=]+)\.([a-zA-Z0-9_\-+/=]+)$/;
|
|
17
|
+
/**
|
|
18
|
+
* Validate the access_token format from authorization header and return it.
|
|
19
|
+
*
|
|
20
|
+
* @param {string} authHeader
|
|
21
|
+
* @return {*} {string}
|
|
22
|
+
*/
|
|
23
|
+
const getAccessTokenFromHeader = (authHeader) => {
|
|
24
|
+
if (authHeader.split(' ')[0] !== 'Bearer') {
|
|
25
|
+
logger.warning('The authorization header is not "Bearer" type.');
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const accessTokenUuidRegExpArray = _regexUuidAccessToken.exec(authHeader);
|
|
29
|
+
const accessTokenJwtRegExpArray = _regexJwtAccessToken.exec(authHeader);
|
|
30
|
+
if (_.isNil(accessTokenUuidRegExpArray) && _.isNil(accessTokenJwtRegExpArray)) {
|
|
31
|
+
logger.warning('Could not find a valid access token from the authorization header');
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
if (!_.isNil(accessTokenJwtRegExpArray)) {
|
|
35
|
+
return accessTokenJwtRegExpArray[0];
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
return accessTokenUuidRegExpArray[0];
|
|
39
|
+
}
|
|
40
|
+
};
|
|
12
41
|
/**
|
|
13
42
|
* Token transformation Middleware. It will generate extended jwt
|
|
14
43
|
* in exchange for an access token.
|
|
@@ -21,26 +50,31 @@ const tokenTransformationMiddleware = (config) => {
|
|
|
21
50
|
// Validate the authorization header
|
|
22
51
|
const authHeader = req.get(http_header_fields_typed_1.default.AUTHORIZATION);
|
|
23
52
|
if (general_utils_1.utils.isBlank(authHeader)) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
message: 'authorization header is empty',
|
|
28
|
-
});
|
|
53
|
+
logger.warning('The authorization header is empty.');
|
|
54
|
+
next();
|
|
55
|
+
return;
|
|
29
56
|
}
|
|
30
57
|
// Extract the access token value from the authorization header
|
|
31
|
-
const
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
target: 'access_token',
|
|
36
|
-
message: 'could not find a valid access token from the authorization header',
|
|
37
|
-
});
|
|
58
|
+
const accessToken = getAccessTokenFromHeader(authHeader);
|
|
59
|
+
if (_.isNil(accessToken)) {
|
|
60
|
+
next();
|
|
61
|
+
return;
|
|
38
62
|
}
|
|
39
|
-
const
|
|
63
|
+
const source = {
|
|
64
|
+
url: `${req.protocol}://${req?.headers.host}${req.url}`,
|
|
65
|
+
method: req.method,
|
|
66
|
+
serviceName: configs_1.configs.getSourceProjectName(),
|
|
67
|
+
clientIp: '10.0.0.1',
|
|
68
|
+
};
|
|
69
|
+
const inputAccessToken = {
|
|
70
|
+
accessToken,
|
|
71
|
+
source,
|
|
72
|
+
extensions: config.extensions,
|
|
73
|
+
};
|
|
40
74
|
// Call the service endpoint to exchange the access token for a extended jwt
|
|
41
75
|
superagent
|
|
42
76
|
.post(config.service.uri)
|
|
43
|
-
.send(
|
|
77
|
+
.send(inputAccessToken)
|
|
44
78
|
.then((response) => {
|
|
45
79
|
const extendedJwt = response.body.jwts?.extended;
|
|
46
80
|
logger.debug(extendedJwt, 'Extended jwt content.');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokenTransformationMiddleware.js","sourceRoot":"","sources":["../../../src/middleware/tokenTransformationMiddleware.ts"],"names":[],"mappings":";;;AAAA,kEAAuD;AAEvD,uEAA6D;AAC7D,mDAAgD;
|
|
1
|
+
{"version":3,"file":"tokenTransformationMiddleware.js","sourceRoot":"","sources":["../../../src/middleware/tokenTransformationMiddleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An input access token
|
|
3
|
+
*/
|
|
4
|
+
export interface IInputAccessTokenExtensionsJwtCustomDataProvider {
|
|
5
|
+
uri: string;
|
|
6
|
+
method?: string;
|
|
7
|
+
options?: any;
|
|
8
|
+
name?: string;
|
|
9
|
+
useJwtInAuthHeader?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface IInputAccessTokenExtensionsJwt {
|
|
12
|
+
customDataProvider: IInputAccessTokenExtensionsJwtCustomDataProvider;
|
|
13
|
+
}
|
|
14
|
+
export interface IInputAccessTokenExtensions {
|
|
15
|
+
jwt: IInputAccessTokenExtensionsJwt;
|
|
16
|
+
}
|
|
17
|
+
export interface IInputAccessTokenSource {
|
|
18
|
+
url: string;
|
|
19
|
+
method: string;
|
|
20
|
+
serviceName: string;
|
|
21
|
+
basicJwtCacheKey?: string;
|
|
22
|
+
extendedJwtCacheKey?: string;
|
|
23
|
+
extendedJwtConfigDigest?: string;
|
|
24
|
+
clientIp: string;
|
|
25
|
+
}
|
|
26
|
+
export interface IInputAccessToken {
|
|
27
|
+
accessToken: string;
|
|
28
|
+
accessTokenIssuer?: string;
|
|
29
|
+
source?: IInputAccessTokenSource;
|
|
30
|
+
jwt?: string;
|
|
31
|
+
extensions?: IInputAccessTokenExtensions;
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessToken.js","sourceRoot":"","sources":["../../../src/models/accessToken.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
package/src/config/configs.ts
CHANGED
|
@@ -26,12 +26,23 @@ export class Configs {
|
|
|
26
26
|
*/
|
|
27
27
|
private _cacheDuration: number = constants.default.cacheDuration;
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* When this library is used as a dependency in a project, the source project name will be the property name in the package.json of this project
|
|
31
|
+
*
|
|
32
|
+
* @private
|
|
33
|
+
* @type {string}
|
|
34
|
+
* @memberof Configs
|
|
35
|
+
*/
|
|
36
|
+
private readonly _sourceProjectName: string;
|
|
37
|
+
|
|
29
38
|
private _loggerCreator: (name: string) => ILogger;
|
|
30
39
|
private _correlationIdProvider: () => string;
|
|
31
40
|
|
|
32
41
|
constructor() {
|
|
33
42
|
this.libRoot = path.normalize(__dirname + '/../../..');
|
|
34
43
|
this.isWindows = os.platform() === 'win32';
|
|
44
|
+
const sourcePackageJson = require(`${constants.appRoot}/package.json`);
|
|
45
|
+
this._sourceProjectName = sourcePackageJson?.name ? sourcePackageJson.name : '';
|
|
35
46
|
}
|
|
36
47
|
|
|
37
48
|
/**
|
|
@@ -129,6 +140,16 @@ export class Configs {
|
|
|
129
140
|
this._correlationIdProvider = correlationIdProvider;
|
|
130
141
|
}
|
|
131
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Get the source project name where this library is imported
|
|
145
|
+
*
|
|
146
|
+
* @return {*} {string}
|
|
147
|
+
* @memberof Configs
|
|
148
|
+
*/
|
|
149
|
+
public getSourceProjectName(): string {
|
|
150
|
+
return this._sourceProjectName;
|
|
151
|
+
}
|
|
152
|
+
|
|
132
153
|
/**
|
|
133
154
|
* The Correlation Id provider
|
|
134
155
|
*/
|
|
@@ -1,16 +1,46 @@
|
|
|
1
1
|
import { utils } from '@villedemontreal/general-utils';
|
|
2
2
|
import * as express from 'express';
|
|
3
3
|
import httpHeaderFieldsTyped from 'http-header-fields-typed';
|
|
4
|
+
import * as _ from 'lodash';
|
|
5
|
+
import { configs } from '../config/configs';
|
|
4
6
|
import { constants } from '../config/constants';
|
|
5
7
|
import { ITokenTtransformationMiddlewareConfig } from '../config/tokenTransformationMiddlewareConfig';
|
|
6
|
-
import {
|
|
8
|
+
import { IInputAccessToken, IInputAccessTokenSource } from '../models/accessToken';
|
|
9
|
+
import { createInvalidJwtError } from '../models/customError';
|
|
7
10
|
import { createLogger } from '../utils/logger';
|
|
8
11
|
import superagent = require('superagent');
|
|
9
12
|
|
|
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
13
|
const logger = createLogger('Token transformation middleware');
|
|
13
14
|
|
|
15
|
+
/** Regex to test the UUID format of the Authorization header */
|
|
16
|
+
const _regexUuidAccessToken = /([a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12})/;
|
|
17
|
+
/** Regex to test the JWT format of the Authorization header */
|
|
18
|
+
const _regexJwtAccessToken = /([a-zA-Z0-9_=]+)\.([a-zA-Z0-9_=]+)\.([a-zA-Z0-9_\-+/=]+)$/;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Validate the access_token format from authorization header and return it.
|
|
22
|
+
*
|
|
23
|
+
* @param {string} authHeader
|
|
24
|
+
* @return {*} {string}
|
|
25
|
+
*/
|
|
26
|
+
const getAccessTokenFromHeader = (authHeader: string): string | null => {
|
|
27
|
+
if (authHeader.split(' ')[0] !== 'Bearer') {
|
|
28
|
+
logger.warning('The authorization header is not "Bearer" type.');
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const accessTokenUuidRegExpArray = _regexUuidAccessToken.exec(authHeader);
|
|
32
|
+
const accessTokenJwtRegExpArray = _regexJwtAccessToken.exec(authHeader);
|
|
33
|
+
if (_.isNil(accessTokenUuidRegExpArray) && _.isNil(accessTokenJwtRegExpArray)) {
|
|
34
|
+
logger.warning('Could not find a valid access token from the authorization header');
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
if (!_.isNil(accessTokenJwtRegExpArray)) {
|
|
38
|
+
return accessTokenJwtRegExpArray[0];
|
|
39
|
+
} else {
|
|
40
|
+
return accessTokenUuidRegExpArray[0];
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
14
44
|
/**
|
|
15
45
|
* Token transformation Middleware. It will generate extended jwt
|
|
16
46
|
* in exchange for an access token.
|
|
@@ -25,30 +55,37 @@ export const tokenTransformationMiddleware: (
|
|
|
25
55
|
return (req: express.Request, res: express.Response, next: express.NextFunction): void => {
|
|
26
56
|
try {
|
|
27
57
|
// Validate the authorization header
|
|
28
|
-
const authHeader
|
|
58
|
+
const authHeader = req.get(httpHeaderFieldsTyped.AUTHORIZATION);
|
|
29
59
|
if (utils.isBlank(authHeader)) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
message: 'authorization header is empty',
|
|
34
|
-
});
|
|
60
|
+
logger.warning('The authorization header is empty.');
|
|
61
|
+
next();
|
|
62
|
+
return;
|
|
35
63
|
}
|
|
36
64
|
|
|
37
65
|
// Extract the access token value from the authorization header
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
target: 'access_token',
|
|
43
|
-
message: 'could not find a valid access token from the authorization header',
|
|
44
|
-
});
|
|
66
|
+
const accessToken = getAccessTokenFromHeader(authHeader);
|
|
67
|
+
if (_.isNil(accessToken)) {
|
|
68
|
+
next();
|
|
69
|
+
return;
|
|
45
70
|
}
|
|
46
|
-
|
|
71
|
+
|
|
72
|
+
const source: IInputAccessTokenSource = {
|
|
73
|
+
url: `${req.protocol}://${req?.headers.host}${req.url}`,
|
|
74
|
+
method: req.method,
|
|
75
|
+
serviceName: configs.getSourceProjectName(),
|
|
76
|
+
clientIp: '10.0.0.1',
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const inputAccessToken: IInputAccessToken = {
|
|
80
|
+
accessToken,
|
|
81
|
+
source,
|
|
82
|
+
extensions: config.extensions,
|
|
83
|
+
};
|
|
47
84
|
|
|
48
85
|
// Call the service endpoint to exchange the access token for a extended jwt
|
|
49
86
|
superagent
|
|
50
87
|
.post(config.service.uri)
|
|
51
|
-
.send(
|
|
88
|
+
.send(inputAccessToken)
|
|
52
89
|
.then((response) => {
|
|
53
90
|
const extendedJwt = response.body.jwts?.extended;
|
|
54
91
|
logger.debug(extendedJwt, 'Extended jwt content.');
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An input access token
|
|
3
|
+
*/
|
|
4
|
+
export interface IInputAccessTokenExtensionsJwtCustomDataProvider {
|
|
5
|
+
uri: string;
|
|
6
|
+
method?: string;
|
|
7
|
+
options?: any;
|
|
8
|
+
name?: string;
|
|
9
|
+
useJwtInAuthHeader?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface IInputAccessTokenExtensionsJwt {
|
|
12
|
+
customDataProvider: IInputAccessTokenExtensionsJwtCustomDataProvider;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface IInputAccessTokenExtensions {
|
|
16
|
+
jwt: IInputAccessTokenExtensionsJwt;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface IInputAccessTokenSource {
|
|
20
|
+
url: string;
|
|
21
|
+
method: string;
|
|
22
|
+
serviceName: string;
|
|
23
|
+
basicJwtCacheKey?: string; // the name of the cache key for the basic JWT, to use the same key as the Kong plugin
|
|
24
|
+
extendedJwtCacheKey?: string; // the name of the cache key for the extended JWT, to use the same key as the Kong plugin
|
|
25
|
+
extendedJwtConfigDigest?: string; // the digest that should be included in the extended JWT, to verify later that we retrieved a cached JWT matching the right config.
|
|
26
|
+
clientIp: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface IInputAccessToken {
|
|
30
|
+
accessToken: string;
|
|
31
|
+
accessTokenIssuer?: string;
|
|
32
|
+
source?: IInputAccessTokenSource;
|
|
33
|
+
jwt?: string;
|
|
34
|
+
extensions?: IInputAccessTokenExtensions;
|
|
35
|
+
}
|