jose 4.11.4 → 4.12.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/README.md CHANGED
@@ -38,7 +38,7 @@ const jose = require('jose')
38
38
 
39
39
  **`example`** Deno import
40
40
  ```js
41
- import * as jose from 'https://deno.land/x/jose@v4.11.4/index.ts'
41
+ import * as jose from 'https://deno.land/x/jose@v4.12.0/index.ts'
42
42
  ```
43
43
 
44
44
  - JSON Web Tokens (JWT)
@@ -38,7 +38,7 @@ export class LocalJWKSet {
38
38
  this._jwks = clone(jwks);
39
39
  }
40
40
  async getKey(protectedHeader, token) {
41
- const { alg, kid } = { ...protectedHeader, ...token.header };
41
+ const { alg, kid } = { ...protectedHeader, ...token === null || token === void 0 ? void 0 : token.header };
42
42
  const kty = getKtyFromAlg(alg);
43
43
  const candidates = this._jwks.keys.filter((jwk) => {
44
44
  let candidate = kty === jwk.kty;
@@ -80,18 +80,33 @@ export class LocalJWKSet {
80
80
  throw new JWKSNoMatchingKey();
81
81
  }
82
82
  else if (length !== 1) {
83
- throw new JWKSMultipleMatchingKeys();
83
+ const error = new JWKSMultipleMatchingKeys();
84
+ const { _cached } = this;
85
+ error[Symbol.asyncIterator] = async function* () {
86
+ for (const jwk of candidates) {
87
+ try {
88
+ yield await importWithAlgCache(_cached, jwk, alg);
89
+ }
90
+ catch (_a) {
91
+ continue;
92
+ }
93
+ }
94
+ };
95
+ throw error;
84
96
  }
85
- const cached = this._cached.get(jwk) || this._cached.set(jwk, {}).get(jwk);
86
- if (cached[alg] === undefined) {
87
- const keyObject = await importJWK({ ...jwk, ext: true }, alg);
88
- if (keyObject instanceof Uint8Array || keyObject.type !== 'public') {
89
- throw new JWKSInvalid('JSON Web Key Set members must be public keys');
90
- }
91
- cached[alg] = keyObject;
97
+ return importWithAlgCache(this._cached, jwk, alg);
98
+ }
99
+ }
100
+ async function importWithAlgCache(cache, jwk, alg) {
101
+ const cached = cache.get(jwk) || cache.set(jwk, {}).get(jwk);
102
+ if (cached[alg] === undefined) {
103
+ const keyObject = await importJWK({ ...jwk, ext: true }, alg);
104
+ if (keyObject.type !== 'public') {
105
+ throw new JWKSInvalid('JSON Web Key Set members must be public keys');
92
106
  }
93
- return cached[alg];
107
+ cached[alg] = keyObject;
94
108
  }
109
+ return cached[alg];
95
110
  }
96
111
  export function createLocalJWKSet(jwks) {
97
112
  return LocalJWKSet.prototype.getKey.bind(new LocalJWKSet(jwks));
@@ -125,6 +125,7 @@ export class JWKSMultipleMatchingKeys extends JOSEError {
125
125
  return 'ERR_JWKS_MULTIPLE_MATCHING_KEYS';
126
126
  }
127
127
  }
128
+ Symbol.asyncIterator;
128
129
  export class JWKSTimeout extends JOSEError {
129
130
  constructor() {
130
131
  super(...arguments);
@@ -42,7 +42,7 @@ class LocalJWKSet {
42
42
  this._jwks = clone(jwks);
43
43
  }
44
44
  async getKey(protectedHeader, token) {
45
- const { alg, kid } = { ...protectedHeader, ...token.header };
45
+ const { alg, kid } = { ...protectedHeader, ...token === null || token === void 0 ? void 0 : token.header };
46
46
  const kty = getKtyFromAlg(alg);
47
47
  const candidates = this._jwks.keys.filter((jwk) => {
48
48
  let candidate = kty === jwk.kty;
@@ -84,20 +84,35 @@ class LocalJWKSet {
84
84
  throw new errors_js_1.JWKSNoMatchingKey();
85
85
  }
86
86
  else if (length !== 1) {
87
- throw new errors_js_1.JWKSMultipleMatchingKeys();
88
- }
89
- const cached = this._cached.get(jwk) || this._cached.set(jwk, {}).get(jwk);
90
- if (cached[alg] === undefined) {
91
- const keyObject = await (0, import_js_1.importJWK)({ ...jwk, ext: true }, alg);
92
- if (keyObject instanceof Uint8Array || keyObject.type !== 'public') {
93
- throw new errors_js_1.JWKSInvalid('JSON Web Key Set members must be public keys');
94
- }
95
- cached[alg] = keyObject;
87
+ const error = new errors_js_1.JWKSMultipleMatchingKeys();
88
+ const { _cached } = this;
89
+ error[Symbol.asyncIterator] = async function* () {
90
+ for (const jwk of candidates) {
91
+ try {
92
+ yield await importWithAlgCache(_cached, jwk, alg);
93
+ }
94
+ catch {
95
+ continue;
96
+ }
97
+ }
98
+ };
99
+ throw error;
96
100
  }
97
- return cached[alg];
101
+ return importWithAlgCache(this._cached, jwk, alg);
98
102
  }
99
103
  }
100
104
  exports.LocalJWKSet = LocalJWKSet;
105
+ async function importWithAlgCache(cache, jwk, alg) {
106
+ const cached = cache.get(jwk) || cache.set(jwk, {}).get(jwk);
107
+ if (cached[alg] === undefined) {
108
+ const keyObject = await (0, import_js_1.importJWK)({ ...jwk, ext: true }, alg);
109
+ if (keyObject.type !== 'public') {
110
+ throw new errors_js_1.JWKSInvalid('JSON Web Key Set members must be public keys');
111
+ }
112
+ cached[alg] = keyObject;
113
+ }
114
+ return cached[alg];
115
+ }
101
116
  function createLocalJWKSet(jwks) {
102
117
  return LocalJWKSet.prototype.getKey.bind(new LocalJWKSet(jwks));
103
118
  }
@@ -141,6 +141,7 @@ class JWKSMultipleMatchingKeys extends JOSEError {
141
141
  }
142
142
  }
143
143
  exports.JWKSMultipleMatchingKeys = JWKSMultipleMatchingKeys;
144
+ Symbol.asyncIterator;
144
145
  class JWKSTimeout extends JOSEError {
145
146
  constructor() {
146
147
  super(...arguments);
@@ -38,7 +38,7 @@ export class LocalJWKSet {
38
38
  this._jwks = clone(jwks);
39
39
  }
40
40
  async getKey(protectedHeader, token) {
41
- const { alg, kid } = { ...protectedHeader, ...token.header };
41
+ const { alg, kid } = { ...protectedHeader, ...token === null || token === void 0 ? void 0 : token.header };
42
42
  const kty = getKtyFromAlg(alg);
43
43
  const candidates = this._jwks.keys.filter((jwk) => {
44
44
  let candidate = kty === jwk.kty;
@@ -80,18 +80,33 @@ export class LocalJWKSet {
80
80
  throw new JWKSNoMatchingKey();
81
81
  }
82
82
  else if (length !== 1) {
83
- throw new JWKSMultipleMatchingKeys();
83
+ const error = new JWKSMultipleMatchingKeys();
84
+ const { _cached } = this;
85
+ error[Symbol.asyncIterator] = async function* () {
86
+ for (const jwk of candidates) {
87
+ try {
88
+ yield await importWithAlgCache(_cached, jwk, alg);
89
+ }
90
+ catch {
91
+ continue;
92
+ }
93
+ }
94
+ };
95
+ throw error;
84
96
  }
85
- const cached = this._cached.get(jwk) || this._cached.set(jwk, {}).get(jwk);
86
- if (cached[alg] === undefined) {
87
- const keyObject = await importJWK({ ...jwk, ext: true }, alg);
88
- if (keyObject instanceof Uint8Array || keyObject.type !== 'public') {
89
- throw new JWKSInvalid('JSON Web Key Set members must be public keys');
90
- }
91
- cached[alg] = keyObject;
97
+ return importWithAlgCache(this._cached, jwk, alg);
98
+ }
99
+ }
100
+ async function importWithAlgCache(cache, jwk, alg) {
101
+ const cached = cache.get(jwk) || cache.set(jwk, {}).get(jwk);
102
+ if (cached[alg] === undefined) {
103
+ const keyObject = await importJWK({ ...jwk, ext: true }, alg);
104
+ if (keyObject.type !== 'public') {
105
+ throw new JWKSInvalid('JSON Web Key Set members must be public keys');
92
106
  }
93
- return cached[alg];
107
+ cached[alg] = keyObject;
94
108
  }
109
+ return cached[alg];
95
110
  }
96
111
  export function createLocalJWKSet(jwks) {
97
112
  return LocalJWKSet.prototype.getKey.bind(new LocalJWKSet(jwks));
@@ -125,6 +125,7 @@ export class JWKSMultipleMatchingKeys extends JOSEError {
125
125
  return 'ERR_JWKS_MULTIPLE_MATCHING_KEYS';
126
126
  }
127
127
  }
128
+ Symbol.asyncIterator;
128
129
  export class JWKSTimeout extends JOSEError {
129
130
  constructor() {
130
131
  super(...arguments);
@@ -1,4 +1,4 @@
1
- import type { KeyLike, JWSHeaderParameters, JSONWebKeySet, FlattenedJWSInput, GetKeyFunction } from '../types';
1
+ import type { KeyLike, JWSHeaderParameters, JSONWebKeySet, FlattenedJWSInput } from '../types';
2
2
  /** @private */
3
3
  export declare function isJWKSLike(jwks: unknown): jwks is JSONWebKeySet;
4
4
  /** @private */
@@ -6,13 +6,20 @@ export declare class LocalJWKSet {
6
6
  protected _jwks?: JSONWebKeySet;
7
7
  private _cached;
8
8
  constructor(jwks: unknown);
9
- getKey(protectedHeader: JWSHeaderParameters, token: FlattenedJWSInput): Promise<KeyLike>;
9
+ getKey(protectedHeader?: JWSHeaderParameters, token?: FlattenedJWSInput): Promise<KeyLike>;
10
10
  }
11
11
  /**
12
12
  * Returns a function that resolves to a key object from a locally stored, or otherwise available,
13
13
  * JSON Web Key Set.
14
14
  *
15
- * Only a single public key must match the selection process.
15
+ * It uses the "alg" (JWS Algorithm) Header Parameter to determine the right JWK "kty" (Key Type),
16
+ * then proceeds to match the JWK "kid" (Key ID) with one found in the JWS Header Parameters (if
17
+ * there is one) while also respecting the JWK "use" (Public Key Use) and JWK "key_ops" (Key
18
+ * Operations) Parameters (if they are present on the JWK).
19
+ *
20
+ * Only a single public key must match the selection process. As shown in the example below when
21
+ * multiple keys get matched it is possible to opt-in to iterate over the matched keys and attempt
22
+ * verification in an iterative manner.
16
23
  *
17
24
  * @example Usage
18
25
  *
@@ -43,6 +50,36 @@ export declare class LocalJWKSet {
43
50
  * console.log(payload)
44
51
  * ```
45
52
  *
53
+ * @example Opting-in to multiple JWKS matches using `createLocalJWKSet`
54
+ *
55
+ * ```js
56
+ * const options = {
57
+ * issuer: 'urn:example:issuer',
58
+ * audience: 'urn:example:audience',
59
+ * }
60
+ * const { payload, protectedHeader } = await jose
61
+ * .jwtVerify(jwt, JWKS, options)
62
+ * .catch(async (error) => {
63
+ * if (error?.code === 'ERR_JWKS_MULTIPLE_MATCHING_KEYS') {
64
+ * for await (const publicKey of error) {
65
+ * try {
66
+ * return await jose.jwtVerify(jwt, publicKey, options)
67
+ * } catch (innerError) {
68
+ * if (innerError?.code === 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') {
69
+ * continue
70
+ * }
71
+ * throw innerError
72
+ * }
73
+ * }
74
+ * throw new jose.errors.JWSSignatureVerificationFailed()
75
+ * }
76
+ *
77
+ * throw error
78
+ * })
79
+ * console.log(protectedHeader)
80
+ * console.log(payload)
81
+ * ```
82
+ *
46
83
  * @param jwks JSON Web Key Set formatted object.
47
84
  */
48
- export declare function createLocalJWKSet(jwks: JSONWebKeySet): GetKeyFunction<JWSHeaderParameters, FlattenedJWSInput>;
85
+ export declare function createLocalJWKSet(jwks: JSONWebKeySet): (protectedHeader?: JWSHeaderParameters | undefined, token?: FlattenedJWSInput | undefined) => Promise<KeyLike>;
@@ -1,4 +1,4 @@
1
- import type { JWSHeaderParameters, FlattenedJWSInput, GetKeyFunction } from '../types';
1
+ import type { KeyLike, JWSHeaderParameters, FlattenedJWSInput } from '../types';
2
2
  /** Options for the remote JSON Web Key Set. */
3
3
  export interface RemoteJWKSetOptions {
4
4
  /**
@@ -30,10 +30,18 @@ export interface RemoteJWKSetOptions {
30
30
  }
31
31
  /**
32
32
  * Returns a function that resolves to a key object downloaded from a remote endpoint returning a
33
- * JSON Web Key Set, that is, for example, an OAuth 2.0 or OIDC jwks_uri. Only a single public key
34
- * must match the selection process. The JSON Web Key Set is fetched when no key matches the
35
- * selection process but only as frequently as the `cooldownDuration` option allows, to prevent
36
- * abuse.
33
+ * JSON Web Key Set, that is, for example, an OAuth 2.0 or OIDC jwks_uri. The JSON Web Key Set is
34
+ * fetched when no key matches the selection process but only as frequently as the
35
+ * `cooldownDuration` option allows to prevent abuse.
36
+ *
37
+ * It uses the "alg" (JWS Algorithm) Header Parameter to determine the right JWK "kty" (Key Type),
38
+ * then proceeds to match the JWK "kid" (Key ID) with one found in the JWS Header Parameters (if
39
+ * there is one) while also respecting the JWK "use" (Public Key Use) and JWK "key_ops" (Key
40
+ * Operations) Parameters (if they are present on the JWK).
41
+ *
42
+ * Only a single public key must match the selection process. As shown in the example below when
43
+ * multiple keys get matched it is possible to opt-in to iterate over the matched keys and attempt
44
+ * verification in an iterative manner.
37
45
  *
38
46
  * @example Usage
39
47
  *
@@ -48,7 +56,37 @@ export interface RemoteJWKSetOptions {
48
56
  * console.log(payload)
49
57
  * ```
50
58
  *
59
+ * @example Opting-in to multiple JWKS matches using `createRemoteJWKSet`
60
+ *
61
+ * ```js
62
+ * const options = {
63
+ * issuer: 'urn:example:issuer',
64
+ * audience: 'urn:example:audience',
65
+ * }
66
+ * const { payload, protectedHeader } = await jose
67
+ * .jwtVerify(jwt, JWKS, options)
68
+ * .catch(async (error) => {
69
+ * if (error?.code === 'ERR_JWKS_MULTIPLE_MATCHING_KEYS') {
70
+ * for await (const publicKey of error) {
71
+ * try {
72
+ * return await jose.jwtVerify(jwt, publicKey, options)
73
+ * } catch (innerError) {
74
+ * if (innerError?.code === 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') {
75
+ * continue
76
+ * }
77
+ * throw innerError
78
+ * }
79
+ * }
80
+ * throw new jose.errors.JWSSignatureVerificationFailed()
81
+ * }
82
+ *
83
+ * throw error
84
+ * })
85
+ * console.log(protectedHeader)
86
+ * console.log(payload)
87
+ * ```
88
+ *
51
89
  * @param url URL to fetch the JSON Web Key Set from.
52
90
  * @param options Options for the remote JSON Web Key Set.
53
91
  */
54
- export declare function createRemoteJWKSet(url: URL, options?: RemoteJWKSetOptions): GetKeyFunction<JWSHeaderParameters, FlattenedJWSInput>;
92
+ export declare function createRemoteJWKSet(url: URL, options?: RemoteJWKSetOptions): (protectedHeader?: JWSHeaderParameters | undefined, token?: FlattenedJWSInput | undefined) => Promise<KeyLike>;
@@ -53,7 +53,7 @@ import { ProduceJWT } from './produce';
53
53
  * 59lLWBCsocvvbd1cvDMNlRywAAyhsCb1SuX4nEAK9mrSBdfmoF2Nm3eilfsOds0f
54
54
  * K5mX069IKG82CMqh3Mzptd7e7lyb9lsoGO0BAtjho3cWtha/UZ70vfaMzGuZ6JmQ
55
55
  * ak6k+8+UFd93M4z0Qo74OhXB
56
- * -----END PRIVATE KEY----`
56
+ * -----END PRIVATE KEY-----`
57
57
  * const privateKey = await jose.importPKCS8(pkcs8, alg)
58
58
  *
59
59
  * const jwt = await new jose.SignJWT({ 'urn:example:claim': true })
@@ -1,3 +1,4 @@
1
+ import type { KeyLike } from '../types';
1
2
  /** A generic Error subclass that all other specific JOSE Error subclasses inherit from. */
2
3
  export declare class JOSEError extends Error {
3
4
  /** A unique error code for the particular error subclass. */
@@ -78,6 +79,8 @@ export declare class JWKSNoMatchingKey extends JOSEError {
78
79
  }
79
80
  /** An error subclass thrown when multiple keys match from a JWKS. */
80
81
  export declare class JWKSMultipleMatchingKeys extends JOSEError {
82
+ /** @ignore */
83
+ [Symbol.asyncIterator]: () => AsyncIterableIterator<KeyLike>;
81
84
  static get code(): 'ERR_JWKS_MULTIPLE_MATCHING_KEYS';
82
85
  code: string;
83
86
  message: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jose",
3
- "version": "4.11.4",
3
+ "version": "4.12.0",
4
4
  "description": "'JSON Web Almost Everything' - JWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes",
5
5
  "keywords": [
6
6
  "browser",