unjwt 0.6.0 → 0.7.0

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.
@@ -1,21 +1,4 @@
1
- //#region src/core/error.ts
2
- /**
3
- * Base error class for all unjwt errors. Every error thrown by the library is
4
- * an instance of `JWTError`, so a single `instanceof` check is sufficient to
5
- * distinguish library errors from other thrown values. Use the `code` property
6
- * (or the `isJWTError` type guard) to narrow to a specific error condition.
7
- *
8
- * @example
9
- * import { JWTError, isJWTError } from "unjwt/jws";
10
- *
11
- * try {
12
- * await verify(token, key);
13
- * } catch (err) {
14
- * if (isJWTError(err, "ERR_JWT_EXPIRED")) {
15
- * console.log(err.cause.jti); // string | undefined
16
- * }
17
- * }
18
- */
1
+ import { c as safeJsonParse, i as base64UrlDecode, o as textDecoder, s as textEncoder } from "./libs/unsecure.mjs";
19
2
  var JWTError = class extends Error {
20
3
  code;
21
4
  cause;
@@ -30,52 +13,164 @@ function isJWTError(error, code) {
30
13
  if (!(error instanceof JWTError)) return false;
31
14
  return code === void 0 || error.code === code;
32
15
  }
33
- //#endregion
34
- //#region src/core/utils/jwt.ts
35
- /**
36
- * Apply default typ/cty semantics shared by JWS & JWE.
37
- * - If typ is undefined and payload is a non-Uint8Array object, set typ="JWT".
38
- * - If typ is "JWT" and payload is a non-Uint8Array object, ensure cty defaults to "application/json".
39
- */
16
+ function inferJWSAllowedAlgorithms(key) {
17
+ if (!key || typeof key === "function") return void 0;
18
+ if (typeof key === "string" || key instanceof Uint8Array) return void 0;
19
+ if (typeof key !== "object") return void 0;
20
+ if (isCryptoKeyPair(key)) return inferJWSAllowedAlgorithms(key.publicKey);
21
+ if (isJWKSet(key)) return unionFromJWKSet(key.keys, jwsAlgsFromJWK);
22
+ if (isCryptoKey(key)) return jwsAlgsFromCryptoKey(key);
23
+ if (isJWK(key)) return jwsAlgsFromJWK(key);
24
+ }
25
+ function inferJWEAllowedAlgorithms(key) {
26
+ if (!key || typeof key === "function") return void 0;
27
+ if (typeof key === "string" || key instanceof Uint8Array) return [
28
+ "PBES2-HS256+A128KW",
29
+ "PBES2-HS384+A192KW",
30
+ "PBES2-HS512+A256KW",
31
+ "dir"
32
+ ];
33
+ if (typeof key !== "object") return void 0;
34
+ if (isCryptoKeyPair(key)) return inferJWEAllowedAlgorithms(key.privateKey);
35
+ if (isJWKSet(key)) return unionFromJWKSet(key.keys, jweAlgsFromJWK);
36
+ if (isCryptoKey(key)) return jweAlgsFromCryptoKey(key);
37
+ if (isJWK(key)) return jweAlgsFromJWK(key);
38
+ }
39
+ function unionFromJWKSet(keys, mapper) {
40
+ const algs = /* @__PURE__ */ new Set();
41
+ for (const k of keys) {
42
+ const mapped = mapper(k);
43
+ if (!mapped) return void 0;
44
+ for (const a of mapped) algs.add(a);
45
+ }
46
+ return algs.size > 0 ? [...algs] : void 0;
47
+ }
48
+ function jwsAlgsFromJWK(jwk) {
49
+ if (jwk.alg) return [jwk.alg];
50
+ if (jwk.kty === "EC") {
51
+ const crv = jwk.crv;
52
+ if (crv === "P-256") return ["ES256"];
53
+ if (crv === "P-384") return ["ES384"];
54
+ if (crv === "P-521") return ["ES512"];
55
+ }
56
+ if (jwk.kty === "OKP") {
57
+ const crv = jwk.crv;
58
+ if (crv === "Ed25519") return ["Ed25519", "EdDSA"];
59
+ if (crv === "Ed448") return ["EdDSA"];
60
+ }
61
+ }
62
+ function jweAlgsFromJWK(jwk) {
63
+ if (jwk.kty === "oct") return jweAlgsFromOctJWK(jwk);
64
+ if (jwk.alg) return [jwk.alg];
65
+ if (jwk.kty === "EC") {
66
+ const crv = jwk.crv;
67
+ if (crv === "P-256" || crv === "P-384" || crv === "P-521") return [
68
+ "ECDH-ES",
69
+ "ECDH-ES+A128KW",
70
+ "ECDH-ES+A192KW",
71
+ "ECDH-ES+A256KW"
72
+ ];
73
+ }
74
+ if (jwk.kty === "OKP") {
75
+ const crv = jwk.crv;
76
+ if (crv === "X25519" || crv === "X448") return [
77
+ "ECDH-ES",
78
+ "ECDH-ES+A128KW",
79
+ "ECDH-ES+A192KW",
80
+ "ECDH-ES+A256KW"
81
+ ];
82
+ }
83
+ }
84
+ function jweAlgsFromOctJWK(jwk) {
85
+ const alg = jwk.alg;
86
+ if (!alg) return void 0;
87
+ if (alg === "A128GCM") return ["A128GCMKW", "dir"];
88
+ if (alg === "A192GCM") return ["A192GCMKW", "dir"];
89
+ if (alg === "A256GCM") return ["A256GCMKW", "dir"];
90
+ if (alg === "A128KW" || alg === "A192KW" || alg === "A256KW") return [alg, "dir"];
91
+ if (alg === "A128GCMKW" || alg === "A192GCMKW" || alg === "A256GCMKW") return [alg, "dir"];
92
+ if (alg === "A128CBC-HS256" || alg === "A192CBC-HS384" || alg === "A256CBC-HS512") return ["dir"];
93
+ if (alg === "dir") return ["dir"];
94
+ return [alg];
95
+ }
96
+ function jwsAlgsFromCryptoKey(key) {
97
+ const name = key.algorithm.name;
98
+ const hashName = key.algorithm.hash?.name;
99
+ const hashBits = hashName?.startsWith("SHA-") ? hashName.slice(4) : void 0;
100
+ switch (name) {
101
+ case "HMAC": return hashBits ? [`HS${hashBits}`] : void 0;
102
+ case "RSASSA-PKCS1-v1_5": return hashBits ? [`RS${hashBits}`] : void 0;
103
+ case "RSA-PSS": return hashBits ? [`PS${hashBits}`] : void 0;
104
+ case "ECDSA": {
105
+ const curve = key.algorithm.namedCurve;
106
+ if (curve === "P-256") return ["ES256"];
107
+ if (curve === "P-384") return ["ES384"];
108
+ if (curve === "P-521") return ["ES512"];
109
+ return;
110
+ }
111
+ case "Ed25519": return ["Ed25519", "EdDSA"];
112
+ default: return;
113
+ }
114
+ }
115
+ function jweAlgsFromCryptoKey(key) {
116
+ const name = key.algorithm.name;
117
+ const hashName = key.algorithm.hash?.name;
118
+ const length = key.algorithm.length;
119
+ switch (name) {
120
+ case "AES-KW":
121
+ if (length === 128) return ["A128KW", "dir"];
122
+ if (length === 192) return ["A192KW", "dir"];
123
+ if (length === 256) return ["A256KW", "dir"];
124
+ return;
125
+ case "AES-GCM":
126
+ if (length === 128) return ["A128GCMKW", "dir"];
127
+ if (length === 192) return ["A192GCMKW", "dir"];
128
+ if (length === 256) return ["A256GCMKW", "dir"];
129
+ return;
130
+ case "RSA-OAEP":
131
+ if (hashName === "SHA-1") return ["RSA-OAEP"];
132
+ if (hashName === "SHA-256") return ["RSA-OAEP-256"];
133
+ if (hashName === "SHA-384") return ["RSA-OAEP-384"];
134
+ if (hashName === "SHA-512") return ["RSA-OAEP-512"];
135
+ return;
136
+ case "ECDH":
137
+ case "X25519":
138
+ case "X448": return [
139
+ "ECDH-ES",
140
+ "ECDH-ES+A128KW",
141
+ "ECDH-ES+A192KW",
142
+ "ECDH-ES+A256KW"
143
+ ];
144
+ default: return;
145
+ }
146
+ }
40
147
  function applyTypCtyDefaults(header, payload) {
41
- const isObjectPayload = typeof payload === "object" && payload !== null && !(payload instanceof Uint8Array);
42
- if (header.typ === void 0 && isObjectPayload) header.typ = "JWT";
43
- if (header.typ?.toLowerCase().includes("jwt") && isObjectPayload) header.cty ||= "application/json";
148
+ if (!(typeof payload === "object" && payload !== null && !(payload instanceof Uint8Array))) return header;
149
+ let typ = header.typ;
150
+ if (typ === void 0) header.typ = typ = "JWT";
151
+ if (typ === "JWT" || typ === "jwt" || typ.toLowerCase().includes("jwt")) header.cty ||= "application/json";
44
152
  return header;
45
153
  }
46
- /** Returns true when headers indicate JSON content. */
47
154
  function isJWTContent(header) {
48
155
  if (!header) return false;
49
156
  if (header.typ?.toLowerCase().includes("jwt")) return true;
50
157
  const cty = header.cty?.toLowerCase();
51
158
  return cty === "json" || cty === "application/json" || !!cty && cty.endsWith("+json");
52
159
  }
53
- /**
54
- * Given a decoded string and JOSE headers, return an object if JSON, otherwise the string.
55
- */
56
- function decodeMaybeJWTString(decodedString, header) {
57
- if (isJWTContent(header)) {
58
- if (decodedString.startsWith("{") && decodedString.endsWith("}") || decodedString.startsWith("[") && decodedString.endsWith("]")) try {
59
- return sanitizeObject(JSON.parse(decodedString));
60
- } catch {}
61
- }
160
+ function decodeMaybeJWTString(decodedString) {
161
+ if (decodedString.startsWith("{") && decodedString.endsWith("}") || decodedString.startsWith("[") && decodedString.endsWith("]")) try {
162
+ return safeJsonParse(decodedString);
163
+ } catch {}
62
164
  return decodedString;
63
165
  }
64
- /**
65
- * Decode a payload that is represented as raw bytes into T | string, honoring forceUint8Array and headers.
66
- */
67
- function decodePayloadFromBytes(bytes, header, forceUint8Array) {
166
+ function decodePayloadFromBytes(bytes, forceUint8Array) {
68
167
  if (forceUint8Array) return bytes;
69
- return decodeMaybeJWTString(textDecoder.decode(bytes), header);
168
+ return decodeMaybeJWTString(textDecoder.decode(bytes));
70
169
  }
71
- /**
72
- * Decode a payload that is a Base64URL segment into T | string | Uint8Array based on flags and headers.
73
- */
74
- function decodePayloadFromB64UrlSegment(payloadEncoded, header, forceUint8Array) {
75
- if (forceUint8Array) return base64UrlDecode(payloadEncoded, false);
76
- return decodeMaybeJWTString(base64UrlDecode(payloadEncoded), header);
170
+ function decodePayloadFromB64UrlSegment(payloadEncoded, forceUint8Array) {
171
+ if (forceUint8Array) return base64UrlDecode(payloadEncoded, { returnAs: "uint8array" });
172
+ return decodeMaybeJWTString(base64UrlDecode(payloadEncoded));
77
173
  }
78
- /** Convert plaintext input to bytes, shared by JWS & JWE when preparing payload. */
79
174
  function getPlaintextBytes(payload) {
80
175
  if (payload instanceof Uint8Array) return payload;
81
176
  if (typeof payload === "string") return textEncoder.encode(payload);
@@ -105,41 +200,73 @@ const TIME_CONSTANTS = Object.freeze({
105
200
  year: 31536e3,
106
201
  years: 31536e3
107
202
  });
108
- const EXPIRES_IN_REGEX = /^(\d+)(s|second|seconds|m|minute|minutes|h|hour|hours|D|day|days|W|week|weeks|M|month|months|Y|year|years)?$/;
109
- function computeExpiresInSeconds(expiresIn) {
110
- if (typeof expiresIn === "number") {
111
- if (!Number.isInteger(expiresIn) || expiresIn <= 0) throw new TypeError("If 'expiresIn' is a number, it must be a positive integer representing seconds.");
112
- return expiresIn;
203
+ const DURATION_REGEX = /^(\d+)(s|second|seconds|m|minute|minutes|h|hour|hours|D|day|days|W|week|weeks|M|month|months|Y|year|years)?$/;
204
+ function _parseDurationSeconds(value) {
205
+ if (typeof value === "number") {
206
+ if (!Number.isInteger(value)) throw new TypeError("Duration must be an integer number of seconds.");
207
+ return value;
113
208
  }
114
- if (typeof expiresIn === "string") {
115
- const match = expiresIn.match(EXPIRES_IN_REGEX);
116
- if (!match || !match[1]) throw new TypeError("Invalid 'expiresIn' format. Must be a positive integer or a string like '10m', '2h', '7D', etc.");
209
+ if (typeof value === "string") {
210
+ const match = value.match(DURATION_REGEX);
211
+ if (!match || !match[1]) throw new TypeError("Invalid duration format. Must be an integer or a string like '10m', '2h', '7D', etc.");
117
212
  return Number.parseInt(match[1], 10) * TIME_CONSTANTS[match[2] || "s"];
118
213
  }
119
- throw new TypeError("'expiresIn' must be a number or a string representing time duration.");
214
+ throw new TypeError("Duration must be a number or a string representing time.");
215
+ }
216
+ function computeDurationInSeconds(duration) {
217
+ const seconds = _parseDurationSeconds(duration);
218
+ if (seconds <= 0) throw new TypeError("Duration must be a positive integer number of seconds.");
219
+ return seconds;
120
220
  }
121
- const computeMaxTokenAgeSeconds = computeExpiresInSeconds;
122
- /** Optionally compute iat/exp when signing JWTs. */
123
- function computeJwtTimeClaims(payload, headerTyp, expiresIn, currentDate = /* @__PURE__ */ new Date()) {
124
- if (expiresIn === void 0 || !headerTyp?.toLowerCase().includes("jwt") || !(payload && typeof payload === "object") || payload instanceof Uint8Array || payload.exp) return;
125
- const now = Math.round(currentDate.getTime() / 1e3);
126
- const claims = { ...payload };
221
+ const computeExpiresInSeconds = computeDurationInSeconds;
222
+ const computeMaxTokenAgeSeconds = computeDurationInSeconds;
223
+ function computeJwtTimeClaims(payload, options) {
224
+ const { currentDate, expiresIn, expiresAt, notBeforeIn, notBeforeAt } = options ?? {};
225
+ if (expiresIn !== void 0 && expiresAt !== void 0) throw new JWTError("\"expiresIn\" and \"expiresAt\" are mutually exclusive; pass only one.", "ERR_JWT_CLAIM_INVALID");
226
+ if (notBeforeIn !== void 0 && notBeforeAt !== void 0) throw new JWTError("\"notBeforeIn\" and \"notBeforeAt\" are mutually exclusive; pass only one.", "ERR_JWT_CLAIM_INVALID");
227
+ const hasExpHint = expiresIn !== void 0 || expiresAt !== void 0;
228
+ const hasNbfHint = notBeforeIn !== void 0 || notBeforeAt !== void 0;
229
+ if (!hasExpHint && !hasNbfHint) return void 0;
230
+ if (!payload || typeof payload !== "object" || payload instanceof Uint8Array) return void 0;
231
+ const typedPayload = payload;
232
+ const wantsExp = hasExpHint && typedPayload.exp === void 0;
233
+ const wantsNbf = hasNbfHint && typedPayload.nbf === void 0;
234
+ if (!wantsExp && !wantsNbf) return void 0;
235
+ const now = Math.round((currentDate ?? /* @__PURE__ */ new Date()).getTime() / 1e3);
236
+ const claims = { ...typedPayload };
127
237
  claims.iat ||= now;
128
- claims.exp = claims.iat + computeExpiresInSeconds(expiresIn);
238
+ if (wantsExp) if (expiresAt !== void 0) {
239
+ const expMs = expiresAt.getTime();
240
+ if (!Number.isFinite(expMs)) throw new JWTError("\"expiresAt\" must be a valid Date.", "ERR_JWT_CLAIM_INVALID");
241
+ claims.exp = Math.floor(expMs / 1e3);
242
+ } else claims.exp = claims.iat + computeDurationInSeconds(expiresIn);
243
+ if (wantsNbf) if (notBeforeAt !== void 0) {
244
+ const nbfMs = notBeforeAt.getTime();
245
+ if (!Number.isFinite(nbfMs)) throw new JWTError("\"notBeforeAt\" must be a valid Date.", "ERR_JWT_CLAIM_INVALID");
246
+ claims.nbf = Math.floor(nbfMs / 1e3);
247
+ } else {
248
+ const offset = _parseDurationSeconds(notBeforeIn);
249
+ if (offset < 0) throw new JWTError("\"notBeforeIn\" must be zero or a positive duration.", "ERR_JWT_CLAIM_INVALID");
250
+ claims.nbf = claims.iat + offset;
251
+ }
129
252
  return claims;
130
253
  }
131
- /** Validate JWT claims under JWS verification rules (or custom JWE implementations). */
132
254
  function validateJwtClaims(jwtClaims, options = {}) {
133
255
  const clockTolerance = options.clockTolerance ?? 0;
134
256
  const currentTime = Math.round((options.currentDate ?? /* @__PURE__ */ new Date()).getTime() / 1e3);
135
- const allRequiredClaims = new Set(options.requiredClaims || []);
136
- const missingClaims = /* @__PURE__ */ new Set();
137
- if (options.issuer) allRequiredClaims.add("iss");
138
- if (options.audience) allRequiredClaims.add("aud");
139
- if (options.subject) allRequiredClaims.add("sub");
140
- if (options.maxTokenAge) allRequiredClaims.add("iat");
141
- for (const claimName of allRequiredClaims) if (!(claimName in jwtClaims)) missingClaims.add(claimName);
142
- if (missingClaims.size > 0) throw new JWTError(`Missing required JWT Claims: ${[...missingClaims].join(", ")}`, "ERR_JWT_CLAIM_MISSING");
257
+ if (options.requiredClaims || options.issuer || options.audience || options.subject || options.maxTokenAge) {
258
+ const allRequiredClaims = new Set(options.requiredClaims || []);
259
+ if (options.issuer) allRequiredClaims.add("iss");
260
+ if (options.audience) allRequiredClaims.add("aud");
261
+ if (options.subject) allRequiredClaims.add("sub");
262
+ if (options.maxTokenAge) allRequiredClaims.add("iat");
263
+ const missingClaims = [];
264
+ for (const claimName of allRequiredClaims) if (!(claimName in jwtClaims)) missingClaims.push(claimName);
265
+ if (missingClaims.length > 0) throw new JWTError(`Missing required JWT Claims: ${missingClaims.join(", ")}`, "ERR_JWT_CLAIM_MISSING");
266
+ }
267
+ if (jwtClaims.exp !== void 0 && !Number.isFinite(jwtClaims.exp)) throw new JWTError("JWT \"exp\" (Expiration Time) Claim must be a number.", "ERR_JWT_CLAIM_INVALID");
268
+ if (jwtClaims.nbf !== void 0 && !Number.isFinite(jwtClaims.nbf)) throw new JWTError("JWT \"nbf\" (Not Before) Claim must be a number.", "ERR_JWT_CLAIM_INVALID");
269
+ if (jwtClaims.iat !== void 0 && !Number.isFinite(jwtClaims.iat)) throw new JWTError("JWT \"iat\" (Issued At) Claim must be a number.", "ERR_JWT_CLAIM_INVALID");
143
270
  if (options.issuer) {
144
271
  const expectedIssuers = maybeArray(options.issuer);
145
272
  if (!jwtClaims.iss || !expectedIssuers.includes(jwtClaims.iss)) throw new JWTError(`Invalid JWT "iss" (Issuer) Claim: Expected ${expectedIssuers.join(" or ")}, got ${jwtClaims.iss}`, "ERR_JWT_CLAIM_INVALID");
@@ -159,157 +286,52 @@ function validateJwtClaims(jwtClaims, options = {}) {
159
286
  if (options.maxTokenAge) {
160
287
  if (typeof jwtClaims.iat !== "number") throw new JWTError("JWT \"iat\" (Issued At) Claim must be a number when maxTokenAge is set.", "ERR_JWT_CLAIM_INVALID");
161
288
  if (jwtClaims.iat > currentTime + clockTolerance) throw new JWTError(`JWT "iat" (Issued At) Claim validation failed: Token was issued in the future (iat: ${(/* @__PURE__ */ new Date(jwtClaims.iat * 1e3)).toISOString()})`, "ERR_JWT_CLAIM_INVALID");
162
- if (jwtClaims.iat < currentTime - computeMaxTokenAgeSeconds(options.maxTokenAge) - clockTolerance) throw new JWTError(`JWT "iat" (Issued At) Claim validation failed: Token is too old (maxTokenAge: ${options.maxTokenAge}s, iat: ${(/* @__PURE__ */ new Date(jwtClaims.iat * 1e3)).toISOString()})`, "ERR_JWT_EXPIRED", {
289
+ if (jwtClaims.iat < currentTime - computeDurationInSeconds(options.maxTokenAge) - clockTolerance) throw new JWTError(`JWT "iat" (Issued At) Claim validation failed: Token is too old (maxTokenAge: ${options.maxTokenAge}s, iat: ${(/* @__PURE__ */ new Date(jwtClaims.iat * 1e3)).toISOString()})`, "ERR_JWT_EXPIRED", {
163
290
  jti: jwtClaims.jti,
164
291
  iat: jwtClaims.iat,
165
292
  exp: jwtClaims.exp
166
293
  });
167
294
  }
168
295
  }
169
- const BASE_HEADER_PARAMS = [
296
+ const BASE_PROCESSED_JWS = [
170
297
  "alg",
171
298
  "typ",
172
299
  "cty",
173
300
  "kid",
174
- "jwk",
175
- "jku",
176
- "x5c",
177
- "x5t",
178
- "x5u"
301
+ "b64"
302
+ ];
303
+ const BASE_PROCESSED_JWE = [
304
+ "alg",
305
+ "enc",
306
+ "typ",
307
+ "cty",
308
+ "kid",
309
+ "iv",
310
+ "tag",
311
+ "p2s",
312
+ "p2c",
313
+ "epk",
314
+ "apu",
315
+ "apv"
179
316
  ];
180
- /** Validate critical headers in JWS semantics. */
181
317
  function validateCriticalHeadersJWS(protectedHeader, recognizedHeaders = []) {
182
318
  if (!protectedHeader.crit) return;
183
319
  const missingHeaderParams = /* @__PURE__ */ new Set();
184
- const recognizedParams = new Set([
185
- ...recognizedHeaders,
186
- ...BASE_HEADER_PARAMS,
187
- "b64"
188
- ]);
320
+ const recognizedParams = new Set([...recognizedHeaders, ...BASE_PROCESSED_JWS]);
189
321
  for (const param of protectedHeader.crit) {
190
322
  if (recognizedParams.has(param) && (param in protectedHeader || param === "b64")) continue;
191
323
  missingHeaderParams.add(param);
192
324
  }
193
325
  if (missingHeaderParams.size > 0) throw new JWTError(`Missing critical header parameters: ${[...missingHeaderParams].join(", ")}`, "ERR_JWS_INVALID");
194
326
  }
195
- /** Validate critical headers in JWE semantics. */
196
327
  function validateCriticalHeadersJWE(protectedHeader, recognizedHeaders) {
197
- if (protectedHeader.crit && !recognizedHeaders) throw new JWTError(`Unprocessed critical header parameters: ${protectedHeader.crit.join(", ")}`, "ERR_JWE_INVALID");
198
328
  if (!protectedHeader.crit) return;
199
- const understoodParams = new Set([
200
- ...recognizedHeaders || [],
201
- ...BASE_HEADER_PARAMS,
202
- "enc",
203
- "iv",
204
- "tag",
205
- "p2s",
206
- "p2c",
207
- "epk",
208
- "apu",
209
- "apv"
210
- ]);
329
+ const understoodParams = new Set([...recognizedHeaders || [], ...BASE_PROCESSED_JWE]);
211
330
  for (const critParam of protectedHeader.crit) {
212
331
  if (!Object.prototype.hasOwnProperty.call(protectedHeader, critParam)) throw new JWTError(`Critical header parameter "${critParam}" listed in "crit" but not present in the protected header.`, "ERR_JWE_INVALID");
213
332
  if (!understoodParams.has(critParam)) throw new JWTError(`Unprocessed critical header parameters: ${critParam}`, "ERR_JWE_INVALID");
214
333
  }
215
334
  }
216
- //#endregion
217
- //#region src/core/utils/sanitize.ts
218
- /**
219
- * Originally derived from unsecure@0.0.4
220
- * Licensed under the MIT License.
221
- *
222
- * Source: https://github.com/sandros94/unsecure/blob/913ed5580c551153a19154b4916c67a8efb2be85/src/sanitize.ts
223
- */
224
- /**
225
- * Returns a deep structural copy of `obj` with prototype-pollution vectors
226
- * stripped at every level (`__proto__`, `prototype`, `constructor`).
227
- *
228
- * The input is never modified. Arrays are copied element-by-element;
229
- * nested objects are recursed with a WeakSet cycle guard.
230
- */
231
- function sanitizeObject(obj) {
232
- if (!obj || typeof obj !== "object") return obj;
233
- return _sanitizeCopy(obj, /* @__PURE__ */ new WeakSet());
234
- }
235
- function _sanitizeCopy(current, seen) {
236
- seen.add(current);
237
- if (Array.isArray(current)) return current.map((item) => item && typeof item === "object" && !seen.has(item) ? _sanitizeCopy(item, seen) : item);
238
- const result = {};
239
- for (const [key, value] of Object.entries(current)) {
240
- if (key === "__proto__" || key === "prototype" || key === "constructor") continue;
241
- result[key] = value && typeof value === "object" && !seen.has(value) ? _sanitizeCopy(value, seen) : value;
242
- }
243
- return result;
244
- }
245
- //#endregion
246
- //#region src/core/utils/index.ts
247
- const textEncoder = /* @__PURE__ */ new TextEncoder();
248
- const textDecoder = /* @__PURE__ */ new TextDecoder();
249
- const _Buffer = /* @__PURE__ */ (() => {
250
- try {
251
- return globalThis.Buffer;
252
- } catch {
253
- return;
254
- }
255
- })();
256
- const _hasBuffer = typeof _Buffer?.from === "function";
257
- const _hasToBase64 = !_hasBuffer && typeof Uint8Array.prototype.toBase64 === "function";
258
- const _hasFromBase64 = !_hasBuffer && typeof Uint8Array.fromBase64 === "function";
259
- const _b64UrlEncOpts = /* @__PURE__ */ Object.freeze({
260
- alphabet: "base64url",
261
- omitPadding: true
262
- });
263
- const _b64UrlDecOpts = /* @__PURE__ */ Object.freeze({ alphabet: "base64url" });
264
- function _toBuffer(data) {
265
- return data instanceof Uint8Array ? _Buffer.from(data.buffer, data.byteOffset, data.byteLength) : _Buffer.from(data);
266
- }
267
- function base64Encode(data) {
268
- if (_hasBuffer) return _toBuffer(data).toString("base64");
269
- const encodedData = data instanceof Uint8Array ? data : textEncoder.encode(data);
270
- if (_hasToBase64) return encodedData.toBase64();
271
- return btoa(String.fromCodePoint(...encodedData));
272
- }
273
- function base64UrlEncode(data) {
274
- if (_hasBuffer) return _toBuffer(data).toString("base64url");
275
- const encodedData = data instanceof Uint8Array ? data : textEncoder.encode(data);
276
- if (_hasToBase64) return encodedData.toBase64(_b64UrlEncOpts);
277
- return btoa(String.fromCodePoint(...encodedData)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
278
- }
279
- function base64Decode(str, toString) {
280
- const decodeToString = toString !== false;
281
- if (!str) return decodeToString ? "" : new Uint8Array(0);
282
- if (_hasBuffer) {
283
- const buf = _Buffer.from(str, "base64");
284
- return decodeToString ? buf.toString("utf8") : new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
285
- }
286
- const data = _hasFromBase64 ? Uint8Array.fromBase64(str) : Uint8Array.from(atob(str), (b) => b.codePointAt(0));
287
- return decodeToString ? textDecoder.decode(data) : data;
288
- }
289
- function base64UrlDecode(str, toString) {
290
- const decodeToString = toString !== false;
291
- if (!str) return decodeToString ? "" : new Uint8Array(0);
292
- if (_hasBuffer) {
293
- const buf = _Buffer.from(str, "base64url");
294
- return decodeToString ? buf.toString("utf8") : new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
295
- }
296
- let data;
297
- if (_hasFromBase64) data = Uint8Array.fromBase64(str, _b64UrlDecOpts);
298
- else {
299
- str = str.replace(/-/g, "+").replace(/_/g, "/");
300
- while (str.length % 4) str += "=";
301
- data = Uint8Array.from(atob(str), (b) => b.codePointAt(0));
302
- }
303
- return decodeToString ? textDecoder.decode(data) : data;
304
- }
305
- function randomBytes(length) {
306
- return crypto.getRandomValues(new Uint8Array(length));
307
- }
308
- /**
309
- * Concatenates multiple Uint8Arrays
310
- * @param arrays Arrays to concatenate
311
- * @returns Concatenated array
312
- */
313
335
  function concatUint8Arrays(...arrays) {
314
336
  const totalLength = arrays.reduce((length, arr) => length + arr.length, 0);
315
337
  const result = new Uint8Array(totalLength);
@@ -336,18 +358,12 @@ function isCryptoKey(key) {
336
358
  return key?.[Symbol.toStringTag] === "CryptoKey";
337
359
  }
338
360
  const isCryptoKeyPair = (key) => key && typeof key === "object" && isCryptoKey(key.publicKey) && isCryptoKey(key.privateKey);
339
- /** Returns true if the JWK is a symmetric (oct) key. */
340
361
  function isSymmetricJWK(key) {
341
362
  return isJWK(key) && key.kty === "oct" && typeof key.k === "string";
342
363
  }
343
- /** Returns true if the JWK is an asymmetric (RSA, EC, OKP) key. */
344
364
  function isAsymmetricJWK(key) {
345
365
  return !isSymmetricJWK(key);
346
366
  }
347
- /**
348
- * Type guard that checks if the provided JWK contains private key material.
349
- * It relies purely on the presence of private components (e.g. "d"), not on the alg value.
350
- */
351
367
  function isPrivateJWK(key) {
352
368
  if (!isJWK(key)) return false;
353
369
  if (key.kty === "EC") return typeof key.d === "string";
@@ -355,10 +371,6 @@ function isPrivateJWK(key) {
355
371
  if (key.kty === "RSA") return typeof key.d === "string";
356
372
  return false;
357
373
  }
358
- /**
359
- * Type guard that checks if the provided JWK is a public (asymmetric) key.
360
- * This checks for the presence of public components and absence of private material.
361
- */
362
374
  function isPublicJWK(key) {
363
375
  if (!isJWK(key)) return false;
364
376
  if (key.kty === "EC") {
@@ -375,5 +387,4 @@ function isPublicJWK(key) {
375
387
  }
376
388
  return false;
377
389
  }
378
- //#endregion
379
- export { validateCriticalHeadersJWS as A, computeMaxTokenAgeSeconds as C, getPlaintextBytes as D, decodePayloadFromBytes as E, JWTError as M, isJWTError as N, isJWTContent as O, computeJwtTimeClaims as S, decodePayloadFromB64UrlSegment as T, textDecoder as _, base64UrlEncode as a, applyTypCtyDefaults as b, isCryptoKey as c, isJWKSet as d, isPrivateJWK as f, randomBytes as g, maybeArray as h, base64UrlDecode as i, validateJwtClaims as j, validateCriticalHeadersJWE as k, isCryptoKeyPair as l, isSymmetricJWK as m, base64Decode as n, concatUint8Arrays as o, isPublicJWK as p, base64Encode as r, isAsymmetricJWK as s, assertCryptoKey as t, isJWK as u, textEncoder as v, decodeMaybeJWTString as w, computeExpiresInSeconds as x, sanitizeObject as y };
390
+ export { validateCriticalHeadersJWS as C, JWTError as D, inferJWSAllowedAlgorithms as E, isJWTError as O, validateCriticalHeadersJWE as S, inferJWEAllowedAlgorithms as T, decodeMaybeJWTString as _, isCryptoKeyPair as a, getPlaintextBytes as b, isPrivateJWK as c, maybeArray as d, applyTypCtyDefaults as f, computeMaxTokenAgeSeconds as g, computeJwtTimeClaims as h, isCryptoKey as i, isPublicJWK as l, computeExpiresInSeconds as m, concatUint8Arrays as n, isJWK as o, computeDurationInSeconds as p, isAsymmetricJWK as r, isJWKSet as s, assertCryptoKey as t, isSymmetricJWK as u, decodePayloadFromB64UrlSegment as v, validateJwtClaims as w, isJWTContent as x, decodePayloadFromBytes as y };
@@ -1,8 +1,7 @@
1
- import { $ as JWK_HMAC, A as GenerateJWKReturn, B as JWKParameters, C as JWTClaims, D as DeriveKeyOptions, E as ContentEncryptionAlgorithm, F as JWKAlgorithm, G as JWK_AES_KW, H as JWK_AES_CBC_HMAC, I as JWKCacheAdapter, J as JWK_EC, K as JWK_Asymmetric, L as JWKLookupFunction, M as GenerateKeyOptions, N as GenerateKeyReturn, O as DeriveKeyReturn, P as JWK, Q as JWK_EC_Public, R as JWKLookupFunctionHeader, S as JWTClaimValidationOptions, T as CompositeKey, U as JWK_AES_GCM, V as JWKSet, W as JWK_AES_GCM_KW, X as JWK_ECDSA, Y as JWK_ECDH_ES, Z as JWK_EC_Private, _ as JWSSignOptions, _t as KeyManagementAlgorithm, a as ExpiresIn, at as JWK_Pair, b as JOSEPayload, bt as WrapKeyResult, c as JWEDecryptResult, ct as JWK_RSA, d as JWEKeyManagementHeaderParameters, dt as JWK_RSA_Private, et as JWK_OKP, f as JWEProtectedHeader, ft as JWK_RSA_Public, g as JWSProtectedHeader, gt as JWK_oct, h as JWSHeaderParameters, ht as JWK_Symmetric_Algorithm, i as importFromPEM, it as JWK_PBES2, j as GenerateKeyAlgorithm, k as GenerateJWKOptions, l as JWEEncryptOptions, lt as JWK_RSA_ENC, m as JWSAlgorithm, mt as JWK_Symmetric, n as exportToPEM, nt as JWK_OKP_Public, o as MaxTokenAge, ot as JWK_Private, p as StrictOmit, pt as JWK_RSA_SIGN, q as JWK_Asymmetric_Algorithm, r as generateJWK, rt as JWK_OKP_SIGN, s as JWEDecryptOptions, st as JWK_Public, t as deriveJWKFromPassword, tt as JWK_OKP_Private, u as JWEHeaderParameters, ut as JWK_RSA_PSS, v as JWSVerifyOptions, vt as UnwrapKeyOptions, w as JoseHeaderParameters, x as JWT, y as JWSVerifyResult, yt as WrapKeyOptions, z as JWKPEMAlgorithm } from "../../_chunks/jwk.mjs";
1
+ import { $ as JWKAlgorithm, A as JWSSignOptions, At as JWK_RSA_Public, B as JWTClaimValidationOptions, C as JWSMultiSignOptions, Ct as JWK_Pair, D as JWSMultiVerifyOutcome, Dt as JWK_RSA_ENC, E as JWSMultiVerifyOptions, Et as JWK_RSA, Ft as KeyManagementAlgorithm, G as DeriveKeyOptions, H as JoseHeaderParameters, It as UnwrapKeyOptions, J as GenerateJWKReturn, K as DeriveKeyReturn, Lt as WrapKeyOptions, M as JWSVerifyResult, Mt as JWK_Symmetric, N as StrictOmit, Nt as JWK_Symmetric_Algorithm, O as JWSMultiVerifyResult, Ot as JWK_RSA_PSS, Pt as JWK_oct, Q as JWK, R as JOSEPayload, Rt as WrapKeyResult, S as JWSHeaderParameters, St as JWK_PBES2, T as JWSMultiVerifyAllOptions, Tt as JWK_Public, U as CompositeKey, V as JWTClaims, W as ContentEncryptionAlgorithm, X as GenerateKeyOptions, Y as GenerateKeyAlgorithm, Z as GenerateKeyReturn, _ as JWEProtectedHeader, _t as JWK_HMAC, a as JWEDecryptOptions, at as JWKSet, b as JWSGeneralSerialization, bt as JWK_OKP_Public, c as JWEFlattenedSerialization, ct as JWK_AES_GCM_KW, d as JWEHeaderParameters, dt as JWK_Asymmetric_Algorithm, et as JWKCacheAdapter, f as JWEKeyManagementHeaderParameters, ft as JWK_EC, g as JWEMultiRecipient, gt as JWK_EC_Public, h as JWEMultiEncryptOptions, ht as JWK_EC_Private, i as NotBeforeIn, it as JWKParameters, j as JWSVerifyOptions, jt as JWK_RSA_SIGN, k as JWSProtectedHeader, kt as JWK_RSA_Private, l as JWEGeneralRecipient, lt as JWK_AES_KW, m as JWEMultiDecryptResult, mt as JWK_ECDSA, n as ExpiresIn, nt as JWKLookupFunctionHeader, o as JWEDecryptResult, ot as JWK_AES_CBC_HMAC, p as JWEMultiDecryptOptions, pt as JWK_ECDH_ES, q as GenerateJWKOptions, r as MaxTokenAge, rt as JWKPEMAlgorithm, s as JWEEncryptOptions, st as JWK_AES_GCM, t as Duration, tt as JWKLookupFunction, u as JWEGeneralSerialization, ut as JWK_Asymmetric, v as JWSAlgorithm, vt as JWK_OKP, w as JWSMultiSigner, wt as JWK_Private, x as JWSGeneralSignature, xt as JWK_OKP_SIGN, y as JWSFlattenedSerialization, yt as JWK_OKP_Private, z as JWT } from "../../_chunks/index.mjs";
2
+ import { deriveJWKFromPassword, exportPEM, exportToPEM, generateJWK, importFromPEM, importPEM } from "../../core/jwk.mjs";
2
3
  import { H3Event } from "h3";
3
4
  import { CookieSerializeOptions } from "cookie-es";
4
-
5
- //#region src/adapters/h3v1/session/types.d.ts
6
5
  interface SessionClaims extends Required<Pick<JWTClaims, "jti" | "iat">>, StrictOmit<JWTClaims, "jti" | "iat"> {}
7
6
  type SessionData<T extends Record<string, any> = SessionClaims> = StrictOmit<T, "jti" | "iat" | "exp">;
8
7
  type SessionUpdate<T extends Record<string, any> = SessionClaims> = Partial<SessionData<T>> | ((oldData: SessionData<T>) => Partial<SessionData<T>> | undefined);
@@ -22,12 +21,13 @@ interface SessionManager<T extends Record<string, any> = SessionClaims, ConfigMa
22
21
  readonly expiresAt: undefined;
23
22
  }>;
24
23
  }
25
- //#endregion
26
- //#region src/adapters/h3v1/session/jwe.d.ts
27
24
  declare const kGetSessionPromise$1: unique symbol;
28
25
  interface SessionJWE<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined> {
26
+ /** Session ID — mirrors the JWT `jti` claim. `undefined` until the session is persisted. */
29
27
  id: string | undefined;
28
+ /** Session creation time in ms — mirrors the JWT `iat` claim (seconds). */
30
29
  createdAt: number;
30
+ /** Session expiry time in ms — mirrors the JWT `exp` claim (seconds). */
31
31
  expiresAt: MaxAge extends ExpiresIn ? number : T["exp"];
32
32
  data: SessionData<T>;
33
33
  token: string | undefined;
@@ -149,12 +149,13 @@ declare function unsealJWESession<T extends Record<string, any> = SessionClaims,
149
149
  * Clear the session (delete from context and drop cookie).
150
150
  */
151
151
  declare function clearJWESession<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends CompatEvent$1 | H3Event = CompatEvent$1 | H3Event>(event: TEvent, config: SessionConfigJWE<T, MaxAge, TEvent>): Promise<void>;
152
- //#endregion
153
- //#region src/adapters/h3v1/session/jws.d.ts
154
152
  declare const kGetSessionPromise: unique symbol;
155
153
  interface SessionJWS<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined> {
154
+ /** Session ID — mirrors the JWT `jti` claim. `undefined` until the session is persisted. */
156
155
  id: string | undefined;
156
+ /** Session creation time in ms — mirrors the JWT `iat` claim (seconds). */
157
157
  createdAt: number;
158
+ /** Session expiry time in ms — mirrors the JWT `exp` claim (seconds). */
158
159
  expiresAt: MaxAge extends ExpiresIn ? number : T["exp"];
159
160
  data: SessionData<T>;
160
161
  token: string | undefined;
@@ -278,5 +279,4 @@ declare function verifyJWSSession<T extends Record<string, any> = SessionClaims,
278
279
  * Destroy the session (context + cookie).
279
280
  */
280
281
  declare function clearJWSSession<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends CompatEvent | H3Event = CompatEvent | H3Event>(event: TEvent, config: SessionConfigJWS<T, MaxAge, TEvent>): Promise<void>;
281
- //#endregion
282
- export { CompositeKey, ContentEncryptionAlgorithm, DeriveKeyOptions, DeriveKeyReturn, ExpiresIn, GenerateJWKOptions, GenerateJWKReturn, GenerateKeyAlgorithm, GenerateKeyOptions, GenerateKeyReturn, JOSEPayload, JWEDecryptOptions, JWEDecryptResult, JWEEncryptOptions, JWEHeaderParameters, JWEKeyManagementHeaderParameters, JWEProtectedHeader, JWK, JWKAlgorithm, JWKCacheAdapter, JWKLookupFunction, JWKLookupFunctionHeader, JWKPEMAlgorithm, JWKParameters, JWKSet, JWK_AES_CBC_HMAC, JWK_AES_GCM, JWK_AES_GCM_KW, JWK_AES_KW, JWK_Asymmetric, JWK_Asymmetric_Algorithm, JWK_EC, JWK_ECDH_ES, JWK_ECDSA, JWK_EC_Private, JWK_EC_Public, JWK_HMAC, JWK_OKP, JWK_OKP_Private, JWK_OKP_Public, JWK_OKP_SIGN, JWK_PBES2, JWK_Pair, JWK_Private, JWK_Public, JWK_RSA, JWK_RSA_ENC, JWK_RSA_PSS, JWK_RSA_Private, JWK_RSA_Public, JWK_RSA_SIGN, JWK_Symmetric, JWK_Symmetric_Algorithm, JWK_oct, JWSAlgorithm, JWSHeaderParameters, JWSProtectedHeader, JWSSignOptions, JWSVerifyOptions, JWSVerifyResult, JWT, JWTClaimValidationOptions, JWTClaims, JoseHeaderParameters, KeyManagementAlgorithm, MaxTokenAge, type SessionClaims, type SessionConfigJWE, type SessionConfigJWS, type SessionData, type SessionHooksJWE, type SessionHooksJWS, type SessionJWE, type SessionJWS, type SessionManager, type SessionUpdate, UnwrapKeyOptions, WrapKeyOptions, WrapKeyResult, clearJWESession, clearJWSSession, deriveJWKFromPassword, exportToPEM, generateJWK, getJWESession, getJWESessionToken, getJWSSession, importFromPEM, sealJWESession, signJWSSession, unsealJWESession, updateJWESession, updateJWSSession, useJWESession, useJWSSession, verifyJWSSession };
282
+ export { CompositeKey, ContentEncryptionAlgorithm, DeriveKeyOptions, DeriveKeyReturn, Duration, ExpiresIn, GenerateJWKOptions, GenerateJWKReturn, GenerateKeyAlgorithm, GenerateKeyOptions, GenerateKeyReturn, JOSEPayload, JWEDecryptOptions, JWEDecryptResult, JWEEncryptOptions, JWEFlattenedSerialization, JWEGeneralRecipient, JWEGeneralSerialization, JWEHeaderParameters, JWEKeyManagementHeaderParameters, JWEMultiDecryptOptions, JWEMultiDecryptResult, JWEMultiEncryptOptions, JWEMultiRecipient, JWEProtectedHeader, JWK, JWKAlgorithm, JWKCacheAdapter, JWKLookupFunction, JWKLookupFunctionHeader, JWKPEMAlgorithm, JWKParameters, JWKSet, JWK_AES_CBC_HMAC, JWK_AES_GCM, JWK_AES_GCM_KW, JWK_AES_KW, JWK_Asymmetric, JWK_Asymmetric_Algorithm, JWK_EC, JWK_ECDH_ES, JWK_ECDSA, JWK_EC_Private, JWK_EC_Public, JWK_HMAC, JWK_OKP, JWK_OKP_Private, JWK_OKP_Public, JWK_OKP_SIGN, JWK_PBES2, JWK_Pair, JWK_Private, JWK_Public, JWK_RSA, JWK_RSA_ENC, JWK_RSA_PSS, JWK_RSA_Private, JWK_RSA_Public, JWK_RSA_SIGN, JWK_Symmetric, JWK_Symmetric_Algorithm, JWK_oct, JWSAlgorithm, JWSFlattenedSerialization, JWSGeneralSerialization, JWSGeneralSignature, JWSHeaderParameters, JWSMultiSignOptions, JWSMultiSigner, JWSMultiVerifyAllOptions, JWSMultiVerifyOptions, JWSMultiVerifyOutcome, JWSMultiVerifyResult, JWSProtectedHeader, JWSSignOptions, JWSVerifyOptions, JWSVerifyResult, JWT, JWTClaimValidationOptions, JWTClaims, JoseHeaderParameters, KeyManagementAlgorithm, MaxTokenAge, NotBeforeIn, type SessionClaims, type SessionConfigJWE, type SessionConfigJWS, type SessionData, type SessionHooksJWE, type SessionHooksJWS, type SessionJWE, type SessionJWS, type SessionManager, type SessionUpdate, UnwrapKeyOptions, WrapKeyOptions, WrapKeyResult, clearJWESession, clearJWSSession, deriveJWKFromPassword, exportPEM, exportToPEM, generateJWK, getJWESession, getJWESessionToken, getJWSSession, importFromPEM, importPEM, sealJWESession, signJWSSession, unsealJWESession, updateJWESession, updateJWSSession, useJWESession, useJWSSession, verifyJWSSession };