openid-client 5.5.0 → 5.6.0
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/lib/client.js +175 -105
- package/lib/helpers/client.js +1 -1
- package/lib/helpers/keystore.js +30 -26
- package/package.json +7 -8
package/lib/client.js
CHANGED
|
@@ -4,6 +4,7 @@ const crypto = require('crypto');
|
|
|
4
4
|
const { strict: assert } = require('assert');
|
|
5
5
|
const querystring = require('querystring');
|
|
6
6
|
const url = require('url');
|
|
7
|
+
const { URL, URLSearchParams } = require('url');
|
|
7
8
|
|
|
8
9
|
const jose = require('jose');
|
|
9
10
|
const tokenHash = require('oidc-token-hash');
|
|
@@ -62,6 +63,12 @@ function authorizationHeaderValue(token, tokenType = 'Bearer') {
|
|
|
62
63
|
return `${tokenType} ${token}`;
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
function getSearchParams(input) {
|
|
67
|
+
const parsed = url.parse(input);
|
|
68
|
+
if (!parsed.search) return {};
|
|
69
|
+
return querystring.parse(parsed.search.substring(1));
|
|
70
|
+
}
|
|
71
|
+
|
|
65
72
|
function verifyPresence(payload, jwt, prop) {
|
|
66
73
|
if (payload[prop] === undefined) {
|
|
67
74
|
throw new RPError({
|
|
@@ -251,13 +258,21 @@ class BaseClient {
|
|
|
251
258
|
throw new TypeError('params must be a plain object');
|
|
252
259
|
}
|
|
253
260
|
assertIssuerConfiguration(this.issuer, 'authorization_endpoint');
|
|
254
|
-
const target =
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
+
const target = new URL(this.issuer.authorization_endpoint);
|
|
262
|
+
|
|
263
|
+
for (const [name, value] of Object.entries(authorizationParams.call(this, params))) {
|
|
264
|
+
if (Array.isArray(value)) {
|
|
265
|
+
target.searchParams.delete(name);
|
|
266
|
+
for (const member of value) {
|
|
267
|
+
target.searchParams.append(name, member);
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
target.searchParams.set(name, value);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// TODO: is the replace needed?
|
|
275
|
+
return target.href.replace('+', '%20');
|
|
261
276
|
}
|
|
262
277
|
|
|
263
278
|
authorizationPost(params = {}) {
|
|
@@ -297,10 +312,9 @@ class BaseClient {
|
|
|
297
312
|
id_token_hint = id_token_hint.id_token;
|
|
298
313
|
}
|
|
299
314
|
|
|
300
|
-
const target = url.parse(this.issuer.end_session_endpoint
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
target.query,
|
|
315
|
+
const target = url.parse(this.issuer.end_session_endpoint);
|
|
316
|
+
const query = defaults(
|
|
317
|
+
getSearchParams(this.issuer.end_session_endpoint),
|
|
304
318
|
params,
|
|
305
319
|
{
|
|
306
320
|
post_logout_redirect_uri,
|
|
@@ -309,12 +323,15 @@ class BaseClient {
|
|
|
309
323
|
{ id_token_hint },
|
|
310
324
|
);
|
|
311
325
|
|
|
312
|
-
Object.entries(
|
|
326
|
+
Object.entries(query).forEach(([key, value]) => {
|
|
313
327
|
if (value === null || value === undefined) {
|
|
314
|
-
delete
|
|
328
|
+
delete query[key];
|
|
315
329
|
}
|
|
316
330
|
});
|
|
317
331
|
|
|
332
|
+
target.search = null;
|
|
333
|
+
target.query = query;
|
|
334
|
+
|
|
318
335
|
return url.format(target);
|
|
319
336
|
}
|
|
320
337
|
|
|
@@ -331,7 +348,7 @@ class BaseClient {
|
|
|
331
348
|
if (isIncomingMessage) {
|
|
332
349
|
switch (input.method) {
|
|
333
350
|
case 'GET':
|
|
334
|
-
return pickCb(
|
|
351
|
+
return pickCb(getSearchParams(input.url));
|
|
335
352
|
case 'POST':
|
|
336
353
|
if (input.body === undefined) {
|
|
337
354
|
throw new TypeError(
|
|
@@ -356,7 +373,7 @@ class BaseClient {
|
|
|
356
373
|
throw new TypeError('invalid IncomingMessage method');
|
|
357
374
|
}
|
|
358
375
|
} else {
|
|
359
|
-
return pickCb(
|
|
376
|
+
return pickCb(getSearchParams(input));
|
|
360
377
|
}
|
|
361
378
|
}
|
|
362
379
|
|
|
@@ -703,11 +720,15 @@ class BaseClient {
|
|
|
703
720
|
if (expectedAlg.match(/^(?:RSA|ECDH)/)) {
|
|
704
721
|
const keystore = await keystores.get(this);
|
|
705
722
|
|
|
706
|
-
|
|
707
|
-
|
|
723
|
+
const protectedHeader = jose.decodeProtectedHeader(jwe);
|
|
724
|
+
|
|
725
|
+
for (const key of keystore.all({
|
|
726
|
+
...protectedHeader,
|
|
708
727
|
use: 'enc',
|
|
709
728
|
})) {
|
|
710
|
-
plaintext = await jose
|
|
729
|
+
plaintext = await jose
|
|
730
|
+
.compactDecrypt(jwe, await key.keyObject(protectedHeader.alg))
|
|
731
|
+
.then(getPlaintext, () => {});
|
|
711
732
|
if (plaintext) break;
|
|
712
733
|
}
|
|
713
734
|
} else {
|
|
@@ -1016,7 +1037,13 @@ class BaseClient {
|
|
|
1016
1037
|
assert(isPlainObject(payload.sub_jwk));
|
|
1017
1038
|
const key = await jose.importJWK(payload.sub_jwk, header.alg);
|
|
1018
1039
|
assert.equal(key.type, 'public');
|
|
1019
|
-
keys = [
|
|
1040
|
+
keys = [
|
|
1041
|
+
{
|
|
1042
|
+
keyObject() {
|
|
1043
|
+
return key;
|
|
1044
|
+
},
|
|
1045
|
+
},
|
|
1046
|
+
];
|
|
1020
1047
|
} catch (err) {
|
|
1021
1048
|
throw new RPError({
|
|
1022
1049
|
message: 'failed to use sub_jwk claim as an asymmetric JSON Web Key',
|
|
@@ -1041,7 +1068,7 @@ class BaseClient {
|
|
|
1041
1068
|
|
|
1042
1069
|
for (const key of keys) {
|
|
1043
1070
|
const verified = await jose
|
|
1044
|
-
.compactVerify(jwt, key instanceof Uint8Array ? key : key.keyObject)
|
|
1071
|
+
.compactVerify(jwt, key instanceof Uint8Array ? key : await key.keyObject(header.alg))
|
|
1045
1072
|
.catch(() => {});
|
|
1046
1073
|
if (verified) {
|
|
1047
1074
|
return {
|
|
@@ -1195,12 +1222,12 @@ class BaseClient {
|
|
|
1195
1222
|
targetUrl = this.issuer.mtls_endpoint_aliases.userinfo_endpoint;
|
|
1196
1223
|
}
|
|
1197
1224
|
|
|
1198
|
-
targetUrl = new
|
|
1225
|
+
targetUrl = new URL(targetUrl || this.issuer.userinfo_endpoint);
|
|
1199
1226
|
|
|
1200
1227
|
if (via === 'body') {
|
|
1201
1228
|
options.headers.Authorization = undefined;
|
|
1202
1229
|
options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
1203
|
-
options.body = new
|
|
1230
|
+
options.body = new URLSearchParams();
|
|
1204
1231
|
options.body.append(
|
|
1205
1232
|
'access_token',
|
|
1206
1233
|
accessToken instanceof TokenSet ? accessToken.access_token : accessToken,
|
|
@@ -1220,7 +1247,7 @@ class BaseClient {
|
|
|
1220
1247
|
});
|
|
1221
1248
|
} else {
|
|
1222
1249
|
// POST && via header
|
|
1223
|
-
options.body = new
|
|
1250
|
+
options.body = new URLSearchParams();
|
|
1224
1251
|
options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
1225
1252
|
Object.entries(params).forEach(([key, value]) => {
|
|
1226
1253
|
options.body.append(key, value);
|
|
@@ -1515,7 +1542,7 @@ class BaseClient {
|
|
|
1515
1542
|
...header,
|
|
1516
1543
|
kid: symmetric ? undefined : key.jwk.kid,
|
|
1517
1544
|
})
|
|
1518
|
-
.sign(symmetric ? key : key.keyObject);
|
|
1545
|
+
.sign(symmetric ? key : await key.keyObject(signingAlgorithm));
|
|
1519
1546
|
}
|
|
1520
1547
|
|
|
1521
1548
|
if (!eKeyManagement) {
|
|
@@ -1539,7 +1566,7 @@ class BaseClient {
|
|
|
1539
1566
|
...fields,
|
|
1540
1567
|
kid: key instanceof Uint8Array ? undefined : key.jwk.kid,
|
|
1541
1568
|
})
|
|
1542
|
-
.encrypt(key instanceof Uint8Array ? key : key.keyObject);
|
|
1569
|
+
.encrypt(key instanceof Uint8Array ? key : await key.keyObject(fields.alg));
|
|
1543
1570
|
}
|
|
1544
1571
|
|
|
1545
1572
|
async pushedAuthorizationRequest(params = {}, { clientAssertionPayload } = {}) {
|
|
@@ -1625,33 +1652,18 @@ class BaseClient {
|
|
|
1625
1652
|
let privateKey;
|
|
1626
1653
|
if (isKeyObject(privateKeyInput)) {
|
|
1627
1654
|
privateKey = privateKeyInput;
|
|
1628
|
-
} else {
|
|
1655
|
+
} else if (privateKeyInput[Symbol.toStringTag] === 'CryptoKey') {
|
|
1656
|
+
privateKey = privateKeyInput;
|
|
1657
|
+
} else if (jose.cryptoRuntime === 'node:crypto') {
|
|
1629
1658
|
privateKey = crypto.createPrivateKey(privateKeyInput);
|
|
1659
|
+
} else {
|
|
1660
|
+
throw new TypeError('unrecognized crypto runtime');
|
|
1630
1661
|
}
|
|
1631
1662
|
|
|
1632
1663
|
if (privateKey.type !== 'private') {
|
|
1633
1664
|
throw new TypeError('"DPoP" option must be a private key');
|
|
1634
1665
|
}
|
|
1635
|
-
let alg;
|
|
1636
|
-
switch (privateKey.asymmetricKeyType) {
|
|
1637
|
-
case 'ed25519':
|
|
1638
|
-
case 'ed448':
|
|
1639
|
-
alg = 'EdDSA';
|
|
1640
|
-
break;
|
|
1641
|
-
case 'ec':
|
|
1642
|
-
alg = determineEcAlgorithm(privateKey, privateKeyInput);
|
|
1643
|
-
break;
|
|
1644
|
-
case 'rsa':
|
|
1645
|
-
case rsaPssParams && 'rsa-pss':
|
|
1646
|
-
alg = determineRsaAlgorithm(
|
|
1647
|
-
privateKey,
|
|
1648
|
-
privateKeyInput,
|
|
1649
|
-
this.issuer.dpop_signing_alg_values_supported,
|
|
1650
|
-
);
|
|
1651
|
-
break;
|
|
1652
|
-
default:
|
|
1653
|
-
throw new TypeError('unsupported DPoP private key asymmetric key type');
|
|
1654
|
-
}
|
|
1666
|
+
let alg = determineDPoPAlgorithm.call(this, privateKey, privateKeyInput);
|
|
1655
1667
|
|
|
1656
1668
|
if (!alg) {
|
|
1657
1669
|
throw new TypeError('could not determine DPoP JWS Algorithm');
|
|
@@ -1674,81 +1686,138 @@ class BaseClient {
|
|
|
1674
1686
|
}
|
|
1675
1687
|
}
|
|
1676
1688
|
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1689
|
+
function determineDPoPAlgorithmFromCryptoKey(cryptoKey) {
|
|
1690
|
+
switch (cryptoKey.algorithm.name) {
|
|
1691
|
+
case 'Ed25519':
|
|
1692
|
+
case 'Ed448':
|
|
1693
|
+
return 'EdDSA';
|
|
1694
|
+
case 'ECDSA': {
|
|
1695
|
+
switch (cryptoKey.algorithm.namedCurve) {
|
|
1696
|
+
case 'P-256':
|
|
1697
|
+
return 'ES256';
|
|
1698
|
+
case 'P-384':
|
|
1699
|
+
return 'ES384';
|
|
1700
|
+
case 'P-521':
|
|
1701
|
+
return 'ES512';
|
|
1702
|
+
default:
|
|
1703
|
+
break;
|
|
1704
|
+
}
|
|
1705
|
+
break;
|
|
1706
|
+
}
|
|
1707
|
+
case 'RSASSA-PKCS1-v1_5':
|
|
1708
|
+
return `RS${cryptoKey.algorithm.hash.name.slice(4)}`;
|
|
1709
|
+
case 'RSA-PSS':
|
|
1710
|
+
return `PS${cryptoKey.algorithm.hash.name.slice(4)}`;
|
|
1711
|
+
default:
|
|
1712
|
+
throw new TypeError('unsupported DPoP private key');
|
|
1685
1713
|
}
|
|
1714
|
+
}
|
|
1686
1715
|
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1716
|
+
let determineDPoPAlgorithm;
|
|
1717
|
+
if (jose.cryptoRuntime === 'node:crypto') {
|
|
1718
|
+
determineDPoPAlgorithm = function (privateKey, privateKeyInput) {
|
|
1719
|
+
if (privateKeyInput[Symbol.toStringTag] === 'CryptoKey') {
|
|
1720
|
+
return determineDPoPAlgorithmFromCryptoKey(privateKey);
|
|
1691
1721
|
}
|
|
1692
|
-
return ['PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS384'].find((preferred) =>
|
|
1693
|
-
candidates.includes(preferred),
|
|
1694
|
-
);
|
|
1695
|
-
}
|
|
1696
1722
|
|
|
1697
|
-
|
|
1698
|
-
|
|
1723
|
+
switch (privateKey.asymmetricKeyType) {
|
|
1724
|
+
case 'ed25519':
|
|
1725
|
+
case 'ed448':
|
|
1726
|
+
return 'EdDSA';
|
|
1727
|
+
case 'ec':
|
|
1728
|
+
return determineEcAlgorithm(privateKey, privateKeyInput);
|
|
1729
|
+
case 'rsa':
|
|
1730
|
+
case rsaPssParams && 'rsa-pss':
|
|
1731
|
+
return determineRsaAlgorithm(
|
|
1732
|
+
privateKey,
|
|
1733
|
+
privateKeyInput,
|
|
1734
|
+
this.issuer.dpop_signing_alg_values_supported,
|
|
1735
|
+
);
|
|
1736
|
+
default:
|
|
1737
|
+
throw new TypeError('unsupported DPoP private key');
|
|
1738
|
+
}
|
|
1739
|
+
};
|
|
1699
1740
|
|
|
1700
|
-
const
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1741
|
+
const RSPS = /^(?:RS|PS)(?:256|384|512)$/;
|
|
1742
|
+
function determineRsaAlgorithm(privateKey, privateKeyInput, valuesSupported) {
|
|
1743
|
+
if (
|
|
1744
|
+
typeof privateKeyInput === 'object' &&
|
|
1745
|
+
privateKeyInput.format === 'jwk' &&
|
|
1746
|
+
privateKeyInput.key &&
|
|
1747
|
+
privateKeyInput.key.alg
|
|
1748
|
+
) {
|
|
1749
|
+
return privateKeyInput.key.alg;
|
|
1750
|
+
}
|
|
1704
1751
|
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1752
|
+
if (Array.isArray(valuesSupported)) {
|
|
1753
|
+
let candidates = valuesSupported.filter(RegExp.prototype.test.bind(RSPS));
|
|
1754
|
+
if (privateKey.asymmetricKeyType === 'rsa-pss') {
|
|
1755
|
+
candidates = candidates.filter((value) => value.startsWith('PS'));
|
|
1756
|
+
}
|
|
1757
|
+
return ['PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS384'].find((preferred) =>
|
|
1758
|
+
candidates.includes(preferred),
|
|
1759
|
+
);
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
return 'PS256';
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
const p256 = Buffer.from([42, 134, 72, 206, 61, 3, 1, 7]);
|
|
1766
|
+
const p384 = Buffer.from([43, 129, 4, 0, 34]);
|
|
1767
|
+
const p521 = Buffer.from([43, 129, 4, 0, 35]);
|
|
1768
|
+
const secp256k1 = Buffer.from([43, 129, 4, 0, 10]);
|
|
1769
|
+
|
|
1770
|
+
function determineEcAlgorithm(privateKey, privateKeyInput) {
|
|
1771
|
+
// If input was a JWK
|
|
1772
|
+
switch (
|
|
1773
|
+
typeof privateKeyInput === 'object' &&
|
|
1774
|
+
typeof privateKeyInput.key === 'object' &&
|
|
1775
|
+
privateKeyInput.key.crv
|
|
1776
|
+
) {
|
|
1777
|
+
case 'P-256':
|
|
1778
|
+
return 'ES256';
|
|
1779
|
+
case 'secp256k1':
|
|
1780
|
+
return 'ES256K';
|
|
1781
|
+
case 'P-384':
|
|
1782
|
+
return 'ES384';
|
|
1783
|
+
case 'P-512':
|
|
1784
|
+
return 'ES512';
|
|
1785
|
+
default:
|
|
1786
|
+
break;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
const buf = privateKey.export({ format: 'der', type: 'pkcs8' });
|
|
1790
|
+
const i = buf[1] < 128 ? 17 : 18;
|
|
1791
|
+
const len = buf[i];
|
|
1792
|
+
const curveOid = buf.slice(i + 1, i + 1 + len);
|
|
1793
|
+
if (curveOid.equals(p256)) {
|
|
1713
1794
|
return 'ES256';
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
if (curveOid.equals(p384)) {
|
|
1717
1798
|
return 'ES384';
|
|
1718
|
-
|
|
1799
|
+
}
|
|
1800
|
+
if (curveOid.equals(p521)) {
|
|
1719
1801
|
return 'ES512';
|
|
1720
|
-
|
|
1721
|
-
break;
|
|
1722
|
-
}
|
|
1723
|
-
|
|
1724
|
-
const buf = privateKey.export({ format: 'der', type: 'pkcs8' });
|
|
1725
|
-
const i = buf[1] < 128 ? 17 : 18;
|
|
1726
|
-
const len = buf[i];
|
|
1727
|
-
const curveOid = buf.slice(i + 1, i + 1 + len);
|
|
1728
|
-
if (curveOid.equals(p256)) {
|
|
1729
|
-
return 'ES256';
|
|
1730
|
-
}
|
|
1802
|
+
}
|
|
1731
1803
|
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
if (curveOid.equals(p521)) {
|
|
1736
|
-
return 'ES512';
|
|
1737
|
-
}
|
|
1804
|
+
if (curveOid.equals(secp256k1)) {
|
|
1805
|
+
return 'ES256K';
|
|
1806
|
+
}
|
|
1738
1807
|
|
|
1739
|
-
|
|
1740
|
-
return 'ES256K';
|
|
1808
|
+
throw new TypeError('unsupported DPoP private key curve');
|
|
1741
1809
|
}
|
|
1742
|
-
|
|
1743
|
-
|
|
1810
|
+
} else {
|
|
1811
|
+
determineDPoPAlgorithm = determineDPoPAlgorithmFromCryptoKey;
|
|
1744
1812
|
}
|
|
1745
1813
|
|
|
1746
1814
|
const jwkCache = new WeakMap();
|
|
1747
|
-
async function getJwk(
|
|
1815
|
+
async function getJwk(keyObject, privateKeyInput) {
|
|
1748
1816
|
if (
|
|
1817
|
+
jose.cryptoRuntime === 'node:crypto' &&
|
|
1749
1818
|
typeof privateKeyInput === 'object' &&
|
|
1750
1819
|
typeof privateKeyInput.key === 'object' &&
|
|
1751
|
-
privateKeyInput.
|
|
1820
|
+
privateKeyInput.format === 'jwk'
|
|
1752
1821
|
) {
|
|
1753
1822
|
return pick(privateKeyInput.key, 'kty', 'crv', 'x', 'y', 'e', 'n');
|
|
1754
1823
|
}
|
|
@@ -1757,9 +1826,9 @@ async function getJwk(privateKey, privateKeyInput) {
|
|
|
1757
1826
|
return jwkCache.get(privateKeyInput);
|
|
1758
1827
|
}
|
|
1759
1828
|
|
|
1760
|
-
const jwk = pick(await jose.exportJWK(
|
|
1829
|
+
const jwk = pick(await jose.exportJWK(keyObject), 'kty', 'crv', 'x', 'y', 'e', 'n');
|
|
1761
1830
|
|
|
1762
|
-
if (isKeyObject(privateKeyInput)) {
|
|
1831
|
+
if (isKeyObject(privateKeyInput) || jose.cryptoRuntime === 'WebCryptoAPI') {
|
|
1763
1832
|
jwkCache.set(privateKeyInput, jwk);
|
|
1764
1833
|
}
|
|
1765
1834
|
|
|
@@ -1776,4 +1845,5 @@ module.exports = (issuer, aadIssValidation = false) =>
|
|
|
1776
1845
|
return issuer;
|
|
1777
1846
|
}
|
|
1778
1847
|
};
|
|
1848
|
+
|
|
1779
1849
|
module.exports.BaseClient = BaseClient;
|
package/lib/helpers/client.js
CHANGED
|
@@ -70,7 +70,7 @@ async function clientAssertion(endpoint, payload) {
|
|
|
70
70
|
|
|
71
71
|
return new jose.CompactSign(Buffer.from(JSON.stringify(payload)))
|
|
72
72
|
.setProtectedHeader({ alg, kid: key.jwk && key.jwk.kid })
|
|
73
|
-
.sign(key.keyObject);
|
|
73
|
+
.sign(await key.keyObject(alg));
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
async function authFor(endpoint, { clientAssertionPayload } = {}) {
|
package/lib/helpers/keystore.js
CHANGED
|
@@ -2,7 +2,6 @@ const jose = require('jose');
|
|
|
2
2
|
|
|
3
3
|
const clone = require('./deep_clone');
|
|
4
4
|
const isPlainObject = require('./is_plain_object');
|
|
5
|
-
const isKeyObject = require('./is_key_object');
|
|
6
5
|
|
|
7
6
|
const internal = Symbol();
|
|
8
7
|
|
|
@@ -51,7 +50,7 @@ function getKtyFromAlg(alg) {
|
|
|
51
50
|
|
|
52
51
|
function getAlgorithms(use, alg, kty, crv) {
|
|
53
52
|
// Ed25519, Ed448, and secp256k1 always have "alg"
|
|
54
|
-
// OKP always has use
|
|
53
|
+
// OKP always has "use"
|
|
55
54
|
if (alg) {
|
|
56
55
|
return new Set([alg]);
|
|
57
56
|
}
|
|
@@ -65,7 +64,20 @@ function getAlgorithms(use, alg, kty, crv) {
|
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
if (use === 'sig' || use === undefined) {
|
|
68
|
-
|
|
67
|
+
switch (crv) {
|
|
68
|
+
case 'P-256':
|
|
69
|
+
case 'P-384':
|
|
70
|
+
algs = algs.concat([`ES${crv.slice(-3)}`.replace('21', '12')]);
|
|
71
|
+
break;
|
|
72
|
+
case 'P-521':
|
|
73
|
+
algs = algs.concat(['ES512']);
|
|
74
|
+
break;
|
|
75
|
+
case 'secp256k1':
|
|
76
|
+
if (jose.cryptoRuntime === 'node:crypto') {
|
|
77
|
+
algs = algs.concat(['ES256K']);
|
|
78
|
+
}
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
69
81
|
}
|
|
70
82
|
|
|
71
83
|
return new Set(algs);
|
|
@@ -77,7 +89,10 @@ function getAlgorithms(use, alg, kty, crv) {
|
|
|
77
89
|
let algs = [];
|
|
78
90
|
|
|
79
91
|
if (use === 'enc' || use === undefined) {
|
|
80
|
-
algs = algs.concat(['RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512'
|
|
92
|
+
algs = algs.concat(['RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512']);
|
|
93
|
+
if (jose.cryptoRuntime === 'node:crypto') {
|
|
94
|
+
algs = algs.concat(['RSA1_5']);
|
|
95
|
+
}
|
|
81
96
|
}
|
|
82
97
|
|
|
83
98
|
if (use === 'sig' || use === undefined) {
|
|
@@ -225,36 +240,25 @@ module.exports = class KeyStore {
|
|
|
225
240
|
}
|
|
226
241
|
}
|
|
227
242
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if (!keyObject) continue;
|
|
231
|
-
|
|
232
|
-
if (keyObject instanceof Uint8Array || keyObject.type === 'secret') {
|
|
233
|
-
if (onlyPrivate) {
|
|
234
|
-
throw new Error('jwks must only contain private keys');
|
|
235
|
-
}
|
|
236
|
-
continue;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (!isKeyObject(keyObject)) {
|
|
240
|
-
throw new Error('what?!');
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (onlyPrivate && keyObject.type !== 'private') {
|
|
243
|
+
if (onlyPrivate && (jwk.kty === 'oct' || !jwk.d)) {
|
|
244
244
|
throw new Error('jwks must only contain private keys');
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
if (onlyPublic &&
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (kty === 'RSA' && keyObject.asymmetricKeySize < 2048) {
|
|
247
|
+
if (onlyPublic && (jwk.d || jwk.k)) {
|
|
252
248
|
continue;
|
|
253
249
|
}
|
|
254
250
|
|
|
255
251
|
keys.push({
|
|
256
252
|
jwk: { ...jwk, alg, use },
|
|
257
|
-
keyObject
|
|
253
|
+
async keyObject(alg) {
|
|
254
|
+
if (this[alg]) {
|
|
255
|
+
return this[alg];
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const keyObject = await jose.importJWK(this.jwk, alg);
|
|
259
|
+
this[alg] = keyObject;
|
|
260
|
+
return keyObject;
|
|
261
|
+
},
|
|
258
262
|
get algorithms() {
|
|
259
263
|
Object.defineProperty(this, 'algorithms', {
|
|
260
264
|
value: getAlgorithms(this.jwk.use, this.jwk.alg, this.jwk.kty, this.jwk.crv),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openid-client",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.6.0",
|
|
4
4
|
"description": "OpenID Connect Relying Party (RP, Client) implementation for Node.js runtime, supports passportjs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"auth",
|
|
@@ -45,23 +45,22 @@
|
|
|
45
45
|
"test": "mocha test/**/*.test.js"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"jose": "^4.
|
|
48
|
+
"jose": "^4.15.1",
|
|
49
49
|
"lru-cache": "^6.0.0",
|
|
50
50
|
"object-hash": "^2.2.0",
|
|
51
51
|
"oidc-token-hash": "^5.0.3"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@types/node": "^16.18.
|
|
55
|
-
"@types/passport": "^1.0.
|
|
54
|
+
"@types/node": "^16.18.55",
|
|
55
|
+
"@types/passport": "^1.0.13",
|
|
56
56
|
"base64url": "^3.0.1",
|
|
57
|
-
"chai": "^4.3.
|
|
58
|
-
"jose2": "npm:jose@^2.0.6",
|
|
57
|
+
"chai": "^4.3.10",
|
|
59
58
|
"mocha": "^10.2.0",
|
|
60
|
-
"nock": "^13.3.
|
|
59
|
+
"nock": "^13.3.3",
|
|
61
60
|
"prettier": "^2.8.8",
|
|
62
61
|
"readable-mock-req": "^0.2.2",
|
|
63
62
|
"sinon": "^9.2.4",
|
|
64
|
-
"timekeeper": "^2.
|
|
63
|
+
"timekeeper": "^2.3.1"
|
|
65
64
|
},
|
|
66
65
|
"standard-version": {
|
|
67
66
|
"scripts": {
|