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.
Files changed (44) hide show
  1. package/README.md +34 -21
  2. package/dist/_chunks/algorithms.mjs +44 -20
  3. package/dist/_chunks/digest.d.mts +4 -4
  4. package/dist/_chunks/digest.mjs +23 -15
  5. package/dist/_chunks/error.d.mts +29 -42
  6. package/dist/_chunks/export.mjs +1 -4
  7. package/dist/_chunks/index.d.mts +270 -234
  8. package/dist/_chunks/index2.d.mts +82 -189
  9. package/dist/_chunks/index4.d.mts +223 -60
  10. package/dist/_chunks/index5.d.mts +68 -31
  11. package/dist/_chunks/index6.d.mts +45 -64
  12. package/dist/_chunks/jwe.mjs +53 -48
  13. package/dist/_chunks/jwk.mjs +9 -3
  14. package/dist/_chunks/jws.mjs +55 -50
  15. package/dist/_chunks/jwt.d.mts +13 -49
  16. package/dist/_chunks/jwt.mjs +4 -158
  17. package/dist/_chunks/jwt2.mjs +162 -0
  18. package/dist/_chunks/libs/unsecure.d.mts +0 -6
  19. package/dist/_chunks/limits.d.mts +3 -2
  20. package/dist/_chunks/payload.mjs +13 -7
  21. package/dist/_chunks/wrap.mjs +3 -1
  22. package/dist/core/internal.d.mts +50 -2
  23. package/dist/core/internal.mjs +3 -1
  24. package/dist/core/jwe/index.d.mts +1 -1
  25. package/dist/core/jwk/index.d.mts +2 -2
  26. package/dist/core/jwk/index.mjs +2 -2
  27. package/dist/dpop/index.d.mts +77 -18
  28. package/dist/index.d.mts +6 -6
  29. package/dist/index.mjs +4 -4
  30. package/dist/session/index.d.mts +568 -22
  31. package/dist/session/index.mjs +1386 -34
  32. package/dist/utils/index.d.mts +4 -4
  33. package/dist/utils/index.mjs +2 -10
  34. package/package.json +4 -32
  35. package/dist/_chunks/codec-jwe.d.mts +0 -280
  36. package/dist/_chunks/codec-jws.mjs +0 -1005
  37. package/dist/_chunks/cookie-attributes.d.mts +0 -12
  38. package/dist/_chunks/cookie.mjs +0 -50
  39. package/dist/adapters/elysia/index.d.mts +0 -123
  40. package/dist/adapters/elysia/index.mjs +0 -120
  41. package/dist/adapters/h3/index.d.mts +0 -135
  42. package/dist/adapters/h3/index.mjs +0 -117
  43. package/dist/adapters/h3v1/index.d.mts +0 -148
  44. 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 a payload with multiple signers as a JWS General JSON Serialization (RFC 7515 §7.2.1).
6
- * Each signer's `alg` is inferred from its key when omitted (`ERR_JWS_SIGNER_ALG_INFERENCE`);
7
- * `b64` must match across signers (RFC 7797 §3, `ERR_JWS_B64_INCONSISTENT`).
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
- * Per-signature failures are collected and the last one is thrown when none verifies.
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 independently and returns one outcome per signature; only
25
- * a malformed envelope throws (`ERR_JWS_INVALID_SERIALIZATION`). The payload type follows `options.payload`.
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
- /** Converts a single-signature General JWS to Flattened form (RFC 7515 §7.2.2); otherwise `ERR_JWS_INVALID_SERIALIZATION`. */
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
- * Creates a JWS Compact Serialization; an object payload is JSON-serialized, `alg` is required unless the JWK carries one.
43
- * An unencoded (`b64: false`) compact payload cannot contain "." (RFC 7797 §5.2).
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 resolver.
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
- import { ComputeJwtTimeClaimsOptions as ComputeJwtTimeClaimsOptions$1, ParsePayloadOptions, applyTypDefault, assertCritShape as assertCritShape$1, computeJwtTimeClaims as computeJwtTimeClaims$1, getPlaintextBytes as getPlaintextBytes$1, parsePayloadBytes, parsePayloadSegment, validateCriticalHeadersJWE as validateCriticalHeadersJWE$1, validateCriticalHeadersJWS as validateCriticalHeadersJWS$1 } from "./jwt.mjs";
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
- * Output shape: UTF-8 text, or the bytes themselves.
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
- /** Encode bytes, or the UTF-8 bytes of a string, as unpadded base64url (RFC 7515 §2). */
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
- /** Encode bytes, or the UTF-8 bytes of a string, as padded standard base64 (RFC 4648 §4). */
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
- * Decode unpadded base64url (RFC 7515 §2). Strict: only the canonical encoding of a byte string
21
- * is accepted, so `=`, whitespace, characters outside the URL-safe alphabet and set bits past
22
- * the final byte are rejected; a `string` result must be valid UTF-8.
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` for text that is not such an encoding; `INVALID_TYPE` for input that is not a string
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
- * Decode padded standard base64 (RFC 4648 §4). Strict: the canonical encoding with its `=` run
32
- * present, no whitespace, no set bits past the final byte; a `string` result must be valid UTF-8.
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` for text that is not such an encoding; `INVALID_TYPE` for input that is not a string
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
- /** @deprecated Renamed to {@link Base64ParseOptions}; removed in v0.9.0. */
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
- /** Wrap a value in an array; returns the array unchanged if already an array. */
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
- /** Asserts that `key` is a `CryptoKey`; throws `ERR_JWK_INVALID` otherwise. */
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`, checked via `Symbol.toStringTag` to work across realms. */
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` is a `CryptoKeyPair` of two CryptoKeys. */
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 (`oct`); its `k` is always secret. */
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 (RSA, EC, OKP). */
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 holds asymmetric private material (`d`). It is `false` for `oct` keys,
72
- * so `!isPrivateJWK(key)` is not a "safe to publish" check; use `isPublicJWK`.
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
- /** Whether the JWK is an asymmetric public key without `d`: the "safe to publish" check. */
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
- * Infer the JWS algorithms a key unambiguously supports, or `undefined` when it cannot
86
- * (raw bytes, ambiguous JWK, lookup function) and `options.algorithms` must be passed.
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, options?: InferAlgorithmsOptions): JWSAlgorithm[] | undefined;
89
+ export declare function inferJWSAllowedAlgorithms(key: unknown): JWSAlgorithm[] | undefined;
89
90
  /**
90
- * Infer the JWE key management algorithms a key unambiguously supports, or `undefined`
91
- * when it cannot; a password string infers the PBES2 variants, raw bytes nothing.
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, options?: InferAlgorithmsOptions): KeyManagementAlgorithm[] | undefined;
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;
@@ -1,7 +1,8 @@
1
1
  import { secureRandomBytes, textEncoder } from "./libs/unsecure.mjs";
2
2
  import { JWTError } from "../error.mjs";
3
- import { applyTypDefault, base64UrlStringify, computeJwtTimeClaims, getPlaintextBytes, isCryptoKey, isJWK, isJWKSet, parsePayloadBytes, validateCriticalHeadersJWE } from "./jwt.mjs";
4
- 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, decodeProtectedHeader, decodeSegment, decrypt as decrypt$1, encrypt as encrypt$1, encryptKey, generateCEK, generateIV, getJWKsFromSet, importKey, keyMatchesHeader, resolveKeyInput, resolveLimit, resolvePasswordKey, serializeProtectedHeader } from "./algorithms.mjs";
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, allowSymmetric, keyCandidates, pbes2Budget } = options;
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) try {
142
- if (alg === "dir") assertDirectKeyRole(candidate, enc);
143
- pbes2Budget.spend(unwrapKeyOptions);
144
- const unwrappingKey = await importKey(candidate, {
145
- alg,
146
- expect: "private"
147
- });
148
- const cek = await unwrapKey(encryptedKey, unwrappingKey, unwrapKeyOptions);
149
- return {
150
- cek,
151
- plaintextBytes: await decrypt$1(enc, cek, ciphertext, iv, tag, aad)
152
- };
153
- } catch (err) {
154
- attempts.push(asDecryptionError(err));
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
- let lastError;
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
- lastError = shapeError;
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
- lastError = recipientError;
423
+ failures.push(recipientError);
419
424
  continue;
420
425
  }
421
426
  try {
422
427
  _assertDisjointHeaders(protectedHeader, sharedUnprotected, recipientHeader);
423
428
  } catch (err) {
424
- lastError = asDecryptionError(err);
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
- lastError = new JWTError(`Recipient[${i}] has no "alg" in the effective header.`, "ERR_JWE_INVALID");
435
+ failures.push(new JWTError(`Recipient[${i}] has no "alg" in the effective header.`, "ERR_JWE_INVALID"));
431
436
  continue;
432
437
  }
433
- if (!isJWEAlgSupported(alg)) {
434
- lastError = checkJWEAlgSupported(alg);
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
- lastError = directKeyError;
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
- lastError = asKeyLookupError(err, `recipient[${i}]`);
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
- lastError = algError;
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
- lastError = asDecryptionError(err);
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
- lastError = asDecryptionError(err);
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
- throw lastError ?? new JWTError("JWE decryption failed.", "ERR_JWE_DECRYPTION_FAILED");
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
  });
@@ -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 finalUsages = keyUsage ?? ["wrapKey", "unwrapKey"];
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", "ERR_JWK_INVALID");
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
- return deriveECDHESKey(pubCryptoKey, privCryptoKey, alg, keyLength, partyUInfo ?? /* @__PURE__ */ new Uint8Array(0), partyVInfo ?? /* @__PURE__ */ new Uint8Array(0));
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");