openid-client 5.0.2 → 5.1.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/README.md +4 -2
- package/lib/client.js +99 -18
- package/lib/helpers/issuer.js +1 -1
- package/lib/helpers/keystore.js +2 -2
- package/lib/helpers/pick.js +1 -1
- package/lib/helpers/process_response.js +2 -9
- package/lib/helpers/request.js +19 -5
- package/lib/helpers/www_authenticate_parser.js +14 -0
- package/lib/issuer.js +1 -1
- package/package.json +3 -3
- package/types/index.d.ts +1 -0
package/README.md
CHANGED
|
@@ -45,7 +45,8 @@ openid-client.
|
|
|
45
45
|
- [OpenID Connect RP-Initiated Logout 1.0 - draft 01][feature-rp-logout]
|
|
46
46
|
- [Financial-grade API Security Profile 1.0 - Part 2: Advanced (FAPI)][feature-fapi]
|
|
47
47
|
- [JWT Secured Authorization Response Mode for OAuth 2.0 (JARM) - ID1][feature-jarm]
|
|
48
|
-
- [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer (DPoP) - draft
|
|
48
|
+
- [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer (DPoP) - draft 04][feature-dpop]
|
|
49
|
+
- [OAuth 2.0 Authorization Server Issuer Identification - draft-04][feature-iss]
|
|
49
50
|
|
|
50
51
|
Updates to draft specifications (DPoP, JARM, etc) are released as MINOR library versions,
|
|
51
52
|
if you utilize these specification implementations consider using the tilde `~` operator in your
|
|
@@ -276,9 +277,10 @@ See [Customizing (docs)][documentation-customizing].
|
|
|
276
277
|
[feature-rp-logout]: https://openid.net/specs/openid-connect-rpinitiated-1_0-01.html
|
|
277
278
|
[feature-jarm]: https://openid.net/specs/openid-financial-api-jarm-ID1.html
|
|
278
279
|
[feature-fapi]: https://openid.net/specs/openid-financial-api-part-2-1_0.html
|
|
279
|
-
[feature-dpop]: https://tools.ietf.org/html/draft-ietf-oauth-dpop-
|
|
280
|
+
[feature-dpop]: https://tools.ietf.org/html/draft-ietf-oauth-dpop-04
|
|
280
281
|
[feature-par]: https://www.rfc-editor.org/rfc/rfc9126.html
|
|
281
282
|
[feature-jar]: https://www.rfc-editor.org/rfc/rfc9101.html
|
|
283
|
+
[feature-iss]: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-iss-auth-resp-04
|
|
282
284
|
[openid-certified-link]: https://openid.net/certification/
|
|
283
285
|
[passport-url]: http://passportjs.org
|
|
284
286
|
[npm-url]: https://www.npmjs.com/package/openid-client
|
package/lib/client.js
CHANGED
|
@@ -12,6 +12,7 @@ const isKeyObject = require('./helpers/is_key_object');
|
|
|
12
12
|
const decodeJWT = require('./helpers/decode_jwt');
|
|
13
13
|
const base64url = require('./helpers/base64url');
|
|
14
14
|
const defaults = require('./helpers/defaults');
|
|
15
|
+
const parseWwwAuthenticate = require('./helpers/www_authenticate_parser');
|
|
15
16
|
const { assertSigningAlgValuesSupport, assertIssuerConfiguration } = require('./helpers/assert');
|
|
16
17
|
const pick = require('./helpers/pick');
|
|
17
18
|
const isPlainObject = require('./helpers/is_plain_object');
|
|
@@ -30,26 +31,30 @@ const { queryKeyStore } = require('./helpers/issuer');
|
|
|
30
31
|
const DeviceFlowHandle = require('./device_flow_handle');
|
|
31
32
|
|
|
32
33
|
const [major, minor] = process.version
|
|
33
|
-
.
|
|
34
|
+
.slice(1)
|
|
34
35
|
.split('.')
|
|
35
36
|
.map((str) => parseInt(str, 10));
|
|
36
37
|
|
|
37
38
|
const rsaPssParams = major >= 17 || (major === 16 && minor >= 9);
|
|
39
|
+
const retryAttempt = Symbol();
|
|
40
|
+
const skipNonceCheck = Symbol();
|
|
41
|
+
const skipMaxAgeCheck = Symbol();
|
|
38
42
|
|
|
39
43
|
function pickCb(input) {
|
|
40
44
|
return pick(
|
|
41
45
|
input,
|
|
42
46
|
'access_token', // OAuth 2.0
|
|
43
47
|
'code', // OAuth 2.0
|
|
44
|
-
'error', // OAuth 2.0
|
|
45
48
|
'error_description', // OAuth 2.0
|
|
46
49
|
'error_uri', // OAuth 2.0
|
|
50
|
+
'error', // OAuth 2.0
|
|
47
51
|
'expires_in', // OAuth 2.0
|
|
48
52
|
'id_token', // OIDC Core 1.0
|
|
53
|
+
'iss', // draft-ietf-oauth-iss-auth-resp
|
|
54
|
+
'response', // FAPI JARM
|
|
55
|
+
'session_state', // OIDC Session Management
|
|
49
56
|
'state', // OAuth 2.0
|
|
50
57
|
'token_type', // OAuth 2.0
|
|
51
|
-
'session_state', // OIDC Session Management
|
|
52
|
-
'response', // FAPI JARM
|
|
53
58
|
);
|
|
54
59
|
}
|
|
55
60
|
|
|
@@ -396,6 +401,25 @@ class BaseClient {
|
|
|
396
401
|
});
|
|
397
402
|
}
|
|
398
403
|
|
|
404
|
+
if ('iss' in params) {
|
|
405
|
+
assertIssuerConfiguration(this.issuer, 'issuer');
|
|
406
|
+
if (params.iss !== this.issuer.issuer) {
|
|
407
|
+
throw new RPError({
|
|
408
|
+
printf: ['iss mismatch, expected %s, got: %s', this.issuer.issuer, params.iss],
|
|
409
|
+
params,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
} else if (
|
|
413
|
+
this.issuer.authorization_response_iss_parameter_supported &&
|
|
414
|
+
!('id_token' in params) &&
|
|
415
|
+
!('response' in parameters)
|
|
416
|
+
) {
|
|
417
|
+
throw new RPError({
|
|
418
|
+
message: 'iss missing from the response',
|
|
419
|
+
params,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
399
423
|
if (params.error) {
|
|
400
424
|
throw new OPError(params);
|
|
401
425
|
}
|
|
@@ -522,10 +546,37 @@ class BaseClient {
|
|
|
522
546
|
});
|
|
523
547
|
}
|
|
524
548
|
|
|
549
|
+
if ('iss' in params) {
|
|
550
|
+
assertIssuerConfiguration(this.issuer, 'issuer');
|
|
551
|
+
if (params.iss !== this.issuer.issuer) {
|
|
552
|
+
throw new RPError({
|
|
553
|
+
printf: ['iss mismatch, expected %s, got: %s', this.issuer.issuer, params.iss],
|
|
554
|
+
params,
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
} else if (
|
|
558
|
+
this.issuer.authorization_response_iss_parameter_supported &&
|
|
559
|
+
!('id_token' in params) &&
|
|
560
|
+
!('response' in parameters)
|
|
561
|
+
) {
|
|
562
|
+
throw new RPError({
|
|
563
|
+
message: 'iss missing from the response',
|
|
564
|
+
params,
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
|
|
525
568
|
if (params.error) {
|
|
526
569
|
throw new OPError(params);
|
|
527
570
|
}
|
|
528
571
|
|
|
572
|
+
if ('id_token' in params) {
|
|
573
|
+
throw new RPError({
|
|
574
|
+
message:
|
|
575
|
+
'id_token detected in the response, you must use client.callback() instead of client.oauthCallback()',
|
|
576
|
+
params,
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
|
|
529
580
|
const RESPONSE_TYPE_REQUIRED_PARAMS = {
|
|
530
581
|
code: ['code'],
|
|
531
582
|
token: ['access_token', 'token_type'],
|
|
@@ -569,6 +620,14 @@ class BaseClient {
|
|
|
569
620
|
{ clientAssertionPayload, DPoP },
|
|
570
621
|
);
|
|
571
622
|
|
|
623
|
+
if ('id_token' in tokenset) {
|
|
624
|
+
throw new RPError({
|
|
625
|
+
message:
|
|
626
|
+
'id_token detected in the response, you must use client.callback() instead of client.oauthCallback()',
|
|
627
|
+
params,
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
|
|
572
631
|
if (tokenset.scope && checks.scope && this.fapi()) {
|
|
573
632
|
const expected = new Set(checks.scope.split(' '));
|
|
574
633
|
const actual = tokenset.scope.split(' ');
|
|
@@ -705,7 +764,7 @@ class BaseClient {
|
|
|
705
764
|
const timestamp = now();
|
|
706
765
|
const { protected: header, payload, key } = await this.validateJWT(idToken, expectedAlg);
|
|
707
766
|
|
|
708
|
-
if (maxAge || (maxAge !==
|
|
767
|
+
if (typeof maxAge === 'number' || (maxAge !== skipMaxAgeCheck && this.require_auth_time)) {
|
|
709
768
|
if (!payload.auth_time) {
|
|
710
769
|
throw new RPError({
|
|
711
770
|
message: 'missing required JWT property auth_time',
|
|
@@ -720,7 +779,7 @@ class BaseClient {
|
|
|
720
779
|
}
|
|
721
780
|
}
|
|
722
781
|
|
|
723
|
-
if (maxAge && payload.auth_time + maxAge < timestamp - this[CLOCK_TOLERANCE]) {
|
|
782
|
+
if (typeof maxAge === 'number' && payload.auth_time + maxAge < timestamp - this[CLOCK_TOLERANCE]) {
|
|
724
783
|
throw new RPError({
|
|
725
784
|
printf: [
|
|
726
785
|
'too much time has elapsed since the last End-User authentication, max_age %i, auth_time: %i, now %i',
|
|
@@ -735,7 +794,7 @@ class BaseClient {
|
|
|
735
794
|
});
|
|
736
795
|
}
|
|
737
796
|
|
|
738
|
-
if (nonce !==
|
|
797
|
+
if (nonce !== skipNonceCheck && (payload.nonce || nonce !== undefined) && payload.nonce !== nonce) {
|
|
739
798
|
throw new RPError({
|
|
740
799
|
printf: ['nonce mismatch, expected %s, got: %s', nonce, payload.nonce],
|
|
741
800
|
jwt: idToken,
|
|
@@ -1033,7 +1092,7 @@ class BaseClient {
|
|
|
1033
1092
|
|
|
1034
1093
|
if (tokenset.id_token) {
|
|
1035
1094
|
await this.decryptIdToken(tokenset);
|
|
1036
|
-
await this.validateIdToken(tokenset,
|
|
1095
|
+
await this.validateIdToken(tokenset, skipNonceCheck, 'token', skipMaxAgeCheck);
|
|
1037
1096
|
|
|
1038
1097
|
if (refreshToken instanceof TokenSet && refreshToken.id_token) {
|
|
1039
1098
|
const expectedSub = refreshToken.claims().sub;
|
|
@@ -1064,6 +1123,7 @@ class BaseClient {
|
|
|
1064
1123
|
? accessToken.token_type
|
|
1065
1124
|
: 'Bearer',
|
|
1066
1125
|
} = {},
|
|
1126
|
+
retry,
|
|
1067
1127
|
) {
|
|
1068
1128
|
if (accessToken instanceof TokenSet) {
|
|
1069
1129
|
if (!accessToken.access_token) {
|
|
@@ -1088,7 +1148,7 @@ class BaseClient {
|
|
|
1088
1148
|
|
|
1089
1149
|
const mTLS = !!this.tls_client_certificate_bound_access_tokens;
|
|
1090
1150
|
|
|
1091
|
-
|
|
1151
|
+
const response = await request.call(
|
|
1092
1152
|
this,
|
|
1093
1153
|
{
|
|
1094
1154
|
...requestOpts,
|
|
@@ -1098,6 +1158,24 @@ class BaseClient {
|
|
|
1098
1158
|
},
|
|
1099
1159
|
{ accessToken, mTLS, DPoP },
|
|
1100
1160
|
);
|
|
1161
|
+
|
|
1162
|
+
const wwwAuthenticate = response.headers['www-authenticate'];
|
|
1163
|
+
if (
|
|
1164
|
+
retry !== retryAttempt &&
|
|
1165
|
+
wwwAuthenticate &&
|
|
1166
|
+
wwwAuthenticate.toLowerCase().startsWith('dpop ') &&
|
|
1167
|
+
parseWwwAuthenticate(wwwAuthenticate).error === 'use_dpop_nonce'
|
|
1168
|
+
) {
|
|
1169
|
+
return this.requestResource(resourceUrl, accessToken, {
|
|
1170
|
+
method,
|
|
1171
|
+
headers,
|
|
1172
|
+
body,
|
|
1173
|
+
DPoP,
|
|
1174
|
+
tokenType,
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
return response;
|
|
1101
1179
|
}
|
|
1102
1180
|
|
|
1103
1181
|
async userinfo(accessToken, { method = 'GET', via = 'header', tokenType, params, DPoP } = {}) {
|
|
@@ -1243,15 +1321,10 @@ class BaseClient {
|
|
|
1243
1321
|
return this.encryptionSecret(parseInt(RegExp.$2 || RegExp.$1, 10));
|
|
1244
1322
|
}
|
|
1245
1323
|
|
|
1246
|
-
|
|
1247
|
-
Math.max(parseInt(alg.substr(-3), 10) >> 3, this.client_secret.length),
|
|
1248
|
-
);
|
|
1249
|
-
secret.write(this.client_secret);
|
|
1250
|
-
|
|
1251
|
-
return secret;
|
|
1324
|
+
return new TextEncoder().encode(this.client_secret);
|
|
1252
1325
|
}
|
|
1253
1326
|
|
|
1254
|
-
async grant(body, { clientAssertionPayload, DPoP } = {}) {
|
|
1327
|
+
async grant(body, { clientAssertionPayload, DPoP } = {}, retry) {
|
|
1255
1328
|
assertIssuerConfiguration(this.issuer, 'token_endpoint');
|
|
1256
1329
|
const response = await authenticatedPost.call(
|
|
1257
1330
|
this,
|
|
@@ -1262,7 +1335,15 @@ class BaseClient {
|
|
|
1262
1335
|
},
|
|
1263
1336
|
{ clientAssertionPayload, DPoP },
|
|
1264
1337
|
);
|
|
1265
|
-
|
|
1338
|
+
let responseBody;
|
|
1339
|
+
try {
|
|
1340
|
+
responseBody = processResponse(response);
|
|
1341
|
+
} catch (err) {
|
|
1342
|
+
if (retry !== retryAttempt && err instanceof OPError && err.error === 'use_dpop_nonce') {
|
|
1343
|
+
return this.grant(body, { clientAssertionPayload, DPoP }, retryAttempt);
|
|
1344
|
+
}
|
|
1345
|
+
throw err;
|
|
1346
|
+
}
|
|
1266
1347
|
|
|
1267
1348
|
return new TokenSet(responseBody);
|
|
1268
1349
|
}
|
|
@@ -1724,7 +1805,7 @@ Object.defineProperty(BaseClient.prototype, 'dpopProof', {
|
|
|
1724
1805
|
configurable: true,
|
|
1725
1806
|
value(...args) {
|
|
1726
1807
|
process.emitWarning(
|
|
1727
|
-
'The DPoP APIs implements an IETF draft (https://www.ietf.org/archive/id/draft-ietf-oauth-dpop-
|
|
1808
|
+
'The DPoP APIs implements an IETF draft (https://www.ietf.org/archive/id/draft-ietf-oauth-dpop-04.html). Breaking draft implementations are included as minor versions of the openid-client library, therefore, the ~ semver operator should be used and close attention be payed to library changelog as well as the drafts themselves.',
|
|
1728
1809
|
'DraftWarning',
|
|
1729
1810
|
);
|
|
1730
1811
|
Object.defineProperty(BaseClient.prototype, 'dpopProof', {
|
package/lib/helpers/issuer.js
CHANGED
package/lib/helpers/keystore.js
CHANGED
|
@@ -39,7 +39,7 @@ const keyscore = (key, { alg, use }) => {
|
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
function getKtyFromAlg(alg) {
|
|
42
|
-
switch (typeof alg === 'string' && alg.
|
|
42
|
+
switch (typeof alg === 'string' && alg.slice(0, 2)) {
|
|
43
43
|
case 'RS':
|
|
44
44
|
case 'PS':
|
|
45
45
|
return 'RSA';
|
|
@@ -68,7 +68,7 @@ function getAlgorithms(use, alg, kty, crv) {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
if (use === 'sig' || use === undefined) {
|
|
71
|
-
algs = algs.concat([`ES${crv.
|
|
71
|
+
algs = algs.concat([`ES${crv.slice(-3)}`.replace('21', '12')]);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
return new Set(algs);
|
package/lib/helpers/pick.js
CHANGED
|
@@ -2,17 +2,10 @@ const { STATUS_CODES } = require('http');
|
|
|
2
2
|
const { format } = require('util');
|
|
3
3
|
|
|
4
4
|
const { OPError } = require('../errors');
|
|
5
|
+
const parseWwwAuthenticate = require('./www_authenticate_parser');
|
|
5
6
|
|
|
6
|
-
const REGEXP = /(\w+)=("[^"]*")/g;
|
|
7
7
|
const throwAuthenticateErrors = (response) => {
|
|
8
|
-
const params =
|
|
9
|
-
try {
|
|
10
|
-
while (REGEXP.exec(response.headers['www-authenticate']) !== null) {
|
|
11
|
-
if (RegExp.$1 && RegExp.$2) {
|
|
12
|
-
params[RegExp.$1] = RegExp.$2.slice(1, -1);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
} catch (err) {}
|
|
8
|
+
const params = parseWwwAuthenticate(response.headers['www-authenticate']);
|
|
16
9
|
|
|
17
10
|
if (params.error) {
|
|
18
11
|
throw new OPError(params, response);
|
package/lib/helpers/request.js
CHANGED
|
@@ -4,6 +4,8 @@ const http = require('http');
|
|
|
4
4
|
const https = require('https');
|
|
5
5
|
const { once } = require('events');
|
|
6
6
|
|
|
7
|
+
const LRU = require('lru-cache');
|
|
8
|
+
|
|
7
9
|
const pkg = require('../../package.json');
|
|
8
10
|
const { RPError } = require('../errors');
|
|
9
11
|
|
|
@@ -12,6 +14,7 @@ const { deep: defaultsDeep } = require('./defaults');
|
|
|
12
14
|
const { HTTP_OPTIONS } = require('./consts');
|
|
13
15
|
|
|
14
16
|
let DEFAULT_HTTP_OPTIONS;
|
|
17
|
+
const NQCHAR = /^[\x21\x23-\x5B\x5D-\x7E]+$/;
|
|
15
18
|
|
|
16
19
|
const allowed = [
|
|
17
20
|
'agent',
|
|
@@ -52,6 +55,8 @@ function send(req, body, contentType) {
|
|
|
52
55
|
req.end();
|
|
53
56
|
}
|
|
54
57
|
|
|
58
|
+
const nonces = new LRU({ max: 100 });
|
|
59
|
+
|
|
55
60
|
module.exports = async function request(options, { accessToken, mTLS = false, DPoP } = {}) {
|
|
56
61
|
let url;
|
|
57
62
|
try {
|
|
@@ -64,12 +69,14 @@ module.exports = async function request(options, { accessToken, mTLS = false, DP
|
|
|
64
69
|
const optsFn = this[HTTP_OPTIONS];
|
|
65
70
|
let opts = options;
|
|
66
71
|
|
|
72
|
+
const nonceKey = `${url.origin}${url.pathname}`;
|
|
67
73
|
if (DPoP && 'dpopProof' in this) {
|
|
68
74
|
opts.headers = opts.headers || {};
|
|
69
75
|
opts.headers.DPoP = await this.dpopProof(
|
|
70
76
|
{
|
|
71
|
-
htu: url,
|
|
77
|
+
htu: url.href,
|
|
72
78
|
htm: options.method,
|
|
79
|
+
nonce: nonces.get(nonceKey),
|
|
73
80
|
},
|
|
74
81
|
DPoP,
|
|
75
82
|
accessToken,
|
|
@@ -173,10 +180,17 @@ module.exports = async function request(options, { accessToken, mTLS = false, DP
|
|
|
173
180
|
}
|
|
174
181
|
|
|
175
182
|
return response;
|
|
176
|
-
})()
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
183
|
+
})()
|
|
184
|
+
.catch((err) => {
|
|
185
|
+
if (response) Object.defineProperty(err, 'response', { value: response });
|
|
186
|
+
throw err;
|
|
187
|
+
})
|
|
188
|
+
.finally(() => {
|
|
189
|
+
const dpopNonce = response && response.headers['dpop-nonce'];
|
|
190
|
+
if (dpopNonce && NQCHAR.test(dpopNonce)) {
|
|
191
|
+
nonces.set(nonceKey, dpopNonce);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
180
194
|
};
|
|
181
195
|
|
|
182
196
|
module.exports.setDefaults = setDefaults.bind(undefined, allowed);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const REGEXP = /(\w+)=("[^"]*")/g;
|
|
2
|
+
|
|
3
|
+
module.exports = (wwwAuthenticate) => {
|
|
4
|
+
const params = {};
|
|
5
|
+
try {
|
|
6
|
+
while (REGEXP.exec(wwwAuthenticate) !== null) {
|
|
7
|
+
if (RegExp.$1 && RegExp.$2) {
|
|
8
|
+
params[RegExp.$1] = RegExp.$2.slice(1, -1);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
} catch (err) {}
|
|
12
|
+
|
|
13
|
+
return params;
|
|
14
|
+
};
|
package/lib/issuer.js
CHANGED
|
@@ -16,7 +16,7 @@ const AAD_MULTITENANT_DISCOVERY = [
|
|
|
16
16
|
'https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration',
|
|
17
17
|
'https://login.microsoftonline.com/consumers/v2.0/.well-known/openid-configuration',
|
|
18
18
|
];
|
|
19
|
-
const AAD_MULTITENANT = Symbol(
|
|
19
|
+
const AAD_MULTITENANT = Symbol();
|
|
20
20
|
const ISSUER_DEFAULTS = {
|
|
21
21
|
claim_types_supported: ['normal'],
|
|
22
22
|
claims_parameter_supported: false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openid-client",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.3",
|
|
4
4
|
"description": "OpenID Connect Relying Party (RP, Client) implementation for Node.js runtime, supports passportjs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"auth",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
]
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"jose": "^4.1.
|
|
54
|
+
"jose": "^4.1.4",
|
|
55
55
|
"lru-cache": "^6.0.0",
|
|
56
56
|
"object-hash": "^2.0.1",
|
|
57
57
|
"oidc-token-hash": "^5.0.1"
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
},
|
|
85
85
|
{
|
|
86
86
|
"type": "fix",
|
|
87
|
-
"section": "
|
|
87
|
+
"section": "Fixes"
|
|
88
88
|
},
|
|
89
89
|
{
|
|
90
90
|
"type": "chore",
|