jose 4.11.4 → 4.12.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 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.1/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));
@@ -1,102 +1,25 @@
1
- import { decode as decodeBase64URL, encodeBase64, decodeBase64 } from '../runtime/base64url.js';
2
- import { fromSPKI as importPublic } from '../runtime/asn1.js';
3
- import { fromPKCS8 as importPrivate } from '../runtime/asn1.js';
1
+ import { decode as decodeBase64URL } from '../runtime/base64url.js';
2
+ import { fromSPKI, fromPKCS8, fromX509 } from '../runtime/asn1.js';
4
3
  import asKeyObject from '../runtime/jwk_to_key.js';
5
4
  import { JOSENotSupported } from '../util/errors.js';
6
- import formatPEM from '../lib/format_pem.js';
7
5
  import isObject from '../lib/is_object.js';
8
- function getElement(seq) {
9
- let result = [];
10
- let next = 0;
11
- while (next < seq.length) {
12
- let nextPart = parseElement(seq.subarray(next));
13
- result.push(nextPart);
14
- next += nextPart.byteLength;
15
- }
16
- return result;
17
- }
18
- function parseElement(bytes) {
19
- let position = 0;
20
- let tag = bytes[0] & 0x1f;
21
- position++;
22
- if (tag === 0x1f) {
23
- tag = 0;
24
- while (bytes[position] >= 0x80) {
25
- tag = tag * 128 + bytes[position] - 0x80;
26
- position++;
27
- }
28
- tag = tag * 128 + bytes[position] - 0x80;
29
- position++;
30
- }
31
- let length = 0;
32
- if (bytes[position] < 0x80) {
33
- length = bytes[position];
34
- position++;
35
- }
36
- else if (length === 0x80) {
37
- length = 0;
38
- while (bytes[position + length] !== 0 || bytes[position + length + 1] !== 0) {
39
- if (length > bytes.byteLength) {
40
- throw new TypeError('invalid indefinite form length');
41
- }
42
- length++;
43
- }
44
- const byteLength = position + length + 2;
45
- return {
46
- byteLength,
47
- contents: bytes.subarray(position, position + length),
48
- raw: bytes.subarray(0, byteLength),
49
- };
50
- }
51
- else {
52
- let numberOfDigits = bytes[position] & 0x7f;
53
- position++;
54
- length = 0;
55
- for (let i = 0; i < numberOfDigits; i++) {
56
- length = length * 256 + bytes[position];
57
- position++;
58
- }
59
- }
60
- const byteLength = position + length;
61
- return {
62
- byteLength,
63
- contents: bytes.subarray(position, byteLength),
64
- raw: bytes.subarray(0, byteLength),
65
- };
66
- }
67
- function spkiFromX509(buf) {
68
- const tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents);
69
- return encodeBase64(tbsCertificate[tbsCertificate[0].raw[0] === 0xa0 ? 6 : 5].raw);
70
- }
71
- function getSPKI(x509) {
72
- const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, '');
73
- const raw = decodeBase64(pem);
74
- return formatPEM(spkiFromX509(raw), 'PUBLIC KEY');
75
- }
76
6
  export async function importSPKI(spki, alg, options) {
77
7
  if (typeof spki !== 'string' || spki.indexOf('-----BEGIN PUBLIC KEY-----') !== 0) {
78
8
  throw new TypeError('"spki" must be SPKI formatted string');
79
9
  }
80
- return importPublic(spki, alg, options);
10
+ return fromSPKI(spki, alg, options);
81
11
  }
82
12
  export async function importX509(x509, alg, options) {
83
13
  if (typeof x509 !== 'string' || x509.indexOf('-----BEGIN CERTIFICATE-----') !== 0) {
84
14
  throw new TypeError('"x509" must be X.509 formatted string');
85
15
  }
86
- let spki;
87
- try {
88
- spki = getSPKI(x509);
89
- }
90
- catch (cause) {
91
- throw new TypeError('failed to parse the X.509 certificate', { cause });
92
- }
93
- return importPublic(spki, alg, options);
16
+ return fromX509(x509, alg, options);
94
17
  }
95
18
  export async function importPKCS8(pkcs8, alg, options) {
96
19
  if (typeof pkcs8 !== 'string' || pkcs8.indexOf('-----BEGIN PRIVATE KEY-----') !== 0) {
97
20
  throw new TypeError('"pkcs8" must be PKCS#8 formatted string');
98
21
  }
99
- return importPrivate(pkcs8, alg, options);
22
+ return fromPKCS8(pkcs8, alg, options);
100
23
  }
101
24
  export async function importJWK(jwk, alg, octAsKeyObject) {
102
25
  var _a;
@@ -104,9 +27,6 @@ export async function importJWK(jwk, alg, octAsKeyObject) {
104
27
  throw new TypeError('JWK must be an object');
105
28
  }
106
29
  alg || (alg = jwk.alg);
107
- if (typeof alg !== 'string' || !alg) {
108
- throw new TypeError('"alg" argument is required when "jwk.alg" is not present');
109
- }
110
30
  switch (jwk.kty) {
111
31
  case 'oct':
112
32
  if (typeof jwk.k !== 'string' || !jwk.k) {
@@ -1,7 +1,7 @@
1
1
  import { isCloudflareWorkers } from './env.js';
2
2
  import crypto, { isCryptoKey } from './webcrypto.js';
3
3
  import invalidKeyInput from '../lib/invalid_key_input.js';
4
- import { encodeBase64 } from './base64url.js';
4
+ import { encodeBase64, decodeBase64 } from './base64url.js';
5
5
  import formatPEM from '../lib/format_pem.js';
6
6
  import { JOSENotSupported } from '../util/errors.js';
7
7
  import { types } from './is_key_like.js';
@@ -134,3 +134,81 @@ export const fromPKCS8 = (pem, alg, options) => {
134
134
  export const fromSPKI = (pem, alg, options) => {
135
135
  return genericImport(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g, 'spki', pem, alg, options);
136
136
  };
137
+ function getElement(seq) {
138
+ let result = [];
139
+ let next = 0;
140
+ while (next < seq.length) {
141
+ let nextPart = parseElement(seq.subarray(next));
142
+ result.push(nextPart);
143
+ next += nextPart.byteLength;
144
+ }
145
+ return result;
146
+ }
147
+ function parseElement(bytes) {
148
+ let position = 0;
149
+ let tag = bytes[0] & 0x1f;
150
+ position++;
151
+ if (tag === 0x1f) {
152
+ tag = 0;
153
+ while (bytes[position] >= 0x80) {
154
+ tag = tag * 128 + bytes[position] - 0x80;
155
+ position++;
156
+ }
157
+ tag = tag * 128 + bytes[position] - 0x80;
158
+ position++;
159
+ }
160
+ let length = 0;
161
+ if (bytes[position] < 0x80) {
162
+ length = bytes[position];
163
+ position++;
164
+ }
165
+ else if (length === 0x80) {
166
+ length = 0;
167
+ while (bytes[position + length] !== 0 || bytes[position + length + 1] !== 0) {
168
+ if (length > bytes.byteLength) {
169
+ throw new TypeError('invalid indefinite form length');
170
+ }
171
+ length++;
172
+ }
173
+ const byteLength = position + length + 2;
174
+ return {
175
+ byteLength,
176
+ contents: bytes.subarray(position, position + length),
177
+ raw: bytes.subarray(0, byteLength),
178
+ };
179
+ }
180
+ else {
181
+ let numberOfDigits = bytes[position] & 0x7f;
182
+ position++;
183
+ length = 0;
184
+ for (let i = 0; i < numberOfDigits; i++) {
185
+ length = length * 256 + bytes[position];
186
+ position++;
187
+ }
188
+ }
189
+ const byteLength = position + length;
190
+ return {
191
+ byteLength,
192
+ contents: bytes.subarray(position, byteLength),
193
+ raw: bytes.subarray(0, byteLength),
194
+ };
195
+ }
196
+ function spkiFromX509(buf) {
197
+ const tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents);
198
+ return encodeBase64(tbsCertificate[tbsCertificate[0].raw[0] === 0xa0 ? 6 : 5].raw);
199
+ }
200
+ function getSPKI(x509) {
201
+ const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, '');
202
+ const raw = decodeBase64(pem);
203
+ return formatPEM(spkiFromX509(raw), 'PUBLIC KEY');
204
+ }
205
+ export const fromX509 = (pem, alg, options) => {
206
+ let spki;
207
+ try {
208
+ spki = getSPKI(pem);
209
+ }
210
+ catch (cause) {
211
+ throw new TypeError('failed to parse the X.509 certificate', { cause });
212
+ }
213
+ return fromSPKI(spki, alg, options);
214
+ };
@@ -124,6 +124,9 @@ function subtleMapping(jwk) {
124
124
  }
125
125
  const parse = async (jwk) => {
126
126
  var _a, _b;
127
+ if (!jwk.alg) {
128
+ throw new TypeError('"alg" argument is required when "jwk.alg" is not present');
129
+ }
127
130
  const { algorithm, keyUsages } = subtleMapping(jwk);
128
131
  const rest = [
129
132
  algorithm,
@@ -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
  }
@@ -3,79 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.importJWK = exports.importPKCS8 = exports.importX509 = exports.importSPKI = void 0;
4
4
  const base64url_js_1 = require("../runtime/base64url.js");
5
5
  const asn1_js_1 = require("../runtime/asn1.js");
6
- const asn1_js_2 = require("../runtime/asn1.js");
7
6
  const jwk_to_key_js_1 = require("../runtime/jwk_to_key.js");
8
7
  const errors_js_1 = require("../util/errors.js");
9
- const format_pem_js_1 = require("../lib/format_pem.js");
10
8
  const is_object_js_1 = require("../lib/is_object.js");
11
- function getElement(seq) {
12
- let result = [];
13
- let next = 0;
14
- while (next < seq.length) {
15
- let nextPart = parseElement(seq.subarray(next));
16
- result.push(nextPart);
17
- next += nextPart.byteLength;
18
- }
19
- return result;
20
- }
21
- function parseElement(bytes) {
22
- let position = 0;
23
- let tag = bytes[0] & 0x1f;
24
- position++;
25
- if (tag === 0x1f) {
26
- tag = 0;
27
- while (bytes[position] >= 0x80) {
28
- tag = tag * 128 + bytes[position] - 0x80;
29
- position++;
30
- }
31
- tag = tag * 128 + bytes[position] - 0x80;
32
- position++;
33
- }
34
- let length = 0;
35
- if (bytes[position] < 0x80) {
36
- length = bytes[position];
37
- position++;
38
- }
39
- else if (length === 0x80) {
40
- length = 0;
41
- while (bytes[position + length] !== 0 || bytes[position + length + 1] !== 0) {
42
- if (length > bytes.byteLength) {
43
- throw new TypeError('invalid indefinite form length');
44
- }
45
- length++;
46
- }
47
- const byteLength = position + length + 2;
48
- return {
49
- byteLength,
50
- contents: bytes.subarray(position, position + length),
51
- raw: bytes.subarray(0, byteLength),
52
- };
53
- }
54
- else {
55
- let numberOfDigits = bytes[position] & 0x7f;
56
- position++;
57
- length = 0;
58
- for (let i = 0; i < numberOfDigits; i++) {
59
- length = length * 256 + bytes[position];
60
- position++;
61
- }
62
- }
63
- const byteLength = position + length;
64
- return {
65
- byteLength,
66
- contents: bytes.subarray(position, byteLength),
67
- raw: bytes.subarray(0, byteLength),
68
- };
69
- }
70
- function spkiFromX509(buf) {
71
- const tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents);
72
- return (0, base64url_js_1.encodeBase64)(tbsCertificate[tbsCertificate[0].raw[0] === 0xa0 ? 6 : 5].raw);
73
- }
74
- function getSPKI(x509) {
75
- const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, '');
76
- const raw = (0, base64url_js_1.decodeBase64)(pem);
77
- return (0, format_pem_js_1.default)(spkiFromX509(raw), 'PUBLIC KEY');
78
- }
79
9
  async function importSPKI(spki, alg, options) {
80
10
  if (typeof spki !== 'string' || spki.indexOf('-----BEGIN PUBLIC KEY-----') !== 0) {
81
11
  throw new TypeError('"spki" must be SPKI formatted string');
@@ -87,21 +17,14 @@ async function importX509(x509, alg, options) {
87
17
  if (typeof x509 !== 'string' || x509.indexOf('-----BEGIN CERTIFICATE-----') !== 0) {
88
18
  throw new TypeError('"x509" must be X.509 formatted string');
89
19
  }
90
- let spki;
91
- try {
92
- spki = getSPKI(x509);
93
- }
94
- catch (cause) {
95
- throw new TypeError('failed to parse the X.509 certificate', { cause });
96
- }
97
- return (0, asn1_js_1.fromSPKI)(spki, alg, options);
20
+ return (0, asn1_js_1.fromX509)(x509, alg, options);
98
21
  }
99
22
  exports.importX509 = importX509;
100
23
  async function importPKCS8(pkcs8, alg, options) {
101
24
  if (typeof pkcs8 !== 'string' || pkcs8.indexOf('-----BEGIN PRIVATE KEY-----') !== 0) {
102
25
  throw new TypeError('"pkcs8" must be PKCS#8 formatted string');
103
26
  }
104
- return (0, asn1_js_2.fromPKCS8)(pkcs8, alg, options);
27
+ return (0, asn1_js_1.fromPKCS8)(pkcs8, alg, options);
105
28
  }
106
29
  exports.importPKCS8 = importPKCS8;
107
30
  async function importJWK(jwk, alg, octAsKeyObject) {
@@ -110,9 +33,6 @@ async function importJWK(jwk, alg, octAsKeyObject) {
110
33
  throw new TypeError('JWK must be an object');
111
34
  }
112
35
  alg || (alg = jwk.alg);
113
- if (typeof alg !== 'string' || !alg) {
114
- throw new TypeError('"alg" argument is required when "jwk.alg" is not present');
115
- }
116
36
  switch (jwk.kty) {
117
37
  case 'oct':
118
38
  if (typeof jwk.k !== 'string' || !jwk.k) {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fromSPKI = exports.fromPKCS8 = exports.toPKCS8 = exports.toSPKI = void 0;
3
+ exports.fromX509 = exports.fromSPKI = exports.fromPKCS8 = exports.toPKCS8 = exports.toSPKI = void 0;
4
4
  const crypto_1 = require("crypto");
5
5
  const buffer_1 = require("buffer");
6
6
  const webcrypto_js_1 = require("./webcrypto.js");
@@ -46,3 +46,9 @@ const fromSPKI = (pem) => (0, crypto_1.createPublicKey)({
46
46
  format: 'der',
47
47
  });
48
48
  exports.fromSPKI = fromSPKI;
49
+ const fromX509 = (pem) => (0, crypto_1.createPublicKey)({
50
+ key: pem,
51
+ type: 'spki',
52
+ format: 'pem',
53
+ });
54
+ exports.fromX509 = fromX509;
@@ -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));
@@ -1,102 +1,25 @@
1
- import { decode as decodeBase64URL, encodeBase64, decodeBase64 } from '../runtime/base64url.js';
2
- import { fromSPKI as importPublic } from '../runtime/asn1.js';
3
- import { fromPKCS8 as importPrivate } from '../runtime/asn1.js';
1
+ import { decode as decodeBase64URL } from '../runtime/base64url.js';
2
+ import { fromSPKI, fromPKCS8, fromX509 } from '../runtime/asn1.js';
4
3
  import asKeyObject from '../runtime/jwk_to_key.js';
5
4
  import { JOSENotSupported } from '../util/errors.js';
6
- import formatPEM from '../lib/format_pem.js';
7
5
  import isObject from '../lib/is_object.js';
8
- function getElement(seq) {
9
- let result = [];
10
- let next = 0;
11
- while (next < seq.length) {
12
- let nextPart = parseElement(seq.subarray(next));
13
- result.push(nextPart);
14
- next += nextPart.byteLength;
15
- }
16
- return result;
17
- }
18
- function parseElement(bytes) {
19
- let position = 0;
20
- let tag = bytes[0] & 0x1f;
21
- position++;
22
- if (tag === 0x1f) {
23
- tag = 0;
24
- while (bytes[position] >= 0x80) {
25
- tag = tag * 128 + bytes[position] - 0x80;
26
- position++;
27
- }
28
- tag = tag * 128 + bytes[position] - 0x80;
29
- position++;
30
- }
31
- let length = 0;
32
- if (bytes[position] < 0x80) {
33
- length = bytes[position];
34
- position++;
35
- }
36
- else if (length === 0x80) {
37
- length = 0;
38
- while (bytes[position + length] !== 0 || bytes[position + length + 1] !== 0) {
39
- if (length > bytes.byteLength) {
40
- throw new TypeError('invalid indefinite form length');
41
- }
42
- length++;
43
- }
44
- const byteLength = position + length + 2;
45
- return {
46
- byteLength,
47
- contents: bytes.subarray(position, position + length),
48
- raw: bytes.subarray(0, byteLength),
49
- };
50
- }
51
- else {
52
- let numberOfDigits = bytes[position] & 0x7f;
53
- position++;
54
- length = 0;
55
- for (let i = 0; i < numberOfDigits; i++) {
56
- length = length * 256 + bytes[position];
57
- position++;
58
- }
59
- }
60
- const byteLength = position + length;
61
- return {
62
- byteLength,
63
- contents: bytes.subarray(position, byteLength),
64
- raw: bytes.subarray(0, byteLength),
65
- };
66
- }
67
- function spkiFromX509(buf) {
68
- const tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents);
69
- return encodeBase64(tbsCertificate[tbsCertificate[0].raw[0] === 0xa0 ? 6 : 5].raw);
70
- }
71
- function getSPKI(x509) {
72
- const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, '');
73
- const raw = decodeBase64(pem);
74
- return formatPEM(spkiFromX509(raw), 'PUBLIC KEY');
75
- }
76
6
  export async function importSPKI(spki, alg, options) {
77
7
  if (typeof spki !== 'string' || spki.indexOf('-----BEGIN PUBLIC KEY-----') !== 0) {
78
8
  throw new TypeError('"spki" must be SPKI formatted string');
79
9
  }
80
- return importPublic(spki, alg, options);
10
+ return fromSPKI(spki, alg, options);
81
11
  }
82
12
  export async function importX509(x509, alg, options) {
83
13
  if (typeof x509 !== 'string' || x509.indexOf('-----BEGIN CERTIFICATE-----') !== 0) {
84
14
  throw new TypeError('"x509" must be X.509 formatted string');
85
15
  }
86
- let spki;
87
- try {
88
- spki = getSPKI(x509);
89
- }
90
- catch (cause) {
91
- throw new TypeError('failed to parse the X.509 certificate', { cause });
92
- }
93
- return importPublic(spki, alg, options);
16
+ return fromX509(x509, alg, options);
94
17
  }
95
18
  export async function importPKCS8(pkcs8, alg, options) {
96
19
  if (typeof pkcs8 !== 'string' || pkcs8.indexOf('-----BEGIN PRIVATE KEY-----') !== 0) {
97
20
  throw new TypeError('"pkcs8" must be PKCS#8 formatted string');
98
21
  }
99
- return importPrivate(pkcs8, alg, options);
22
+ return fromPKCS8(pkcs8, alg, options);
100
23
  }
101
24
  export async function importJWK(jwk, alg, octAsKeyObject) {
102
25
  var _a;
@@ -104,9 +27,6 @@ export async function importJWK(jwk, alg, octAsKeyObject) {
104
27
  throw new TypeError('JWK must be an object');
105
28
  }
106
29
  alg || (alg = jwk.alg);
107
- if (typeof alg !== 'string' || !alg) {
108
- throw new TypeError('"alg" argument is required when "jwk.alg" is not present');
109
- }
110
30
  switch (jwk.kty) {
111
31
  case 'oct':
112
32
  if (typeof jwk.k !== 'string' || !jwk.k) {
@@ -39,3 +39,8 @@ export const fromSPKI = (pem) => createPublicKey({
39
39
  type: 'spki',
40
40
  format: 'der',
41
41
  });
42
+ export const fromX509 = (pem) => createPublicKey({
43
+ key: pem,
44
+ type: 'spki',
45
+ format: 'pem',
46
+ });
@@ -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 })
@@ -17,7 +17,7 @@ export interface GenerateKeyPairOptions {
17
17
  */
18
18
  modulusLength?: number;
19
19
  /**
20
- * (Web Cryptography API specific) The value to use as
20
+ * (Only effective in Web Crypto API runtimes) The value to use as
21
21
  * [SubtleCrypto.generateKey()](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/generateKey)
22
22
  * `extractable` argument. Default is false.
23
23
  */
@@ -27,7 +27,7 @@ export interface GenerateKeyPairOptions {
27
27
  * Generates a private and a public key for a given JWA algorithm identifier. This can only generate
28
28
  * asymmetric key pairs. For symmetric secrets use the `generateSecret` function.
29
29
  *
30
- * Note: Under Web Cryptography API runtime the `privateKey` is generated with `extractable` set to
30
+ * Note: Under Web Crypto API runtime the `privateKey` is generated with `extractable` set to
31
31
  * `false` by default.
32
32
  *
33
33
  * @example Usage
@@ -1,7 +1,7 @@
1
1
  import type { KeyLike } from '../types';
2
2
  export interface GenerateSecretOptions {
3
3
  /**
4
- * (Web Cryptography API specific) The value to use as
4
+ * (Only effective in Web Crypto API runtimes) The value to use as
5
5
  * [SubtleCrypto.generateKey()](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/generateKey)
6
6
  * `extractable` argument. Default is false.
7
7
  */
@@ -10,8 +10,8 @@ export interface GenerateSecretOptions {
10
10
  /**
11
11
  * Generates a symmetric secret key for a given JWA algorithm identifier.
12
12
  *
13
- * Note: Under Web Cryptography API runtime the secret key is generated with `extractable` set to
14
- * `false` by default.
13
+ * Note: Under Web Crypto API runtime the secret key is generated with `extractable` set to `false`
14
+ * by default.
15
15
  *
16
16
  * @example Usage
17
17
  *
@@ -1,7 +1,7 @@
1
1
  import type { JWK, KeyLike } from '../types';
2
2
  export interface PEMImportOptions {
3
3
  /**
4
- * (Web Cryptography API specific) The value to use as
4
+ * (Only effective in Web Crypto API runtimes) The value to use as
5
5
  * [SubtleCrypto.importKey()](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey)
6
6
  * `extractable` argument. Default is false.
7
7
  */
@@ -24,7 +24,8 @@ export interface PEMImportOptions {
24
24
  * ```
25
25
  *
26
26
  * @param pem PEM-encoded SPKI string
27
- * @param alg JSON Web Algorithm identifier to be used with the imported key.
27
+ * @param alg (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used
28
+ * with the imported key, its presence is only enforced in Web Crypto API runtimes.
28
29
  */
29
30
  export declare function importSPKI(spki: string, alg: string, options?: PEMImportOptions): Promise<KeyLike>;
30
31
  /**
@@ -51,7 +52,8 @@ export declare function importSPKI(spki: string, alg: string, options?: PEMImpor
51
52
  * ```
52
53
  *
53
54
  * @param pem X.509 certificate string
54
- * @param alg JSON Web Algorithm identifier to be used with the imported key.
55
+ * @param alg (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used
56
+ * with the imported key, its presence is only enforced in Web Crypto API runtimes.
55
57
  */
56
58
  export declare function importX509(x509: string, alg: string, options?: PEMImportOptions): Promise<KeyLike>;
57
59
  /**
@@ -72,7 +74,8 @@ export declare function importX509(x509: string, alg: string, options?: PEMImpor
72
74
  * ```
73
75
  *
74
76
  * @param pem PEM-encoded PKCS#8 string
75
- * @param alg JSON Web Algorithm identifier to be used with the imported key.
77
+ * @param alg (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used
78
+ * with the imported key, its presence is only enforced in Web Crypto API runtimes.
76
79
  */
77
80
  export declare function importPKCS8(pkcs8: string, alg: string, options?: PEMImportOptions): Promise<KeyLike>;
78
81
  /**
@@ -107,8 +110,9 @@ export declare function importPKCS8(pkcs8: string, alg: string, options?: PEMImp
107
110
  * ```
108
111
  *
109
112
  * @param jwk JSON Web Key.
110
- * @param alg JSON Web Algorithm identifier to be used with the imported key. Default is the "alg"
111
- * property on the JWK.
113
+ * @param alg (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used
114
+ * with the imported key. Default is the "alg" property on the JWK, its presence is only enforced
115
+ * in Web Crypto API runtimes.
112
116
  * @param octAsKeyObject Forces a symmetric key to be imported to a KeyObject or CryptoKey. Default
113
117
  * is true unless JWK "ext" (Extractable) is true.
114
118
  */
@@ -36,7 +36,7 @@
36
36
  * const ecPublicKey = await jose.importSPKI(spki, algorithm)
37
37
  * ```
38
38
  *
39
- * @example Import a X.509 Certificate
39
+ * @example Import SPKI from an X.509 Certificate
40
40
  *
41
41
  * ```js
42
42
  * const algorithm = 'ES256'
@@ -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.1",
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",
@@ -1,6 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = (b64, descriptor) => {
4
- const newlined = (b64.match(/.{1,64}/g) || []).join('\n');
5
- return `-----BEGIN ${descriptor}-----\n${newlined}\n-----END ${descriptor}-----`;
6
- };
@@ -1,4 +0,0 @@
1
- export default (b64, descriptor) => {
2
- const newlined = (b64.match(/.{1,64}/g) || []).join('\n');
3
- return `-----BEGIN ${descriptor}-----\n${newlined}\n-----END ${descriptor}-----`;
4
- };