jose 5.2.0 → 5.2.2

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.
Files changed (38) hide show
  1. package/README.md +6 -6
  2. package/dist/browser/jwe/compact/decrypt.js +3 -3
  3. package/dist/browser/jwe/flattened/decrypt.js +19 -15
  4. package/dist/browser/jwe/general/encrypt.js +2 -2
  5. package/dist/browser/jwks/local.js +3 -7
  6. package/dist/browser/jwks/remote.js +2 -4
  7. package/dist/browser/lib/buffer_utils.js +2 -2
  8. package/dist/browser/lib/invalid_key_input.js +1 -1
  9. package/dist/browser/lib/validate_crit.js +1 -1
  10. package/dist/browser/runtime/asn1.js +4 -4
  11. package/dist/browser/runtime/decrypt.js +7 -1
  12. package/dist/browser/runtime/generate.js +2 -1
  13. package/dist/node/cjs/jwe/compact/decrypt.js +3 -3
  14. package/dist/node/cjs/jwe/flattened/decrypt.js +19 -15
  15. package/dist/node/cjs/jwe/general/encrypt.js +2 -2
  16. package/dist/node/cjs/jwks/local.js +3 -7
  17. package/dist/node/cjs/jwks/remote.js +2 -4
  18. package/dist/node/cjs/lib/buffer_utils.js +2 -2
  19. package/dist/node/cjs/lib/invalid_key_input.js +1 -1
  20. package/dist/node/cjs/lib/validate_crit.js +1 -1
  21. package/dist/node/cjs/runtime/decrypt.js +6 -0
  22. package/dist/node/cjs/runtime/generate.js +2 -1
  23. package/dist/node/cjs/runtime/get_named_curve.js +1 -1
  24. package/dist/node/esm/jwe/compact/decrypt.js +3 -3
  25. package/dist/node/esm/jwe/flattened/decrypt.js +19 -15
  26. package/dist/node/esm/jwe/general/encrypt.js +2 -2
  27. package/dist/node/esm/jwks/local.js +3 -7
  28. package/dist/node/esm/jwks/remote.js +2 -4
  29. package/dist/node/esm/lib/buffer_utils.js +2 -2
  30. package/dist/node/esm/lib/invalid_key_input.js +1 -1
  31. package/dist/node/esm/lib/validate_crit.js +1 -1
  32. package/dist/node/esm/runtime/decrypt.js +7 -1
  33. package/dist/node/esm/runtime/generate.js +2 -1
  34. package/dist/node/esm/runtime/get_named_curve.js +1 -1
  35. package/dist/types/key/import.d.ts +20 -7
  36. package/dist/types/types.d.ts +2 -2
  37. package/dist/types/util/errors.d.ts +22 -2
  38. package/package.json +2 -2
package/README.md CHANGED
@@ -28,10 +28,10 @@ const jose = require('jose')
28
28
 
29
29
  The `jose` module supports JSON Web Tokens (JWT) and provides functionality for signing and verifying tokens, as well as their JWT Claims Set validation.
30
30
 
31
+ - [JWT Claims Set Validation & Signature Verification](docs/functions/jwt_verify.jwtVerify.md) using the `jwtVerify` function
32
+ - [Using a remote JSON Web Key Set (JWKS)](docs/functions/jwks_remote.createRemoteJWKSet.md)
33
+ - [Using a local JSON Web Key Set (JWKS)](docs/functions/jwks_local.createLocalJWKSet.md)
31
34
  - [Signing](docs/classes/jwt_sign.SignJWT.md) using the `SignJWT` class
32
- - [Verification & JWT Claims Set Validation](docs/functions/jwt_verify.jwtVerify.md) using the `jwtVerify` function
33
- - [Using a remote JWKS](docs/functions/jwks_remote.createRemoteJWKSet.md)
34
- - [Using a local JWKS](docs/functions/jwks_local.createLocalJWKSet.md)
35
35
  - Utility functions
36
36
  - [Decoding Token's Protected Header](docs/functions/util_decode_protected_header.decodeProtectedHeader.md)
37
37
  - [Decoding JWT Claims Set](docs/functions/util_decode_jwt.decodeJwt.md) prior to its validation
@@ -40,8 +40,8 @@ The `jose` module supports JSON Web Tokens (JWT) and provides functionality for
40
40
 
41
41
  The `jose` module supports encrypted JSON Web Tokens and provides functionality for encrypting and decrypting tokens, as well as their JWT Claims Set validation.
42
42
 
43
- - [Encryption](docs/classes/jwt_encrypt.EncryptJWT.md) using the `EncryptJWT` class
44
43
  - [Decryption & JWT Claims Set Validation](docs/functions/jwt_decrypt.jwtDecrypt.md) using the `jwtDecrypt` function
44
+ - [Encryption](docs/classes/jwt_encrypt.EncryptJWT.md) using the `EncryptJWT` class
45
45
  - Utility functions
46
46
  - [Decoding Token's Protected Header](docs/functions/util_decode_protected_header.decodeProtectedHeader.md)
47
47
 
@@ -68,8 +68,8 @@ The `jose` module supports signing and verification of JWS messages with arbitra
68
68
 
69
69
  - Signing - [Compact](docs/classes/jws_compact_sign.CompactSign.md), [Flattened JSON](docs/classes/jws_flattened_sign.FlattenedSign.md), [General JSON](docs/classes/jws_general_sign.GeneralSign.md)
70
70
  - Verification - [Compact](docs/functions/jws_compact_verify.compactVerify.md), [Flattened JSON](docs/functions/jws_flattened_verify.flattenedVerify.md), [General JSON](docs/functions/jws_general_verify.generalVerify.md)
71
- - [Verify using a remote JWKS](docs/functions/jwks_remote.createRemoteJWKSet.md)
72
- - [Verify using a local JWKS](docs/functions/jwks_local.createLocalJWKSet.md)
71
+ - [Using a remote JSON Web Key Set (JWKS)](docs/functions/jwks_remote.createRemoteJWKSet.md)
72
+ - [Using a local JSON Web Key Set (JWKS)](docs/functions/jwks_local.createLocalJWKSet.md)
73
73
  - Utility functions
74
74
  - [Decoding Token's Protected Header](docs/functions/util_decode_protected_header.decodeProtectedHeader.md)
75
75
 
@@ -14,9 +14,9 @@ export async function compactDecrypt(jwe, key, options) {
14
14
  }
15
15
  const decrypted = await flattenedDecrypt({
16
16
  ciphertext,
17
- iv: (iv || undefined),
18
- protected: protectedHeader || undefined,
19
- tag: (tag || undefined),
17
+ iv: iv || undefined,
18
+ protected: protectedHeader,
19
+ tag: tag || undefined,
20
20
  encrypted_key: encryptedKey || undefined,
21
21
  }, key, options);
22
22
  const result = { plaintext: decrypted.plaintext, protectedHeader: decrypted.protectedHeader };
@@ -15,14 +15,14 @@ export async function flattenedDecrypt(jwe, key, options) {
15
15
  if (jwe.protected === undefined && jwe.header === undefined && jwe.unprotected === undefined) {
16
16
  throw new JWEInvalid('JOSE Header missing');
17
17
  }
18
- if (typeof jwe.iv !== 'string') {
19
- throw new JWEInvalid('JWE Initialization Vector missing or incorrect type');
18
+ if (jwe.iv !== undefined && typeof jwe.iv !== 'string') {
19
+ throw new JWEInvalid('JWE Initialization Vector incorrect type');
20
20
  }
21
21
  if (typeof jwe.ciphertext !== 'string') {
22
22
  throw new JWEInvalid('JWE Ciphertext missing or incorrect type');
23
23
  }
24
- if (typeof jwe.tag !== 'string') {
25
- throw new JWEInvalid('JWE Authentication Tag missing or incorrect type');
24
+ if (jwe.tag !== undefined && typeof jwe.tag !== 'string') {
25
+ throw new JWEInvalid('JWE Authentication Tag incorrect type');
26
26
  }
27
27
  if (jwe.protected !== undefined && typeof jwe.protected !== 'string') {
28
28
  throw new JWEInvalid('JWE Protected Header incorrect type');
@@ -104,17 +104,21 @@ export async function flattenedDecrypt(jwe, key, options) {
104
104
  }
105
105
  let iv;
106
106
  let tag;
107
- try {
108
- iv = base64url(jwe.iv);
109
- }
110
- catch {
111
- throw new JWEInvalid('Failed to base64url decode the iv');
112
- }
113
- try {
114
- tag = base64url(jwe.tag);
107
+ if (jwe.iv !== undefined) {
108
+ try {
109
+ iv = base64url(jwe.iv);
110
+ }
111
+ catch {
112
+ throw new JWEInvalid('Failed to base64url decode the iv');
113
+ }
115
114
  }
116
- catch {
117
- throw new JWEInvalid('Failed to base64url decode the tag');
115
+ if (jwe.tag !== undefined) {
116
+ try {
117
+ tag = base64url(jwe.tag);
118
+ }
119
+ catch {
120
+ throw new JWEInvalid('Failed to base64url decode the tag');
121
+ }
118
122
  }
119
123
  const protectedHeader = encoder.encode(jwe.protected ?? '');
120
124
  let additionalData;
@@ -131,7 +135,7 @@ export async function flattenedDecrypt(jwe, key, options) {
131
135
  catch {
132
136
  throw new JWEInvalid('Failed to base64url decode the ciphertext');
133
137
  }
134
- let plaintext = await decrypt(enc, cek, ciphertext, iv, tag, additionalData);
138
+ const plaintext = await decrypt(enc, cek, ciphertext, iv, tag, additionalData);
135
139
  const result = { plaintext };
136
140
  if (jwe.protected !== undefined) {
137
141
  result.protectedHeader = parsedProt;
@@ -68,7 +68,7 @@ export class GeneralEncrypt {
68
68
  .setSharedUnprotectedHeader(this._unprotectedHeader)
69
69
  .setUnprotectedHeader(recipient.unprotectedHeader)
70
70
  .encrypt(recipient.key, { ...recipient.options });
71
- let jwe = {
71
+ const jwe = {
72
72
  ciphertext: flattened.ciphertext,
73
73
  iv: flattened.iv,
74
74
  recipients: [{}],
@@ -119,7 +119,7 @@ export class GeneralEncrypt {
119
119
  }
120
120
  }
121
121
  const cek = generateCek(enc);
122
- let jwe = {
122
+ const jwe = {
123
123
  ciphertext: '',
124
124
  iv: '',
125
125
  recipients: [],
@@ -79,7 +79,7 @@ export class LocalJWKSet {
79
79
  if (length === 0) {
80
80
  throw new JWKSNoMatchingKey();
81
81
  }
82
- else if (length !== 1) {
82
+ if (length !== 1) {
83
83
  const error = new JWKSMultipleMatchingKeys();
84
84
  const { _cached } = this;
85
85
  error[Symbol.asyncIterator] = async function* () {
@@ -87,9 +87,7 @@ export class LocalJWKSet {
87
87
  try {
88
88
  yield await importWithAlgCache(_cached, jwk, alg);
89
89
  }
90
- catch {
91
- continue;
92
- }
90
+ catch { }
93
91
  }
94
92
  };
95
93
  throw error;
@@ -110,7 +108,5 @@ async function importWithAlgCache(cache, jwk, alg) {
110
108
  }
111
109
  export function createLocalJWKSet(jwks) {
112
110
  const set = new LocalJWKSet(jwks);
113
- return async function (protectedHeader, token) {
114
- return set.getKey(protectedHeader, token);
115
- };
111
+ return async (protectedHeader, token) => set.getKey(protectedHeader, token);
116
112
  }
@@ -9,7 +9,7 @@ function isCloudflareWorkers() {
9
9
  let USER_AGENT;
10
10
  if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {
11
11
  const NAME = 'jose';
12
- const VERSION = 'v5.2.0';
12
+ const VERSION = 'v5.2.2';
13
13
  USER_AGENT = `${NAME}/${VERSION}`;
14
14
  }
15
15
  class RemoteJWKSet extends LocalJWKSet {
@@ -81,7 +81,5 @@ class RemoteJWKSet extends LocalJWKSet {
81
81
  }
82
82
  export function createRemoteJWKSet(url, options) {
83
83
  const set = new RemoteJWKSet(url, options);
84
- return async function (protectedHeader, token) {
85
- return set.getKey(protectedHeader, token);
86
- };
84
+ return async (protectedHeader, token) => set.getKey(protectedHeader, token);
87
85
  }
@@ -6,10 +6,10 @@ export function concat(...buffers) {
6
6
  const size = buffers.reduce((acc, { length }) => acc + length, 0);
7
7
  const buf = new Uint8Array(size);
8
8
  let i = 0;
9
- buffers.forEach((buffer) => {
9
+ for (const buffer of buffers) {
10
10
  buf.set(buffer, i);
11
11
  i += buffer.length;
12
- });
12
+ }
13
13
  return buf;
14
14
  }
15
15
  export function p2s(alg, p2sInput) {
@@ -16,7 +16,7 @@ function message(msg, actual, ...types) {
16
16
  msg += ` Received function ${actual.name}`;
17
17
  }
18
18
  else if (typeof actual === 'object' && actual != null) {
19
- if (actual.constructor && actual.constructor.name) {
19
+ if (actual.constructor?.name) {
20
20
  msg += ` Received an instance of ${actual.constructor.name}`;
21
21
  }
22
22
  }
@@ -25,7 +25,7 @@ function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader,
25
25
  if (joseHeader[parameter] === undefined) {
26
26
  throw new Err(`Extension Header Parameter "${parameter}" is missing`);
27
27
  }
28
- else if (recognized.get(parameter) && protectedHeader[parameter] === undefined) {
28
+ if (recognized.get(parameter) && protectedHeader[parameter] === undefined) {
29
29
  throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`);
30
30
  }
31
31
  }
@@ -27,7 +27,7 @@ const findOid = (keyData, oid, from = 0) => {
27
27
  oid.unshift(oid.length);
28
28
  oid.unshift(0x06);
29
29
  }
30
- let i = keyData.indexOf(oid[0], from);
30
+ const i = keyData.indexOf(oid[0], from);
31
31
  if (i === -1)
32
32
  return false;
33
33
  const sub = keyData.subarray(i, i + oid.length);
@@ -122,10 +122,10 @@ export const fromSPKI = (pem, alg, options) => {
122
122
  return genericImport(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g, 'spki', pem, alg, options);
123
123
  };
124
124
  function getElement(seq) {
125
- let result = [];
125
+ const result = [];
126
126
  let next = 0;
127
127
  while (next < seq.length) {
128
- let nextPart = parseElement(seq.subarray(next));
128
+ const nextPart = parseElement(seq.subarray(next));
129
129
  result.push(nextPart);
130
130
  next += nextPart.byteLength;
131
131
  }
@@ -165,7 +165,7 @@ function parseElement(bytes) {
165
165
  };
166
166
  }
167
167
  else {
168
- let numberOfDigits = bytes[position] & 0x7f;
168
+ const numberOfDigits = bytes[position] & 0x7f;
169
169
  position++;
170
170
  length = 0;
171
171
  for (let i = 0; i < numberOfDigits; i++) {
@@ -2,7 +2,7 @@ import { concat, uint64be } from '../lib/buffer_utils.js';
2
2
  import checkIvLength from '../lib/check_iv_length.js';
3
3
  import checkCekLength from './check_cek_length.js';
4
4
  import timingSafeEqual from './timing_safe_equal.js';
5
- import { JOSENotSupported, JWEDecryptionFailed } from '../util/errors.js';
5
+ import { JOSENotSupported, JWEDecryptionFailed, JWEInvalid } from '../util/errors.js';
6
6
  import crypto, { isCryptoKey } from './webcrypto.js';
7
7
  import { checkEncCryptoKey } from '../lib/crypto_key.js';
8
8
  import invalidKeyInput from '../lib/invalid_key_input.js';
@@ -64,6 +64,12 @@ const decrypt = async (enc, cek, ciphertext, iv, tag, aad) => {
64
64
  if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) {
65
65
  throw new TypeError(invalidKeyInput(cek, ...types, 'Uint8Array'));
66
66
  }
67
+ if (!iv) {
68
+ throw new JWEInvalid('JWE Initialization Vector missing');
69
+ }
70
+ if (!tag) {
71
+ throw new JWEInvalid('JWE Authentication Tag missing');
72
+ }
67
73
  checkIvLength(enc, iv);
68
74
  switch (enc) {
69
75
  case 'A128CBC-HS256':
@@ -97,7 +97,7 @@ export async function generateKeyPair(alg, options) {
97
97
  algorithm = { name: 'ECDSA', namedCurve: 'P-521' };
98
98
  keyUsages = ['sign', 'verify'];
99
99
  break;
100
- case 'EdDSA':
100
+ case 'EdDSA': {
101
101
  keyUsages = ['sign', 'verify'];
102
102
  const crv = options?.crv ?? 'Ed25519';
103
103
  switch (crv) {
@@ -109,6 +109,7 @@ export async function generateKeyPair(alg, options) {
109
109
  throw new JOSENotSupported('Invalid or unsupported crv option provided');
110
110
  }
111
111
  break;
112
+ }
112
113
  case 'ECDH-ES':
113
114
  case 'ECDH-ES+A128KW':
114
115
  case 'ECDH-ES+A192KW':
@@ -17,9 +17,9 @@ async function compactDecrypt(jwe, key, options) {
17
17
  }
18
18
  const decrypted = await (0, decrypt_js_1.flattenedDecrypt)({
19
19
  ciphertext,
20
- iv: (iv || undefined),
21
- protected: protectedHeader || undefined,
22
- tag: (tag || undefined),
20
+ iv: iv || undefined,
21
+ protected: protectedHeader,
22
+ tag: tag || undefined,
23
23
  encrypted_key: encryptedKey || undefined,
24
24
  }, key, options);
25
25
  const result = { plaintext: decrypted.plaintext, protectedHeader: decrypted.protectedHeader };
@@ -18,14 +18,14 @@ async function flattenedDecrypt(jwe, key, options) {
18
18
  if (jwe.protected === undefined && jwe.header === undefined && jwe.unprotected === undefined) {
19
19
  throw new errors_js_1.JWEInvalid('JOSE Header missing');
20
20
  }
21
- if (typeof jwe.iv !== 'string') {
22
- throw new errors_js_1.JWEInvalid('JWE Initialization Vector missing or incorrect type');
21
+ if (jwe.iv !== undefined && typeof jwe.iv !== 'string') {
22
+ throw new errors_js_1.JWEInvalid('JWE Initialization Vector incorrect type');
23
23
  }
24
24
  if (typeof jwe.ciphertext !== 'string') {
25
25
  throw new errors_js_1.JWEInvalid('JWE Ciphertext missing or incorrect type');
26
26
  }
27
- if (typeof jwe.tag !== 'string') {
28
- throw new errors_js_1.JWEInvalid('JWE Authentication Tag missing or incorrect type');
27
+ if (jwe.tag !== undefined && typeof jwe.tag !== 'string') {
28
+ throw new errors_js_1.JWEInvalid('JWE Authentication Tag incorrect type');
29
29
  }
30
30
  if (jwe.protected !== undefined && typeof jwe.protected !== 'string') {
31
31
  throw new errors_js_1.JWEInvalid('JWE Protected Header incorrect type');
@@ -107,17 +107,21 @@ async function flattenedDecrypt(jwe, key, options) {
107
107
  }
108
108
  let iv;
109
109
  let tag;
110
- try {
111
- iv = (0, base64url_js_1.decode)(jwe.iv);
112
- }
113
- catch {
114
- throw new errors_js_1.JWEInvalid('Failed to base64url decode the iv');
115
- }
116
- try {
117
- tag = (0, base64url_js_1.decode)(jwe.tag);
110
+ if (jwe.iv !== undefined) {
111
+ try {
112
+ iv = (0, base64url_js_1.decode)(jwe.iv);
113
+ }
114
+ catch {
115
+ throw new errors_js_1.JWEInvalid('Failed to base64url decode the iv');
116
+ }
118
117
  }
119
- catch {
120
- throw new errors_js_1.JWEInvalid('Failed to base64url decode the tag');
118
+ if (jwe.tag !== undefined) {
119
+ try {
120
+ tag = (0, base64url_js_1.decode)(jwe.tag);
121
+ }
122
+ catch {
123
+ throw new errors_js_1.JWEInvalid('Failed to base64url decode the tag');
124
+ }
121
125
  }
122
126
  const protectedHeader = buffer_utils_js_1.encoder.encode(jwe.protected ?? '');
123
127
  let additionalData;
@@ -134,7 +138,7 @@ async function flattenedDecrypt(jwe, key, options) {
134
138
  catch {
135
139
  throw new errors_js_1.JWEInvalid('Failed to base64url decode the ciphertext');
136
140
  }
137
- let plaintext = await (0, decrypt_js_1.default)(enc, cek, ciphertext, iv, tag, additionalData);
141
+ const plaintext = await (0, decrypt_js_1.default)(enc, cek, ciphertext, iv, tag, additionalData);
138
142
  const result = { plaintext };
139
143
  if (jwe.protected !== undefined) {
140
144
  result.protectedHeader = parsedProt;
@@ -79,7 +79,7 @@ class GeneralEncrypt {
79
79
  .setSharedUnprotectedHeader(this._unprotectedHeader)
80
80
  .setUnprotectedHeader(recipient.unprotectedHeader)
81
81
  .encrypt(recipient.key, { ...recipient.options });
82
- let jwe = {
82
+ const jwe = {
83
83
  ciphertext: flattened.ciphertext,
84
84
  iv: flattened.iv,
85
85
  recipients: [{}],
@@ -130,7 +130,7 @@ class GeneralEncrypt {
130
130
  }
131
131
  }
132
132
  const cek = (0, cek_js_1.default)(enc);
133
- let jwe = {
133
+ const jwe = {
134
134
  ciphertext: '',
135
135
  iv: '',
136
136
  recipients: [],
@@ -84,7 +84,7 @@ class LocalJWKSet {
84
84
  if (length === 0) {
85
85
  throw new errors_js_1.JWKSNoMatchingKey();
86
86
  }
87
- else if (length !== 1) {
87
+ if (length !== 1) {
88
88
  const error = new errors_js_1.JWKSMultipleMatchingKeys();
89
89
  const { _cached } = this;
90
90
  error[Symbol.asyncIterator] = async function* () {
@@ -92,9 +92,7 @@ class LocalJWKSet {
92
92
  try {
93
93
  yield await importWithAlgCache(_cached, jwk, alg);
94
94
  }
95
- catch {
96
- continue;
97
- }
95
+ catch { }
98
96
  }
99
97
  };
100
98
  throw error;
@@ -116,8 +114,6 @@ async function importWithAlgCache(cache, jwk, alg) {
116
114
  }
117
115
  function createLocalJWKSet(jwks) {
118
116
  const set = new LocalJWKSet(jwks);
119
- return async function (protectedHeader, token) {
120
- return set.getKey(protectedHeader, token);
121
- };
117
+ return async (protectedHeader, token) => set.getKey(protectedHeader, token);
122
118
  }
123
119
  exports.createLocalJWKSet = createLocalJWKSet;
@@ -12,7 +12,7 @@ function isCloudflareWorkers() {
12
12
  let USER_AGENT;
13
13
  if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {
14
14
  const NAME = 'jose';
15
- const VERSION = 'v5.2.0';
15
+ const VERSION = 'v5.2.2';
16
16
  USER_AGENT = `${NAME}/${VERSION}`;
17
17
  }
18
18
  class RemoteJWKSet extends local_js_1.LocalJWKSet {
@@ -91,8 +91,6 @@ class RemoteJWKSet extends local_js_1.LocalJWKSet {
91
91
  }
92
92
  function createRemoteJWKSet(url, options) {
93
93
  const set = new RemoteJWKSet(url, options);
94
- return async function (protectedHeader, token) {
95
- return set.getKey(protectedHeader, token);
96
- };
94
+ return async (protectedHeader, token) => set.getKey(protectedHeader, token);
97
95
  }
98
96
  exports.createRemoteJWKSet = createRemoteJWKSet;
@@ -9,10 +9,10 @@ function concat(...buffers) {
9
9
  const size = buffers.reduce((acc, { length }) => acc + length, 0);
10
10
  const buf = new Uint8Array(size);
11
11
  let i = 0;
12
- buffers.forEach((buffer) => {
12
+ for (const buffer of buffers) {
13
13
  buf.set(buffer, i);
14
14
  i += buffer.length;
15
- });
15
+ }
16
16
  return buf;
17
17
  }
18
18
  exports.concat = concat;
@@ -19,7 +19,7 @@ function message(msg, actual, ...types) {
19
19
  msg += ` Received function ${actual.name}`;
20
20
  }
21
21
  else if (typeof actual === 'object' && actual != null) {
22
- if (actual.constructor && actual.constructor.name) {
22
+ if (actual.constructor?.name) {
23
23
  msg += ` Received an instance of ${actual.constructor.name}`;
24
24
  }
25
25
  }
@@ -27,7 +27,7 @@ function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader,
27
27
  if (joseHeader[parameter] === undefined) {
28
28
  throw new Err(`Extension Header Parameter "${parameter}" is missing`);
29
29
  }
30
- else if (recognized.get(parameter) && protectedHeader[parameter] === undefined) {
30
+ if (recognized.get(parameter) && protectedHeader[parameter] === undefined) {
31
31
  throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`);
32
32
  }
33
33
  }
@@ -79,6 +79,12 @@ const decrypt = (enc, cek, ciphertext, iv, tag, aad) => {
79
79
  else {
80
80
  throw new TypeError((0, invalid_key_input_js_1.default)(cek, ...is_key_like_js_1.types, 'Uint8Array'));
81
81
  }
82
+ if (!iv) {
83
+ throw new errors_js_1.JWEInvalid('JWE Initialization Vector missing');
84
+ }
85
+ if (!tag) {
86
+ throw new errors_js_1.JWEInvalid('JWE Authentication Tag missing');
87
+ }
82
88
  (0, check_cek_length_js_1.default)(enc, key);
83
89
  (0, check_iv_length_js_1.default)(enc, iv);
84
90
  switch (enc) {
@@ -79,7 +79,7 @@ async function generateKeyPair(alg, options) {
79
79
  case 'ECDH-ES':
80
80
  case 'ECDH-ES+A128KW':
81
81
  case 'ECDH-ES+A192KW':
82
- case 'ECDH-ES+A256KW':
82
+ case 'ECDH-ES+A256KW': {
83
83
  const crv = options?.crv ?? 'P-256';
84
84
  switch (crv) {
85
85
  case undefined:
@@ -94,6 +94,7 @@ async function generateKeyPair(alg, options) {
94
94
  default:
95
95
  throw new errors_js_1.JOSENotSupported('Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, X25519, and X448');
96
96
  }
97
+ }
97
98
  default:
98
99
  throw new errors_js_1.JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
99
100
  }
@@ -44,7 +44,7 @@ const getNamedCurve = (kee, raw) => {
44
44
  case 'x448':
45
45
  return `X${key.asymmetricKeyType.slice(1)}`;
46
46
  case 'ec': {
47
- let namedCurve = key.asymmetricKeyDetails.namedCurve;
47
+ const namedCurve = key.asymmetricKeyDetails.namedCurve;
48
48
  if (raw) {
49
49
  return namedCurve;
50
50
  }
@@ -14,9 +14,9 @@ export async function compactDecrypt(jwe, key, options) {
14
14
  }
15
15
  const decrypted = await flattenedDecrypt({
16
16
  ciphertext,
17
- iv: (iv || undefined),
18
- protected: protectedHeader || undefined,
19
- tag: (tag || undefined),
17
+ iv: iv || undefined,
18
+ protected: protectedHeader,
19
+ tag: tag || undefined,
20
20
  encrypted_key: encryptedKey || undefined,
21
21
  }, key, options);
22
22
  const result = { plaintext: decrypted.plaintext, protectedHeader: decrypted.protectedHeader };
@@ -15,14 +15,14 @@ export async function flattenedDecrypt(jwe, key, options) {
15
15
  if (jwe.protected === undefined && jwe.header === undefined && jwe.unprotected === undefined) {
16
16
  throw new JWEInvalid('JOSE Header missing');
17
17
  }
18
- if (typeof jwe.iv !== 'string') {
19
- throw new JWEInvalid('JWE Initialization Vector missing or incorrect type');
18
+ if (jwe.iv !== undefined && typeof jwe.iv !== 'string') {
19
+ throw new JWEInvalid('JWE Initialization Vector incorrect type');
20
20
  }
21
21
  if (typeof jwe.ciphertext !== 'string') {
22
22
  throw new JWEInvalid('JWE Ciphertext missing or incorrect type');
23
23
  }
24
- if (typeof jwe.tag !== 'string') {
25
- throw new JWEInvalid('JWE Authentication Tag missing or incorrect type');
24
+ if (jwe.tag !== undefined && typeof jwe.tag !== 'string') {
25
+ throw new JWEInvalid('JWE Authentication Tag incorrect type');
26
26
  }
27
27
  if (jwe.protected !== undefined && typeof jwe.protected !== 'string') {
28
28
  throw new JWEInvalid('JWE Protected Header incorrect type');
@@ -104,17 +104,21 @@ export async function flattenedDecrypt(jwe, key, options) {
104
104
  }
105
105
  let iv;
106
106
  let tag;
107
- try {
108
- iv = base64url(jwe.iv);
109
- }
110
- catch {
111
- throw new JWEInvalid('Failed to base64url decode the iv');
112
- }
113
- try {
114
- tag = base64url(jwe.tag);
107
+ if (jwe.iv !== undefined) {
108
+ try {
109
+ iv = base64url(jwe.iv);
110
+ }
111
+ catch {
112
+ throw new JWEInvalid('Failed to base64url decode the iv');
113
+ }
115
114
  }
116
- catch {
117
- throw new JWEInvalid('Failed to base64url decode the tag');
115
+ if (jwe.tag !== undefined) {
116
+ try {
117
+ tag = base64url(jwe.tag);
118
+ }
119
+ catch {
120
+ throw new JWEInvalid('Failed to base64url decode the tag');
121
+ }
118
122
  }
119
123
  const protectedHeader = encoder.encode(jwe.protected ?? '');
120
124
  let additionalData;
@@ -131,7 +135,7 @@ export async function flattenedDecrypt(jwe, key, options) {
131
135
  catch {
132
136
  throw new JWEInvalid('Failed to base64url decode the ciphertext');
133
137
  }
134
- let plaintext = await decrypt(enc, cek, ciphertext, iv, tag, additionalData);
138
+ const plaintext = await decrypt(enc, cek, ciphertext, iv, tag, additionalData);
135
139
  const result = { plaintext };
136
140
  if (jwe.protected !== undefined) {
137
141
  result.protectedHeader = parsedProt;
@@ -76,7 +76,7 @@ export class GeneralEncrypt {
76
76
  .setSharedUnprotectedHeader(this._unprotectedHeader)
77
77
  .setUnprotectedHeader(recipient.unprotectedHeader)
78
78
  .encrypt(recipient.key, { ...recipient.options });
79
- let jwe = {
79
+ const jwe = {
80
80
  ciphertext: flattened.ciphertext,
81
81
  iv: flattened.iv,
82
82
  recipients: [{}],
@@ -127,7 +127,7 @@ export class GeneralEncrypt {
127
127
  }
128
128
  }
129
129
  const cek = generateCek(enc);
130
- let jwe = {
130
+ const jwe = {
131
131
  ciphertext: '',
132
132
  iv: '',
133
133
  recipients: [],
@@ -80,7 +80,7 @@ export class LocalJWKSet {
80
80
  if (length === 0) {
81
81
  throw new JWKSNoMatchingKey();
82
82
  }
83
- else if (length !== 1) {
83
+ if (length !== 1) {
84
84
  const error = new JWKSMultipleMatchingKeys();
85
85
  const { _cached } = this;
86
86
  error[Symbol.asyncIterator] = async function* () {
@@ -88,9 +88,7 @@ export class LocalJWKSet {
88
88
  try {
89
89
  yield await importWithAlgCache(_cached, jwk, alg);
90
90
  }
91
- catch {
92
- continue;
93
- }
91
+ catch { }
94
92
  }
95
93
  };
96
94
  throw error;
@@ -111,7 +109,5 @@ async function importWithAlgCache(cache, jwk, alg) {
111
109
  }
112
110
  export function createLocalJWKSet(jwks) {
113
111
  const set = new LocalJWKSet(jwks);
114
- return async function (protectedHeader, token) {
115
- return set.getKey(protectedHeader, token);
116
- };
112
+ return async (protectedHeader, token) => set.getKey(protectedHeader, token);
117
113
  }
@@ -9,7 +9,7 @@ function isCloudflareWorkers() {
9
9
  let USER_AGENT;
10
10
  if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {
11
11
  const NAME = 'jose';
12
- const VERSION = 'v5.2.0';
12
+ const VERSION = 'v5.2.2';
13
13
  USER_AGENT = `${NAME}/${VERSION}`;
14
14
  }
15
15
  class RemoteJWKSet extends LocalJWKSet {
@@ -88,7 +88,5 @@ class RemoteJWKSet extends LocalJWKSet {
88
88
  }
89
89
  export function createRemoteJWKSet(url, options) {
90
90
  const set = new RemoteJWKSet(url, options);
91
- return async function (protectedHeader, token) {
92
- return set.getKey(protectedHeader, token);
93
- };
91
+ return async (protectedHeader, token) => set.getKey(protectedHeader, token);
94
92
  }
@@ -6,10 +6,10 @@ export function concat(...buffers) {
6
6
  const size = buffers.reduce((acc, { length }) => acc + length, 0);
7
7
  const buf = new Uint8Array(size);
8
8
  let i = 0;
9
- buffers.forEach((buffer) => {
9
+ for (const buffer of buffers) {
10
10
  buf.set(buffer, i);
11
11
  i += buffer.length;
12
- });
12
+ }
13
13
  return buf;
14
14
  }
15
15
  export function p2s(alg, p2sInput) {
@@ -16,7 +16,7 @@ function message(msg, actual, ...types) {
16
16
  msg += ` Received function ${actual.name}`;
17
17
  }
18
18
  else if (typeof actual === 'object' && actual != null) {
19
- if (actual.constructor && actual.constructor.name) {
19
+ if (actual.constructor?.name) {
20
20
  msg += ` Received an instance of ${actual.constructor.name}`;
21
21
  }
22
22
  }
@@ -25,7 +25,7 @@ function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader,
25
25
  if (joseHeader[parameter] === undefined) {
26
26
  throw new Err(`Extension Header Parameter "${parameter}" is missing`);
27
27
  }
28
- else if (recognized.get(parameter) && protectedHeader[parameter] === undefined) {
28
+ if (recognized.get(parameter) && protectedHeader[parameter] === undefined) {
29
29
  throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`);
30
30
  }
31
31
  }
@@ -2,7 +2,7 @@ import { createDecipheriv, KeyObject } from 'node:crypto';
2
2
  import checkIvLength from '../lib/check_iv_length.js';
3
3
  import checkCekLength from './check_cek_length.js';
4
4
  import { concat } from '../lib/buffer_utils.js';
5
- import { JOSENotSupported, JWEDecryptionFailed } from '../util/errors.js';
5
+ import { JOSENotSupported, JWEDecryptionFailed, JWEInvalid } from '../util/errors.js';
6
6
  import timingSafeEqual from './timing_safe_equal.js';
7
7
  import cbcTag from './cbc_tag.js';
8
8
  import { isCryptoKey } from './webcrypto.js';
@@ -77,6 +77,12 @@ const decrypt = (enc, cek, ciphertext, iv, tag, aad) => {
77
77
  else {
78
78
  throw new TypeError(invalidKeyInput(cek, ...types, 'Uint8Array'));
79
79
  }
80
+ if (!iv) {
81
+ throw new JWEInvalid('JWE Initialization Vector missing');
82
+ }
83
+ if (!tag) {
84
+ throw new JWEInvalid('JWE Authentication Tag missing');
85
+ }
80
86
  checkCekLength(enc, key);
81
87
  checkIvLength(enc, iv);
82
88
  switch (enc) {
@@ -75,7 +75,7 @@ export async function generateKeyPair(alg, options) {
75
75
  case 'ECDH-ES':
76
76
  case 'ECDH-ES+A128KW':
77
77
  case 'ECDH-ES+A192KW':
78
- case 'ECDH-ES+A256KW':
78
+ case 'ECDH-ES+A256KW': {
79
79
  const crv = options?.crv ?? 'P-256';
80
80
  switch (crv) {
81
81
  case undefined:
@@ -90,6 +90,7 @@ export async function generateKeyPair(alg, options) {
90
90
  default:
91
91
  throw new JOSENotSupported('Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, X25519, and X448');
92
92
  }
93
+ }
93
94
  default:
94
95
  throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
95
96
  }
@@ -41,7 +41,7 @@ const getNamedCurve = (kee, raw) => {
41
41
  case 'x448':
42
42
  return `X${key.asymmetricKeyType.slice(1)}`;
43
43
  case 'ec': {
44
- let namedCurve = key.asymmetricKeyDetails.namedCurve;
44
+ const namedCurve = key.asymmetricKeyDetails.namedCurve;
45
45
  if (raw) {
46
46
  return namedCurve;
47
47
  }
@@ -11,7 +11,11 @@ export interface PEMImportOptions {
11
11
  * Imports a PEM-encoded SPKI string as a runtime-specific public key representation (KeyObject or
12
12
  * CryptoKey).
13
13
  *
14
- * @param pem PEM-encoded SPKI string
14
+ * Note: The OID id-RSASSA-PSS (1.2.840.113549.1.1.10) is not supported in
15
+ * {@link https://w3c.github.io/webcrypto/ Web Cryptography API}, use the OID rsaEncryption
16
+ * (1.2.840.113549.1.1.1) instead for all RSA algorithms.
17
+ *
18
+ * @param spki PEM-encoded SPKI string
15
19
  * @param alg (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used
16
20
  * with the imported key, its presence is only enforced in Web Crypto API runtimes. See
17
21
  * {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}.
@@ -21,7 +25,11 @@ export declare function importSPKI<KeyLikeType extends KeyLike = KeyLike>(spki:
21
25
  * Imports the SPKI from an X.509 string certificate as a runtime-specific public key representation
22
26
  * (KeyObject or CryptoKey).
23
27
  *
24
- * @param pem X.509 certificate string
28
+ * Note: The OID id-RSASSA-PSS (1.2.840.113549.1.1.10) is not supported in
29
+ * {@link https://w3c.github.io/webcrypto/ Web Cryptography API}, use the OID rsaEncryption
30
+ * (1.2.840.113549.1.1.1) instead for all RSA algorithms.
31
+ *
32
+ * @param x509 X.509 certificate string
25
33
  * @param alg (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used
26
34
  * with the imported key, its presence is only enforced in Web Crypto API runtimes. See
27
35
  * {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}.
@@ -31,17 +39,22 @@ export declare function importX509<KeyLikeType extends KeyLike = KeyLike>(x509:
31
39
  * Imports a PEM-encoded PKCS#8 string as a runtime-specific private key representation (KeyObject
32
40
  * or CryptoKey).
33
41
  *
34
- * @param pem PEM-encoded PKCS#8 string
42
+ * Note: The OID id-RSASSA-PSS (1.2.840.113549.1.1.10) is not supported in
43
+ * {@link https://w3c.github.io/webcrypto/ Web Cryptography API}, use the OID rsaEncryption
44
+ * (1.2.840.113549.1.1.1) instead for all RSA algorithms.
45
+ *
46
+ * @param pkcs8 PEM-encoded PKCS#8 string
35
47
  * @param alg (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used
36
48
  * with the imported key, its presence is only enforced in Web Crypto API runtimes. See
37
49
  * {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}.
38
50
  */
39
51
  export declare function importPKCS8<KeyLikeType extends KeyLike = KeyLike>(pkcs8: string, alg: string, options?: PEMImportOptions): Promise<KeyLikeType>;
40
52
  /**
41
- * Imports a JWK to a runtime-specific key representation (KeyLike). Either JWK "alg" (Algorithm)
42
- * Parameter must be present or the optional "alg" argument. When running on a runtime using
43
- * {@link https://www.w3.org/TR/WebCryptoAPI/ Web Cryptography API} the jwk parameters "use",
44
- * "key_ops", and "ext" are also used in the resulting `CryptoKey`.
53
+ * Imports a JWK to a runtime-specific key representation (KeyLike). Either the JWK "alg"
54
+ * (Algorithm) Parameter, or the optional "alg" argument, must be present.
55
+ *
56
+ * Note: When the runtime is using {@link https://w3c.github.io/webcrypto/ Web Cryptography API} the
57
+ * jwk parameters "use", "key_ops", and "ext" are also used in the resulting `CryptoKey`.
45
58
  *
46
59
  * @param jwk JSON Web Key.
47
60
  * @param alg (Only effective in Web Crypto API runtimes) JSON Web Algorithm identifier to be used
@@ -264,7 +264,7 @@ export interface FlattenedJWE {
264
264
  * The "iv" member MUST be present and contain the value BASE64URL(JWE Initialization Vector) when
265
265
  * the JWE Initialization Vector value is non-empty; otherwise, it MUST be absent.
266
266
  */
267
- iv: string
267
+ iv?: string
268
268
 
269
269
  /**
270
270
  * The "protected" member MUST be present and contain the value BASE64URL(UTF8(JWE Protected
@@ -277,7 +277,7 @@ export interface FlattenedJWE {
277
277
  * The "tag" member MUST be present and contain the value BASE64URL(JWE Authentication Tag) when
278
278
  * the JWE Authentication Tag value is non-empty; otherwise, it MUST be absent.
279
279
  */
280
- tag: string
280
+ tag?: string
281
281
 
282
282
  /**
283
283
  * The "unprotected" member MUST be present and contain the value JWE Shared Unprotected Header
@@ -4,10 +4,15 @@ import type { KeyLike } from '../types';
4
4
  *
5
5
  */
6
6
  export declare class JOSEError extends Error {
7
- /** A unique error code for the particular error subclass. */
7
+ /**
8
+ * A unique error code for the particular error subclass.
9
+ *
10
+ * @ignore
11
+ */
8
12
  static get code(): string;
9
- /** A unique error code for the particular error subclass. */
13
+ /** A unique error code for this particular error subclass. */
10
14
  code: string;
15
+ /** @ignore */
11
16
  constructor(message?: string);
12
17
  }
13
18
  /**
@@ -15,12 +20,14 @@ export declare class JOSEError extends Error {
15
20
  *
16
21
  */
17
22
  export declare class JWTClaimValidationFailed extends JOSEError {
23
+ /** @ignore */
18
24
  static get code(): 'ERR_JWT_CLAIM_VALIDATION_FAILED';
19
25
  code: string;
20
26
  /** The Claim for which the validation failed. */
21
27
  claim: string;
22
28
  /** Reason code for the validation failure. */
23
29
  reason: string;
30
+ /** @ignore */
24
31
  constructor(message: string, claim?: string, reason?: string);
25
32
  }
26
33
  /**
@@ -28,12 +35,14 @@ export declare class JWTClaimValidationFailed extends JOSEError {
28
35
  *
29
36
  */
30
37
  export declare class JWTExpired extends JOSEError implements JWTClaimValidationFailed {
38
+ /** @ignore */
31
39
  static get code(): 'ERR_JWT_EXPIRED';
32
40
  code: string;
33
41
  /** The Claim for which the validation failed. */
34
42
  claim: string;
35
43
  /** Reason code for the validation failure. */
36
44
  reason: string;
45
+ /** @ignore */
37
46
  constructor(message: string, claim?: string, reason?: string);
38
47
  }
39
48
  /**
@@ -41,6 +50,7 @@ export declare class JWTExpired extends JOSEError implements JWTClaimValidationF
41
50
  *
42
51
  */
43
52
  export declare class JOSEAlgNotAllowed extends JOSEError {
53
+ /** @ignore */
44
54
  static get code(): 'ERR_JOSE_ALG_NOT_ALLOWED';
45
55
  code: string;
46
56
  }
@@ -50,6 +60,7 @@ export declare class JOSEAlgNotAllowed extends JOSEError {
50
60
  *
51
61
  */
52
62
  export declare class JOSENotSupported extends JOSEError {
63
+ /** @ignore */
53
64
  static get code(): 'ERR_JOSE_NOT_SUPPORTED';
54
65
  code: string;
55
66
  }
@@ -58,6 +69,7 @@ export declare class JOSENotSupported extends JOSEError {
58
69
  *
59
70
  */
60
71
  export declare class JWEDecryptionFailed extends JOSEError {
72
+ /** @ignore */
61
73
  static get code(): 'ERR_JWE_DECRYPTION_FAILED';
62
74
  code: string;
63
75
  message: string;
@@ -67,6 +79,7 @@ export declare class JWEDecryptionFailed extends JOSEError {
67
79
  *
68
80
  */
69
81
  export declare class JWEInvalid extends JOSEError {
82
+ /** @ignore */
70
83
  static get code(): 'ERR_JWE_INVALID';
71
84
  code: string;
72
85
  }
@@ -75,6 +88,7 @@ export declare class JWEInvalid extends JOSEError {
75
88
  *
76
89
  */
77
90
  export declare class JWSInvalid extends JOSEError {
91
+ /** @ignore */
78
92
  static get code(): 'ERR_JWS_INVALID';
79
93
  code: string;
80
94
  }
@@ -83,6 +97,7 @@ export declare class JWSInvalid extends JOSEError {
83
97
  *
84
98
  */
85
99
  export declare class JWTInvalid extends JOSEError {
100
+ /** @ignore */
86
101
  static get code(): 'ERR_JWT_INVALID';
87
102
  code: string;
88
103
  }
@@ -91,6 +106,7 @@ export declare class JWTInvalid extends JOSEError {
91
106
  *
92
107
  */
93
108
  export declare class JWKInvalid extends JOSEError {
109
+ /** @ignore */
94
110
  static get code(): 'ERR_JWK_INVALID';
95
111
  code: string;
96
112
  }
@@ -99,6 +115,7 @@ export declare class JWKInvalid extends JOSEError {
99
115
  *
100
116
  */
101
117
  export declare class JWKSInvalid extends JOSEError {
118
+ /** @ignore */
102
119
  static get code(): 'ERR_JWKS_INVALID';
103
120
  code: string;
104
121
  }
@@ -107,6 +124,7 @@ export declare class JWKSInvalid extends JOSEError {
107
124
  *
108
125
  */
109
126
  export declare class JWKSNoMatchingKey extends JOSEError {
127
+ /** @ignore */
110
128
  static get code(): 'ERR_JWKS_NO_MATCHING_KEY';
111
129
  code: string;
112
130
  message: string;
@@ -127,6 +145,7 @@ export declare class JWKSMultipleMatchingKeys extends JOSEError {
127
145
  *
128
146
  */
129
147
  export declare class JWKSTimeout extends JOSEError {
148
+ /** @ignore */
130
149
  static get code(): 'ERR_JWKS_TIMEOUT';
131
150
  code: string;
132
151
  message: string;
@@ -136,6 +155,7 @@ export declare class JWKSTimeout extends JOSEError {
136
155
  *
137
156
  */
138
157
  export declare class JWSSignatureVerificationFailed extends JOSEError {
158
+ /** @ignore */
139
159
  static get code(): 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED';
140
160
  code: string;
141
161
  message: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "jose",
3
- "version": "5.2.0",
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",
3
+ "version": "5.2.2",
4
+ "description": "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",
7
7
  "bun",