unjwt 0.5.37 → 0.5.39

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 (41) hide show
  1. package/README.md +142 -663
  2. package/dist/_chunks/chunk.mjs +13 -0
  3. package/dist/_chunks/h3v1.mjs +598 -0
  4. package/dist/_chunks/index.d.mts +250 -0
  5. package/dist/_chunks/index2.d.mts +108 -0
  6. package/dist/_chunks/jwe.d.mts +43 -0
  7. package/dist/_chunks/jwe.mjs +132 -0
  8. package/dist/_chunks/jwk.d.mts +575 -0
  9. package/dist/_chunks/jwk.mjs +1592 -0
  10. package/dist/_chunks/jws.d.mts +46 -0
  11. package/dist/_chunks/jws.mjs +2069 -0
  12. package/dist/_chunks/utils.mjs +374 -0
  13. package/dist/adapters/h3.d.mts +3 -265
  14. package/dist/adapters/h3.mjs +3 -706
  15. package/dist/adapters/h3v1.d.mts +3 -8
  16. package/dist/adapters/h3v1.mjs +3 -7
  17. package/dist/adapters/h3v2.d.mts +145 -146
  18. package/dist/adapters/h3v2.mjs +505 -596
  19. package/dist/index.d.mts +6 -8
  20. package/dist/index.mjs +5 -4
  21. package/dist/jwe.d.mts +3 -4
  22. package/dist/jwe.mjs +4 -3
  23. package/dist/jwk.d.mts +3 -2
  24. package/dist/jwk.mjs +3 -2
  25. package/dist/jws.d.mts +3 -3
  26. package/dist/jws.mjs +4 -3
  27. package/dist/utils.d.mts +3 -3
  28. package/dist/utils.mjs +2 -1
  29. package/package.json +33 -31
  30. package/dist/shared/unjwt.4JQCoID_.d.mts +0 -161
  31. package/dist/shared/unjwt.B-v62plj.mjs +0 -249
  32. package/dist/shared/unjwt.BCi3FEWr.d.mts +0 -328
  33. package/dist/shared/unjwt.BYDTEWa-.d.mts +0 -269
  34. package/dist/shared/unjwt.Be8NxwFK.mjs +0 -438
  35. package/dist/shared/unjwt.BqdNUKDr.d.mts +0 -64
  36. package/dist/shared/unjwt.CMNtw8ek.d.mts +0 -155
  37. package/dist/shared/unjwt.Cy2MlkUe.d.mts +0 -174
  38. package/dist/shared/unjwt.D0_ccT7g.d.mts +0 -166
  39. package/dist/shared/unjwt.DdLN9vwI.mjs +0 -163
  40. package/dist/shared/unjwt.Dpl__ZAR.mjs +0 -2121
  41. package/dist/shared/unjwt.JdKqVANs.d.mts +0 -5
@@ -0,0 +1,13 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __exportAll = (all, no_symbols) => {
4
+ let target = {};
5
+ for (var name in all) __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true
8
+ });
9
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
10
+ return target;
11
+ };
12
+ //#endregion
13
+ export { __exportAll as t };
@@ -0,0 +1,598 @@
1
+ import { d as isSymmetricJWK, f as computeExpiresInSeconds, i as encrypt, l as isPrivateJWK, n as verify, r as decrypt, t as sign, u as isPublicJWK } from "./jws.mjs";
2
+ import { isEvent, setCookie } from "h3";
3
+ import { parse } from "cookie-es";
4
+ //#region src/adapters/h3v1/session/jwe.ts
5
+ const kGetSessionPromise$1 = Symbol("h3_jwe_getSession");
6
+ const DEFAULT_NAME$1 = "h3-jwe";
7
+ const DEFAULT_COOKIE$1 = {
8
+ path: "/",
9
+ secure: true,
10
+ httpOnly: true
11
+ };
12
+ /**
13
+ * Create a session manager for the current request.
14
+ */
15
+ async function useJWESession(event, config) {
16
+ const sessionName = config.name || DEFAULT_NAME$1;
17
+ await getJWESession(event, config);
18
+ const sessionManager = {
19
+ get id() {
20
+ return event.context.sessions?.[sessionName]?.id;
21
+ },
22
+ get createdAt() {
23
+ return event.context.sessions?.[sessionName]?.createdAt || Date.now();
24
+ },
25
+ get expiresAt() {
26
+ return event.context.sessions?.[sessionName]?.expiresAt;
27
+ },
28
+ get data() {
29
+ return event.context.sessions?.[sessionName]?.data || {};
30
+ },
31
+ get token() {
32
+ return getJWESessionToken(event, config);
33
+ },
34
+ update: async (update) => {
35
+ if (!isEvent(event)) throw new Error("[h3] Cannot update read-only session.");
36
+ await updateJWESession(event, config, update);
37
+ return sessionManager;
38
+ },
39
+ clear: () => {
40
+ if (!isEvent(event)) throw new Error("[h3] Cannot clear read-only session.");
41
+ clearJWESession(event, config);
42
+ return Promise.resolve(sessionManager);
43
+ }
44
+ };
45
+ return sessionManager;
46
+ }
47
+ /**
48
+ * Get (and lazily initialize) the session for the current request.
49
+ */
50
+ async function getJWESession(event, config) {
51
+ const sessionName = config.name || DEFAULT_NAME$1;
52
+ if (!event.context.sessions) event.context.sessions = Object.create(null);
53
+ const existingSession = event.context.sessions[sessionName];
54
+ if (existingSession) {
55
+ const session = existingSession[kGetSessionPromise$1] ? await existingSession[kGetSessionPromise$1] : existingSession;
56
+ /**
57
+ * We check if a session is expired before returning it. If it is expired we clear it and create a new one,
58
+ * unless we have a read-only event in which case we just return it as it was valid at the time of reading
59
+ * the cookie/header (like in a websocket upgrade)
60
+ */
61
+ if (session.expiresAt !== void 0 && session.expiresAt < Date.now() && isEvent(event)) {
62
+ await config.hooks?.onExpire?.({
63
+ event,
64
+ error: /* @__PURE__ */ new Error(`JWT "exp" (Expiration Time) Claim validation failed: Token has expired (exp: ${(/* @__PURE__ */ new Date(session.expiresAt * 1e3)).toISOString()})`),
65
+ config
66
+ });
67
+ return clearJWESession(event, config).then(() => getJWESession(event, config));
68
+ }
69
+ await config.hooks?.onRead?.({
70
+ event,
71
+ session,
72
+ config
73
+ });
74
+ return session;
75
+ }
76
+ const now = config.jwe?.encryptOptions?.currentDate?.getTime() ?? Date.now();
77
+ const createdAt = now - now % 1e3;
78
+ const session = {
79
+ id: void 0,
80
+ createdAt,
81
+ expiresAt: config.maxAge === void 0 ? void 0 : createdAt + computeExpiresInSeconds(config.maxAge) * 1e3,
82
+ data: Object.create(null)
83
+ };
84
+ event.context.sessions[sessionName] = session;
85
+ const token = getJWESessionToken(event, config);
86
+ if (token) {
87
+ const promise = unsealJWESession(event, config, token).catch(async (error_) => {
88
+ if (error_ instanceof Error && (error_.message.includes("Token has expired") || error_.message.includes("Token is too old"))) {
89
+ await config.hooks?.onExpire?.({
90
+ event,
91
+ error: error_,
92
+ config
93
+ });
94
+ return;
95
+ }
96
+ await config.hooks?.onError?.({
97
+ event,
98
+ error: error_,
99
+ config
100
+ });
101
+ }).then((unsealed) => {
102
+ if (unsealed) Object.assign(session, unsealed);
103
+ delete event.context.sessions[sessionName][kGetSessionPromise$1];
104
+ return session;
105
+ });
106
+ event.context.sessions[sessionName][kGetSessionPromise$1] = promise;
107
+ await promise;
108
+ }
109
+ await config.hooks?.onRead?.({
110
+ event,
111
+ session,
112
+ config
113
+ });
114
+ return session;
115
+ }
116
+ function getJWESessionToken(event, config) {
117
+ const sessionName = config.name || DEFAULT_NAME$1;
118
+ let token;
119
+ if (config.sessionHeader !== false) {
120
+ const headerValue = _getReqHeader$1(event, typeof config.sessionHeader === "string" ? config.sessionHeader.toLowerCase() : `x-${sessionName.toLowerCase()}-session`);
121
+ if (typeof headerValue === "string") token = headerValue.startsWith("Bearer ") ? headerValue.slice(7).trim() : headerValue;
122
+ }
123
+ if (config.cookie !== false) {
124
+ const setCookie = _getResHeader$1(event, "set-cookie");
125
+ if (typeof setCookie === "string") token = findSetCookie$1(setCookie, sessionName);
126
+ else if (Array.isArray(setCookie)) for (const sc of setCookie) {
127
+ token = findSetCookie$1(sc, sessionName);
128
+ if (token) break;
129
+ }
130
+ }
131
+ if (!token) {
132
+ const cookieHeader = _getReqHeader$1(event, "cookie");
133
+ if (cookieHeader) token = parse(String(cookieHeader))[sessionName];
134
+ }
135
+ return token;
136
+ }
137
+ function _getReqHeader$1(event, name) {
138
+ if (event.node) return event.node?.req.headers[name];
139
+ if (event.request) return event.request.headers?.get(name);
140
+ if (event.headers) return event.headers.get(name);
141
+ }
142
+ function _getResHeader$1(event, name) {
143
+ if (event.node) return event.node?.res.getHeader(name);
144
+ if (event.response) return event.response.headers?.get(name);
145
+ }
146
+ /**
147
+ * Update the session (optionally mutating the session data) and re-issue the JWE token.
148
+ */
149
+ async function updateJWESession(event, config, update) {
150
+ if (!isEvent(event)) throw new Error("Cannot update a new session. Use `useSession(event)` within the main handler.");
151
+ const sessionName = config.name || DEFAULT_NAME$1;
152
+ const session = event.context.sessions?.[sessionName] || await getJWESession(event, config);
153
+ if (typeof update === "function") update = update(session.data);
154
+ if (update) {
155
+ const oldSession = {
156
+ ...session,
157
+ data: { ...session.data }
158
+ };
159
+ Object.assign(session.data, update);
160
+ await config.hooks?.onUpdate?.({
161
+ session,
162
+ oldSession,
163
+ event,
164
+ config
165
+ });
166
+ }
167
+ const now = config.jwe?.encryptOptions?.currentDate?.getTime() ?? Date.now();
168
+ const createdAt = now - now % 1e3;
169
+ Object.assign(session, {
170
+ id: config.generateId?.() || crypto.randomUUID(),
171
+ createdAt,
172
+ expiresAt: config.maxAge === void 0 ? void 0 : createdAt + computeExpiresInSeconds(config.maxAge) * 1e3
173
+ });
174
+ if (config.cookie !== false) setCookie(event, sessionName, await sealJWESession(event, config), {
175
+ ...DEFAULT_COOKIE$1,
176
+ ...config.cookie,
177
+ expires: config.maxAge === void 0 ? void 0 : new Date(session.createdAt + computeExpiresInSeconds(config.maxAge) * 1e3)
178
+ });
179
+ return session;
180
+ }
181
+ /**
182
+ * Produce a JWE for the current session.
183
+ * Payload structure:
184
+ * {
185
+ * jti: string;
186
+ * iat: number; (seconds)
187
+ * exp?: number; (seconds)
188
+ * data: Record<string, any>;
189
+ * }
190
+ */
191
+ async function sealJWESession(event, config) {
192
+ const key = getEncryptKey(config.key || config.secret);
193
+ const sessionName = config.name || DEFAULT_NAME$1;
194
+ const session = event.context.sessions?.[sessionName] || await getJWESession(event, config);
195
+ const iat = Math.floor(session.createdAt / 1e3);
196
+ const exp = session.expiresAt ? Math.floor(session.expiresAt / 1e3) : void 0;
197
+ const payload = {
198
+ ...session.data,
199
+ jti: session.id,
200
+ iat
201
+ };
202
+ if (exp) payload.exp = exp;
203
+ let typ = void 0;
204
+ if (config.jwe?.encryptOptions?.protectedHeader?.typ && typeof config.jwe.encryptOptions.protectedHeader.typ === "string" && config.jwe.encryptOptions.protectedHeader.typ.toLowerCase().includes("jwt")) typ = config.jwe.encryptOptions.protectedHeader.typ;
205
+ return await encrypt(payload, key, {
206
+ ...config.jwe?.encryptOptions,
207
+ expiresIn: void 0,
208
+ protectedHeader: {
209
+ ...config.jwe?.encryptOptions?.protectedHeader,
210
+ kid: typeof key === "string" ? void 0 : key.kid,
211
+ typ: typ || "JWT",
212
+ cty: "application/json"
213
+ }
214
+ });
215
+ }
216
+ /**
217
+ * Decrypt the JWE and return a Session-compatible object.
218
+ */
219
+ async function unsealJWESession(event, config, sealed) {
220
+ const key = config.hooks?.onUnsealKeyLookup ? (header) => config.hooks.onUnsealKeyLookup({
221
+ header,
222
+ event,
223
+ config
224
+ }) : getDecryptKey(config.key || config.secret);
225
+ const alg = config.jwe?.encryptOptions?.alg;
226
+ const enc = config.jwe?.encryptOptions?.enc;
227
+ let typ = void 0;
228
+ if (config.jwe?.encryptOptions?.protectedHeader?.typ && typeof config.jwe.encryptOptions.protectedHeader.typ === "string" && config.jwe.encryptOptions.protectedHeader.typ.toLowerCase().includes("jwt")) typ = config.jwe.encryptOptions.protectedHeader.typ;
229
+ const { payload } = await decrypt(sealed, key, {
230
+ ...config.jwe?.decryptOptions,
231
+ requiredClaims: [...new Set([
232
+ ...config.jwe?.decryptOptions?.requiredClaims || [],
233
+ "jti",
234
+ "iat"
235
+ ])],
236
+ typ: typ || "JWT",
237
+ maxTokenAge: config.maxAge,
238
+ algorithms: alg ? [alg] : void 0,
239
+ encryptionAlgorithms: enc ? [enc] : void 0,
240
+ unwrappedKeyAlgorithm: void 0,
241
+ keyUsage: void 0,
242
+ forceUint8Array: false,
243
+ validateJWT: true
244
+ }).catch((error_) => {
245
+ if (error_ instanceof Error) {
246
+ const message = error_.message;
247
+ throw new Error(`Invalid session token: ${message}`);
248
+ }
249
+ throw new Error(error_);
250
+ });
251
+ const { jti, iat, exp, ...data } = payload;
252
+ return {
253
+ id: jti,
254
+ createdAt: iat * 1e3,
255
+ expiresAt: exp ? exp * 1e3 : void 0,
256
+ data: data && typeof data === "object" ? data : Object.create(null)
257
+ };
258
+ }
259
+ /**
260
+ * Clear the session (delete from context and drop cookie).
261
+ */
262
+ async function clearJWESession(event, config) {
263
+ const sessionName = config.name || DEFAULT_NAME$1;
264
+ let session = event.context.sessions?.[sessionName];
265
+ if (session && session[kGetSessionPromise$1]) session = await session[kGetSessionPromise$1];
266
+ if (event.context.sessions?.[sessionName]) delete event.context.sessions[sessionName];
267
+ setCookie(event, sessionName, "", {
268
+ ...DEFAULT_COOKIE$1,
269
+ ...config.cookie,
270
+ expires: /* @__PURE__ */ new Date(0),
271
+ maxAge: void 0
272
+ });
273
+ await config.hooks?.onClear?.({
274
+ session,
275
+ event,
276
+ config
277
+ });
278
+ }
279
+ function getEncryptKey(key) {
280
+ if (!key) throw new Error("Session: JWE key is required.");
281
+ let _key;
282
+ if (typeof key === "string") _key = key;
283
+ else if (isSymmetricJWK(key) || isPrivateJWK(key)) _key = key;
284
+ else if ("publicKey" in key && isPublicJWK(key.publicKey)) _key = key.publicKey;
285
+ else if ("privateKey" in key && isPrivateJWK(key.privateKey)) _key = key.privateKey;
286
+ if (!_key) throw new Error("Session: Invalid JWE key. It must be a password string or valid JWK.", { cause: key });
287
+ return _key;
288
+ }
289
+ function getDecryptKey(key) {
290
+ if (!key) throw new Error("Session: JWE key is required.");
291
+ let _key = void 0;
292
+ if (typeof key === "string") _key = key;
293
+ else if (isSymmetricJWK(key)) _key = key;
294
+ else if ("privateKey" in key) _key = key.privateKey;
295
+ if (!_key) throw new Error("Session: Invalid JWE key. It must be a password string or a valid private JWK.", { cause: key });
296
+ return _key;
297
+ }
298
+ function findSetCookie$1(setCookie, name) {
299
+ const regex = new RegExp(`(?:^|,\\s*)${name}=([^;]+)`);
300
+ const match = setCookie.match(regex);
301
+ return match ? match[1] : void 0;
302
+ }
303
+ //#endregion
304
+ //#region src/adapters/h3v1/session/jws.ts
305
+ const kGetSessionPromise = Symbol("h3_jws_getSession");
306
+ const DEFAULT_NAME = "h3-jws";
307
+ const DEFAULT_COOKIE = {
308
+ path: "/",
309
+ secure: true,
310
+ httpOnly: false
311
+ };
312
+ /**
313
+ * Create a session manager for the current request using JWS (signed only, not encrypted).
314
+ * NOTE: Contents are visible to clients (Base64URL-decoded JSON). Do not store secrets in session data.
315
+ */
316
+ async function useJWSSession(event, config) {
317
+ const sessionName = config.name || DEFAULT_NAME;
318
+ await getJWSSession(event, config);
319
+ const sessionManager = {
320
+ get id() {
321
+ return event.context.sessions?.[sessionName]?.id;
322
+ },
323
+ get createdAt() {
324
+ return event.context.sessions?.[sessionName]?.createdAt || Date.now();
325
+ },
326
+ get expiresAt() {
327
+ return event.context.sessions?.[sessionName]?.expiresAt;
328
+ },
329
+ get data() {
330
+ return event.context.sessions?.[sessionName]?.data || {};
331
+ },
332
+ get token() {
333
+ return getJWSSessionToken(event, config);
334
+ },
335
+ update: async (update) => {
336
+ if (!isEvent(event)) throw new Error("[h3] Cannot update read-only session.");
337
+ await updateJWSSession(event, config, update);
338
+ return sessionManager;
339
+ },
340
+ clear: () => {
341
+ if (!isEvent(event)) throw new Error("[h3] Cannot clear read-only session.");
342
+ clearJWSSession(event, config);
343
+ return Promise.resolve(sessionManager);
344
+ }
345
+ };
346
+ return sessionManager;
347
+ }
348
+ /**
349
+ * Retrieve (and lazily initialize) the session.
350
+ */
351
+ async function getJWSSession(event, config) {
352
+ const sessionName = config.name || DEFAULT_NAME;
353
+ if (!event.context.sessions) event.context.sessions = Object.create(null);
354
+ const existingSession = event.context.sessions[sessionName];
355
+ if (existingSession) {
356
+ const session = existingSession[kGetSessionPromise] ? await existingSession[kGetSessionPromise] : existingSession;
357
+ /**
358
+ * We check if a session is expired before returning it. If it is expired we clear it and create a new one,
359
+ * unless we have a read-only event in which case we just return it as it was valid at the time of reading
360
+ * the cookie/header (like in a websocket upgrade)
361
+ */
362
+ if (session.expiresAt !== void 0 && session.expiresAt < Date.now() && isEvent(event)) {
363
+ await config.hooks?.onExpire?.({
364
+ event,
365
+ error: /* @__PURE__ */ new Error(`JWT "exp" (Expiration Time) Claim validation failed: Token has expired (exp: ${(/* @__PURE__ */ new Date(session.expiresAt * 1e3)).toISOString()})`),
366
+ config
367
+ });
368
+ return clearJWSSession(event, config).then(() => getJWSSession(event, config));
369
+ }
370
+ await config.hooks?.onRead?.({
371
+ event,
372
+ session,
373
+ config
374
+ });
375
+ return session;
376
+ }
377
+ const now = config.jws?.signOptions?.currentDate?.getTime() ?? Date.now();
378
+ const createdAt = now - now % 1e3;
379
+ const session = {
380
+ id: void 0,
381
+ createdAt,
382
+ expiresAt: config.maxAge === void 0 ? void 0 : createdAt + computeExpiresInSeconds(config.maxAge) * 1e3,
383
+ data: Object.create(null)
384
+ };
385
+ event.context.sessions[sessionName] = session;
386
+ const token = getJWSSessionToken(event, config);
387
+ if (token) {
388
+ const promise = verifyJWSSession(event, config, token).catch(async (error_) => {
389
+ if (error_ instanceof Error && (error_.message.includes("Token has expired") || error_.message.includes("Token is too old"))) {
390
+ await config.hooks?.onExpire?.({
391
+ event,
392
+ error: error_,
393
+ config
394
+ });
395
+ return;
396
+ }
397
+ await config.hooks?.onError?.({
398
+ event,
399
+ error: error_,
400
+ config
401
+ });
402
+ }).then((unsealed) => {
403
+ if (unsealed) Object.assign(session, unsealed);
404
+ delete event.context.sessions[sessionName][kGetSessionPromise];
405
+ return session;
406
+ });
407
+ session[kGetSessionPromise] = promise;
408
+ await promise;
409
+ }
410
+ await config.hooks?.onRead?.({
411
+ event,
412
+ session,
413
+ config
414
+ });
415
+ return session;
416
+ }
417
+ function getJWSSessionToken(event, config) {
418
+ const sessionName = config.name || DEFAULT_NAME;
419
+ let token;
420
+ if (config.sessionHeader !== false) {
421
+ const headerValue = _getReqHeader(event, typeof config.sessionHeader === "string" ? config.sessionHeader.toLowerCase() : `x-${sessionName.toLowerCase()}-session`);
422
+ if (typeof headerValue === "string") token = headerValue.startsWith("Bearer ") ? headerValue.slice(7).trim() : headerValue;
423
+ }
424
+ if (config.cookie !== false) {
425
+ const setCookie = _getResHeader(event, "set-cookie");
426
+ if (typeof setCookie === "string") token = findSetCookie(setCookie, sessionName);
427
+ else if (Array.isArray(setCookie)) for (const sc of setCookie) {
428
+ token = findSetCookie(sc, sessionName);
429
+ if (token) break;
430
+ }
431
+ }
432
+ if (!token) {
433
+ const cookieHeader = _getReqHeader(event, "cookie");
434
+ if (cookieHeader) token = parse(String(cookieHeader))[sessionName];
435
+ }
436
+ return token;
437
+ }
438
+ function _getReqHeader(event, name) {
439
+ if (event.node) return event.node.req.headers[name];
440
+ if (event.request) return event.request.headers.get(name);
441
+ if (event.headers) return event.headers.get(name);
442
+ }
443
+ function _getResHeader(event, name) {
444
+ if (event.node) return event.node?.res.getHeader(name);
445
+ if (event.response) return event.response.headers?.get(name);
446
+ }
447
+ /**
448
+ * Update session data (if provided) and reissue JWS.
449
+ */
450
+ async function updateJWSSession(event, config, update) {
451
+ if (!isEvent(event)) throw new Error("Cannot update a new session. Use `useSession(event)` within the main handler.");
452
+ const sessionName = config.name || DEFAULT_NAME;
453
+ const session = event.context.sessions?.[sessionName] || await getJWSSession(event, config);
454
+ if (typeof update === "function") update = update(session.data);
455
+ if (update) {
456
+ const oldSession = {
457
+ ...session,
458
+ data: { ...session.data }
459
+ };
460
+ Object.assign(session.data, update);
461
+ await config.hooks?.onUpdate?.({
462
+ session,
463
+ oldSession,
464
+ event,
465
+ config
466
+ });
467
+ }
468
+ const now = config.jws?.signOptions?.currentDate?.getTime() ?? Date.now();
469
+ const createdAt = now - now % 1e3;
470
+ Object.assign(session, {
471
+ id: config.generateId?.() || crypto.randomUUID(),
472
+ createdAt,
473
+ expiresAt: config.maxAge === void 0 ? void 0 : createdAt + computeExpiresInSeconds(config.maxAge) * 1e3
474
+ });
475
+ if (config.cookie !== false) setCookie(event, sessionName, await signJWSSession(event, config), {
476
+ ...DEFAULT_COOKIE,
477
+ ...config.cookie,
478
+ expires: config.maxAge === void 0 ? void 0 : new Date(session.createdAt + computeExpiresInSeconds(config.maxAge) * 1e3)
479
+ });
480
+ return session;
481
+ }
482
+ /**
483
+ * Sign current session as a compact JWS.
484
+ * Payload claims:
485
+ * jti, iat, exp?, data, plus optional extraClaims (cannot override reserved)
486
+ */
487
+ async function signJWSSession(event, config) {
488
+ const key = getSignKey(config.key);
489
+ const sessionName = config.name || DEFAULT_NAME;
490
+ const session = event.context.sessions?.[sessionName] || await getJWSSession(event, config);
491
+ const iat = Math.floor(session.createdAt / 1e3);
492
+ const exp = session.expiresAt ? Math.floor(session.expiresAt / 1e3) : void 0;
493
+ const payload = {
494
+ ...session.data,
495
+ jti: session.id,
496
+ iat
497
+ };
498
+ if (exp) payload.exp = exp;
499
+ let typ = void 0;
500
+ if (config.jws?.signOptions?.protectedHeader?.typ && typeof config.jws.signOptions.protectedHeader.typ === "string" && config.jws.signOptions.protectedHeader.typ.toLowerCase().includes("jwt")) typ = config.jws.signOptions.protectedHeader.typ;
501
+ return await sign(payload, key, {
502
+ ...config.jws?.signOptions,
503
+ expiresIn: void 0,
504
+ protectedHeader: {
505
+ ...config.jws?.signOptions?.protectedHeader,
506
+ kid: key.kid,
507
+ typ: typ || "JWT",
508
+ cty: "application/json"
509
+ }
510
+ });
511
+ }
512
+ /**
513
+ * Verify and parse a compact JWS into a Session structure.
514
+ * Performs:
515
+ * - cryptographic signature verification
516
+ * - (optional) standard claim checks if validateJWT true
517
+ * - ensures jti & iat presence
518
+ * - enforces maxAge if provided
519
+ */
520
+ async function verifyJWSSession(event, config, token) {
521
+ const alg = config.jws?.signOptions?.alg;
522
+ const jwk = config.hooks?.onVerifyKeyLookup ? (header) => config.hooks.onVerifyKeyLookup({
523
+ header,
524
+ event,
525
+ config
526
+ }) : getVerifyKey(config.key);
527
+ let typ = void 0;
528
+ if (config.jws?.signOptions?.protectedHeader?.typ && typeof config.jws.signOptions.protectedHeader.typ === "string" && config.jws.signOptions.protectedHeader.typ.toLowerCase().includes("jwt")) typ = config.jws.signOptions.protectedHeader.typ;
529
+ const { payload } = await verify(token, jwk, {
530
+ ...config.jws?.verifyOptions,
531
+ requiredClaims: [...new Set([
532
+ ...config.jws?.verifyOptions?.requiredClaims || [],
533
+ "jti",
534
+ "iat"
535
+ ])],
536
+ typ: typ || "JWT",
537
+ algorithms: alg ? [alg] : void 0,
538
+ forceUint8Array: false,
539
+ validateJWT: true
540
+ }).catch((error_) => {
541
+ const message = error_ instanceof Error ? error_.message : String(error_);
542
+ throw new Error(`Invalid session token: ${message}`);
543
+ });
544
+ const { jti, iat, exp, ...data } = payload;
545
+ return {
546
+ id: jti,
547
+ createdAt: iat * 1e3,
548
+ expiresAt: exp ? exp * 1e3 : void 0,
549
+ data: data && typeof data === "object" ? data : Object.create(null)
550
+ };
551
+ }
552
+ /**
553
+ * Destroy the session (context + cookie).
554
+ */
555
+ async function clearJWSSession(event, config) {
556
+ const sessionName = config.name || DEFAULT_NAME;
557
+ let session = event.context.sessions?.[sessionName];
558
+ if (session && session[kGetSessionPromise]) session = await session[kGetSessionPromise];
559
+ if (event.context.sessions?.[sessionName]) delete event.context.sessions[sessionName];
560
+ setCookie(event, sessionName, "", {
561
+ ...DEFAULT_COOKIE,
562
+ ...config.cookie,
563
+ expires: /* @__PURE__ */ new Date(0),
564
+ maxAge: void 0
565
+ });
566
+ await config.hooks?.onClear?.({
567
+ session,
568
+ event,
569
+ config
570
+ });
571
+ }
572
+ function getSignKey(key) {
573
+ if (!key) throw new Error("Session: JWS key is required.");
574
+ let _key = void 0;
575
+ if (isSymmetricJWK(key)) _key = key;
576
+ else if ("privateKey" in key && isPrivateJWK(key.privateKey)) _key = key.privateKey;
577
+ if (!_key) throw new Error("Session: Invalid JWS key. It must be a symmetric JWK or a private JWK.", { cause: key });
578
+ return _key;
579
+ }
580
+ function getVerifyKey(key) {
581
+ if (!key) throw new Error("Session: JWS key is required.");
582
+ let _key = void 0;
583
+ if (isSymmetricJWK(key)) _key = key;
584
+ else if ("publicKey" in key && isPublicJWK(key.publicKey)) _key = key.publicKey;
585
+ else if ("publicKey" in key && Array.isArray(key.publicKey)) {
586
+ const keys = key.publicKey.filter((k) => isPublicJWK(k));
587
+ if (keys && keys.length > 0) _key = { keys };
588
+ }
589
+ if (!_key) throw new Error("Session: Invalid JWS key. It must be a symmetric JWK or a public JWK/set.", { cause: key });
590
+ return _key;
591
+ }
592
+ function findSetCookie(setCookie, name) {
593
+ const regex = new RegExp(`(?:^|,\\s*)${name}=([^;]+)`);
594
+ const match = setCookie.match(regex);
595
+ return match ? match[1] : void 0;
596
+ }
597
+ //#endregion
598
+ export { useJWSSession as a, getJWESession as c, unsealJWESession as d, updateJWESession as f, updateJWSSession as i, getJWESessionToken as l, getJWSSession as n, verifyJWSSession as o, useJWESession as p, signJWSSession as r, clearJWESession as s, clearJWSSession as t, sealJWESession as u };