jose 4.3.8 → 4.5.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 +15 -13
- package/dist/browser/index.js +2 -0
- package/dist/browser/jwk/thumbprint.js +5 -0
- package/dist/browser/jwks/local.js +97 -0
- package/dist/browser/jwks/remote.js +29 -82
- package/dist/browser/lib/buffer_utils.js +2 -1
- package/dist/browser/lib/encrypt_key_management.js +3 -1
- package/dist/browser/runtime/ecdhes.js +1 -2
- package/dist/browser/util/decode_jwt.js +32 -0
- package/dist/node/cjs/index.js +5 -1
- package/dist/node/cjs/jwk/thumbprint.js +5 -0
- package/dist/node/cjs/jwks/local.js +103 -0
- package/dist/node/cjs/jwks/remote.js +28 -81
- package/dist/node/cjs/lib/buffer_utils.js +4 -3
- package/dist/node/cjs/lib/encrypt_key_management.js +3 -1
- package/dist/node/cjs/runtime/base64url.js +4 -4
- package/dist/node/cjs/runtime/ecdhes.js +1 -2
- package/dist/node/cjs/util/decode_jwt.js +36 -0
- package/dist/node/esm/index.js +2 -0
- package/dist/node/esm/jwk/thumbprint.js +5 -0
- package/dist/node/esm/jwks/local.js +97 -0
- package/dist/node/esm/jwks/remote.js +29 -82
- package/dist/node/esm/lib/buffer_utils.js +2 -1
- package/dist/node/esm/lib/encrypt_key_management.js +3 -1
- package/dist/node/esm/runtime/base64url.js +4 -4
- package/dist/node/esm/runtime/ecdhes.js +1 -2
- package/dist/node/esm/util/decode_jwt.js +32 -0
- package/dist/types/index.d.ts +3 -1
- package/dist/types/jwks/local.d.ts +9 -0
- package/dist/types/types.d.ts +4 -1
- package/dist/types/util/decode_jwt.d.ts +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,10 +42,10 @@ import * as jose from 'https://deno.land/x/jose/index.ts'
|
|
|
42
42
|
|
|
43
43
|
- JSON Web Tokens (JWT)
|
|
44
44
|
- [Signing](docs/classes/jwt_sign.SignJWT.md#readme)
|
|
45
|
-
- [Verification & Claims Set Validation](docs/functions/jwt_verify.jwtVerify.md#readme)
|
|
45
|
+
- [Verification & JWT Claims Set Validation](docs/functions/jwt_verify.jwtVerify.md#readme)
|
|
46
46
|
- Encrypted JSON Web Tokens
|
|
47
47
|
- [Encryption](docs/classes/jwt_encrypt.EncryptJWT.md#readme)
|
|
48
|
-
- [Decryption & Claims Set Validation](docs/functions/jwt_decrypt.jwtDecrypt.md#readme)
|
|
48
|
+
- [Decryption & JWT Claims Set Validation](docs/functions/jwt_decrypt.jwtDecrypt.md#readme)
|
|
49
49
|
- Key Import
|
|
50
50
|
- [JWK Import](docs/functions/key_import.importJWK.md#readme)
|
|
51
51
|
- [Public Key Import (SPKI)](docs/functions/key_import.importSPKI.md#readme)
|
|
@@ -61,6 +61,7 @@ import * as jose from 'https://deno.land/x/jose/index.ts'
|
|
|
61
61
|
- [Thumbprints](docs/functions/jwk_thumbprint.calculateJwkThumbprint.md#readme)
|
|
62
62
|
- [EmbeddedJWK](docs/functions/jwk_embedded.EmbeddedJWK.md#readme)
|
|
63
63
|
- JSON Web Key Set (JWKS)
|
|
64
|
+
- [Verify using a local JWKSet](docs/functions/jwks_local.createLocalJWKSet.md#readme)
|
|
64
65
|
- [Verify using a remote JWKSet](docs/functions/jwks_remote.createRemoteJWKSet.md#readme)
|
|
65
66
|
- Key Pair or Secret Generation
|
|
66
67
|
- [Asymmetric Key Pair Generation](docs/functions/key_generate_key_pair.generateKeyPair.md#readme)
|
|
@@ -71,6 +72,7 @@ import * as jose from 'https://deno.land/x/jose/index.ts'
|
|
|
71
72
|
- [Public Key Export](docs/functions/key_export.exportSPKI.md#readme)
|
|
72
73
|
- Utilities
|
|
73
74
|
- [Decoding Token's Protected Header](docs/functions/util_decode_protected_header.decodeProtectedHeader.md#readme)
|
|
75
|
+
- [Decoding JWT Claims Set](docs/functions/util_decode_jwt.decodeJwt.md#readme)
|
|
74
76
|
- [Unsecured JWT](docs/classes/jwt_unsecured.UnsecuredJWT.md#readme)
|
|
75
77
|
- [JOSE Errors](docs/modules/util_errors.md#readme)
|
|
76
78
|
|
|
@@ -89,7 +91,7 @@ import * as jose from 'https://deno.land/x/jose/index.ts'
|
|
|
89
91
|
|
|
90
92
|
| Version | Security Fixes 🔑 | Other Bug Fixes 🐞 | New Features ⭐ |
|
|
91
93
|
| ------- | --------- | -------- | -------- |
|
|
92
|
-
| [4.x.x](https://github.com/panva/jose) | ✅ | ✅ | ✅ |
|
|
94
|
+
| [4.x.x](https://github.com/panva/jose/tree/v4.x) | ✅ | ✅ | ✅ |
|
|
93
95
|
| [3.x.x](https://github.com/panva/jose/tree/v3.x) | ✅ | ✅ until 2022-04-30 | ❌ |
|
|
94
96
|
| [2.x.x](https://github.com/panva/jose/tree/v2.x) | ✅ | ✅ until 2022-04-30 | ❌ |
|
|
95
97
|
| [1.x.x](https://github.com/panva/jose/tree/v1.x) | ✅ | ❌ | ❌ |
|
|
@@ -134,16 +136,16 @@ install size should not be a cause for concern.
|
|
|
134
136
|
|
|
135
137
|
[documentation]: /docs/README.md
|
|
136
138
|
[node-jose]: https://github.com/cisco/node-jose
|
|
137
|
-
[spec-b64]: https://
|
|
138
|
-
[spec-cookbook]: https://
|
|
139
|
-
[spec-jwa]: https://
|
|
140
|
-
[spec-jwe]: https://
|
|
141
|
-
[spec-jwk]: https://
|
|
142
|
-
[spec-jws]: https://
|
|
143
|
-
[spec-jwt]: https://
|
|
144
|
-
[spec-okp]: https://
|
|
145
|
-
[spec-secp256k1]: https://
|
|
146
|
-
[spec-thumbprint]: https://
|
|
139
|
+
[spec-b64]: https://www.rfc-editor.org/rfc/rfc7797
|
|
140
|
+
[spec-cookbook]: https://www.rfc-editor.org/rfc/rfc7520
|
|
141
|
+
[spec-jwa]: https://www.rfc-editor.org/rfc/rfc7518
|
|
142
|
+
[spec-jwe]: https://www.rfc-editor.org/rfc/rfc7516
|
|
143
|
+
[spec-jwk]: https://www.rfc-editor.org/rfc/rfc7517
|
|
144
|
+
[spec-jws]: https://www.rfc-editor.org/rfc/rfc7515
|
|
145
|
+
[spec-jwt]: https://www.rfc-editor.org/rfc/rfc7519
|
|
146
|
+
[spec-okp]: https://www.rfc-editor.org/rfc/rfc8037
|
|
147
|
+
[spec-secp256k1]: https://www.rfc-editor.org/rfc/rfc8812
|
|
148
|
+
[spec-thumbprint]: https://www.rfc-editor.org/rfc/rfc7638
|
|
147
149
|
[support-sponsor]: https://github.com/sponsors/panva
|
|
148
150
|
[conditional-exports]: https://nodejs.org/api/packages.html#packages_conditional_exports
|
|
149
151
|
[webcrypto]: https://www.w3.org/TR/WebCryptoAPI/
|
package/dist/browser/index.js
CHANGED
|
@@ -16,11 +16,13 @@ export { SignJWT } from './jwt/sign.js';
|
|
|
16
16
|
export { EncryptJWT } from './jwt/encrypt.js';
|
|
17
17
|
export { calculateJwkThumbprint } from './jwk/thumbprint.js';
|
|
18
18
|
export { EmbeddedJWK } from './jwk/embedded.js';
|
|
19
|
+
export { createLocalJWKSet } from './jwks/local.js';
|
|
19
20
|
export { createRemoteJWKSet } from './jwks/remote.js';
|
|
20
21
|
export { UnsecuredJWT } from './jwt/unsecured.js';
|
|
21
22
|
export { exportPKCS8, exportSPKI, exportJWK } from './key/export.js';
|
|
22
23
|
export { importSPKI, importPKCS8, importX509, importJWK } from './key/import.js';
|
|
23
24
|
export { decodeProtectedHeader } from './util/decode_protected_header.js';
|
|
25
|
+
export { decodeJwt } from './util/decode_jwt.js';
|
|
24
26
|
import * as errors_1 from './util/errors.js';
|
|
25
27
|
export { errors_1 as errors };
|
|
26
28
|
export { generateKeyPair } from './key/generate_key_pair.js';
|
|
@@ -12,6 +12,11 @@ export async function calculateJwkThumbprint(jwk, digestAlgorithm = 'sha256') {
|
|
|
12
12
|
if (!isObject(jwk)) {
|
|
13
13
|
throw new TypeError('JWK must be an object');
|
|
14
14
|
}
|
|
15
|
+
if (digestAlgorithm !== 'sha256' &&
|
|
16
|
+
digestAlgorithm !== 'sha384' &&
|
|
17
|
+
digestAlgorithm !== 'sha512') {
|
|
18
|
+
throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"');
|
|
19
|
+
}
|
|
15
20
|
let components;
|
|
16
21
|
switch (jwk.kty) {
|
|
17
22
|
case 'EC':
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { importJWK } from '../key/import.js';
|
|
2
|
+
import { JWKSInvalid, JOSENotSupported, JWKSNoMatchingKey, JWKSMultipleMatchingKeys, } from '../util/errors.js';
|
|
3
|
+
import isObject from '../lib/is_object.js';
|
|
4
|
+
function getKtyFromAlg(alg) {
|
|
5
|
+
switch (typeof alg === 'string' && alg.slice(0, 2)) {
|
|
6
|
+
case 'RS':
|
|
7
|
+
case 'PS':
|
|
8
|
+
return 'RSA';
|
|
9
|
+
case 'ES':
|
|
10
|
+
return 'EC';
|
|
11
|
+
case 'Ed':
|
|
12
|
+
return 'OKP';
|
|
13
|
+
default:
|
|
14
|
+
throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function isJWKSLike(jwks) {
|
|
18
|
+
return (jwks &&
|
|
19
|
+
typeof jwks === 'object' &&
|
|
20
|
+
Array.isArray(jwks.keys) &&
|
|
21
|
+
jwks.keys.every(isJWKLike));
|
|
22
|
+
}
|
|
23
|
+
function isJWKLike(key) {
|
|
24
|
+
return isObject(key);
|
|
25
|
+
}
|
|
26
|
+
function clone(obj) {
|
|
27
|
+
if (typeof structuredClone === 'function') {
|
|
28
|
+
return structuredClone(obj);
|
|
29
|
+
}
|
|
30
|
+
return JSON.parse(JSON.stringify(obj));
|
|
31
|
+
}
|
|
32
|
+
export class LocalJWKSet {
|
|
33
|
+
constructor(jwks) {
|
|
34
|
+
this._cached = new WeakMap();
|
|
35
|
+
if (!isJWKSLike(jwks)) {
|
|
36
|
+
throw new JWKSInvalid('JSON Web Key Set malformed');
|
|
37
|
+
}
|
|
38
|
+
this._jwks = clone(jwks);
|
|
39
|
+
}
|
|
40
|
+
async getKey(protectedHeader, token) {
|
|
41
|
+
const { alg, kid } = { ...protectedHeader, ...token.header };
|
|
42
|
+
const candidates = this._jwks.keys.filter((jwk) => {
|
|
43
|
+
let candidate = jwk.kty === getKtyFromAlg(alg);
|
|
44
|
+
if (candidate && typeof kid === 'string') {
|
|
45
|
+
candidate = kid === jwk.kid;
|
|
46
|
+
}
|
|
47
|
+
if (candidate && typeof jwk.alg === 'string') {
|
|
48
|
+
candidate = alg === jwk.alg;
|
|
49
|
+
}
|
|
50
|
+
if (candidate && typeof jwk.use === 'string') {
|
|
51
|
+
candidate = jwk.use === 'sig';
|
|
52
|
+
}
|
|
53
|
+
if (candidate && Array.isArray(jwk.key_ops)) {
|
|
54
|
+
candidate = jwk.key_ops.includes('verify');
|
|
55
|
+
}
|
|
56
|
+
if (candidate && alg === 'EdDSA') {
|
|
57
|
+
candidate = jwk.crv === 'Ed25519' || jwk.crv === 'Ed448';
|
|
58
|
+
}
|
|
59
|
+
if (candidate) {
|
|
60
|
+
switch (alg) {
|
|
61
|
+
case 'ES256':
|
|
62
|
+
candidate = jwk.crv === 'P-256';
|
|
63
|
+
break;
|
|
64
|
+
case 'ES256K':
|
|
65
|
+
candidate = jwk.crv === 'secp256k1';
|
|
66
|
+
break;
|
|
67
|
+
case 'ES384':
|
|
68
|
+
candidate = jwk.crv === 'P-384';
|
|
69
|
+
break;
|
|
70
|
+
case 'ES512':
|
|
71
|
+
candidate = jwk.crv === 'P-521';
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return candidate;
|
|
76
|
+
});
|
|
77
|
+
const { 0: jwk, length } = candidates;
|
|
78
|
+
if (length === 0) {
|
|
79
|
+
throw new JWKSNoMatchingKey();
|
|
80
|
+
}
|
|
81
|
+
else if (length !== 1) {
|
|
82
|
+
throw new JWKSMultipleMatchingKeys();
|
|
83
|
+
}
|
|
84
|
+
const cached = this._cached.get(jwk) || this._cached.set(jwk, {}).get(jwk);
|
|
85
|
+
if (cached[alg] === undefined) {
|
|
86
|
+
const keyObject = await importJWK({ ...jwk, ext: true }, alg);
|
|
87
|
+
if (keyObject instanceof Uint8Array || keyObject.type !== 'public') {
|
|
88
|
+
throw new JWKSInvalid('JSON Web Key Set members must be public keys');
|
|
89
|
+
}
|
|
90
|
+
cached[alg] = keyObject;
|
|
91
|
+
}
|
|
92
|
+
return cached[alg];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export function createLocalJWKSet(jwks) {
|
|
96
|
+
return LocalJWKSet.prototype.getKey.bind(new LocalJWKSet(jwks));
|
|
97
|
+
}
|
|
@@ -1,26 +1,11 @@
|
|
|
1
1
|
import fetchJwks from '../runtime/fetch_jwks.js';
|
|
2
|
-
import {
|
|
3
|
-
import { JWKSInvalid,
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
switch (typeof alg === 'string' && alg.slice(0, 2)) {
|
|
7
|
-
case 'RS':
|
|
8
|
-
case 'PS':
|
|
9
|
-
return 'RSA';
|
|
10
|
-
case 'ES':
|
|
11
|
-
return 'EC';
|
|
12
|
-
case 'Ed':
|
|
13
|
-
return 'OKP';
|
|
14
|
-
default:
|
|
15
|
-
throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set');
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
function isJWKLike(key) {
|
|
19
|
-
return isObject(key);
|
|
20
|
-
}
|
|
21
|
-
class RemoteJWKSet {
|
|
2
|
+
import { isCloudflareWorkers } from '../runtime/env.js';
|
|
3
|
+
import { JWKSInvalid, JWKSNoMatchingKey } from '../util/errors.js';
|
|
4
|
+
import { isJWKSLike, LocalJWKSet } from './local.js';
|
|
5
|
+
class RemoteJWKSet extends LocalJWKSet {
|
|
22
6
|
constructor(url, options) {
|
|
23
|
-
|
|
7
|
+
super({ keys: [] });
|
|
8
|
+
this._jwks = undefined;
|
|
24
9
|
if (!(url instanceof URL)) {
|
|
25
10
|
throw new TypeError('url must be an instance of URL');
|
|
26
11
|
}
|
|
@@ -38,78 +23,40 @@ class RemoteJWKSet {
|
|
|
38
23
|
return Date.now() < this._cooldownStarted + this._cooldownDuration;
|
|
39
24
|
}
|
|
40
25
|
async getKey(protectedHeader, token) {
|
|
41
|
-
const joseHeader = {
|
|
42
|
-
...protectedHeader,
|
|
43
|
-
...token.header,
|
|
44
|
-
};
|
|
45
26
|
if (!this._jwks) {
|
|
46
27
|
await this.reload();
|
|
47
28
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (candidate && typeof joseHeader.kid === 'string') {
|
|
51
|
-
candidate = joseHeader.kid === jwk.kid;
|
|
52
|
-
}
|
|
53
|
-
if (candidate && typeof jwk.alg === 'string') {
|
|
54
|
-
candidate = joseHeader.alg === jwk.alg;
|
|
55
|
-
}
|
|
56
|
-
if (candidate && typeof jwk.use === 'string') {
|
|
57
|
-
candidate = jwk.use === 'sig';
|
|
58
|
-
}
|
|
59
|
-
if (candidate && Array.isArray(jwk.key_ops)) {
|
|
60
|
-
candidate = jwk.key_ops.includes('verify');
|
|
61
|
-
}
|
|
62
|
-
if (candidate && joseHeader.alg === 'EdDSA') {
|
|
63
|
-
candidate = jwk.crv === 'Ed25519' || jwk.crv === 'Ed448';
|
|
64
|
-
}
|
|
65
|
-
if (candidate) {
|
|
66
|
-
switch (joseHeader.alg) {
|
|
67
|
-
case 'ES256':
|
|
68
|
-
candidate = jwk.crv === 'P-256';
|
|
69
|
-
break;
|
|
70
|
-
case 'ES256K':
|
|
71
|
-
candidate = jwk.crv === 'secp256k1';
|
|
72
|
-
break;
|
|
73
|
-
case 'ES384':
|
|
74
|
-
candidate = jwk.crv === 'P-384';
|
|
75
|
-
break;
|
|
76
|
-
case 'ES512':
|
|
77
|
-
candidate = jwk.crv === 'P-521';
|
|
78
|
-
break;
|
|
79
|
-
default:
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return candidate;
|
|
83
|
-
});
|
|
84
|
-
const { 0: jwk, length } = candidates;
|
|
85
|
-
if (length === 0) {
|
|
86
|
-
if (this.coolingDown() === false) {
|
|
87
|
-
await this.reload();
|
|
88
|
-
return this.getKey(protectedHeader, token);
|
|
89
|
-
}
|
|
90
|
-
throw new JWKSNoMatchingKey();
|
|
91
|
-
}
|
|
92
|
-
else if (length !== 1) {
|
|
93
|
-
throw new JWKSMultipleMatchingKeys();
|
|
29
|
+
try {
|
|
30
|
+
return await super.getKey(protectedHeader, token);
|
|
94
31
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
32
|
+
catch (err) {
|
|
33
|
+
if (err instanceof JWKSNoMatchingKey) {
|
|
34
|
+
if (this.coolingDown() === false) {
|
|
35
|
+
await this.reload();
|
|
36
|
+
return super.getKey(protectedHeader, token);
|
|
37
|
+
}
|
|
100
38
|
}
|
|
101
|
-
|
|
39
|
+
throw err;
|
|
102
40
|
}
|
|
103
|
-
return cached[joseHeader.alg];
|
|
104
41
|
}
|
|
105
42
|
async reload() {
|
|
43
|
+
if (this._pendingFetch && isCloudflareWorkers()) {
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
const isDone = () => {
|
|
46
|
+
if (this._pendingFetch === undefined) {
|
|
47
|
+
resolve();
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
setTimeout(isDone, 5);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
isDone();
|
|
54
|
+
});
|
|
55
|
+
}
|
|
106
56
|
if (!this._pendingFetch) {
|
|
107
57
|
this._pendingFetch = fetchJwks(this._url, this._timeoutDuration, this._options)
|
|
108
58
|
.then((json) => {
|
|
109
|
-
if (
|
|
110
|
-
!json ||
|
|
111
|
-
!Array.isArray(json.keys) ||
|
|
112
|
-
!json.keys.every(isJWKLike)) {
|
|
59
|
+
if (!isJWKSLike(json)) {
|
|
113
60
|
throw new JWKSInvalid('JSON Web Key Set malformed');
|
|
114
61
|
}
|
|
115
62
|
this._jwks = { keys: json.keys };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import digest from '../runtime/digest.js';
|
|
1
2
|
export const encoder = new TextEncoder();
|
|
2
3
|
export const decoder = new TextDecoder();
|
|
3
4
|
const MAX_INT32 = 2 ** 32;
|
|
@@ -36,7 +37,7 @@ export function uint32be(value) {
|
|
|
36
37
|
export function lengthAndInput(input) {
|
|
37
38
|
return concat(uint32be(input.length), input);
|
|
38
39
|
}
|
|
39
|
-
export async function concatKdf(
|
|
40
|
+
export async function concatKdf(secret, bits, value) {
|
|
40
41
|
const iterations = Math.ceil((bits >> 3) / 32);
|
|
41
42
|
let res;
|
|
42
43
|
for (let iter = 1; iter <= iterations; iter++) {
|
|
@@ -30,7 +30,9 @@ async function encryptKeyManagement(alg, enc, key, providedCek, providedParamete
|
|
|
30
30
|
ephemeralKey || (ephemeralKey = (await ECDH.generateEpk(key)).privateKey);
|
|
31
31
|
const { x, y, crv, kty } = await exportJWK(ephemeralKey);
|
|
32
32
|
const sharedSecret = await ECDH.deriveKey(key, ephemeralKey, alg === 'ECDH-ES' ? enc : alg, alg === 'ECDH-ES' ? cekLength(enc) : parseInt(alg.slice(-5, -2), 10), apu, apv);
|
|
33
|
-
parameters = { epk: { x,
|
|
33
|
+
parameters = { epk: { x, crv, kty } };
|
|
34
|
+
if (kty === 'EC')
|
|
35
|
+
parameters.epk.y = y;
|
|
34
36
|
if (apu)
|
|
35
37
|
parameters.apu = base64url(apu);
|
|
36
38
|
if (apv)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { encoder, concat, uint32be, lengthAndInput, concatKdf } from '../lib/buffer_utils.js';
|
|
2
2
|
import crypto, { isCryptoKey } from './webcrypto.js';
|
|
3
3
|
import { checkEncCryptoKey } from '../lib/crypto_key.js';
|
|
4
|
-
import digest from './digest.js';
|
|
5
4
|
import invalidKeyInput from '../lib/invalid_key_input.js';
|
|
6
5
|
import { types } from './is_key_like.js';
|
|
7
6
|
export async function deriveKey(publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(0), apv = new Uint8Array(0)) {
|
|
@@ -21,7 +20,7 @@ export async function deriveKey(publicKey, privateKey, algorithm, keyLength, apu
|
|
|
21
20
|
name: 'ECDH',
|
|
22
21
|
public: publicKey,
|
|
23
22
|
}, privateKey, Math.ceil(parseInt(privateKey.algorithm.namedCurve.slice(-3), 10) / 8) << 3));
|
|
24
|
-
return concatKdf(
|
|
23
|
+
return concatKdf(sharedSecret, keyLength, value);
|
|
25
24
|
}
|
|
26
25
|
export async function generateEpk(key) {
|
|
27
26
|
if (!isCryptoKey(key)) {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { decode as base64url } from './base64url.js';
|
|
2
|
+
import { decoder } from '../lib/buffer_utils.js';
|
|
3
|
+
import isObject from '../lib/is_object.js';
|
|
4
|
+
import { JWTInvalid } from './errors.js';
|
|
5
|
+
export function decodeJwt(jwt) {
|
|
6
|
+
if (typeof jwt !== 'string')
|
|
7
|
+
throw new JWTInvalid('JWTs must use Compact JWS serialization, JWT must be a string');
|
|
8
|
+
const { 1: payload, length } = jwt.split('.');
|
|
9
|
+
if (length === 5)
|
|
10
|
+
throw new JWTInvalid('Only JWTs using Compact JWS serialization can be decoded');
|
|
11
|
+
if (length !== 3)
|
|
12
|
+
throw new JWTInvalid('Invalid JWT');
|
|
13
|
+
if (!payload)
|
|
14
|
+
throw new JWTInvalid('JWTs must contain a payload');
|
|
15
|
+
let decoded;
|
|
16
|
+
try {
|
|
17
|
+
decoded = base64url(payload);
|
|
18
|
+
}
|
|
19
|
+
catch (_a) {
|
|
20
|
+
throw new JWTInvalid('Failed to parse the base64url encoded payload');
|
|
21
|
+
}
|
|
22
|
+
let result;
|
|
23
|
+
try {
|
|
24
|
+
result = JSON.parse(decoder.decode(decoded));
|
|
25
|
+
}
|
|
26
|
+
catch (_b) {
|
|
27
|
+
throw new JWTInvalid('Failed to parse the decoded payload as JSON');
|
|
28
|
+
}
|
|
29
|
+
if (!isObject(result))
|
|
30
|
+
throw new JWTInvalid('Invalid JWT Claims Set');
|
|
31
|
+
return result;
|
|
32
|
+
}
|
package/dist/node/cjs/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.base64url = exports.generateSecret = exports.generateKeyPair = exports.errors = exports.decodeProtectedHeader = exports.importJWK = exports.importX509 = exports.importPKCS8 = exports.importSPKI = exports.exportJWK = exports.exportSPKI = exports.exportPKCS8 = exports.UnsecuredJWT = exports.createRemoteJWKSet = exports.EmbeddedJWK = exports.calculateJwkThumbprint = exports.EncryptJWT = exports.SignJWT = exports.GeneralSign = exports.FlattenedSign = exports.CompactSign = exports.FlattenedEncrypt = exports.CompactEncrypt = exports.jwtDecrypt = exports.jwtVerify = exports.generalVerify = exports.flattenedVerify = exports.compactVerify = exports.GeneralEncrypt = exports.generalDecrypt = exports.flattenedDecrypt = exports.compactDecrypt = void 0;
|
|
3
|
+
exports.base64url = exports.generateSecret = exports.generateKeyPair = exports.errors = exports.decodeJwt = exports.decodeProtectedHeader = exports.importJWK = exports.importX509 = exports.importPKCS8 = exports.importSPKI = exports.exportJWK = exports.exportSPKI = exports.exportPKCS8 = exports.UnsecuredJWT = exports.createRemoteJWKSet = exports.createLocalJWKSet = exports.EmbeddedJWK = exports.calculateJwkThumbprint = exports.EncryptJWT = exports.SignJWT = exports.GeneralSign = exports.FlattenedSign = exports.CompactSign = exports.FlattenedEncrypt = exports.CompactEncrypt = exports.jwtDecrypt = exports.jwtVerify = exports.generalVerify = exports.flattenedVerify = exports.compactVerify = exports.GeneralEncrypt = exports.generalDecrypt = exports.flattenedDecrypt = exports.compactDecrypt = void 0;
|
|
4
4
|
var decrypt_js_1 = require("./jwe/compact/decrypt.js");
|
|
5
5
|
Object.defineProperty(exports, "compactDecrypt", { enumerable: true, get: function () { return decrypt_js_1.compactDecrypt; } });
|
|
6
6
|
var decrypt_js_2 = require("./jwe/flattened/decrypt.js");
|
|
@@ -37,6 +37,8 @@ var thumbprint_js_1 = require("./jwk/thumbprint.js");
|
|
|
37
37
|
Object.defineProperty(exports, "calculateJwkThumbprint", { enumerable: true, get: function () { return thumbprint_js_1.calculateJwkThumbprint; } });
|
|
38
38
|
var embedded_js_1 = require("./jwk/embedded.js");
|
|
39
39
|
Object.defineProperty(exports, "EmbeddedJWK", { enumerable: true, get: function () { return embedded_js_1.EmbeddedJWK; } });
|
|
40
|
+
var local_js_1 = require("./jwks/local.js");
|
|
41
|
+
Object.defineProperty(exports, "createLocalJWKSet", { enumerable: true, get: function () { return local_js_1.createLocalJWKSet; } });
|
|
40
42
|
var remote_js_1 = require("./jwks/remote.js");
|
|
41
43
|
Object.defineProperty(exports, "createRemoteJWKSet", { enumerable: true, get: function () { return remote_js_1.createRemoteJWKSet; } });
|
|
42
44
|
var unsecured_js_1 = require("./jwt/unsecured.js");
|
|
@@ -52,6 +54,8 @@ Object.defineProperty(exports, "importX509", { enumerable: true, get: function (
|
|
|
52
54
|
Object.defineProperty(exports, "importJWK", { enumerable: true, get: function () { return import_js_1.importJWK; } });
|
|
53
55
|
var decode_protected_header_js_1 = require("./util/decode_protected_header.js");
|
|
54
56
|
Object.defineProperty(exports, "decodeProtectedHeader", { enumerable: true, get: function () { return decode_protected_header_js_1.decodeProtectedHeader; } });
|
|
57
|
+
var decode_jwt_js_1 = require("./util/decode_jwt.js");
|
|
58
|
+
Object.defineProperty(exports, "decodeJwt", { enumerable: true, get: function () { return decode_jwt_js_1.decodeJwt; } });
|
|
55
59
|
exports.errors = require("./util/errors.js");
|
|
56
60
|
var generate_key_pair_js_1 = require("./key/generate_key_pair.js");
|
|
57
61
|
Object.defineProperty(exports, "generateKeyPair", { enumerable: true, get: function () { return generate_key_pair_js_1.generateKeyPair; } });
|
|
@@ -15,6 +15,11 @@ async function calculateJwkThumbprint(jwk, digestAlgorithm = 'sha256') {
|
|
|
15
15
|
if (!(0, is_object_js_1.default)(jwk)) {
|
|
16
16
|
throw new TypeError('JWK must be an object');
|
|
17
17
|
}
|
|
18
|
+
if (digestAlgorithm !== 'sha256' &&
|
|
19
|
+
digestAlgorithm !== 'sha384' &&
|
|
20
|
+
digestAlgorithm !== 'sha512') {
|
|
21
|
+
throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"');
|
|
22
|
+
}
|
|
18
23
|
let components;
|
|
19
24
|
switch (jwk.kty) {
|
|
20
25
|
case 'EC':
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createLocalJWKSet = exports.LocalJWKSet = exports.isJWKSLike = void 0;
|
|
4
|
+
const import_js_1 = require("../key/import.js");
|
|
5
|
+
const errors_js_1 = require("../util/errors.js");
|
|
6
|
+
const is_object_js_1 = require("../lib/is_object.js");
|
|
7
|
+
function getKtyFromAlg(alg) {
|
|
8
|
+
switch (typeof alg === 'string' && alg.slice(0, 2)) {
|
|
9
|
+
case 'RS':
|
|
10
|
+
case 'PS':
|
|
11
|
+
return 'RSA';
|
|
12
|
+
case 'ES':
|
|
13
|
+
return 'EC';
|
|
14
|
+
case 'Ed':
|
|
15
|
+
return 'OKP';
|
|
16
|
+
default:
|
|
17
|
+
throw new errors_js_1.JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function isJWKSLike(jwks) {
|
|
21
|
+
return (jwks &&
|
|
22
|
+
typeof jwks === 'object' &&
|
|
23
|
+
Array.isArray(jwks.keys) &&
|
|
24
|
+
jwks.keys.every(isJWKLike));
|
|
25
|
+
}
|
|
26
|
+
exports.isJWKSLike = isJWKSLike;
|
|
27
|
+
function isJWKLike(key) {
|
|
28
|
+
return (0, is_object_js_1.default)(key);
|
|
29
|
+
}
|
|
30
|
+
function clone(obj) {
|
|
31
|
+
if (typeof structuredClone === 'function') {
|
|
32
|
+
return structuredClone(obj);
|
|
33
|
+
}
|
|
34
|
+
return JSON.parse(JSON.stringify(obj));
|
|
35
|
+
}
|
|
36
|
+
class LocalJWKSet {
|
|
37
|
+
constructor(jwks) {
|
|
38
|
+
this._cached = new WeakMap();
|
|
39
|
+
if (!isJWKSLike(jwks)) {
|
|
40
|
+
throw new errors_js_1.JWKSInvalid('JSON Web Key Set malformed');
|
|
41
|
+
}
|
|
42
|
+
this._jwks = clone(jwks);
|
|
43
|
+
}
|
|
44
|
+
async getKey(protectedHeader, token) {
|
|
45
|
+
const { alg, kid } = { ...protectedHeader, ...token.header };
|
|
46
|
+
const candidates = this._jwks.keys.filter((jwk) => {
|
|
47
|
+
let candidate = jwk.kty === getKtyFromAlg(alg);
|
|
48
|
+
if (candidate && typeof kid === 'string') {
|
|
49
|
+
candidate = kid === jwk.kid;
|
|
50
|
+
}
|
|
51
|
+
if (candidate && typeof jwk.alg === 'string') {
|
|
52
|
+
candidate = alg === jwk.alg;
|
|
53
|
+
}
|
|
54
|
+
if (candidate && typeof jwk.use === 'string') {
|
|
55
|
+
candidate = jwk.use === 'sig';
|
|
56
|
+
}
|
|
57
|
+
if (candidate && Array.isArray(jwk.key_ops)) {
|
|
58
|
+
candidate = jwk.key_ops.includes('verify');
|
|
59
|
+
}
|
|
60
|
+
if (candidate && alg === 'EdDSA') {
|
|
61
|
+
candidate = jwk.crv === 'Ed25519' || jwk.crv === 'Ed448';
|
|
62
|
+
}
|
|
63
|
+
if (candidate) {
|
|
64
|
+
switch (alg) {
|
|
65
|
+
case 'ES256':
|
|
66
|
+
candidate = jwk.crv === 'P-256';
|
|
67
|
+
break;
|
|
68
|
+
case 'ES256K':
|
|
69
|
+
candidate = jwk.crv === 'secp256k1';
|
|
70
|
+
break;
|
|
71
|
+
case 'ES384':
|
|
72
|
+
candidate = jwk.crv === 'P-384';
|
|
73
|
+
break;
|
|
74
|
+
case 'ES512':
|
|
75
|
+
candidate = jwk.crv === 'P-521';
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return candidate;
|
|
80
|
+
});
|
|
81
|
+
const { 0: jwk, length } = candidates;
|
|
82
|
+
if (length === 0) {
|
|
83
|
+
throw new errors_js_1.JWKSNoMatchingKey();
|
|
84
|
+
}
|
|
85
|
+
else if (length !== 1) {
|
|
86
|
+
throw new errors_js_1.JWKSMultipleMatchingKeys();
|
|
87
|
+
}
|
|
88
|
+
const cached = this._cached.get(jwk) || this._cached.set(jwk, {}).get(jwk);
|
|
89
|
+
if (cached[alg] === undefined) {
|
|
90
|
+
const keyObject = await (0, import_js_1.importJWK)({ ...jwk, ext: true }, alg);
|
|
91
|
+
if (keyObject instanceof Uint8Array || keyObject.type !== 'public') {
|
|
92
|
+
throw new errors_js_1.JWKSInvalid('JSON Web Key Set members must be public keys');
|
|
93
|
+
}
|
|
94
|
+
cached[alg] = keyObject;
|
|
95
|
+
}
|
|
96
|
+
return cached[alg];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports.LocalJWKSet = LocalJWKSet;
|
|
100
|
+
function createLocalJWKSet(jwks) {
|
|
101
|
+
return LocalJWKSet.prototype.getKey.bind(new LocalJWKSet(jwks));
|
|
102
|
+
}
|
|
103
|
+
exports.createLocalJWKSet = createLocalJWKSet;
|
|
@@ -2,28 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createRemoteJWKSet = void 0;
|
|
4
4
|
const fetch_jwks_js_1 = require("../runtime/fetch_jwks.js");
|
|
5
|
-
const
|
|
5
|
+
const env_js_1 = require("../runtime/env.js");
|
|
6
6
|
const errors_js_1 = require("../util/errors.js");
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
switch (typeof alg === 'string' && alg.slice(0, 2)) {
|
|
10
|
-
case 'RS':
|
|
11
|
-
case 'PS':
|
|
12
|
-
return 'RSA';
|
|
13
|
-
case 'ES':
|
|
14
|
-
return 'EC';
|
|
15
|
-
case 'Ed':
|
|
16
|
-
return 'OKP';
|
|
17
|
-
default:
|
|
18
|
-
throw new errors_js_1.JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set');
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
function isJWKLike(key) {
|
|
22
|
-
return (0, is_object_js_1.default)(key);
|
|
23
|
-
}
|
|
24
|
-
class RemoteJWKSet {
|
|
7
|
+
const local_js_1 = require("./local.js");
|
|
8
|
+
class RemoteJWKSet extends local_js_1.LocalJWKSet {
|
|
25
9
|
constructor(url, options) {
|
|
26
|
-
|
|
10
|
+
super({ keys: [] });
|
|
11
|
+
this._jwks = undefined;
|
|
27
12
|
if (!(url instanceof URL)) {
|
|
28
13
|
throw new TypeError('url must be an instance of URL');
|
|
29
14
|
}
|
|
@@ -41,78 +26,40 @@ class RemoteJWKSet {
|
|
|
41
26
|
return Date.now() < this._cooldownStarted + this._cooldownDuration;
|
|
42
27
|
}
|
|
43
28
|
async getKey(protectedHeader, token) {
|
|
44
|
-
const joseHeader = {
|
|
45
|
-
...protectedHeader,
|
|
46
|
-
...token.header,
|
|
47
|
-
};
|
|
48
29
|
if (!this._jwks) {
|
|
49
30
|
await this.reload();
|
|
50
31
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (candidate && typeof joseHeader.kid === 'string') {
|
|
54
|
-
candidate = joseHeader.kid === jwk.kid;
|
|
55
|
-
}
|
|
56
|
-
if (candidate && typeof jwk.alg === 'string') {
|
|
57
|
-
candidate = joseHeader.alg === jwk.alg;
|
|
58
|
-
}
|
|
59
|
-
if (candidate && typeof jwk.use === 'string') {
|
|
60
|
-
candidate = jwk.use === 'sig';
|
|
61
|
-
}
|
|
62
|
-
if (candidate && Array.isArray(jwk.key_ops)) {
|
|
63
|
-
candidate = jwk.key_ops.includes('verify');
|
|
64
|
-
}
|
|
65
|
-
if (candidate && joseHeader.alg === 'EdDSA') {
|
|
66
|
-
candidate = jwk.crv === 'Ed25519' || jwk.crv === 'Ed448';
|
|
67
|
-
}
|
|
68
|
-
if (candidate) {
|
|
69
|
-
switch (joseHeader.alg) {
|
|
70
|
-
case 'ES256':
|
|
71
|
-
candidate = jwk.crv === 'P-256';
|
|
72
|
-
break;
|
|
73
|
-
case 'ES256K':
|
|
74
|
-
candidate = jwk.crv === 'secp256k1';
|
|
75
|
-
break;
|
|
76
|
-
case 'ES384':
|
|
77
|
-
candidate = jwk.crv === 'P-384';
|
|
78
|
-
break;
|
|
79
|
-
case 'ES512':
|
|
80
|
-
candidate = jwk.crv === 'P-521';
|
|
81
|
-
break;
|
|
82
|
-
default:
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return candidate;
|
|
86
|
-
});
|
|
87
|
-
const { 0: jwk, length } = candidates;
|
|
88
|
-
if (length === 0) {
|
|
89
|
-
if (this.coolingDown() === false) {
|
|
90
|
-
await this.reload();
|
|
91
|
-
return this.getKey(protectedHeader, token);
|
|
92
|
-
}
|
|
93
|
-
throw new errors_js_1.JWKSNoMatchingKey();
|
|
94
|
-
}
|
|
95
|
-
else if (length !== 1) {
|
|
96
|
-
throw new errors_js_1.JWKSMultipleMatchingKeys();
|
|
32
|
+
try {
|
|
33
|
+
return await super.getKey(protectedHeader, token);
|
|
97
34
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
35
|
+
catch (err) {
|
|
36
|
+
if (err instanceof errors_js_1.JWKSNoMatchingKey) {
|
|
37
|
+
if (this.coolingDown() === false) {
|
|
38
|
+
await this.reload();
|
|
39
|
+
return super.getKey(protectedHeader, token);
|
|
40
|
+
}
|
|
103
41
|
}
|
|
104
|
-
|
|
42
|
+
throw err;
|
|
105
43
|
}
|
|
106
|
-
return cached[joseHeader.alg];
|
|
107
44
|
}
|
|
108
45
|
async reload() {
|
|
46
|
+
if (this._pendingFetch && (0, env_js_1.isCloudflareWorkers)()) {
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
const isDone = () => {
|
|
49
|
+
if (this._pendingFetch === undefined) {
|
|
50
|
+
resolve();
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
setTimeout(isDone, 5);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
isDone();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
109
59
|
if (!this._pendingFetch) {
|
|
110
60
|
this._pendingFetch = (0, fetch_jwks_js_1.default)(this._url, this._timeoutDuration, this._options)
|
|
111
61
|
.then((json) => {
|
|
112
|
-
if (
|
|
113
|
-
!json ||
|
|
114
|
-
!Array.isArray(json.keys) ||
|
|
115
|
-
!json.keys.every(isJWKLike)) {
|
|
62
|
+
if (!(0, local_js_1.isJWKSLike)(json)) {
|
|
116
63
|
throw new errors_js_1.JWKSInvalid('JSON Web Key Set malformed');
|
|
117
64
|
}
|
|
118
65
|
this._jwks = { keys: json.keys };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.concatKdf = exports.lengthAndInput = exports.uint32be = exports.uint64be = exports.p2s = exports.concat = exports.decoder = exports.encoder = void 0;
|
|
4
|
+
const digest_js_1 = require("../runtime/digest.js");
|
|
4
5
|
exports.encoder = new TextEncoder();
|
|
5
6
|
exports.decoder = new TextDecoder();
|
|
6
7
|
const MAX_INT32 = 2 ** 32;
|
|
@@ -44,7 +45,7 @@ function lengthAndInput(input) {
|
|
|
44
45
|
return concat(uint32be(input.length), input);
|
|
45
46
|
}
|
|
46
47
|
exports.lengthAndInput = lengthAndInput;
|
|
47
|
-
async function concatKdf(
|
|
48
|
+
async function concatKdf(secret, bits, value) {
|
|
48
49
|
const iterations = Math.ceil((bits >> 3) / 32);
|
|
49
50
|
let res;
|
|
50
51
|
for (let iter = 1; iter <= iterations; iter++) {
|
|
@@ -53,10 +54,10 @@ async function concatKdf(digest, secret, bits, value) {
|
|
|
53
54
|
buf.set(secret, 4);
|
|
54
55
|
buf.set(value, 4 + secret.length);
|
|
55
56
|
if (!res) {
|
|
56
|
-
res = await
|
|
57
|
+
res = await (0, digest_js_1.default)('sha256', buf);
|
|
57
58
|
}
|
|
58
59
|
else {
|
|
59
|
-
res = concat(res, await
|
|
60
|
+
res = concat(res, await (0, digest_js_1.default)('sha256', buf));
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
res = res.slice(0, bits >> 3);
|
|
@@ -32,7 +32,9 @@ async function encryptKeyManagement(alg, enc, key, providedCek, providedParamete
|
|
|
32
32
|
ephemeralKey || (ephemeralKey = (await ECDH.generateEpk(key)).privateKey);
|
|
33
33
|
const { x, y, crv, kty } = await (0, export_js_1.exportJWK)(ephemeralKey);
|
|
34
34
|
const sharedSecret = await ECDH.deriveKey(key, ephemeralKey, alg === 'ECDH-ES' ? enc : alg, alg === 'ECDH-ES' ? (0, cek_js_1.bitLength)(enc) : parseInt(alg.slice(-5, -2), 10), apu, apv);
|
|
35
|
-
parameters = { epk: { x,
|
|
35
|
+
parameters = { epk: { x, crv, kty } };
|
|
36
|
+
if (kty === 'EC')
|
|
37
|
+
parameters.epk.y = y;
|
|
36
38
|
if (apu)
|
|
37
39
|
parameters.apu = (0, base64url_js_1.encode)(apu);
|
|
38
40
|
if (apv)
|
|
@@ -3,7 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.decode = exports.encode = exports.encodeBase64 = exports.decodeBase64 = void 0;
|
|
4
4
|
const buffer_1 = require("buffer");
|
|
5
5
|
const buffer_utils_js_1 = require("../lib/buffer_utils.js");
|
|
6
|
-
let
|
|
6
|
+
let encode;
|
|
7
|
+
exports.encode = encode;
|
|
7
8
|
function normalize(input) {
|
|
8
9
|
let encoded = input;
|
|
9
10
|
if (encoded instanceof Uint8Array) {
|
|
@@ -12,15 +13,14 @@ function normalize(input) {
|
|
|
12
13
|
return encoded;
|
|
13
14
|
}
|
|
14
15
|
if (buffer_1.Buffer.isEncoding('base64url')) {
|
|
15
|
-
|
|
16
|
+
exports.encode = encode = (input) => buffer_1.Buffer.from(input).toString('base64url');
|
|
16
17
|
}
|
|
17
18
|
else {
|
|
18
|
-
|
|
19
|
+
exports.encode = encode = (input) => buffer_1.Buffer.from(input).toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
|
|
19
20
|
}
|
|
20
21
|
const decodeBase64 = (input) => buffer_1.Buffer.from(input, 'base64');
|
|
21
22
|
exports.decodeBase64 = decodeBase64;
|
|
22
23
|
const encodeBase64 = (input) => buffer_1.Buffer.from(input).toString('base64');
|
|
23
24
|
exports.encodeBase64 = encodeBase64;
|
|
24
|
-
exports.encode = encodeImpl;
|
|
25
25
|
const decode = (input) => buffer_1.Buffer.from(normalize(input), 'base64');
|
|
26
26
|
exports.decode = decode;
|
|
@@ -5,7 +5,6 @@ const crypto_1 = require("crypto");
|
|
|
5
5
|
const util_1 = require("util");
|
|
6
6
|
const get_named_curve_js_1 = require("./get_named_curve.js");
|
|
7
7
|
const buffer_utils_js_1 = require("../lib/buffer_utils.js");
|
|
8
|
-
const digest_js_1 = require("./digest.js");
|
|
9
8
|
const errors_js_1 = require("../util/errors.js");
|
|
10
9
|
const webcrypto_js_1 = require("./webcrypto.js");
|
|
11
10
|
const crypto_key_js_1 = require("../lib/crypto_key.js");
|
|
@@ -38,7 +37,7 @@ async function deriveKey(publicKee, privateKee, algorithm, keyLength, apu = new
|
|
|
38
37
|
}
|
|
39
38
|
const value = (0, buffer_utils_js_1.concat)((0, buffer_utils_js_1.lengthAndInput)(buffer_utils_js_1.encoder.encode(algorithm)), (0, buffer_utils_js_1.lengthAndInput)(apu), (0, buffer_utils_js_1.lengthAndInput)(apv), (0, buffer_utils_js_1.uint32be)(keyLength));
|
|
40
39
|
const sharedSecret = (0, crypto_1.diffieHellman)({ privateKey, publicKey });
|
|
41
|
-
return (0, buffer_utils_js_1.concatKdf)(
|
|
40
|
+
return (0, buffer_utils_js_1.concatKdf)(sharedSecret, keyLength, value);
|
|
42
41
|
}
|
|
43
42
|
exports.deriveKey = deriveKey;
|
|
44
43
|
async function generateEpk(kee) {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decodeJwt = void 0;
|
|
4
|
+
const base64url_js_1 = require("./base64url.js");
|
|
5
|
+
const buffer_utils_js_1 = require("../lib/buffer_utils.js");
|
|
6
|
+
const is_object_js_1 = require("../lib/is_object.js");
|
|
7
|
+
const errors_js_1 = require("./errors.js");
|
|
8
|
+
function decodeJwt(jwt) {
|
|
9
|
+
if (typeof jwt !== 'string')
|
|
10
|
+
throw new errors_js_1.JWTInvalid('JWTs must use Compact JWS serialization, JWT must be a string');
|
|
11
|
+
const { 1: payload, length } = jwt.split('.');
|
|
12
|
+
if (length === 5)
|
|
13
|
+
throw new errors_js_1.JWTInvalid('Only JWTs using Compact JWS serialization can be decoded');
|
|
14
|
+
if (length !== 3)
|
|
15
|
+
throw new errors_js_1.JWTInvalid('Invalid JWT');
|
|
16
|
+
if (!payload)
|
|
17
|
+
throw new errors_js_1.JWTInvalid('JWTs must contain a payload');
|
|
18
|
+
let decoded;
|
|
19
|
+
try {
|
|
20
|
+
decoded = (0, base64url_js_1.decode)(payload);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
throw new errors_js_1.JWTInvalid('Failed to parse the base64url encoded payload');
|
|
24
|
+
}
|
|
25
|
+
let result;
|
|
26
|
+
try {
|
|
27
|
+
result = JSON.parse(buffer_utils_js_1.decoder.decode(decoded));
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
throw new errors_js_1.JWTInvalid('Failed to parse the decoded payload as JSON');
|
|
31
|
+
}
|
|
32
|
+
if (!(0, is_object_js_1.default)(result))
|
|
33
|
+
throw new errors_js_1.JWTInvalid('Invalid JWT Claims Set');
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
exports.decodeJwt = decodeJwt;
|
package/dist/node/esm/index.js
CHANGED
|
@@ -16,11 +16,13 @@ export { SignJWT } from './jwt/sign.js';
|
|
|
16
16
|
export { EncryptJWT } from './jwt/encrypt.js';
|
|
17
17
|
export { calculateJwkThumbprint } from './jwk/thumbprint.js';
|
|
18
18
|
export { EmbeddedJWK } from './jwk/embedded.js';
|
|
19
|
+
export { createLocalJWKSet } from './jwks/local.js';
|
|
19
20
|
export { createRemoteJWKSet } from './jwks/remote.js';
|
|
20
21
|
export { UnsecuredJWT } from './jwt/unsecured.js';
|
|
21
22
|
export { exportPKCS8, exportSPKI, exportJWK } from './key/export.js';
|
|
22
23
|
export { importSPKI, importPKCS8, importX509, importJWK } from './key/import.js';
|
|
23
24
|
export { decodeProtectedHeader } from './util/decode_protected_header.js';
|
|
25
|
+
export { decodeJwt } from './util/decode_jwt.js';
|
|
24
26
|
export * as errors from './util/errors.js';
|
|
25
27
|
export { generateKeyPair } from './key/generate_key_pair.js';
|
|
26
28
|
export { generateSecret } from './key/generate_secret.js';
|
|
@@ -12,6 +12,11 @@ export async function calculateJwkThumbprint(jwk, digestAlgorithm = 'sha256') {
|
|
|
12
12
|
if (!isObject(jwk)) {
|
|
13
13
|
throw new TypeError('JWK must be an object');
|
|
14
14
|
}
|
|
15
|
+
if (digestAlgorithm !== 'sha256' &&
|
|
16
|
+
digestAlgorithm !== 'sha384' &&
|
|
17
|
+
digestAlgorithm !== 'sha512') {
|
|
18
|
+
throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"');
|
|
19
|
+
}
|
|
15
20
|
let components;
|
|
16
21
|
switch (jwk.kty) {
|
|
17
22
|
case 'EC':
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { importJWK } from '../key/import.js';
|
|
2
|
+
import { JWKSInvalid, JOSENotSupported, JWKSNoMatchingKey, JWKSMultipleMatchingKeys, } from '../util/errors.js';
|
|
3
|
+
import isObject from '../lib/is_object.js';
|
|
4
|
+
function getKtyFromAlg(alg) {
|
|
5
|
+
switch (typeof alg === 'string' && alg.slice(0, 2)) {
|
|
6
|
+
case 'RS':
|
|
7
|
+
case 'PS':
|
|
8
|
+
return 'RSA';
|
|
9
|
+
case 'ES':
|
|
10
|
+
return 'EC';
|
|
11
|
+
case 'Ed':
|
|
12
|
+
return 'OKP';
|
|
13
|
+
default:
|
|
14
|
+
throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function isJWKSLike(jwks) {
|
|
18
|
+
return (jwks &&
|
|
19
|
+
typeof jwks === 'object' &&
|
|
20
|
+
Array.isArray(jwks.keys) &&
|
|
21
|
+
jwks.keys.every(isJWKLike));
|
|
22
|
+
}
|
|
23
|
+
function isJWKLike(key) {
|
|
24
|
+
return isObject(key);
|
|
25
|
+
}
|
|
26
|
+
function clone(obj) {
|
|
27
|
+
if (typeof structuredClone === 'function') {
|
|
28
|
+
return structuredClone(obj);
|
|
29
|
+
}
|
|
30
|
+
return JSON.parse(JSON.stringify(obj));
|
|
31
|
+
}
|
|
32
|
+
export class LocalJWKSet {
|
|
33
|
+
constructor(jwks) {
|
|
34
|
+
this._cached = new WeakMap();
|
|
35
|
+
if (!isJWKSLike(jwks)) {
|
|
36
|
+
throw new JWKSInvalid('JSON Web Key Set malformed');
|
|
37
|
+
}
|
|
38
|
+
this._jwks = clone(jwks);
|
|
39
|
+
}
|
|
40
|
+
async getKey(protectedHeader, token) {
|
|
41
|
+
const { alg, kid } = { ...protectedHeader, ...token.header };
|
|
42
|
+
const candidates = this._jwks.keys.filter((jwk) => {
|
|
43
|
+
let candidate = jwk.kty === getKtyFromAlg(alg);
|
|
44
|
+
if (candidate && typeof kid === 'string') {
|
|
45
|
+
candidate = kid === jwk.kid;
|
|
46
|
+
}
|
|
47
|
+
if (candidate && typeof jwk.alg === 'string') {
|
|
48
|
+
candidate = alg === jwk.alg;
|
|
49
|
+
}
|
|
50
|
+
if (candidate && typeof jwk.use === 'string') {
|
|
51
|
+
candidate = jwk.use === 'sig';
|
|
52
|
+
}
|
|
53
|
+
if (candidate && Array.isArray(jwk.key_ops)) {
|
|
54
|
+
candidate = jwk.key_ops.includes('verify');
|
|
55
|
+
}
|
|
56
|
+
if (candidate && alg === 'EdDSA') {
|
|
57
|
+
candidate = jwk.crv === 'Ed25519' || jwk.crv === 'Ed448';
|
|
58
|
+
}
|
|
59
|
+
if (candidate) {
|
|
60
|
+
switch (alg) {
|
|
61
|
+
case 'ES256':
|
|
62
|
+
candidate = jwk.crv === 'P-256';
|
|
63
|
+
break;
|
|
64
|
+
case 'ES256K':
|
|
65
|
+
candidate = jwk.crv === 'secp256k1';
|
|
66
|
+
break;
|
|
67
|
+
case 'ES384':
|
|
68
|
+
candidate = jwk.crv === 'P-384';
|
|
69
|
+
break;
|
|
70
|
+
case 'ES512':
|
|
71
|
+
candidate = jwk.crv === 'P-521';
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return candidate;
|
|
76
|
+
});
|
|
77
|
+
const { 0: jwk, length } = candidates;
|
|
78
|
+
if (length === 0) {
|
|
79
|
+
throw new JWKSNoMatchingKey();
|
|
80
|
+
}
|
|
81
|
+
else if (length !== 1) {
|
|
82
|
+
throw new JWKSMultipleMatchingKeys();
|
|
83
|
+
}
|
|
84
|
+
const cached = this._cached.get(jwk) || this._cached.set(jwk, {}).get(jwk);
|
|
85
|
+
if (cached[alg] === undefined) {
|
|
86
|
+
const keyObject = await importJWK({ ...jwk, ext: true }, alg);
|
|
87
|
+
if (keyObject instanceof Uint8Array || keyObject.type !== 'public') {
|
|
88
|
+
throw new JWKSInvalid('JSON Web Key Set members must be public keys');
|
|
89
|
+
}
|
|
90
|
+
cached[alg] = keyObject;
|
|
91
|
+
}
|
|
92
|
+
return cached[alg];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export function createLocalJWKSet(jwks) {
|
|
96
|
+
return LocalJWKSet.prototype.getKey.bind(new LocalJWKSet(jwks));
|
|
97
|
+
}
|
|
@@ -1,26 +1,11 @@
|
|
|
1
1
|
import fetchJwks from '../runtime/fetch_jwks.js';
|
|
2
|
-
import {
|
|
3
|
-
import { JWKSInvalid,
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
switch (typeof alg === 'string' && alg.slice(0, 2)) {
|
|
7
|
-
case 'RS':
|
|
8
|
-
case 'PS':
|
|
9
|
-
return 'RSA';
|
|
10
|
-
case 'ES':
|
|
11
|
-
return 'EC';
|
|
12
|
-
case 'Ed':
|
|
13
|
-
return 'OKP';
|
|
14
|
-
default:
|
|
15
|
-
throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set');
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
function isJWKLike(key) {
|
|
19
|
-
return isObject(key);
|
|
20
|
-
}
|
|
21
|
-
class RemoteJWKSet {
|
|
2
|
+
import { isCloudflareWorkers } from '../runtime/env.js';
|
|
3
|
+
import { JWKSInvalid, JWKSNoMatchingKey } from '../util/errors.js';
|
|
4
|
+
import { isJWKSLike, LocalJWKSet } from './local.js';
|
|
5
|
+
class RemoteJWKSet extends LocalJWKSet {
|
|
22
6
|
constructor(url, options) {
|
|
23
|
-
|
|
7
|
+
super({ keys: [] });
|
|
8
|
+
this._jwks = undefined;
|
|
24
9
|
if (!(url instanceof URL)) {
|
|
25
10
|
throw new TypeError('url must be an instance of URL');
|
|
26
11
|
}
|
|
@@ -38,78 +23,40 @@ class RemoteJWKSet {
|
|
|
38
23
|
return Date.now() < this._cooldownStarted + this._cooldownDuration;
|
|
39
24
|
}
|
|
40
25
|
async getKey(protectedHeader, token) {
|
|
41
|
-
const joseHeader = {
|
|
42
|
-
...protectedHeader,
|
|
43
|
-
...token.header,
|
|
44
|
-
};
|
|
45
26
|
if (!this._jwks) {
|
|
46
27
|
await this.reload();
|
|
47
28
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (candidate && typeof joseHeader.kid === 'string') {
|
|
51
|
-
candidate = joseHeader.kid === jwk.kid;
|
|
52
|
-
}
|
|
53
|
-
if (candidate && typeof jwk.alg === 'string') {
|
|
54
|
-
candidate = joseHeader.alg === jwk.alg;
|
|
55
|
-
}
|
|
56
|
-
if (candidate && typeof jwk.use === 'string') {
|
|
57
|
-
candidate = jwk.use === 'sig';
|
|
58
|
-
}
|
|
59
|
-
if (candidate && Array.isArray(jwk.key_ops)) {
|
|
60
|
-
candidate = jwk.key_ops.includes('verify');
|
|
61
|
-
}
|
|
62
|
-
if (candidate && joseHeader.alg === 'EdDSA') {
|
|
63
|
-
candidate = jwk.crv === 'Ed25519' || jwk.crv === 'Ed448';
|
|
64
|
-
}
|
|
65
|
-
if (candidate) {
|
|
66
|
-
switch (joseHeader.alg) {
|
|
67
|
-
case 'ES256':
|
|
68
|
-
candidate = jwk.crv === 'P-256';
|
|
69
|
-
break;
|
|
70
|
-
case 'ES256K':
|
|
71
|
-
candidate = jwk.crv === 'secp256k1';
|
|
72
|
-
break;
|
|
73
|
-
case 'ES384':
|
|
74
|
-
candidate = jwk.crv === 'P-384';
|
|
75
|
-
break;
|
|
76
|
-
case 'ES512':
|
|
77
|
-
candidate = jwk.crv === 'P-521';
|
|
78
|
-
break;
|
|
79
|
-
default:
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return candidate;
|
|
83
|
-
});
|
|
84
|
-
const { 0: jwk, length } = candidates;
|
|
85
|
-
if (length === 0) {
|
|
86
|
-
if (this.coolingDown() === false) {
|
|
87
|
-
await this.reload();
|
|
88
|
-
return this.getKey(protectedHeader, token);
|
|
89
|
-
}
|
|
90
|
-
throw new JWKSNoMatchingKey();
|
|
91
|
-
}
|
|
92
|
-
else if (length !== 1) {
|
|
93
|
-
throw new JWKSMultipleMatchingKeys();
|
|
29
|
+
try {
|
|
30
|
+
return await super.getKey(protectedHeader, token);
|
|
94
31
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
32
|
+
catch (err) {
|
|
33
|
+
if (err instanceof JWKSNoMatchingKey) {
|
|
34
|
+
if (this.coolingDown() === false) {
|
|
35
|
+
await this.reload();
|
|
36
|
+
return super.getKey(protectedHeader, token);
|
|
37
|
+
}
|
|
100
38
|
}
|
|
101
|
-
|
|
39
|
+
throw err;
|
|
102
40
|
}
|
|
103
|
-
return cached[joseHeader.alg];
|
|
104
41
|
}
|
|
105
42
|
async reload() {
|
|
43
|
+
if (this._pendingFetch && isCloudflareWorkers()) {
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
const isDone = () => {
|
|
46
|
+
if (this._pendingFetch === undefined) {
|
|
47
|
+
resolve();
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
setTimeout(isDone, 5);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
isDone();
|
|
54
|
+
});
|
|
55
|
+
}
|
|
106
56
|
if (!this._pendingFetch) {
|
|
107
57
|
this._pendingFetch = fetchJwks(this._url, this._timeoutDuration, this._options)
|
|
108
58
|
.then((json) => {
|
|
109
|
-
if (
|
|
110
|
-
!json ||
|
|
111
|
-
!Array.isArray(json.keys) ||
|
|
112
|
-
!json.keys.every(isJWKLike)) {
|
|
59
|
+
if (!isJWKSLike(json)) {
|
|
113
60
|
throw new JWKSInvalid('JSON Web Key Set malformed');
|
|
114
61
|
}
|
|
115
62
|
this._jwks = { keys: json.keys };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import digest from '../runtime/digest.js';
|
|
1
2
|
export const encoder = new TextEncoder();
|
|
2
3
|
export const decoder = new TextDecoder();
|
|
3
4
|
const MAX_INT32 = 2 ** 32;
|
|
@@ -36,7 +37,7 @@ export function uint32be(value) {
|
|
|
36
37
|
export function lengthAndInput(input) {
|
|
37
38
|
return concat(uint32be(input.length), input);
|
|
38
39
|
}
|
|
39
|
-
export async function concatKdf(
|
|
40
|
+
export async function concatKdf(secret, bits, value) {
|
|
40
41
|
const iterations = Math.ceil((bits >> 3) / 32);
|
|
41
42
|
let res;
|
|
42
43
|
for (let iter = 1; iter <= iterations; iter++) {
|
|
@@ -30,7 +30,9 @@ async function encryptKeyManagement(alg, enc, key, providedCek, providedParamete
|
|
|
30
30
|
ephemeralKey || (ephemeralKey = (await ECDH.generateEpk(key)).privateKey);
|
|
31
31
|
const { x, y, crv, kty } = await exportJWK(ephemeralKey);
|
|
32
32
|
const sharedSecret = await ECDH.deriveKey(key, ephemeralKey, alg === 'ECDH-ES' ? enc : alg, alg === 'ECDH-ES' ? cekLength(enc) : parseInt(alg.slice(-5, -2), 10), apu, apv);
|
|
33
|
-
parameters = { epk: { x,
|
|
33
|
+
parameters = { epk: { x, crv, kty } };
|
|
34
|
+
if (kty === 'EC')
|
|
35
|
+
parameters.epk.y = y;
|
|
34
36
|
if (apu)
|
|
35
37
|
parameters.apu = base64url(apu);
|
|
36
38
|
if (apv)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Buffer } from 'buffer';
|
|
2
2
|
import { decoder } from '../lib/buffer_utils.js';
|
|
3
|
-
let
|
|
3
|
+
let encode;
|
|
4
4
|
function normalize(input) {
|
|
5
5
|
let encoded = input;
|
|
6
6
|
if (encoded instanceof Uint8Array) {
|
|
@@ -9,12 +9,12 @@ function normalize(input) {
|
|
|
9
9
|
return encoded;
|
|
10
10
|
}
|
|
11
11
|
if (Buffer.isEncoding('base64url')) {
|
|
12
|
-
|
|
12
|
+
encode = (input) => Buffer.from(input).toString('base64url');
|
|
13
13
|
}
|
|
14
14
|
else {
|
|
15
|
-
|
|
15
|
+
encode = (input) => Buffer.from(input).toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
|
|
16
16
|
}
|
|
17
17
|
export const decodeBase64 = (input) => Buffer.from(input, 'base64');
|
|
18
18
|
export const encodeBase64 = (input) => Buffer.from(input).toString('base64');
|
|
19
|
-
export
|
|
19
|
+
export { encode };
|
|
20
20
|
export const decode = (input) => Buffer.from(normalize(input), 'base64');
|
|
@@ -2,7 +2,6 @@ import { diffieHellman, generateKeyPair as generateKeyPairCb, KeyObject } from '
|
|
|
2
2
|
import { promisify } from 'util';
|
|
3
3
|
import getNamedCurve from './get_named_curve.js';
|
|
4
4
|
import { encoder, concat, uint32be, lengthAndInput, concatKdf } from '../lib/buffer_utils.js';
|
|
5
|
-
import digest from './digest.js';
|
|
6
5
|
import { JOSENotSupported } from '../util/errors.js';
|
|
7
6
|
import { isCryptoKey } from './webcrypto.js';
|
|
8
7
|
import { checkEncCryptoKey } from '../lib/crypto_key.js';
|
|
@@ -35,7 +34,7 @@ export async function deriveKey(publicKee, privateKee, algorithm, keyLength, apu
|
|
|
35
34
|
}
|
|
36
35
|
const value = concat(lengthAndInput(encoder.encode(algorithm)), lengthAndInput(apu), lengthAndInput(apv), uint32be(keyLength));
|
|
37
36
|
const sharedSecret = diffieHellman({ privateKey, publicKey });
|
|
38
|
-
return concatKdf(
|
|
37
|
+
return concatKdf(sharedSecret, keyLength, value);
|
|
39
38
|
}
|
|
40
39
|
export async function generateEpk(kee) {
|
|
41
40
|
let key;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { decode as base64url } from './base64url.js';
|
|
2
|
+
import { decoder } from '../lib/buffer_utils.js';
|
|
3
|
+
import isObject from '../lib/is_object.js';
|
|
4
|
+
import { JWTInvalid } from './errors.js';
|
|
5
|
+
export function decodeJwt(jwt) {
|
|
6
|
+
if (typeof jwt !== 'string')
|
|
7
|
+
throw new JWTInvalid('JWTs must use Compact JWS serialization, JWT must be a string');
|
|
8
|
+
const { 1: payload, length } = jwt.split('.');
|
|
9
|
+
if (length === 5)
|
|
10
|
+
throw new JWTInvalid('Only JWTs using Compact JWS serialization can be decoded');
|
|
11
|
+
if (length !== 3)
|
|
12
|
+
throw new JWTInvalid('Invalid JWT');
|
|
13
|
+
if (!payload)
|
|
14
|
+
throw new JWTInvalid('JWTs must contain a payload');
|
|
15
|
+
let decoded;
|
|
16
|
+
try {
|
|
17
|
+
decoded = base64url(payload);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
throw new JWTInvalid('Failed to parse the base64url encoded payload');
|
|
21
|
+
}
|
|
22
|
+
let result;
|
|
23
|
+
try {
|
|
24
|
+
result = JSON.parse(decoder.decode(decoded));
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
throw new JWTInvalid('Failed to parse the decoded payload as JSON');
|
|
28
|
+
}
|
|
29
|
+
if (!isObject(result))
|
|
30
|
+
throw new JWTInvalid('Invalid JWT Claims Set');
|
|
31
|
+
return result;
|
|
32
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export { SignJWT } from './jwt/sign';
|
|
|
27
27
|
export { EncryptJWT } from './jwt/encrypt';
|
|
28
28
|
export { calculateJwkThumbprint } from './jwk/thumbprint';
|
|
29
29
|
export { EmbeddedJWK } from './jwk/embedded';
|
|
30
|
+
export { createLocalJWKSet } from './jwks/local';
|
|
30
31
|
export { createRemoteJWKSet } from './jwks/remote';
|
|
31
32
|
export type { RemoteJWKSetOptions } from './jwks/remote';
|
|
32
33
|
export { UnsecuredJWT } from './jwt/unsecured';
|
|
@@ -35,6 +36,7 @@ export { exportPKCS8, exportSPKI, exportJWK } from './key/export';
|
|
|
35
36
|
export { importSPKI, importPKCS8, importX509, importJWK } from './key/import';
|
|
36
37
|
export type { PEMImportOptions } from './key/import';
|
|
37
38
|
export { decodeProtectedHeader } from './util/decode_protected_header';
|
|
39
|
+
export { decodeJwt } from './util/decode_jwt';
|
|
38
40
|
export type { ProtectedHeaderParameters } from './util/decode_protected_header';
|
|
39
41
|
export * as errors from './util/errors';
|
|
40
42
|
export { generateKeyPair } from './key/generate_key_pair';
|
|
@@ -42,4 +44,4 @@ export type { GenerateKeyPairResult, GenerateKeyPairOptions } from './key/genera
|
|
|
42
44
|
export { generateSecret } from './key/generate_secret';
|
|
43
45
|
export type { GenerateSecretOptions } from './key/generate_secret';
|
|
44
46
|
export * as base64url from './util/base64url';
|
|
45
|
-
export type { KeyLike, JWK, FlattenedJWSInput, GeneralJWSInput, FlattenedJWS, GeneralJWS, JoseHeaderParameters, JWSHeaderParameters, JWEKeyManagementHeaderParameters, FlattenedJWE, GeneralJWE, JWEHeaderParameters, CritOption, DeflateOption, DecryptOptions, EncryptOptions, JWTClaimVerificationOptions, VerifyOptions, SignOptions, JWTPayload, DeflateFunction, InflateFunction, FlattenedDecryptResult, GeneralDecryptResult, CompactDecryptResult, FlattenedVerifyResult, GeneralVerifyResult, CompactVerifyResult, JWTVerifyResult, JWTDecryptResult, ResolvedKey, CompactJWEHeaderParameters, CompactJWSHeaderParameters, JWTHeaderParameters, } from './types';
|
|
47
|
+
export type { KeyLike, JWK, FlattenedJWSInput, GeneralJWSInput, FlattenedJWS, GeneralJWS, JoseHeaderParameters, JWSHeaderParameters, JWEKeyManagementHeaderParameters, FlattenedJWE, GeneralJWE, JWEHeaderParameters, CritOption, DeflateOption, DecryptOptions, EncryptOptions, JWTClaimVerificationOptions, VerifyOptions, SignOptions, JWTPayload, DeflateFunction, InflateFunction, FlattenedDecryptResult, GeneralDecryptResult, CompactDecryptResult, FlattenedVerifyResult, GeneralVerifyResult, CompactVerifyResult, JWTVerifyResult, JWTDecryptResult, ResolvedKey, CompactJWEHeaderParameters, CompactJWSHeaderParameters, JWTHeaderParameters, JSONWebKeySet, } from './types';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { KeyLike, JWSHeaderParameters, JSONWebKeySet, FlattenedJWSInput, GetKeyFunction } from '../types';
|
|
2
|
+
export declare function isJWKSLike(jwks: unknown): jwks is JSONWebKeySet;
|
|
3
|
+
export declare class LocalJWKSet {
|
|
4
|
+
protected _jwks?: JSONWebKeySet;
|
|
5
|
+
private _cached;
|
|
6
|
+
constructor(jwks: unknown);
|
|
7
|
+
getKey(protectedHeader: JWSHeaderParameters, token: FlattenedJWSInput): Promise<KeyLike>;
|
|
8
|
+
}
|
|
9
|
+
export declare function createLocalJWKSet(jwks: JSONWebKeySet): GetKeyFunction<JWSHeaderParameters, FlattenedJWSInput>;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -118,7 +118,7 @@ export interface JWK {
|
|
|
118
118
|
[propName: string]: unknown
|
|
119
119
|
}
|
|
120
120
|
export interface GetKeyFunction<T, T2> {
|
|
121
|
-
(protectedHeader: T, token: T2): Promise<KeyLike | Uint8Array>
|
|
121
|
+
(protectedHeader: T, token: T2): Promise<KeyLike | Uint8Array> | KeyLike | Uint8Array
|
|
122
122
|
}
|
|
123
123
|
export interface FlattenedJWSInput {
|
|
124
124
|
header?: JWSHeaderParameters
|
|
@@ -268,3 +268,6 @@ export interface CompactJWEHeaderParameters extends JWEHeaderParameters {
|
|
|
268
268
|
alg: string
|
|
269
269
|
enc: string
|
|
270
270
|
}
|
|
271
|
+
export interface JSONWebKeySet {
|
|
272
|
+
keys: JWK[]
|
|
273
|
+
}
|