unjwt 0.8.5 → 0.9.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 +34 -21
- package/dist/_chunks/algorithms.mjs +44 -20
- package/dist/_chunks/digest.d.mts +4 -4
- package/dist/_chunks/digest.mjs +23 -15
- package/dist/_chunks/error.d.mts +29 -42
- package/dist/_chunks/export.mjs +1 -4
- package/dist/_chunks/index.d.mts +270 -234
- package/dist/_chunks/index2.d.mts +82 -189
- package/dist/_chunks/index4.d.mts +223 -60
- package/dist/_chunks/index5.d.mts +68 -31
- package/dist/_chunks/index6.d.mts +45 -64
- package/dist/_chunks/jwe.mjs +53 -48
- package/dist/_chunks/jwk.mjs +9 -3
- package/dist/_chunks/jws.mjs +55 -50
- package/dist/_chunks/jwt.d.mts +13 -49
- package/dist/_chunks/jwt.mjs +4 -158
- package/dist/_chunks/jwt2.mjs +162 -0
- package/dist/_chunks/libs/unsecure.d.mts +0 -6
- package/dist/_chunks/limits.d.mts +3 -2
- package/dist/_chunks/payload.mjs +13 -7
- package/dist/_chunks/wrap.mjs +3 -1
- package/dist/core/internal.d.mts +50 -2
- package/dist/core/internal.mjs +3 -1
- package/dist/core/jwe/index.d.mts +1 -1
- package/dist/core/jwk/index.d.mts +2 -2
- package/dist/core/jwk/index.mjs +2 -2
- package/dist/dpop/index.d.mts +77 -18
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +4 -4
- package/dist/session/index.d.mts +568 -22
- package/dist/session/index.mjs +1386 -34
- package/dist/utils/index.d.mts +4 -4
- package/dist/utils/index.mjs +2 -10
- package/package.json +4 -32
- package/dist/_chunks/codec-jwe.d.mts +0 -280
- package/dist/_chunks/codec-jws.mjs +0 -1005
- package/dist/_chunks/cookie-attributes.d.mts +0 -12
- package/dist/_chunks/cookie.mjs +0 -50
- package/dist/adapters/elysia/index.d.mts +0 -123
- package/dist/adapters/elysia/index.mjs +0 -120
- package/dist/adapters/h3/index.d.mts +0 -135
- package/dist/adapters/h3/index.mjs +0 -117
- package/dist/adapters/h3v1/index.d.mts +0 -148
- package/dist/adapters/h3v1/index.mjs +0 -128
|
@@ -2,59 +2,96 @@ import "./error.mjs";
|
|
|
2
2
|
import { JOSEPayload, JOSEPayloadMode, JWKSet, JWSAlgorithm, JWSFlattenedSerialization, JWSGeneralSerialization, JWSKeyLookup, JWSMultiSignOptions, JWSMultiSigner, JWSMultiVerifyAllOptions, JWSMultiVerifyOptions, JWSMultiVerifyOutcome, JWSMultiVerifyResult, JWSSignJWK, JWSSignOptions, JWSVerifyJWK, JWSVerifyOptions, JWSVerifyResult, JWTClaims, PayloadOf } from "./index.mjs";
|
|
3
3
|
import "./limits.mjs";
|
|
4
4
|
/**
|
|
5
|
-
* Signs
|
|
6
|
-
*
|
|
7
|
-
* `b64` must
|
|
5
|
+
* Signs `payload` once per signer and returns a JWS General JSON Serialization (RFC 7515 §7.2.1).
|
|
6
|
+
* A signer without `alg` takes it from its JWK or `CryptoKey`.
|
|
7
|
+
* Signers that set `b64` must agree, and the value applies to every signer (RFC 7797 §3).
|
|
8
|
+
*
|
|
9
|
+
* @throws `ERR_JWS_INVALID` if `signers` is empty or not an array.
|
|
10
|
+
* @throws `ERR_JWS_SIGNER_ALG_INFERENCE` if a signer has no `alg` and its key does not name one.
|
|
11
|
+
* @throws `ERR_JWS_B64_INCONSISTENT` if two signers set different `b64` values.
|
|
12
|
+
* @throws `ERR_JWS_HEADER_PARAMS_NOT_DISJOINT` if a signer sets one parameter in both of its headers.
|
|
13
|
+
* @throws `ERR_JWS_ALG_NOT_ALLOWED` if an `alg` is `"none"` or not a supported signing algorithm.
|
|
14
|
+
* @throws `ERR_JWK_INVALID` if a key is a string or cannot sign with its `alg`.
|
|
15
|
+
* @throws `ERR_INVALID_OPTION` if an option is malformed, or a time option is set on a string or bytes payload.
|
|
16
|
+
* @throws `ERR_JWT_CLAIM_INVALID` if both members of a time option pair are set, or `cnf.jwk` is not a public JWK.
|
|
8
17
|
*/
|
|
9
18
|
export declare function signMulti(payload: JOSEPayload, signers: JWSMultiSigner[], options?: JWSMultiSignOptions): Promise<JWSGeneralSerialization>;
|
|
10
19
|
/**
|
|
11
20
|
* Verifies a JWS General or Flattened JSON Serialization (RFC 7515 §7.2) and returns the first signature that verifies.
|
|
12
|
-
*
|
|
21
|
+
* Signatures are tried in order, and a failure scoped to one signature moves on to the next.
|
|
22
|
+
* Once a signature verifies, a `crit`, payload or claim failure throws at once.
|
|
13
23
|
* The payload type follows `options.payload`, as in `verify`.
|
|
24
|
+
*
|
|
25
|
+
* @throws its own error if the only signature fails, as `verify` would.
|
|
26
|
+
* @throws `ERR_JWS_SIGNATURE_INVALID` if none of several signatures verifies, with one `JWTError` per signature as `cause`, in order.
|
|
27
|
+
* @throws `ERR_JWS_NO_MATCHING_SIGNER` if `strictSignerMatch` is set and no signature fits the key.
|
|
28
|
+
* @throws `ERR_JWS_INVALID_SERIALIZATION` if the object is not a General or Flattened JWS.
|
|
29
|
+
* @throws `ERR_JWS_INVALID` if the signatures exceed `limits.signatures`, or the verified signature fails `crit` or payload decoding.
|
|
30
|
+
* @throws `ERR_JWK_KEY_NOT_FOUND` if the key trials exceed `limits.keyAttempts`.
|
|
31
|
+
* @throws `ERR_INVALID_OPTION` if an option is malformed, or a claim option is set where claims are not validated.
|
|
32
|
+
* @throws `ERR_JWT_EXPIRED` if `exp` has passed or the token is older than `maxTokenAge`.
|
|
33
|
+
* @throws `ERR_JWT_NBF` if `nbf` is in the future.
|
|
34
|
+
* @throws `ERR_JWT_CLAIM_INVALID` if a claim does not match its option or has the wrong type.
|
|
35
|
+
* @throws `ERR_JWT_CLAIM_MISSING` if a claim in `requiredClaims` or one an option checks is absent.
|
|
14
36
|
*/
|
|
15
|
-
export declare function verifyMulti<T extends object = JWTClaims>(jws: JWSGeneralSerialization | JWSFlattenedSerialization, keyOrLookup: CryptoKey | JWKSet | JWSVerifyJWK | Uint8Array<ArrayBuffer> | JWSKeyLookup, options?: JWSMultiVerifyOptions<"json">
|
|
16
|
-
forceUint8Array?: false;
|
|
17
|
-
}): Promise<JWSMultiVerifyResult<T>>;
|
|
18
|
-
/** @deprecated `forceUint8Array` is replaced by `payload: "bytes"`; removed in v0.9.0. */
|
|
19
|
-
export declare function verifyMulti(jws: JWSGeneralSerialization | JWSFlattenedSerialization, keyOrLookup: CryptoKey | JWKSet | JWSVerifyJWK | Uint8Array<ArrayBuffer> | JWSKeyLookup, options: JWSMultiVerifyOptions & {
|
|
20
|
-
forceUint8Array: true;
|
|
21
|
-
}): Promise<JWSMultiVerifyResult<Uint8Array<ArrayBuffer>>>;
|
|
37
|
+
export declare function verifyMulti<T extends object = JWTClaims>(jws: JWSGeneralSerialization | JWSFlattenedSerialization, keyOrLookup: CryptoKey | JWKSet | JWSVerifyJWK | Uint8Array<ArrayBuffer> | JWSKeyLookup, options?: JWSMultiVerifyOptions<"json">): Promise<JWSMultiVerifyResult<T>>;
|
|
22
38
|
export declare function verifyMulti<Mode extends JOSEPayloadMode = JOSEPayloadMode>(jws: JWSGeneralSerialization | JWSFlattenedSerialization, keyOrLookup: CryptoKey | JWKSet | JWSVerifyJWK | Uint8Array<ArrayBuffer> | JWSKeyLookup, options?: JWSMultiVerifyOptions<Mode>): Promise<JWSMultiVerifyResult<PayloadOf<Mode>>>;
|
|
23
39
|
/**
|
|
24
|
-
* Verifies every signature
|
|
25
|
-
*
|
|
40
|
+
* Verifies every signature on its own and returns one outcome per signature, in signature order.
|
|
41
|
+
* A failed signature is an outcome with `verified: false` and its `error`.
|
|
42
|
+
* The payload type follows `options.payload`.
|
|
43
|
+
*
|
|
44
|
+
* @throws `ERR_JWS_INVALID_SERIALIZATION` if the object is not a General or Flattened JWS.
|
|
45
|
+
* @throws `ERR_JWS_INVALID` if the signatures exceed `limits.signatures`.
|
|
46
|
+
* @throws `ERR_JWK_KEY_NOT_FOUND` if the key trials exceed `limits.keyAttempts`.
|
|
47
|
+
* @throws `ERR_INVALID_OPTION` if an option is malformed, or a claim option is set where claims are not validated.
|
|
26
48
|
*
|
|
27
49
|
* @example
|
|
28
50
|
* const outcomes = await verifyMultiAll(jws, (header) => jwkSet);
|
|
29
51
|
* if (!outcomes.every((o) => o.verified)) throw new Error("not all signatures verified");
|
|
30
52
|
*/
|
|
31
|
-
export declare function verifyMultiAll<T extends object = JWTClaims>(jws: JWSGeneralSerialization | JWSFlattenedSerialization, keyResolver: JWSKeyLookup, options?: JWSMultiVerifyAllOptions<"json">
|
|
32
|
-
forceUint8Array?: false;
|
|
33
|
-
}): Promise<JWSMultiVerifyOutcome<T>[]>;
|
|
34
|
-
/** @deprecated `forceUint8Array` is replaced by `payload: "bytes"`; removed in v0.9.0. */
|
|
35
|
-
export declare function verifyMultiAll(jws: JWSGeneralSerialization | JWSFlattenedSerialization, keyResolver: JWSKeyLookup, options: JWSMultiVerifyAllOptions & {
|
|
36
|
-
forceUint8Array: true;
|
|
37
|
-
}): Promise<JWSMultiVerifyOutcome<Uint8Array<ArrayBuffer>>[]>;
|
|
53
|
+
export declare function verifyMultiAll<T extends object = JWTClaims>(jws: JWSGeneralSerialization | JWSFlattenedSerialization, keyResolver: JWSKeyLookup, options?: JWSMultiVerifyAllOptions<"json">): Promise<JWSMultiVerifyOutcome<T>[]>;
|
|
38
54
|
export declare function verifyMultiAll<Mode extends JOSEPayloadMode = JOSEPayloadMode>(jws: JWSGeneralSerialization | JWSFlattenedSerialization, keyResolver: JWSKeyLookup, options?: JWSMultiVerifyAllOptions<Mode>): Promise<JWSMultiVerifyOutcome<PayloadOf<Mode>>[]>;
|
|
39
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Converts a General JWS with exactly one signature to the Flattened form (RFC 7515 §7.2.2).
|
|
57
|
+
*
|
|
58
|
+
* @throws `ERR_JWS_INVALID_SERIALIZATION` if the input has no `signatures` array, or not exactly one signature.
|
|
59
|
+
*/
|
|
40
60
|
export declare function generalToFlattenedJWS(jws: JWSGeneralSerialization): JWSFlattenedSerialization;
|
|
41
61
|
/**
|
|
42
|
-
*
|
|
43
|
-
* An
|
|
62
|
+
* Signs `payload` and returns a JWS Compact Serialization (RFC 7515 §7.1).
|
|
63
|
+
* An object payload is serialized as JSON and gets `typ: "JWT"` unless `protectedHeader` sets one.
|
|
64
|
+
* Pass `alg` unless the JWK names one or is an EC or Ed25519 JWK, whose curve names it (`ES256`, `ES384`, `ES512`, `EdDSA`).
|
|
65
|
+
* With `b64: false`, the payload must not contain "." (RFC 7797 §5.2).
|
|
66
|
+
*
|
|
67
|
+
* @throws `ERR_JWS_ALG_MISSING` if no `alg` is given and the key names none: a `CryptoKey`, raw bytes, or an RSA or `oct` JWK without `alg`.
|
|
68
|
+
* @throws `ERR_JWS_ALG_NOT_ALLOWED` if `alg` is `"none"` or not a supported signing algorithm.
|
|
69
|
+
* @throws `ERR_JWK_INVALID` if the key is a string or cannot sign with `alg`.
|
|
70
|
+
* @throws `ERR_INVALID_OPTION` if an option is malformed, or a time option is set on a string or bytes payload.
|
|
71
|
+
* @throws `ERR_JWT_CLAIM_INVALID` if both members of a time option pair are set, or `cnf.jwk` is not a public JWK.
|
|
72
|
+
* @throws `ERR_JWS_INVALID` if an unencoded payload contains ".".
|
|
44
73
|
*/
|
|
45
74
|
export declare function sign(payload: JOSEPayload, key: JWSSignJWK, options?: JWSSignOptions): Promise<string>;
|
|
46
75
|
export declare function sign(payload: JOSEPayload, key: CryptoKey | Uint8Array<ArrayBuffer>, options: JWSSignOptions & {
|
|
47
76
|
alg: JWSAlgorithm;
|
|
48
77
|
}): Promise<string>;
|
|
49
78
|
/**
|
|
50
|
-
* Verifies a JWS Compact Serialization against a key, JWK Set or key
|
|
79
|
+
* Verifies a JWS Compact Serialization against a key, a JWK Set or a key lookup function.
|
|
80
|
+
* The header `alg` must be in `algorithms`, else match the JWK's `alg`, else fit the key type.
|
|
81
|
+
* Pass `algorithms` for a raw-bytes key.
|
|
51
82
|
* The payload type follows `options.payload`: claims `T` for `"json"` (default), `string`, or `Uint8Array`.
|
|
83
|
+
* A `"json"` payload has its claims validated unless `validateClaims: false`.
|
|
84
|
+
*
|
|
85
|
+
* @throws `ERR_JWS_INVALID` if the token is malformed, its `b64` or `typ` is wrong, or a `crit` member is not recognized.
|
|
86
|
+
* @throws `ERR_JWS_ALG_NOT_ALLOWED` if the header `alg` is unsupported or not allowed for the key.
|
|
87
|
+
* @throws `ERR_JWS_SIGNATURE_INVALID` if the signature does not verify.
|
|
88
|
+
* @throws `ERR_JWK_INVALID` if no key is given, or the key cannot verify with the header `alg`.
|
|
89
|
+
* @throws `ERR_JWK_KEY_NOT_FOUND` if the lookup returns `undefined` or throws an error that is not a `JWTError` (as `cause`), or a JWK Set has no candidate.
|
|
90
|
+
* @throws `ERR_INVALID_OPTION` if an option is malformed, or a claim option is set where claims are not validated.
|
|
91
|
+
* @throws `ERR_JWT_EXPIRED` if `exp` has passed or the token is older than `maxTokenAge`.
|
|
92
|
+
* @throws `ERR_JWT_NBF` if `nbf` is in the future.
|
|
93
|
+
* @throws `ERR_JWT_CLAIM_INVALID` if a claim does not match its option or has the wrong type.
|
|
94
|
+
* @throws `ERR_JWT_CLAIM_MISSING` if a claim in `requiredClaims` or one an option checks is absent.
|
|
52
95
|
*/
|
|
53
|
-
export declare function verify<T extends object = JWTClaims>(jws: string, key: CryptoKey | JWKSet | JWSVerifyJWK | Uint8Array<ArrayBuffer> | JWSKeyLookup, options?: JWSVerifyOptions<"json">
|
|
54
|
-
forceUint8Array?: false;
|
|
55
|
-
}): Promise<JWSVerifyResult<T>>;
|
|
56
|
-
/** @deprecated `forceUint8Array` is replaced by `payload: "bytes"`; removed in v0.9.0. */
|
|
57
|
-
export declare function verify(jws: string, key: CryptoKey | JWKSet | JWSVerifyJWK | Uint8Array<ArrayBuffer> | JWSKeyLookup, options: JWSVerifyOptions & {
|
|
58
|
-
forceUint8Array: true;
|
|
59
|
-
}): Promise<JWSVerifyResult<Uint8Array<ArrayBuffer>>>;
|
|
96
|
+
export declare function verify<T extends object = JWTClaims>(jws: string, key: CryptoKey | JWKSet | JWSVerifyJWK | Uint8Array<ArrayBuffer> | JWSKeyLookup, options?: JWSVerifyOptions<"json">): Promise<JWSVerifyResult<T>>;
|
|
60
97
|
export declare function verify<Mode extends JOSEPayloadMode = JOSEPayloadMode>(jws: string, key: CryptoKey | JWKSet | JWSVerifyJWK | Uint8Array<ArrayBuffer> | JWSKeyLookup, options?: JWSVerifyOptions<Mode>): Promise<JWSVerifyResult<PayloadOf<Mode>>>;
|
|
@@ -1,113 +1,94 @@
|
|
|
1
1
|
import "./libs/unsecure.mjs";
|
|
2
2
|
import "./error.mjs";
|
|
3
3
|
import { JWK, JWKSet, JWK_EC_Private, JWK_EC_Public, JWK_OKP_Private, JWK_OKP_Public, JWK_Private, JWK_Public, JWK_RSA_Private, JWK_RSA_Public, JWK_oct, JWSAlgorithm, KeyManagementAlgorithm } from "./index.mjs";
|
|
4
|
+
import "./jwt.mjs";
|
|
4
5
|
import "./digest.mjs";
|
|
5
|
-
|
|
6
|
-
/** Decoder output shape. `"bytes"` aliases `"uint8array"`. */
|
|
6
|
+
/** The decoder output: `"string"` for UTF-8 text, `"uint8array"` or `"bytes"` for the bytes. */
|
|
7
7
|
export type DecodeReturnAs = "string" | "uint8array" | "bytes";
|
|
8
8
|
export interface Base64ParseOptions {
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* Return the decoded bytes as UTF-8 text (`"string"`) or as a `Uint8Array`.
|
|
11
11
|
* @default "string"
|
|
12
12
|
*/
|
|
13
13
|
returnAs?: DecodeReturnAs;
|
|
14
14
|
}
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Encodes bytes, or the UTF-8 bytes of a string, as unpadded base64url (RFC 7515 §2).
|
|
17
|
+
* @throws {UnsecureError} `INVALID_TYPE` if `data` is neither a string nor a `Uint8Array`.
|
|
18
|
+
*/
|
|
16
19
|
export declare function base64UrlStringify(data: string | Uint8Array): string;
|
|
17
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Encodes bytes, or the UTF-8 bytes of a string, as padded standard base64 (RFC 4648 §4).
|
|
22
|
+
* @throws {UnsecureError} `INVALID_TYPE` if `data` is neither a string nor a `Uint8Array`.
|
|
23
|
+
*/
|
|
18
24
|
export declare function base64Stringify(data: string | Uint8Array): string;
|
|
19
25
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
26
|
+
* Decodes unpadded base64url (RFC 7515 §2). Only the canonical encoding of a byte string passes.
|
|
27
|
+
* `=`, whitespace, characters outside the URL-safe alphabet and set bits past the final byte fail.
|
|
28
|
+
* A `string` result must be valid UTF-8.
|
|
23
29
|
*
|
|
24
|
-
* @throws {UnsecureError} `MALFORMED`
|
|
30
|
+
* @throws {UnsecureError} `MALFORMED` if `text` is not such an encoding.
|
|
31
|
+
* @throws {UnsecureError} `INVALID_TYPE` if `text` is not a string.
|
|
25
32
|
*/
|
|
26
33
|
export declare function base64UrlParse<T extends DecodeReturnAs>(text: string, options: Base64ParseOptions & {
|
|
27
34
|
returnAs: T;
|
|
28
35
|
}): T extends "string" ? string : Uint8Array<ArrayBuffer>;
|
|
29
36
|
export declare function base64UrlParse(text: string, options?: Base64ParseOptions): string;
|
|
30
37
|
/**
|
|
31
|
-
*
|
|
32
|
-
* present
|
|
38
|
+
* Decodes padded standard base64 (RFC 4648 §4). Only the canonical encoding passes.
|
|
39
|
+
* The `=` padding must be present. Whitespace and set bits past the final byte fail.
|
|
40
|
+
* A `string` result must be valid UTF-8.
|
|
33
41
|
*
|
|
34
|
-
* @throws {UnsecureError} `MALFORMED`
|
|
42
|
+
* @throws {UnsecureError} `MALFORMED` if `text` is not such an encoding.
|
|
43
|
+
* @throws {UnsecureError} `INVALID_TYPE` if `text` is not a string.
|
|
35
44
|
*/
|
|
36
45
|
export declare function base64Parse<T extends DecodeReturnAs>(text: string, options: Base64ParseOptions & {
|
|
37
46
|
returnAs: T;
|
|
38
47
|
}): T extends "string" ? string : Uint8Array<ArrayBuffer>;
|
|
39
48
|
export declare function base64Parse(text: string, options?: Base64ParseOptions): string;
|
|
40
|
-
/**
|
|
41
|
-
export type DecodeOptions = Base64ParseOptions;
|
|
42
|
-
/** @deprecated Renamed to {@link base64UrlStringify} in v0.8.0; removed in v0.9.0. */
|
|
43
|
-
export declare const base64UrlEncode: typeof base64UrlStringify;
|
|
44
|
-
/** @deprecated Renamed to {@link base64UrlParse} in v0.8.0; removed in v0.9.0. */
|
|
45
|
-
export declare const base64UrlDecode: typeof base64UrlParse;
|
|
46
|
-
/** @deprecated Renamed to {@link base64Stringify} in v0.8.0; removed in v0.9.0. */
|
|
47
|
-
export declare const base64Encode: typeof base64Stringify;
|
|
48
|
-
/** @deprecated Renamed to {@link base64Parse} in v0.8.0; removed in v0.9.0. */
|
|
49
|
-
export declare const base64Decode: typeof base64Parse;
|
|
50
|
-
/** Concatenate multiple `Uint8Array`s into a single contiguous buffer. */
|
|
49
|
+
/** Returns one `Uint8Array` with the bytes of `arrays` in order. */
|
|
51
50
|
export declare function concatUint8Arrays(...arrays: Readonly<Uint8Array<ArrayBuffer>[]>): Uint8Array<ArrayBuffer>;
|
|
52
|
-
/**
|
|
51
|
+
/** Returns `item` when it is an array, else `[item]`. */
|
|
53
52
|
export declare function maybeArray<T>(item: T | T[]): T[];
|
|
54
53
|
/** Whether `key` is a JWK whose `kty` this library models (`oct`, `RSA`, `EC`, `OKP`). */
|
|
55
54
|
export declare function isJWK(key: unknown): key is JWK;
|
|
56
|
-
/** Whether `key` is an object with a `keys` array. */
|
|
55
|
+
/** Whether `key` is an object with a `keys` array. The entries are not checked. */
|
|
57
56
|
export declare function isJWKSet(key: unknown): key is JWKSet;
|
|
58
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* Asserts that `key` is a `CryptoKey`.
|
|
59
|
+
* @throws `ERR_JWK_INVALID` if it is not.
|
|
60
|
+
*/
|
|
59
61
|
export declare function assertCryptoKey(key: unknown): asserts key is CryptoKey;
|
|
60
|
-
/** Whether `key` is a `CryptoKey
|
|
62
|
+
/** Whether `key` is a `CryptoKey`. A key from another realm matches too. */
|
|
61
63
|
export declare function isCryptoKey(key: unknown): key is CryptoKey;
|
|
62
|
-
/** Whether `key`
|
|
64
|
+
/** Whether `key` has a `CryptoKey` in both `publicKey` and `privateKey`. */
|
|
63
65
|
export declare const isCryptoKeyPair: (key: unknown) => key is CryptoKeyPair;
|
|
64
|
-
/** Whether the JWK is symmetric
|
|
66
|
+
/** Whether the JWK is symmetric: `kty` is `oct` and `k` is a string. Its `k` is secret. */
|
|
65
67
|
export declare function isSymmetricJWK<T extends JWK>(key: T): key is Extract<T, JWK_oct>;
|
|
66
68
|
export declare function isSymmetricJWK(key: unknown): key is JWK_oct;
|
|
67
|
-
/** Whether the JWK is asymmetric
|
|
69
|
+
/** Whether the JWK is asymmetric: `kty` is `RSA`, `EC` or `OKP`. */
|
|
68
70
|
export declare function isAsymmetricJWK<T extends JWK>(key: T): key is Exclude<T, JWK_oct>;
|
|
69
71
|
export declare function isAsymmetricJWK(key: unknown): key is Exclude<JWK, JWK_oct>;
|
|
70
72
|
/**
|
|
71
|
-
* Whether the JWK
|
|
72
|
-
* so `!isPrivateJWK(key)`
|
|
73
|
+
* Whether the JWK is an asymmetric private key: it has a string `d`.
|
|
74
|
+
* It is `false` for `oct` keys, so `!isPrivateJWK(key)` does not mean safe to publish.
|
|
75
|
+
* Use `isPublicJWK` for that check.
|
|
73
76
|
*/
|
|
74
77
|
export declare function isPrivateJWK<T extends JWK>(key: T): key is Extract<T, JWK_EC_Private | JWK_OKP_Private | JWK_RSA_Private>;
|
|
75
78
|
export declare function isPrivateJWK(key: unknown): key is JWK_Private;
|
|
76
|
-
/**
|
|
79
|
+
/**
|
|
80
|
+
* Whether the JWK is an asymmetric public key: no `d`, no `k`, and its public members present.
|
|
81
|
+
* Use it to check that a JWK is safe to publish.
|
|
82
|
+
*/
|
|
77
83
|
export declare function isPublicJWK<T extends JWK>(key: T): key is Exclude<Extract<T, JWK_EC_Public | JWK_OKP_Public | JWK_RSA_Public>, JWK_Private>;
|
|
78
84
|
export declare function isPublicJWK(key: unknown): key is JWK_Public;
|
|
79
|
-
/** How a JWK Set's entries are read during algorithm inference. */
|
|
80
|
-
export interface InferAlgorithmsOptions {
|
|
81
|
-
/** Admit `oct` entries of a JWK Set. Defaults to `false`. */
|
|
82
|
-
allowSymmetric?: boolean;
|
|
83
|
-
}
|
|
84
85
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
86
|
+
* Returns the JWS algorithms `key` unambiguously supports: a pair's public key's, a JWK Set's union.
|
|
87
|
+
* Raw bytes, a string, a lookup function or an ambiguous JWK yield `undefined`: pass `algorithms`.
|
|
87
88
|
*/
|
|
88
|
-
export declare function inferJWSAllowedAlgorithms(key: unknown
|
|
89
|
+
export declare function inferJWSAllowedAlgorithms(key: unknown): JWSAlgorithm[] | undefined;
|
|
89
90
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
91
|
+
* Returns the JWE key management algorithms `key` unambiguously supports: a pair's private key's,
|
|
92
|
+
* a JWK Set's union, the PBES2 three for a password. Raw bytes, a function or an ambiguous JWK yield `undefined`.
|
|
92
93
|
*/
|
|
93
|
-
export declare function inferJWEAllowedAlgorithms(key: unknown
|
|
94
|
-
/** @deprecated Use `ParsePayloadOptions` from `unjwt/internal`; removed in v0.9.0. */
|
|
95
|
-
export type DecodePayloadOptions = ParsePayloadOptions;
|
|
96
|
-
/** @deprecated Use `ComputeJwtTimeClaimsOptions` from `unjwt/internal`; removed in v0.9.0. */
|
|
97
|
-
export type ComputeJwtTimeClaimsOptions = ComputeJwtTimeClaimsOptions$1;
|
|
98
|
-
/** @deprecated Use `applyTypDefault` from `unjwt/internal`; removed in v0.9.0. */
|
|
99
|
-
export declare const applyTypCtyDefaults: typeof applyTypDefault;
|
|
100
|
-
/** @deprecated Use `parsePayloadBytes` from `unjwt/internal`; removed in v0.9.0. */
|
|
101
|
-
export declare const decodePayloadFromBytes: typeof parsePayloadBytes;
|
|
102
|
-
/** @deprecated Use `parsePayloadSegment` from `unjwt/internal`; removed in v0.9.0. */
|
|
103
|
-
export declare const decodePayloadFromB64UrlSegment: typeof parsePayloadSegment;
|
|
104
|
-
/** @deprecated Use `getPlaintextBytes` from `unjwt/internal`; removed in v0.9.0. */
|
|
105
|
-
export declare const getPlaintextBytes: typeof getPlaintextBytes$1;
|
|
106
|
-
/** @deprecated Use `computeJwtTimeClaims` from `unjwt/internal`; removed in v0.9.0. */
|
|
107
|
-
export declare const computeJwtTimeClaims: typeof computeJwtTimeClaims$1;
|
|
108
|
-
/** @deprecated Use `validateCriticalHeadersJWS` from `unjwt/internal`; removed in v0.9.0. */
|
|
109
|
-
export declare const validateCriticalHeadersJWS: typeof validateCriticalHeadersJWS$1;
|
|
110
|
-
/** @deprecated Use `validateCriticalHeadersJWE` from `unjwt/internal`; removed in v0.9.0. */
|
|
111
|
-
export declare const validateCriticalHeadersJWE: typeof validateCriticalHeadersJWE$1;
|
|
112
|
-
/** @deprecated Use `assertCritShape` from `unjwt/internal`; removed in v0.9.0. */
|
|
113
|
-
export declare const assertCritShape: typeof assertCritShape$1;
|
|
94
|
+
export declare function inferJWEAllowedAlgorithms(key: unknown): KeyManagementAlgorithm[] | undefined;
|
package/dist/_chunks/jwe.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { secureRandomBytes, textEncoder } from "./libs/unsecure.mjs";
|
|
2
2
|
import { JWTError } from "../error.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { base64UrlStringify, isCryptoKey, isJWK, isJWKSet } from "./jwt.mjs";
|
|
4
|
+
import { applyTypDefault, computeJwtTimeClaims, getPlaintextBytes, parsePayloadBytes, validateCriticalHeadersJWE } from "./jwt2.mjs";
|
|
5
|
+
import { DEFAULT_PBES2_ITERATIONS, asDecryptionError, asKeyLookupError, assertCompactToken, assertConsumerOptions, assertDirectKeyRole, assertEntryLimit, assertJWEEncSupported, assertJWKSetCandidates, assertOptionEntry, assertPBES2Iterations, assertPBES2Params, assertProducerConfirmation, assertProducerCrit, assertProducerKeyHeaders, assertProtectedOnlyOption, assertSerializationObject, bitLengthCEK, bitLengthIV, bitLengthTag, buildECDHAgreementParams, buildJWKSetFilter, checkAlgAllowed, checkAlgSupported, checkJWEEncSupported, checkProtectedTyp, checkSerializationEntry, copyJsonObject, createKeyAttemptBudget, decodeProtectedHeader, decodeSegment, decrypt as decrypt$1, encrypt as encrypt$1, encryptKey, generateCEK, generateIV, getJWKsFromSet, importKey, keyMatchesHeader, resolveKeyInput, resolveLimit, resolvePasswordKey, serializeProtectedHeader } from "./algorithms.mjs";
|
|
5
6
|
import { inferJWEAllowedAlgorithms, isContentEncryptionAlgorithm, isKeyManagementAlgorithm, isPBES2Algorithm } from "./digest.mjs";
|
|
6
7
|
import { resolvePayloadMode, validateClaimsIfJson } from "./payload.mjs";
|
|
7
8
|
import { unwrapKey } from "./wrap.mjs";
|
|
@@ -21,9 +22,6 @@ function checkJWEAlgSupported(alg) {
|
|
|
21
22
|
role: "key management algorithm"
|
|
22
23
|
});
|
|
23
24
|
}
|
|
24
|
-
function isJWEAlgSupported(alg) {
|
|
25
|
-
return isKeyManagementAlgorithm(alg);
|
|
26
|
-
}
|
|
27
25
|
function assertJWEAlgSupported(alg) {
|
|
28
26
|
const error = checkJWEAlgSupported(alg);
|
|
29
27
|
if (error) throw error;
|
|
@@ -124,39 +122,45 @@ function buildKeyManagementParams(alg, source) {
|
|
|
124
122
|
return params;
|
|
125
123
|
}
|
|
126
124
|
async function unwrapAndDecrypt(alg, rawKeyMaterial, options) {
|
|
127
|
-
const { enc, encryptedKey, unwrapKeyOptions, ciphertext, iv, tag, aad, kid,
|
|
125
|
+
const { enc, encryptedKey, unwrapKeyOptions, ciphertext, iv, tag, aad, kid, keyCandidates, pbes2Budget, keyAttempts } = options;
|
|
128
126
|
if (isJWKSet(rawKeyMaterial)) {
|
|
129
127
|
const candidates = getJWKsFromSet(rawKeyMaterial, buildJWKSetFilter({
|
|
130
128
|
kid,
|
|
131
129
|
alg
|
|
132
|
-
}, {
|
|
133
|
-
op: "enc",
|
|
134
|
-
allowSymmetric
|
|
135
|
-
}));
|
|
130
|
+
}, { op: "enc" }));
|
|
136
131
|
assertJWKSetCandidates(candidates, {
|
|
137
132
|
limit: keyCandidates,
|
|
138
133
|
kid
|
|
139
134
|
});
|
|
140
135
|
const attempts = [];
|
|
141
|
-
for (const candidate of candidates)
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
136
|
+
for (const candidate of candidates) {
|
|
137
|
+
try {
|
|
138
|
+
if (alg === "dir") assertDirectKeyRole(candidate, enc);
|
|
139
|
+
pbes2Budget.spend(unwrapKeyOptions);
|
|
140
|
+
} catch (err) {
|
|
141
|
+
attempts.push(asDecryptionError(err));
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
keyAttempts?.spend();
|
|
145
|
+
try {
|
|
146
|
+
const unwrappingKey = await importKey(candidate, {
|
|
147
|
+
alg,
|
|
148
|
+
expect: "private"
|
|
149
|
+
});
|
|
150
|
+
const cek = await unwrapKey(encryptedKey, unwrappingKey, unwrapKeyOptions);
|
|
151
|
+
return {
|
|
152
|
+
cek,
|
|
153
|
+
plaintextBytes: await decrypt$1(enc, cek, ciphertext, iv, tag, aad)
|
|
154
|
+
};
|
|
155
|
+
} catch (err) {
|
|
156
|
+
attempts.push(asDecryptionError(err));
|
|
157
|
+
}
|
|
155
158
|
}
|
|
156
159
|
throw new JWTError("JWE decryption failed.", "ERR_JWE_DECRYPTION_FAILED", attempts);
|
|
157
160
|
}
|
|
158
161
|
if (alg === "dir") assertDirectKeyRole(rawKeyMaterial, enc);
|
|
159
162
|
pbes2Budget.spend(unwrapKeyOptions);
|
|
163
|
+
keyAttempts?.spend();
|
|
160
164
|
try {
|
|
161
165
|
const unwrappingKey = await importKey(resolvePasswordKey(alg, rawKeyMaterial), {
|
|
162
166
|
alg,
|
|
@@ -349,6 +353,7 @@ async function decryptMulti(jwe, keyOrLookup, options = {}) {
|
|
|
349
353
|
const payloadMode = resolvePayloadMode(options);
|
|
350
354
|
const recipientLimit = resolveLimit(options.limits, "recipients");
|
|
351
355
|
const keyCandidates = resolveLimit(options.limits, "keyCandidates");
|
|
356
|
+
const keyAttempts = createKeyAttemptBudget(resolveLimit(options.limits, "keyAttempts"));
|
|
352
357
|
const pbes2Budget = createPBES2Budget(resolveLimit(options.limits, "pbes2Iterations"));
|
|
353
358
|
const general = _normalizeSerialization(jwe);
|
|
354
359
|
if (typeof general.iv !== "string" || typeof general.ciphertext !== "string" || typeof general.tag !== "string" || !Array.isArray(general.recipients) || general.recipients.length === 0) throw new JWTError("Invalid JWE JSON Serialization: missing iv/ciphertext/tag/recipients.", "ERR_JWE_INVALID_SERIALIZATION");
|
|
@@ -395,7 +400,7 @@ async function decryptMulti(jwe, keyOrLookup, options = {}) {
|
|
|
395
400
|
const segmentError = checkJWEContentSegments(enc, ivBytes, tagBytes);
|
|
396
401
|
if (segmentError) throw segmentError;
|
|
397
402
|
const contentAadBytes = textEncoder.encode(general.aad === void 0 ? protectedHeaderEncoded : `${protectedHeaderEncoded}.${general.aad}`);
|
|
398
|
-
|
|
403
|
+
const failures = [];
|
|
399
404
|
let cryptoAttempted = false;
|
|
400
405
|
for (let i = 0; i < general.recipients.length; i++) {
|
|
401
406
|
const shapeError = checkSerializationEntry(general.recipients[i], {
|
|
@@ -405,7 +410,7 @@ async function decryptMulti(jwe, keyOrLookup, options = {}) {
|
|
|
405
410
|
strings: ["encrypted_key"]
|
|
406
411
|
});
|
|
407
412
|
if (shapeError) {
|
|
408
|
-
|
|
413
|
+
failures.push(shapeError);
|
|
409
414
|
continue;
|
|
410
415
|
}
|
|
411
416
|
const wireRecipient = general.recipients[i];
|
|
@@ -415,28 +420,29 @@ async function decryptMulti(jwe, keyOrLookup, options = {}) {
|
|
|
415
420
|
});
|
|
416
421
|
const recipientError = checkJWEUnprotectedCrit(recipientHeader) ?? checkJWEZip(recipientHeader);
|
|
417
422
|
if (recipientError) {
|
|
418
|
-
|
|
423
|
+
failures.push(recipientError);
|
|
419
424
|
continue;
|
|
420
425
|
}
|
|
421
426
|
try {
|
|
422
427
|
_assertDisjointHeaders(protectedHeader, sharedUnprotected, recipientHeader);
|
|
423
428
|
} catch (err) {
|
|
424
|
-
|
|
429
|
+
failures.push(asDecryptionError(err));
|
|
425
430
|
continue;
|
|
426
431
|
}
|
|
427
432
|
const effective = _mergeHeaders(protectedHeader, sharedUnprotected, recipientHeader);
|
|
428
433
|
const alg = effective.alg;
|
|
429
434
|
if (!alg) {
|
|
430
|
-
|
|
435
|
+
failures.push(new JWTError(`Recipient[${i}] has no "alg" in the effective header.`, "ERR_JWE_INVALID"));
|
|
431
436
|
continue;
|
|
432
437
|
}
|
|
433
|
-
|
|
434
|
-
|
|
438
|
+
const supportError = checkJWEAlgSupported(alg);
|
|
439
|
+
if (supportError) {
|
|
440
|
+
failures.push(supportError);
|
|
435
441
|
continue;
|
|
436
442
|
}
|
|
437
443
|
const directKeyError = checkJWEDirectEncryptedKey(alg, wireRecipient.encrypted_key);
|
|
438
444
|
if (directKeyError) {
|
|
439
|
-
|
|
445
|
+
failures.push(directKeyError);
|
|
440
446
|
continue;
|
|
441
447
|
}
|
|
442
448
|
let rawKeyMaterial;
|
|
@@ -447,22 +453,21 @@ async function decryptMulti(jwe, keyOrLookup, options = {}) {
|
|
|
447
453
|
what: `recipient[${i}]`
|
|
448
454
|
});
|
|
449
455
|
} catch (err) {
|
|
450
|
-
|
|
456
|
+
failures.push(asKeyLookupError(err, `recipient[${i}]`));
|
|
451
457
|
continue;
|
|
452
458
|
}
|
|
453
459
|
const algError = checkAlgAllowed(alg, rawKeyMaterial, {
|
|
454
460
|
ctx: JWE_ALG_CTX,
|
|
455
|
-
algorithms: options.algorithms
|
|
456
|
-
allowSymmetric: options.allowSymmetricJWKSet
|
|
461
|
+
algorithms: options.algorithms
|
|
457
462
|
});
|
|
458
463
|
if (algError) {
|
|
459
|
-
|
|
464
|
+
failures.push(algError);
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
if (options.strictRecipientMatch && !keyMatchesHeader(effective, rawKeyMaterial, { ctx: JWE_ALG_CTX })) {
|
|
468
|
+
failures.push(new JWTError(`JWE recipient[${i}] does not match the key under strictRecipientMatch.`, "ERR_JWE_NO_MATCHING_RECIPIENT"));
|
|
460
469
|
continue;
|
|
461
470
|
}
|
|
462
|
-
if (options.strictRecipientMatch && !keyMatchesHeader(effective, rawKeyMaterial, {
|
|
463
|
-
ctx: JWE_ALG_CTX,
|
|
464
|
-
allowSymmetric: options.allowSymmetricJWKSet
|
|
465
|
-
})) continue;
|
|
466
471
|
let encryptedKeyBytes;
|
|
467
472
|
try {
|
|
468
473
|
encryptedKeyBytes = decodeSegment(wireRecipient.encrypted_key ?? "", {
|
|
@@ -470,7 +475,7 @@ async function decryptMulti(jwe, keyOrLookup, options = {}) {
|
|
|
470
475
|
what: "Encrypted key"
|
|
471
476
|
});
|
|
472
477
|
} catch (err) {
|
|
473
|
-
|
|
478
|
+
failures.push(asDecryptionError(err));
|
|
474
479
|
continue;
|
|
475
480
|
}
|
|
476
481
|
const unwrapKeyOpts = buildUnwrapKeyOptions(effective, {
|
|
@@ -490,12 +495,13 @@ async function decryptMulti(jwe, keyOrLookup, options = {}) {
|
|
|
490
495
|
tag: tagBytes,
|
|
491
496
|
aad: contentAadBytes,
|
|
492
497
|
kid: effective.kid,
|
|
493
|
-
allowSymmetric: options.allowSymmetricJWKSet,
|
|
494
498
|
keyCandidates,
|
|
495
|
-
pbes2Budget
|
|
499
|
+
pbes2Budget,
|
|
500
|
+
keyAttempts
|
|
496
501
|
}));
|
|
497
502
|
} catch (err) {
|
|
498
|
-
|
|
503
|
+
if (keyAttempts.exhausted) throw keyAttempts.exhausted;
|
|
504
|
+
failures.push(asDecryptionError(err));
|
|
499
505
|
continue;
|
|
500
506
|
}
|
|
501
507
|
const payload = decodeJWEPayload(plaintextBytes, payloadMode);
|
|
@@ -514,7 +520,8 @@ async function decryptMulti(jwe, keyOrLookup, options = {}) {
|
|
|
514
520
|
return result;
|
|
515
521
|
}
|
|
516
522
|
if (options.strictRecipientMatch && !cryptoAttempted) throw new JWTError("No recipient matched the provided key under strictRecipientMatch.", "ERR_JWE_NO_MATCHING_RECIPIENT");
|
|
517
|
-
|
|
523
|
+
if (failures.length === 1) throw failures[0];
|
|
524
|
+
throw new JWTError("JWE decryption failed.", "ERR_JWE_DECRYPTION_FAILED", failures);
|
|
518
525
|
}
|
|
519
526
|
function generalToFlattened(jwe) {
|
|
520
527
|
assertSerializationObject(jwe, "JWE");
|
|
@@ -634,8 +641,7 @@ async function decrypt(jwe, key, options = {}) {
|
|
|
634
641
|
});
|
|
635
642
|
const algError = checkAlgAllowed(alg, rawKeyMaterial, {
|
|
636
643
|
ctx: JWE_ALG_CTX,
|
|
637
|
-
algorithms: options.algorithms
|
|
638
|
-
allowSymmetric: options.allowSymmetricJWKSet
|
|
644
|
+
algorithms: options.algorithms
|
|
639
645
|
});
|
|
640
646
|
if (algError) throw algError;
|
|
641
647
|
const unwrapKeyOpts = buildUnwrapKeyOptions(protectedHeader, {
|
|
@@ -652,7 +658,6 @@ async function decrypt(jwe, key, options = {}) {
|
|
|
652
658
|
tag: contentAuthTagBytes,
|
|
653
659
|
aad: aadBytes,
|
|
654
660
|
kid: protectedHeader.kid,
|
|
655
|
-
allowSymmetric: options.allowSymmetricJWKSet,
|
|
656
661
|
keyCandidates: keyCandidateLimit,
|
|
657
662
|
pbes2Budget
|
|
658
663
|
});
|
package/dist/_chunks/jwk.mjs
CHANGED
|
@@ -289,7 +289,9 @@ async function deriveKeyFromPassword(password, options) {
|
|
|
289
289
|
const passwordBytes = typeof password === "string" ? textEncoder.encode(password) : password;
|
|
290
290
|
const derivedBytes = await deriveKey(saltBytes, alg, iterations, passwordBytes);
|
|
291
291
|
const wrappingAlg = innerKeyWrapAlg(alg);
|
|
292
|
-
const
|
|
292
|
+
const defaultUsages = ["wrapKey", "unwrapKey"];
|
|
293
|
+
const finalUsages = keyUsage ?? defaultUsages;
|
|
294
|
+
for (const usage of finalUsages) if (!defaultUsages.includes(usage)) throw new JWTError(`keyUsage "${usage}" is not valid for an AES-KW key, use "wrapKey" or "unwrapKey".`, "ERR_INVALID_OPTION");
|
|
293
295
|
const finalExtractable = exportToJWK ? true : extractable === true;
|
|
294
296
|
const derivedKey = await crypto.subtle.importKey("raw", derivedBytes, { name: "AES-KW" }, finalExtractable, finalUsages);
|
|
295
297
|
if (exportToJWK) {
|
|
@@ -417,10 +419,14 @@ async function deriveSharedSecret(publicKey, privateKey, options) {
|
|
|
417
419
|
});
|
|
418
420
|
let keyLength;
|
|
419
421
|
if (requestedKeyLength !== void 0) keyLength = requestedKeyLength;
|
|
420
|
-
else if (alg === "ECDH-ES") throw new JWTError("deriveSharedSecret with enc \"ECDH-ES\" requires an explicit keyLength in options", "
|
|
422
|
+
else if (alg === "ECDH-ES") throw new JWTError("deriveSharedSecret with enc \"ECDH-ES\" requires an explicit keyLength in options", "ERR_INVALID_OPTION");
|
|
421
423
|
else if (isECDHESAlgorithm(alg)) keyLength = aesBitsOf(innerKeyWrapAlg(alg));
|
|
422
424
|
else keyLength = bitLengthCEK(alg);
|
|
423
|
-
|
|
425
|
+
try {
|
|
426
|
+
return await deriveECDHESKey(pubCryptoKey, privCryptoKey, alg, keyLength, partyUInfo ?? /* @__PURE__ */ new Uint8Array(0), partyVInfo ?? /* @__PURE__ */ new Uint8Array(0));
|
|
427
|
+
} catch (error_) {
|
|
428
|
+
throw new JWTError("Key agreement failed: both keys must be agreement keys on the same curve.", "ERR_JWK_INVALID", error_);
|
|
429
|
+
}
|
|
424
430
|
}
|
|
425
431
|
function assertDerivedKeyLength(keyLength) {
|
|
426
432
|
if (!Number.isInteger(keyLength) || keyLength <= 0 || keyLength % 8 !== 0 || keyLength > 8192) throw new JWTError(`keyLength must be a positive multiple of 8 no greater than 8192 bits, got ${keyLength}`, "ERR_INVALID_OPTION");
|