jose 4.4.0 → 4.5.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.
package/README.md CHANGED
@@ -42,10 +42,10 @@ import * as jose from 'https://deno.land/x/jose/index.ts'
42
42
 
43
43
  - JSON Web Tokens (JWT)
44
44
  - [Signing](docs/classes/jwt_sign.SignJWT.md#readme)
45
- - [Verification & Claims Set Validation](docs/functions/jwt_verify.jwtVerify.md#readme)
45
+ - [Verification & JWT Claims Set Validation](docs/functions/jwt_verify.jwtVerify.md#readme)
46
46
  - Encrypted JSON Web Tokens
47
47
  - [Encryption](docs/classes/jwt_encrypt.EncryptJWT.md#readme)
48
- - [Decryption & Claims Set Validation](docs/functions/jwt_decrypt.jwtDecrypt.md#readme)
48
+ - [Decryption & JWT Claims Set Validation](docs/functions/jwt_decrypt.jwtDecrypt.md#readme)
49
49
  - Key Import
50
50
  - [JWK Import](docs/functions/key_import.importJWK.md#readme)
51
51
  - [Public Key Import (SPKI)](docs/functions/key_import.importSPKI.md#readme)
@@ -72,6 +72,7 @@ import * as jose from 'https://deno.land/x/jose/index.ts'
72
72
  - [Public Key Export](docs/functions/key_export.exportSPKI.md#readme)
73
73
  - Utilities
74
74
  - [Decoding Token's Protected Header](docs/functions/util_decode_protected_header.decodeProtectedHeader.md#readme)
75
+ - [Decoding JWT Claims Set](docs/functions/util_decode_jwt.decodeJwt.md#readme)
75
76
  - [Unsecured JWT](docs/classes/jwt_unsecured.UnsecuredJWT.md#readme)
76
77
  - [JOSE Errors](docs/modules/util_errors.md#readme)
77
78
 
@@ -90,7 +91,7 @@ import * as jose from 'https://deno.land/x/jose/index.ts'
90
91
 
91
92
  | Version | Security Fixes 🔑 | Other Bug Fixes 🐞 | New Features ⭐ |
92
93
  | ------- | --------- | -------- | -------- |
93
- | [4.x.x](https://github.com/panva/jose) | ✅ | ✅ | ✅ |
94
+ | [4.x.x](https://github.com/panva/jose/tree/v4.x) | ✅ | ✅ | ✅ |
94
95
  | [3.x.x](https://github.com/panva/jose/tree/v3.x) | ✅ | ✅ until 2022-04-30 | ❌ |
95
96
  | [2.x.x](https://github.com/panva/jose/tree/v2.x) | ✅ | ✅ until 2022-04-30 | ❌ |
96
97
  | [1.x.x](https://github.com/panva/jose/tree/v1.x) | ✅ | ❌ | ❌ |
@@ -135,16 +136,16 @@ install size should not be a cause for concern.
135
136
 
136
137
  [documentation]: /docs/README.md
137
138
  [node-jose]: https://github.com/cisco/node-jose
138
- [spec-b64]: https://tools.ietf.org/html/rfc7797
139
- [spec-cookbook]: https://tools.ietf.org/html/rfc7520
140
- [spec-jwa]: https://tools.ietf.org/html/rfc7518
141
- [spec-jwe]: https://tools.ietf.org/html/rfc7516
142
- [spec-jwk]: https://tools.ietf.org/html/rfc7517
143
- [spec-jws]: https://tools.ietf.org/html/rfc7515
144
- [spec-jwt]: https://tools.ietf.org/html/rfc7519
145
- [spec-okp]: https://tools.ietf.org/html/rfc8037
146
- [spec-secp256k1]: https://tools.ietf.org/html/rfc8812
147
- [spec-thumbprint]: https://tools.ietf.org/html/rfc7638
139
+ [spec-b64]: https://www.rfc-editor.org/rfc/rfc7797
140
+ [spec-cookbook]: https://www.rfc-editor.org/rfc/rfc7520
141
+ [spec-jwa]: https://www.rfc-editor.org/rfc/rfc7518
142
+ [spec-jwe]: https://www.rfc-editor.org/rfc/rfc7516
143
+ [spec-jwk]: https://www.rfc-editor.org/rfc/rfc7517
144
+ [spec-jws]: https://www.rfc-editor.org/rfc/rfc7515
145
+ [spec-jwt]: https://www.rfc-editor.org/rfc/rfc7519
146
+ [spec-okp]: https://www.rfc-editor.org/rfc/rfc8037
147
+ [spec-secp256k1]: https://www.rfc-editor.org/rfc/rfc8812
148
+ [spec-thumbprint]: https://www.rfc-editor.org/rfc/rfc7638
148
149
  [support-sponsor]: https://github.com/sponsors/panva
149
150
  [conditional-exports]: https://nodejs.org/api/packages.html#packages_conditional_exports
150
151
  [webcrypto]: https://www.w3.org/TR/WebCryptoAPI/
@@ -22,6 +22,7 @@ export { UnsecuredJWT } from './jwt/unsecured.js';
22
22
  export { exportPKCS8, exportSPKI, exportJWK } from './key/export.js';
23
23
  export { importSPKI, importPKCS8, importX509, importJWK } from './key/import.js';
24
24
  export { decodeProtectedHeader } from './util/decode_protected_header.js';
25
+ export { decodeJwt } from './util/decode_jwt.js';
25
26
  import * as errors_1 from './util/errors.js';
26
27
  export { errors_1 as errors };
27
28
  export { generateKeyPair } from './key/generate_key_pair.js';
@@ -13,7 +13,7 @@ export async function compactDecrypt(jwe, key, options) {
13
13
  throw new JWEInvalid('Invalid Compact JWE');
14
14
  }
15
15
  const decrypted = await flattenedDecrypt({
16
- ciphertext: (ciphertext || undefined),
16
+ ciphertext,
17
17
  iv: (iv || undefined),
18
18
  protected: protectedHeader || undefined,
19
19
  tag: (tag || undefined),
@@ -12,6 +12,11 @@ export async function calculateJwkThumbprint(jwk, digestAlgorithm = 'sha256') {
12
12
  if (!isObject(jwk)) {
13
13
  throw new TypeError('JWK must be an object');
14
14
  }
15
+ if (digestAlgorithm !== 'sha256' &&
16
+ digestAlgorithm !== 'sha384' &&
17
+ digestAlgorithm !== 'sha512') {
18
+ throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"');
19
+ }
15
20
  let components;
16
21
  switch (jwk.kty) {
17
22
  case 'EC':
@@ -38,17 +38,14 @@ export class LocalJWKSet {
38
38
  this._jwks = clone(jwks);
39
39
  }
40
40
  async getKey(protectedHeader, token) {
41
- const joseHeader = {
42
- ...protectedHeader,
43
- ...token.header,
44
- };
41
+ const { alg, kid } = { ...protectedHeader, ...token.header };
45
42
  const candidates = this._jwks.keys.filter((jwk) => {
46
- let candidate = jwk.kty === getKtyFromAlg(joseHeader.alg);
47
- if (candidate && typeof joseHeader.kid === 'string') {
48
- candidate = joseHeader.kid === jwk.kid;
43
+ let candidate = jwk.kty === getKtyFromAlg(alg);
44
+ if (candidate && typeof kid === 'string') {
45
+ candidate = kid === jwk.kid;
49
46
  }
50
47
  if (candidate && typeof jwk.alg === 'string') {
51
- candidate = joseHeader.alg === jwk.alg;
48
+ candidate = alg === jwk.alg;
52
49
  }
53
50
  if (candidate && typeof jwk.use === 'string') {
54
51
  candidate = jwk.use === 'sig';
@@ -56,11 +53,11 @@ export class LocalJWKSet {
56
53
  if (candidate && Array.isArray(jwk.key_ops)) {
57
54
  candidate = jwk.key_ops.includes('verify');
58
55
  }
59
- if (candidate && joseHeader.alg === 'EdDSA') {
56
+ if (candidate && alg === 'EdDSA') {
60
57
  candidate = jwk.crv === 'Ed25519' || jwk.crv === 'Ed448';
61
58
  }
62
59
  if (candidate) {
63
- switch (joseHeader.alg) {
60
+ switch (alg) {
64
61
  case 'ES256':
65
62
  candidate = jwk.crv === 'P-256';
66
63
  break;
@@ -73,7 +70,6 @@ export class LocalJWKSet {
73
70
  case 'ES512':
74
71
  candidate = jwk.crv === 'P-521';
75
72
  break;
76
- default:
77
73
  }
78
74
  }
79
75
  return candidate;
@@ -86,14 +82,14 @@ export class LocalJWKSet {
86
82
  throw new JWKSMultipleMatchingKeys();
87
83
  }
88
84
  const cached = this._cached.get(jwk) || this._cached.set(jwk, {}).get(jwk);
89
- if (cached[joseHeader.alg] === undefined) {
90
- const keyObject = await importJWK({ ...jwk, ext: true }, joseHeader.alg);
85
+ if (cached[alg] === undefined) {
86
+ const keyObject = await importJWK({ ...jwk, ext: true }, alg);
91
87
  if (keyObject instanceof Uint8Array || keyObject.type !== 'public') {
92
88
  throw new JWKSInvalid('JSON Web Key Set members must be public keys');
93
89
  }
94
- cached[joseHeader.alg] = keyObject;
90
+ cached[alg] = keyObject;
95
91
  }
96
- return cached[joseHeader.alg];
92
+ return cached[alg];
97
93
  }
98
94
  }
99
95
  export function createLocalJWKSet(jwks) {
@@ -1,4 +1,5 @@
1
1
  import fetchJwks from '../runtime/fetch_jwks.js';
2
+ import { isCloudflareWorkers } from '../runtime/env.js';
2
3
  import { JWKSInvalid, JWKSNoMatchingKey } from '../util/errors.js';
3
4
  import { isJWKSLike, LocalJWKSet } from './local.js';
4
5
  class RemoteJWKSet extends LocalJWKSet {
@@ -39,6 +40,19 @@ class RemoteJWKSet extends LocalJWKSet {
39
40
  }
40
41
  }
41
42
  async reload() {
43
+ if (this._pendingFetch && isCloudflareWorkers()) {
44
+ return new Promise((resolve) => {
45
+ const isDone = () => {
46
+ if (this._pendingFetch === undefined) {
47
+ resolve();
48
+ }
49
+ else {
50
+ setTimeout(isDone, 5);
51
+ }
52
+ };
53
+ isDone();
54
+ });
55
+ }
42
56
  if (!this._pendingFetch) {
43
57
  this._pendingFetch = fetchJwks(this._url, this._timeoutDuration, this._options)
44
58
  .then((json) => {
@@ -1,3 +1,4 @@
1
+ import digest from '../runtime/digest.js';
1
2
  export const encoder = new TextEncoder();
2
3
  export const decoder = new TextDecoder();
3
4
  const MAX_INT32 = 2 ** 32;
@@ -36,7 +37,7 @@ export function uint32be(value) {
36
37
  export function lengthAndInput(input) {
37
38
  return concat(uint32be(input.length), input);
38
39
  }
39
- export async function concatKdf(digest, secret, bits, value) {
40
+ export async function concatKdf(secret, bits, value) {
40
41
  const iterations = Math.ceil((bits >> 3) / 32);
41
42
  let res;
42
43
  for (let iter = 1; iter <= iterations; iter++) {
@@ -1,7 +1,6 @@
1
1
  import { encoder, concat, uint32be, lengthAndInput, concatKdf } from '../lib/buffer_utils.js';
2
2
  import crypto, { isCryptoKey } from './webcrypto.js';
3
3
  import { checkEncCryptoKey } from '../lib/crypto_key.js';
4
- import digest from './digest.js';
5
4
  import invalidKeyInput from '../lib/invalid_key_input.js';
6
5
  import { types } from './is_key_like.js';
7
6
  export async function deriveKey(publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(0), apv = new Uint8Array(0)) {
@@ -21,7 +20,7 @@ export async function deriveKey(publicKey, privateKey, algorithm, keyLength, apu
21
20
  name: 'ECDH',
22
21
  public: publicKey,
23
22
  }, privateKey, Math.ceil(parseInt(privateKey.algorithm.namedCurve.slice(-3), 10) / 8) << 3));
24
- return concatKdf(digest, sharedSecret, keyLength, value);
23
+ return concatKdf(sharedSecret, keyLength, value);
25
24
  }
26
25
  export async function generateEpk(key) {
27
26
  if (!isCryptoKey(key)) {
@@ -0,0 +1,32 @@
1
+ import { decode as base64url } from './base64url.js';
2
+ import { decoder } from '../lib/buffer_utils.js';
3
+ import isObject from '../lib/is_object.js';
4
+ import { JWTInvalid } from './errors.js';
5
+ export function decodeJwt(jwt) {
6
+ if (typeof jwt !== 'string')
7
+ throw new JWTInvalid('JWTs must use Compact JWS serialization, JWT must be a string');
8
+ const { 1: payload, length } = jwt.split('.');
9
+ if (length === 5)
10
+ throw new JWTInvalid('Only JWTs using Compact JWS serialization can be decoded');
11
+ if (length !== 3)
12
+ throw new JWTInvalid('Invalid JWT');
13
+ if (!payload)
14
+ throw new JWTInvalid('JWTs must contain a payload');
15
+ let decoded;
16
+ try {
17
+ decoded = base64url(payload);
18
+ }
19
+ catch (_a) {
20
+ throw new JWTInvalid('Failed to parse the base64url encoded payload');
21
+ }
22
+ let result;
23
+ try {
24
+ result = JSON.parse(decoder.decode(decoded));
25
+ }
26
+ catch (_b) {
27
+ throw new JWTInvalid('Failed to parse the decoded payload as JSON');
28
+ }
29
+ if (!isObject(result))
30
+ throw new JWTInvalid('Invalid JWT Claims Set');
31
+ return result;
32
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.base64url = exports.generateSecret = exports.generateKeyPair = exports.errors = exports.decodeProtectedHeader = exports.importJWK = exports.importX509 = exports.importPKCS8 = exports.importSPKI = exports.exportJWK = exports.exportSPKI = exports.exportPKCS8 = exports.UnsecuredJWT = exports.createRemoteJWKSet = exports.createLocalJWKSet = exports.EmbeddedJWK = exports.calculateJwkThumbprint = exports.EncryptJWT = exports.SignJWT = exports.GeneralSign = exports.FlattenedSign = exports.CompactSign = exports.FlattenedEncrypt = exports.CompactEncrypt = exports.jwtDecrypt = exports.jwtVerify = exports.generalVerify = exports.flattenedVerify = exports.compactVerify = exports.GeneralEncrypt = exports.generalDecrypt = exports.flattenedDecrypt = exports.compactDecrypt = void 0;
3
+ exports.base64url = exports.generateSecret = exports.generateKeyPair = exports.errors = exports.decodeJwt = exports.decodeProtectedHeader = exports.importJWK = exports.importX509 = exports.importPKCS8 = exports.importSPKI = exports.exportJWK = exports.exportSPKI = exports.exportPKCS8 = exports.UnsecuredJWT = exports.createRemoteJWKSet = exports.createLocalJWKSet = exports.EmbeddedJWK = exports.calculateJwkThumbprint = exports.EncryptJWT = exports.SignJWT = exports.GeneralSign = exports.FlattenedSign = exports.CompactSign = exports.FlattenedEncrypt = exports.CompactEncrypt = exports.jwtDecrypt = exports.jwtVerify = exports.generalVerify = exports.flattenedVerify = exports.compactVerify = exports.GeneralEncrypt = exports.generalDecrypt = exports.flattenedDecrypt = exports.compactDecrypt = void 0;
4
4
  var decrypt_js_1 = require("./jwe/compact/decrypt.js");
5
5
  Object.defineProperty(exports, "compactDecrypt", { enumerable: true, get: function () { return decrypt_js_1.compactDecrypt; } });
6
6
  var decrypt_js_2 = require("./jwe/flattened/decrypt.js");
@@ -54,6 +54,8 @@ Object.defineProperty(exports, "importX509", { enumerable: true, get: function (
54
54
  Object.defineProperty(exports, "importJWK", { enumerable: true, get: function () { return import_js_1.importJWK; } });
55
55
  var decode_protected_header_js_1 = require("./util/decode_protected_header.js");
56
56
  Object.defineProperty(exports, "decodeProtectedHeader", { enumerable: true, get: function () { return decode_protected_header_js_1.decodeProtectedHeader; } });
57
+ var decode_jwt_js_1 = require("./util/decode_jwt.js");
58
+ Object.defineProperty(exports, "decodeJwt", { enumerable: true, get: function () { return decode_jwt_js_1.decodeJwt; } });
57
59
  exports.errors = require("./util/errors.js");
58
60
  var generate_key_pair_js_1 = require("./key/generate_key_pair.js");
59
61
  Object.defineProperty(exports, "generateKeyPair", { enumerable: true, get: function () { return generate_key_pair_js_1.generateKeyPair; } });
@@ -16,7 +16,7 @@ async function compactDecrypt(jwe, key, options) {
16
16
  throw new errors_js_1.JWEInvalid('Invalid Compact JWE');
17
17
  }
18
18
  const decrypted = await (0, decrypt_js_1.flattenedDecrypt)({
19
- ciphertext: (ciphertext || undefined),
19
+ ciphertext,
20
20
  iv: (iv || undefined),
21
21
  protected: protectedHeader || undefined,
22
22
  tag: (tag || undefined),
@@ -15,6 +15,11 @@ async function calculateJwkThumbprint(jwk, digestAlgorithm = 'sha256') {
15
15
  if (!(0, is_object_js_1.default)(jwk)) {
16
16
  throw new TypeError('JWK must be an object');
17
17
  }
18
+ if (digestAlgorithm !== 'sha256' &&
19
+ digestAlgorithm !== 'sha384' &&
20
+ digestAlgorithm !== 'sha512') {
21
+ throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"');
22
+ }
18
23
  let components;
19
24
  switch (jwk.kty) {
20
25
  case 'EC':
@@ -42,17 +42,14 @@ class LocalJWKSet {
42
42
  this._jwks = clone(jwks);
43
43
  }
44
44
  async getKey(protectedHeader, token) {
45
- const joseHeader = {
46
- ...protectedHeader,
47
- ...token.header,
48
- };
45
+ const { alg, kid } = { ...protectedHeader, ...token.header };
49
46
  const candidates = this._jwks.keys.filter((jwk) => {
50
- let candidate = jwk.kty === getKtyFromAlg(joseHeader.alg);
51
- if (candidate && typeof joseHeader.kid === 'string') {
52
- candidate = joseHeader.kid === jwk.kid;
47
+ let candidate = jwk.kty === getKtyFromAlg(alg);
48
+ if (candidate && typeof kid === 'string') {
49
+ candidate = kid === jwk.kid;
53
50
  }
54
51
  if (candidate && typeof jwk.alg === 'string') {
55
- candidate = joseHeader.alg === jwk.alg;
52
+ candidate = alg === jwk.alg;
56
53
  }
57
54
  if (candidate && typeof jwk.use === 'string') {
58
55
  candidate = jwk.use === 'sig';
@@ -60,11 +57,11 @@ class LocalJWKSet {
60
57
  if (candidate && Array.isArray(jwk.key_ops)) {
61
58
  candidate = jwk.key_ops.includes('verify');
62
59
  }
63
- if (candidate && joseHeader.alg === 'EdDSA') {
60
+ if (candidate && alg === 'EdDSA') {
64
61
  candidate = jwk.crv === 'Ed25519' || jwk.crv === 'Ed448';
65
62
  }
66
63
  if (candidate) {
67
- switch (joseHeader.alg) {
64
+ switch (alg) {
68
65
  case 'ES256':
69
66
  candidate = jwk.crv === 'P-256';
70
67
  break;
@@ -77,7 +74,6 @@ class LocalJWKSet {
77
74
  case 'ES512':
78
75
  candidate = jwk.crv === 'P-521';
79
76
  break;
80
- default:
81
77
  }
82
78
  }
83
79
  return candidate;
@@ -90,14 +86,14 @@ class LocalJWKSet {
90
86
  throw new errors_js_1.JWKSMultipleMatchingKeys();
91
87
  }
92
88
  const cached = this._cached.get(jwk) || this._cached.set(jwk, {}).get(jwk);
93
- if (cached[joseHeader.alg] === undefined) {
94
- const keyObject = await (0, import_js_1.importJWK)({ ...jwk, ext: true }, joseHeader.alg);
89
+ if (cached[alg] === undefined) {
90
+ const keyObject = await (0, import_js_1.importJWK)({ ...jwk, ext: true }, alg);
95
91
  if (keyObject instanceof Uint8Array || keyObject.type !== 'public') {
96
92
  throw new errors_js_1.JWKSInvalid('JSON Web Key Set members must be public keys');
97
93
  }
98
- cached[joseHeader.alg] = keyObject;
94
+ cached[alg] = keyObject;
99
95
  }
100
- return cached[joseHeader.alg];
96
+ return cached[alg];
101
97
  }
102
98
  }
103
99
  exports.LocalJWKSet = LocalJWKSet;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createRemoteJWKSet = void 0;
4
4
  const fetch_jwks_js_1 = require("../runtime/fetch_jwks.js");
5
+ const env_js_1 = require("../runtime/env.js");
5
6
  const errors_js_1 = require("../util/errors.js");
6
7
  const local_js_1 = require("./local.js");
7
8
  class RemoteJWKSet extends local_js_1.LocalJWKSet {
@@ -42,6 +43,19 @@ class RemoteJWKSet extends local_js_1.LocalJWKSet {
42
43
  }
43
44
  }
44
45
  async reload() {
46
+ if (this._pendingFetch && (0, env_js_1.isCloudflareWorkers)()) {
47
+ return new Promise((resolve) => {
48
+ const isDone = () => {
49
+ if (this._pendingFetch === undefined) {
50
+ resolve();
51
+ }
52
+ else {
53
+ setTimeout(isDone, 5);
54
+ }
55
+ };
56
+ isDone();
57
+ });
58
+ }
45
59
  if (!this._pendingFetch) {
46
60
  this._pendingFetch = (0, fetch_jwks_js_1.default)(this._url, this._timeoutDuration, this._options)
47
61
  .then((json) => {
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.concatKdf = exports.lengthAndInput = exports.uint32be = exports.uint64be = exports.p2s = exports.concat = exports.decoder = exports.encoder = void 0;
4
+ const digest_js_1 = require("../runtime/digest.js");
4
5
  exports.encoder = new TextEncoder();
5
6
  exports.decoder = new TextDecoder();
6
7
  const MAX_INT32 = 2 ** 32;
@@ -44,7 +45,7 @@ function lengthAndInput(input) {
44
45
  return concat(uint32be(input.length), input);
45
46
  }
46
47
  exports.lengthAndInput = lengthAndInput;
47
- async function concatKdf(digest, secret, bits, value) {
48
+ async function concatKdf(secret, bits, value) {
48
49
  const iterations = Math.ceil((bits >> 3) / 32);
49
50
  let res;
50
51
  for (let iter = 1; iter <= iterations; iter++) {
@@ -53,10 +54,10 @@ async function concatKdf(digest, secret, bits, value) {
53
54
  buf.set(secret, 4);
54
55
  buf.set(value, 4 + secret.length);
55
56
  if (!res) {
56
- res = await digest('sha256', buf);
57
+ res = await (0, digest_js_1.default)('sha256', buf);
57
58
  }
58
59
  else {
59
- res = concat(res, await digest('sha256', buf));
60
+ res = concat(res, await (0, digest_js_1.default)('sha256', buf));
60
61
  }
61
62
  }
62
63
  res = res.slice(0, bits >> 3);
@@ -3,7 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.decode = exports.encode = exports.encodeBase64 = exports.decodeBase64 = void 0;
4
4
  const buffer_1 = require("buffer");
5
5
  const buffer_utils_js_1 = require("../lib/buffer_utils.js");
6
- let encodeImpl;
6
+ let encode;
7
+ exports.encode = encode;
7
8
  function normalize(input) {
8
9
  let encoded = input;
9
10
  if (encoded instanceof Uint8Array) {
@@ -12,15 +13,14 @@ function normalize(input) {
12
13
  return encoded;
13
14
  }
14
15
  if (buffer_1.Buffer.isEncoding('base64url')) {
15
- encodeImpl = (input) => buffer_1.Buffer.from(input).toString('base64url');
16
+ exports.encode = encode = (input) => buffer_1.Buffer.from(input).toString('base64url');
16
17
  }
17
18
  else {
18
- encodeImpl = (input) => buffer_1.Buffer.from(input).toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
19
+ exports.encode = encode = (input) => buffer_1.Buffer.from(input).toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
19
20
  }
20
21
  const decodeBase64 = (input) => buffer_1.Buffer.from(input, 'base64');
21
22
  exports.decodeBase64 = decodeBase64;
22
23
  const encodeBase64 = (input) => buffer_1.Buffer.from(input).toString('base64');
23
24
  exports.encodeBase64 = encodeBase64;
24
- exports.encode = encodeImpl;
25
25
  const decode = (input) => buffer_1.Buffer.from(normalize(input), 'base64');
26
26
  exports.decode = decode;
@@ -5,7 +5,6 @@ const crypto_1 = require("crypto");
5
5
  const util_1 = require("util");
6
6
  const get_named_curve_js_1 = require("./get_named_curve.js");
7
7
  const buffer_utils_js_1 = require("../lib/buffer_utils.js");
8
- const digest_js_1 = require("./digest.js");
9
8
  const errors_js_1 = require("../util/errors.js");
10
9
  const webcrypto_js_1 = require("./webcrypto.js");
11
10
  const crypto_key_js_1 = require("../lib/crypto_key.js");
@@ -38,7 +37,7 @@ async function deriveKey(publicKee, privateKee, algorithm, keyLength, apu = new
38
37
  }
39
38
  const value = (0, buffer_utils_js_1.concat)((0, buffer_utils_js_1.lengthAndInput)(buffer_utils_js_1.encoder.encode(algorithm)), (0, buffer_utils_js_1.lengthAndInput)(apu), (0, buffer_utils_js_1.lengthAndInput)(apv), (0, buffer_utils_js_1.uint32be)(keyLength));
40
39
  const sharedSecret = (0, crypto_1.diffieHellman)({ privateKey, publicKey });
41
- return (0, buffer_utils_js_1.concatKdf)(digest_js_1.default, sharedSecret, keyLength, value);
40
+ return (0, buffer_utils_js_1.concatKdf)(sharedSecret, keyLength, value);
42
41
  }
43
42
  exports.deriveKey = deriveKey;
44
43
  async function generateEpk(kee) {
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.decodeJwt = void 0;
4
+ const base64url_js_1 = require("./base64url.js");
5
+ const buffer_utils_js_1 = require("../lib/buffer_utils.js");
6
+ const is_object_js_1 = require("../lib/is_object.js");
7
+ const errors_js_1 = require("./errors.js");
8
+ function decodeJwt(jwt) {
9
+ if (typeof jwt !== 'string')
10
+ throw new errors_js_1.JWTInvalid('JWTs must use Compact JWS serialization, JWT must be a string');
11
+ const { 1: payload, length } = jwt.split('.');
12
+ if (length === 5)
13
+ throw new errors_js_1.JWTInvalid('Only JWTs using Compact JWS serialization can be decoded');
14
+ if (length !== 3)
15
+ throw new errors_js_1.JWTInvalid('Invalid JWT');
16
+ if (!payload)
17
+ throw new errors_js_1.JWTInvalid('JWTs must contain a payload');
18
+ let decoded;
19
+ try {
20
+ decoded = (0, base64url_js_1.decode)(payload);
21
+ }
22
+ catch {
23
+ throw new errors_js_1.JWTInvalid('Failed to parse the base64url encoded payload');
24
+ }
25
+ let result;
26
+ try {
27
+ result = JSON.parse(buffer_utils_js_1.decoder.decode(decoded));
28
+ }
29
+ catch {
30
+ throw new errors_js_1.JWTInvalid('Failed to parse the decoded payload as JSON');
31
+ }
32
+ if (!(0, is_object_js_1.default)(result))
33
+ throw new errors_js_1.JWTInvalid('Invalid JWT Claims Set');
34
+ return result;
35
+ }
36
+ exports.decodeJwt = decodeJwt;
@@ -22,6 +22,7 @@ export { UnsecuredJWT } from './jwt/unsecured.js';
22
22
  export { exportPKCS8, exportSPKI, exportJWK } from './key/export.js';
23
23
  export { importSPKI, importPKCS8, importX509, importJWK } from './key/import.js';
24
24
  export { decodeProtectedHeader } from './util/decode_protected_header.js';
25
+ export { decodeJwt } from './util/decode_jwt.js';
25
26
  export * as errors from './util/errors.js';
26
27
  export { generateKeyPair } from './key/generate_key_pair.js';
27
28
  export { generateSecret } from './key/generate_secret.js';
@@ -13,7 +13,7 @@ export async function compactDecrypt(jwe, key, options) {
13
13
  throw new JWEInvalid('Invalid Compact JWE');
14
14
  }
15
15
  const decrypted = await flattenedDecrypt({
16
- ciphertext: (ciphertext || undefined),
16
+ ciphertext,
17
17
  iv: (iv || undefined),
18
18
  protected: protectedHeader || undefined,
19
19
  tag: (tag || undefined),
@@ -12,6 +12,11 @@ export async function calculateJwkThumbprint(jwk, digestAlgorithm = 'sha256') {
12
12
  if (!isObject(jwk)) {
13
13
  throw new TypeError('JWK must be an object');
14
14
  }
15
+ if (digestAlgorithm !== 'sha256' &&
16
+ digestAlgorithm !== 'sha384' &&
17
+ digestAlgorithm !== 'sha512') {
18
+ throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"');
19
+ }
15
20
  let components;
16
21
  switch (jwk.kty) {
17
22
  case 'EC':
@@ -38,17 +38,14 @@ export class LocalJWKSet {
38
38
  this._jwks = clone(jwks);
39
39
  }
40
40
  async getKey(protectedHeader, token) {
41
- const joseHeader = {
42
- ...protectedHeader,
43
- ...token.header,
44
- };
41
+ const { alg, kid } = { ...protectedHeader, ...token.header };
45
42
  const candidates = this._jwks.keys.filter((jwk) => {
46
- let candidate = jwk.kty === getKtyFromAlg(joseHeader.alg);
47
- if (candidate && typeof joseHeader.kid === 'string') {
48
- candidate = joseHeader.kid === jwk.kid;
43
+ let candidate = jwk.kty === getKtyFromAlg(alg);
44
+ if (candidate && typeof kid === 'string') {
45
+ candidate = kid === jwk.kid;
49
46
  }
50
47
  if (candidate && typeof jwk.alg === 'string') {
51
- candidate = joseHeader.alg === jwk.alg;
48
+ candidate = alg === jwk.alg;
52
49
  }
53
50
  if (candidate && typeof jwk.use === 'string') {
54
51
  candidate = jwk.use === 'sig';
@@ -56,11 +53,11 @@ export class LocalJWKSet {
56
53
  if (candidate && Array.isArray(jwk.key_ops)) {
57
54
  candidate = jwk.key_ops.includes('verify');
58
55
  }
59
- if (candidate && joseHeader.alg === 'EdDSA') {
56
+ if (candidate && alg === 'EdDSA') {
60
57
  candidate = jwk.crv === 'Ed25519' || jwk.crv === 'Ed448';
61
58
  }
62
59
  if (candidate) {
63
- switch (joseHeader.alg) {
60
+ switch (alg) {
64
61
  case 'ES256':
65
62
  candidate = jwk.crv === 'P-256';
66
63
  break;
@@ -73,7 +70,6 @@ export class LocalJWKSet {
73
70
  case 'ES512':
74
71
  candidate = jwk.crv === 'P-521';
75
72
  break;
76
- default:
77
73
  }
78
74
  }
79
75
  return candidate;
@@ -86,14 +82,14 @@ export class LocalJWKSet {
86
82
  throw new JWKSMultipleMatchingKeys();
87
83
  }
88
84
  const cached = this._cached.get(jwk) || this._cached.set(jwk, {}).get(jwk);
89
- if (cached[joseHeader.alg] === undefined) {
90
- const keyObject = await importJWK({ ...jwk, ext: true }, joseHeader.alg);
85
+ if (cached[alg] === undefined) {
86
+ const keyObject = await importJWK({ ...jwk, ext: true }, alg);
91
87
  if (keyObject instanceof Uint8Array || keyObject.type !== 'public') {
92
88
  throw new JWKSInvalid('JSON Web Key Set members must be public keys');
93
89
  }
94
- cached[joseHeader.alg] = keyObject;
90
+ cached[alg] = keyObject;
95
91
  }
96
- return cached[joseHeader.alg];
92
+ return cached[alg];
97
93
  }
98
94
  }
99
95
  export function createLocalJWKSet(jwks) {
@@ -1,4 +1,5 @@
1
1
  import fetchJwks from '../runtime/fetch_jwks.js';
2
+ import { isCloudflareWorkers } from '../runtime/env.js';
2
3
  import { JWKSInvalid, JWKSNoMatchingKey } from '../util/errors.js';
3
4
  import { isJWKSLike, LocalJWKSet } from './local.js';
4
5
  class RemoteJWKSet extends LocalJWKSet {
@@ -39,6 +40,19 @@ class RemoteJWKSet extends LocalJWKSet {
39
40
  }
40
41
  }
41
42
  async reload() {
43
+ if (this._pendingFetch && isCloudflareWorkers()) {
44
+ return new Promise((resolve) => {
45
+ const isDone = () => {
46
+ if (this._pendingFetch === undefined) {
47
+ resolve();
48
+ }
49
+ else {
50
+ setTimeout(isDone, 5);
51
+ }
52
+ };
53
+ isDone();
54
+ });
55
+ }
42
56
  if (!this._pendingFetch) {
43
57
  this._pendingFetch = fetchJwks(this._url, this._timeoutDuration, this._options)
44
58
  .then((json) => {
@@ -1,3 +1,4 @@
1
+ import digest from '../runtime/digest.js';
1
2
  export const encoder = new TextEncoder();
2
3
  export const decoder = new TextDecoder();
3
4
  const MAX_INT32 = 2 ** 32;
@@ -36,7 +37,7 @@ export function uint32be(value) {
36
37
  export function lengthAndInput(input) {
37
38
  return concat(uint32be(input.length), input);
38
39
  }
39
- export async function concatKdf(digest, secret, bits, value) {
40
+ export async function concatKdf(secret, bits, value) {
40
41
  const iterations = Math.ceil((bits >> 3) / 32);
41
42
  let res;
42
43
  for (let iter = 1; iter <= iterations; iter++) {
@@ -1,6 +1,6 @@
1
1
  import { Buffer } from 'buffer';
2
2
  import { decoder } from '../lib/buffer_utils.js';
3
- let encodeImpl;
3
+ let encode;
4
4
  function normalize(input) {
5
5
  let encoded = input;
6
6
  if (encoded instanceof Uint8Array) {
@@ -9,12 +9,12 @@ function normalize(input) {
9
9
  return encoded;
10
10
  }
11
11
  if (Buffer.isEncoding('base64url')) {
12
- encodeImpl = (input) => Buffer.from(input).toString('base64url');
12
+ encode = (input) => Buffer.from(input).toString('base64url');
13
13
  }
14
14
  else {
15
- encodeImpl = (input) => Buffer.from(input).toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
15
+ encode = (input) => Buffer.from(input).toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
16
16
  }
17
17
  export const decodeBase64 = (input) => Buffer.from(input, 'base64');
18
18
  export const encodeBase64 = (input) => Buffer.from(input).toString('base64');
19
- export const encode = encodeImpl;
19
+ export { encode };
20
20
  export const decode = (input) => Buffer.from(normalize(input), 'base64');
@@ -2,7 +2,6 @@ import { diffieHellman, generateKeyPair as generateKeyPairCb, KeyObject } from '
2
2
  import { promisify } from 'util';
3
3
  import getNamedCurve from './get_named_curve.js';
4
4
  import { encoder, concat, uint32be, lengthAndInput, concatKdf } from '../lib/buffer_utils.js';
5
- import digest from './digest.js';
6
5
  import { JOSENotSupported } from '../util/errors.js';
7
6
  import { isCryptoKey } from './webcrypto.js';
8
7
  import { checkEncCryptoKey } from '../lib/crypto_key.js';
@@ -35,7 +34,7 @@ export async function deriveKey(publicKee, privateKee, algorithm, keyLength, apu
35
34
  }
36
35
  const value = concat(lengthAndInput(encoder.encode(algorithm)), lengthAndInput(apu), lengthAndInput(apv), uint32be(keyLength));
37
36
  const sharedSecret = diffieHellman({ privateKey, publicKey });
38
- return concatKdf(digest, sharedSecret, keyLength, value);
37
+ return concatKdf(sharedSecret, keyLength, value);
39
38
  }
40
39
  export async function generateEpk(kee) {
41
40
  let key;
@@ -0,0 +1,32 @@
1
+ import { decode as base64url } from './base64url.js';
2
+ import { decoder } from '../lib/buffer_utils.js';
3
+ import isObject from '../lib/is_object.js';
4
+ import { JWTInvalid } from './errors.js';
5
+ export function decodeJwt(jwt) {
6
+ if (typeof jwt !== 'string')
7
+ throw new JWTInvalid('JWTs must use Compact JWS serialization, JWT must be a string');
8
+ const { 1: payload, length } = jwt.split('.');
9
+ if (length === 5)
10
+ throw new JWTInvalid('Only JWTs using Compact JWS serialization can be decoded');
11
+ if (length !== 3)
12
+ throw new JWTInvalid('Invalid JWT');
13
+ if (!payload)
14
+ throw new JWTInvalid('JWTs must contain a payload');
15
+ let decoded;
16
+ try {
17
+ decoded = base64url(payload);
18
+ }
19
+ catch {
20
+ throw new JWTInvalid('Failed to parse the base64url encoded payload');
21
+ }
22
+ let result;
23
+ try {
24
+ result = JSON.parse(decoder.decode(decoded));
25
+ }
26
+ catch {
27
+ throw new JWTInvalid('Failed to parse the decoded payload as JSON');
28
+ }
29
+ if (!isObject(result))
30
+ throw new JWTInvalid('Invalid JWT Claims Set');
31
+ return result;
32
+ }
@@ -36,6 +36,7 @@ export { exportPKCS8, exportSPKI, exportJWK } from './key/export';
36
36
  export { importSPKI, importPKCS8, importX509, importJWK } from './key/import';
37
37
  export type { PEMImportOptions } from './key/import';
38
38
  export { decodeProtectedHeader } from './util/decode_protected_header';
39
+ export { decodeJwt } from './util/decode_jwt';
39
40
  export type { ProtectedHeaderParameters } from './util/decode_protected_header';
40
41
  export * as errors from './util/errors';
41
42
  export { generateKeyPair } from './key/generate_key_pair';
@@ -118,7 +118,7 @@ export interface JWK {
118
118
  [propName: string]: unknown
119
119
  }
120
120
  export interface GetKeyFunction<T, T2> {
121
- (protectedHeader: T, token: T2): Promise<KeyLike | Uint8Array>
121
+ (protectedHeader: T, token: T2): Promise<KeyLike | Uint8Array> | KeyLike | Uint8Array
122
122
  }
123
123
  export interface FlattenedJWSInput {
124
124
  header?: JWSHeaderParameters
@@ -0,0 +1,2 @@
1
+ import type { JWTPayload } from '../types';
2
+ export declare function decodeJwt(jwt: string): JWTPayload;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jose",
3
- "version": "4.4.0",
3
+ "version": "4.5.2",
4
4
  "description": "'JSON Web Almost Everything' - JWA, JWS, JWE, JWT, JWK, JWKS with no dependencies using runtime's native crypto",
5
5
  "keywords": [
6
6
  "browser",