unjwt 0.7.2 → 0.8.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.
Files changed (39) hide show
  1. package/README.md +4 -4
  2. package/dist/THIRD-PARTY-LICENSES.md.gz +0 -0
  3. package/dist/_chunks/_codec-jwe.d.mts +170 -0
  4. package/dist/_chunks/_codec-jws.mjs +878 -0
  5. package/dist/_chunks/_internal.d.mts +7 -0
  6. package/dist/_chunks/_jwt-internal.mjs +388 -0
  7. package/dist/_chunks/algorithms.mjs +186 -0
  8. package/dist/_chunks/index.d.mts +512 -818
  9. package/dist/_chunks/index2.d.mts +11 -0
  10. package/dist/_chunks/index3.d.mts +108 -0
  11. package/dist/_chunks/jwe.d.mts +43 -66
  12. package/dist/_chunks/jwe.mjs +384 -252
  13. package/dist/_chunks/jwk.mjs +1390 -622
  14. package/dist/_chunks/jws.d.mts +48 -103
  15. package/dist/_chunks/jws.mjs +372 -184
  16. package/dist/_chunks/jwt.d.mts +56 -0
  17. package/dist/_chunks/libs/unsecure.d.mts +164 -85
  18. package/dist/_chunks/libs/unsecure.mjs +390 -123
  19. package/dist/adapters/elysia/index.d.mts +33 -171
  20. package/dist/adapters/elysia/index.mjs +106 -575
  21. package/dist/adapters/h3v1/index.d.mts +59 -344
  22. package/dist/adapters/h3v1/index.mjs +114 -619
  23. package/dist/adapters/h3v2/index.d.mts +58 -326
  24. package/dist/adapters/h3v2/index.mjs +108 -611
  25. package/dist/core/internal.d.mts +2 -0
  26. package/dist/core/internal.mjs +1 -0
  27. package/dist/core/jwe.d.mts +4 -2
  28. package/dist/core/jwe.mjs +4 -2
  29. package/dist/core/jwk.d.mts +183 -371
  30. package/dist/core/jwk.mjs +4 -3
  31. package/dist/core/jws.d.mts +4 -2
  32. package/dist/core/jws.mjs +4 -2
  33. package/dist/core/utils/index.d.mts +5 -155
  34. package/dist/core/utils/index.mjs +12 -3
  35. package/dist/index.d.mts +8 -4
  36. package/dist/index.mjs +6 -3
  37. package/package.json +39 -36
  38. package/dist/_chunks/_internal.mjs +0 -88
  39. package/dist/_chunks/utils.mjs +0 -390
@@ -1,652 +1,147 @@
1
- import { JWTError, computeDurationInSeconds, isJWTError, isPrivateJWK, isPublicJWK, isSymmetricJWK } from "../../_chunks/utils.mjs";
2
- import { sanitizeObjectCopy } from "../../_chunks/libs/unsecure.mjs";
3
- import { deriveJWKFromPassword, exportPEM, exportToPEM, generateJWK, importFromPEM, importPEM } from "../../_chunks/jwk.mjs";
4
- import { sign, verify } from "../../_chunks/jws.mjs";
5
- import { decrypt, encrypt } from "../../_chunks/jwe.mjs";
6
- import { isEvent, setCookie } from "h3";
1
+ import { deriveJWKFromPassword, exportPEM, generateJWK, importPEM, thumbprint } from "../../_chunks/jwk.mjs";
2
+ import { bindSession, createJWECodec, createJWSCodec, dropSession, encodeSession, issueSession, loadSession, openSessionToken, readChunkedFrom, readChunkedFromSetCookie, recordSlotStore, removeChunked, resolveSessionToken, responseCookieStore, sessionActions, sessionManager, sessionToken, writeChunked } from "../../_chunks/_codec-jws.mjs";
3
+ import { getCookie, isEvent, setCookie } from "h3";
7
4
  import { parse } from "cookie-es";
8
- const kGetSessionPromise$1 = Symbol("h3_jwe_getSession");
9
- const DEFAULT_NAME$1 = "h3-jwe";
10
- const DEFAULT_COOKIE$1 = {
11
- path: "/",
12
- secure: true,
13
- httpOnly: true
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 (event.context.sessions?.[sessionName])?.token ?? getJWESessionToken(event, config);
33
- },
34
- update: async (update) => {
35
- await updateJWESession(event, config, update);
36
- return sessionManager;
37
- },
38
- clear: async () => {
39
- await clearJWESession(event, config);
40
- return sessionManager;
41
- }
42
- };
43
- return sessionManager;
44
- }
45
- async function getJWESession(event, config) {
46
- const sessionName = config.name || DEFAULT_NAME$1;
47
- if (!event.context.sessions) event.context.sessions = Object.create(null);
48
- const existingSession = event.context.sessions[sessionName];
49
- if (existingSession) {
50
- const session = existingSession[kGetSessionPromise$1] ? await existingSession[kGetSessionPromise$1] : existingSession;
51
- if (session.expiresAt !== void 0 && session.expiresAt < Date.now()) {
52
- await config.hooks?.onExpire?.({
53
- session: {
54
- id: session.id,
55
- createdAt: session.createdAt,
56
- expiresAt: session.expiresAt,
57
- token: session.token
58
- },
59
- event,
60
- error: new JWTError(`JWT "exp" (Expiration Time) Claim validation failed: Token has expired (exp: ${(/* @__PURE__ */ new Date(session.expiresAt * 1e3)).toISOString()})`, "ERR_JWT_EXPIRED", {
61
- jti: session.id,
62
- exp: session.expiresAt / 1e3
63
- }),
64
- config
65
- });
66
- delete event.context.sessions[sessionName];
67
- if (config.cookie !== false) if (isEvent(event)) setCookie(event, sessionName, "", {
68
- ...DEFAULT_COOKIE$1,
69
- ...config.cookie,
70
- expires: /* @__PURE__ */ new Date(0),
71
- maxAge: void 0
72
- });
73
- else console.warn("[unjwt/h3] Session expired but cookie cannot be cleared on a read-only event.");
74
- const freshNow = config.jwe?.encryptOptions?.currentDate?.getTime() ?? Date.now();
75
- const freshCreatedAt = freshNow - freshNow % 1e3;
76
- const freshSession = {
77
- id: void 0,
78
- createdAt: freshCreatedAt,
79
- expiresAt: config.maxAge === void 0 ? void 0 : freshCreatedAt + computeDurationInSeconds(config.maxAge) * 1e3,
80
- data: Object.create(null),
81
- token: void 0
82
- };
83
- event.context.sessions[sessionName] = freshSession;
84
- return freshSession;
85
- }
86
- if (session.id !== void 0 && session.token !== void 0) await config.hooks?.onRead?.({
87
- session,
88
- event,
89
- config
5
+ function eventCookieStore(event) {
6
+ return responseCookieStore({
7
+ request: (name) => getCookie(event, name),
8
+ response: () => responseSetCookies(event),
9
+ write: (name, value, attrs) => setCookie(event, name, value, attrs)
10
+ });
11
+ }
12
+ function responseSetCookies(event) {
13
+ const value = event.node.res.getHeader("set-cookie");
14
+ if (Array.isArray(value)) return value;
15
+ return typeof value === "string" ? [value] : [];
16
+ }
17
+ const PREFIX = "[unjwt/h3]";
18
+ const h3SessionPort = {
19
+ prefix: PREFIX,
20
+ carrier: (event) => ({ event }),
21
+ store: (event, slot) => recordSlotStore(() => getSessionsContext(event), slot),
22
+ readToken: readSessionToken,
23
+ scope: getSessionsContext,
24
+ writable: hasWritableResponse,
25
+ setCookie: (event, sessionName, token, { attrs, chunkMaxLength }) => {
26
+ writeChunked(eventCookieStore(cookieEvent(event)), sessionName, token, {
27
+ attrs,
28
+ chunkMaxLength,
29
+ prefix: PREFIX
90
30
  });
91
- return session;
31
+ },
32
+ deleteCookie: (event, sessionName, { attrs }) => {
33
+ removeChunked(eventCookieStore(cookieEvent(event)), sessionName, { attrs });
92
34
  }
93
- const now = config.jwe?.encryptOptions?.currentDate?.getTime() ?? Date.now();
94
- const createdAt = now - now % 1e3;
95
- const session = {
96
- id: void 0,
97
- createdAt,
98
- expiresAt: config.maxAge === void 0 ? void 0 : createdAt + computeDurationInSeconds(config.maxAge) * 1e3,
99
- data: Object.create(null),
100
- token: void 0
101
- };
102
- event.context.sessions[sessionName] = session;
103
- const token = getJWESessionToken(event, config);
104
- let exclusiveHookFired = false;
105
- if (token) {
106
- session.token = token;
107
- const promise = unsealJWESession(event, config, token).catch(async (error_) => {
108
- exclusiveHookFired = true;
109
- if (isJWTError(error_, "ERR_JWT_EXPIRED")) await config.hooks?.onExpire?.({
110
- session: {
111
- id: error_.cause.jti,
112
- createdAt: error_.cause.iat ? error_.cause.iat * 1e3 : void 0,
113
- expiresAt: error_.cause.exp ? error_.cause.exp * 1e3 : void 0,
114
- token
115
- },
116
- error: error_,
117
- event,
118
- config
119
- });
120
- else await config.hooks?.onError?.({
121
- session,
122
- error: error_,
123
- event,
124
- config
125
- });
126
- }).then((unsealed) => {
127
- if (unsealed) Object.assign(session, unsealed);
128
- delete event.context.sessions[sessionName][kGetSessionPromise$1];
129
- return session;
130
- });
131
- event.context.sessions[sessionName][kGetSessionPromise$1] = promise;
132
- await promise;
133
- }
134
- if (!exclusiveHookFired && session.id !== void 0 && session.token !== void 0) await config.hooks?.onRead?.({
135
- session,
136
- event,
137
- config
35
+ };
36
+ function getSessionsContext(event) {
37
+ return event.context.sessions ||= Object.create(null);
38
+ }
39
+ function hasWritableResponse(event) {
40
+ if (!isEvent(event)) return false;
41
+ const { res } = event.node;
42
+ return !res.headersSent && !res.writableEnded;
43
+ }
44
+ function readSessionToken(event, sessionName, source) {
45
+ return resolveSessionToken(source, sessionName, {
46
+ written: (name) => readChunkedFromSetCookie(getResponseSetCookie(event), name),
47
+ cookie: (name) => readChunkedFrom(getRequestCookies(event), name),
48
+ header: (name) => getRequestHeader(event, name)
138
49
  });
139
- return session;
140
50
  }
141
- function getJWESessionToken(event, config) {
142
- const sessionName = config.name || DEFAULT_NAME$1;
143
- let token;
144
- if (config.sessionHeader !== false) {
145
- const headerValue = _getReqHeader$1(event, typeof config.sessionHeader === "string" ? config.sessionHeader.toLowerCase() : `x-${sessionName.toLowerCase()}-session`);
146
- if (typeof headerValue === "string") token = headerValue.startsWith("Bearer ") ? headerValue.slice(7).trim() : headerValue;
147
- }
148
- if (config.cookie !== false) {
149
- const setCookie = _getResHeader$1(event, "set-cookie");
150
- if (typeof setCookie === "string") token = findSetCookie$1(setCookie, sessionName);
151
- else if (Array.isArray(setCookie)) for (const sc of setCookie) {
152
- token = findSetCookie$1(sc, sessionName);
153
- if (token) break;
154
- }
155
- }
156
- if (!token) {
157
- const cookieHeader = _getReqHeader$1(event, "cookie");
158
- if (cookieHeader) token = parse(String(cookieHeader))[sessionName];
51
+ function cookieEvent(event) {
52
+ return event;
53
+ }
54
+ function getRequestHeader(event, name) {
55
+ if ("node" in event && event.node) {
56
+ const value = event.node.req.headers[name];
57
+ return Array.isArray(value) ? value[0] : value;
159
58
  }
160
- return token;
59
+ return (("request" in event ? event.request?.headers : void 0) ?? ("headers" in event ? event.headers : void 0))?.get(name) ?? void 0;
161
60
  }
162
- function _getReqHeader$1(event, name) {
163
- if (event.node) return event.node?.req.headers[name];
164
- if (event.request) return event.request.headers?.get(name);
165
- if (event.headers) return event.headers.get(name);
61
+ function getRequestCookies(event) {
62
+ const header = getRequestHeader(event, "cookie");
63
+ return header ? parse(header) : Object.create(null);
166
64
  }
167
- function _getResHeader$1(event, name) {
168
- if (event.node) return event.node?.res.getHeader(name);
169
- if (event.response) return event.response.headers?.get(name);
65
+ function getResponseSetCookie(event) {
66
+ if ("node" in event && event.node) return responseSetCookies(event);
67
+ const response = "response" in event ? event.response : void 0;
68
+ return hasHeaders(response) ? response.headers.getSetCookie() : [];
170
69
  }
171
- async function updateJWESession(event, config, update) {
172
- const canWriteCookie = isEvent(event);
173
- if (config.cookie !== false && !canWriteCookie) throw new Error("[unjwt/h3] Cannot update session on read-only event.");
174
- const sessionName = config.name || DEFAULT_NAME$1;
175
- const session = event.context.sessions?.[sessionName] || await getJWESession(event, config);
176
- const oldSession = {
177
- ...session,
178
- data: sanitizeObjectCopy(session.data)
179
- };
180
- if (typeof update === "function") update = update(session.data);
181
- if (update) Object.assign(session.data, update);
182
- const now = config.jwe?.encryptOptions?.currentDate?.getTime() ?? Date.now();
183
- const createdAt = now - now % 1e3;
184
- Object.assign(session, {
185
- id: config.generateId?.() || crypto.randomUUID(),
186
- createdAt,
187
- expiresAt: config.maxAge === void 0 ? void 0 : createdAt + computeDurationInSeconds(config.maxAge) * 1e3
188
- });
189
- let sealed;
190
- try {
191
- sealed = await sealJWESession(event, config);
192
- } catch (error_) {
193
- Object.assign(session, {
194
- id: oldSession.id,
195
- createdAt: oldSession.createdAt,
196
- expiresAt: oldSession.expiresAt,
197
- data: oldSession.data
198
- });
199
- session.token = oldSession.token;
200
- await config.hooks?.onError?.({
201
- session,
202
- event,
203
- error: error_,
204
- config
205
- });
206
- throw error_;
207
- }
208
- session.token = sealed;
209
- if (config.cookie !== false && canWriteCookie) setCookie(event, sessionName, sealed, {
210
- ...DEFAULT_COOKIE$1,
211
- ...config.cookie,
212
- expires: config.maxAge === void 0 ? void 0 : new Date(session.createdAt + computeDurationInSeconds(config.maxAge) * 1e3)
213
- });
214
- await config.hooks?.onUpdate?.({
215
- session,
216
- oldSession,
217
- event,
218
- config
219
- });
220
- return session;
70
+ function hasHeaders(response) {
71
+ return typeof response === "object" && response !== null && "headers" in response && Boolean(response.headers);
72
+ }
73
+ const codec$1 = createJWECodec({
74
+ prefix: PREFIX,
75
+ defaultName: "h3-jwe"
76
+ });
77
+ async function useJWESession(event, config) {
78
+ const binding = bind$1(event, config);
79
+ await loadSession(binding);
80
+ return sessionManager(binding);
81
+ }
82
+ async function getJWESession(event, config) {
83
+ return loadSession(bind$1(event, config));
84
+ }
85
+ function getJWESessionToken(event, config) {
86
+ return sessionToken(bind$1(event, config));
87
+ }
88
+ async function updateJWESession(event, config, update, options) {
89
+ return issueSession(bind$1(event, config), update, options);
221
90
  }
222
91
  async function sealJWESession(event, config) {
223
- const key = getEncryptKey(config.key);
224
- const sessionName = config.name || DEFAULT_NAME$1;
225
- const session = event.context.sessions?.[sessionName] || await getJWESession(event, config);
226
- const iat = Math.floor(session.createdAt / 1e3);
227
- const exp = session.expiresAt ? Math.floor(session.expiresAt / 1e3) : void 0;
228
- const payload = {
229
- ...session.data,
230
- jti: session.id,
231
- iat
232
- };
233
- if (exp) payload.exp = exp;
234
- let typ = void 0;
235
- 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;
236
- return await encrypt(payload, key, {
237
- ...config.jwe?.encryptOptions,
238
- expiresIn: void 0,
239
- protectedHeader: {
240
- ...config.jwe?.encryptOptions?.protectedHeader,
241
- kid: typeof key === "string" ? void 0 : key.kid,
242
- typ: typ || "JWT",
243
- cty: "application/json"
244
- }
245
- });
92
+ return encodeSession(bind$1(event, config));
246
93
  }
247
94
  async function unsealJWESession(event, config, sealed) {
248
- const key = config.hooks?.onUnsealKeyLookup ? (header) => config.hooks.onUnsealKeyLookup({
249
- header,
250
- event,
251
- config
252
- }) : getDecryptKey(config.key);
253
- const alg = config.jwe?.encryptOptions?.alg;
254
- const enc = config.jwe?.encryptOptions?.enc;
255
- let typ = void 0;
256
- 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;
257
- const { payload } = await decrypt(sealed, key, {
258
- ...config.jwe?.decryptOptions,
259
- requiredClaims: [...new Set([
260
- ...config.jwe?.decryptOptions?.requiredClaims || [],
261
- "jti",
262
- "iat"
263
- ])],
264
- typ: typ || "JWT",
265
- maxTokenAge: config.maxAge,
266
- algorithms: alg ? [alg] : void 0,
267
- encryptionAlgorithms: enc ? [enc] : void 0,
268
- unwrappedKeyAlgorithm: void 0,
269
- keyUsage: void 0,
270
- forceUint8Array: false,
271
- validateClaims: true
272
- }).catch((error_) => {
273
- if (isJWTError(error_, "ERR_JWT_EXPIRED")) throw error_;
274
- const message = error_ instanceof Error ? error_.message : String(error_);
275
- throw new Error(`Invalid session token: ${message}`);
95
+ return openSessionToken(codec$1, config, {
96
+ token: sealed,
97
+ carrier: { event }
276
98
  });
277
- const { jti, iat, exp, ...data } = payload;
278
- return {
279
- id: jti,
280
- createdAt: iat * 1e3,
281
- expiresAt: exp ? exp * 1e3 : void 0,
282
- data: data && typeof data === "object" ? data : Object.create(null)
283
- };
284
99
  }
285
100
  async function clearJWESession(event, config) {
286
- const canWriteCookie = isEvent(event);
287
- if (config.cookie !== false && !canWriteCookie) throw new Error("[unjwt/h3] Cannot clear session on read-only event.");
288
- const sessionName = config.name || DEFAULT_NAME$1;
289
- let session = event.context.sessions?.[sessionName];
290
- if (session && session[kGetSessionPromise$1]) session = await session[kGetSessionPromise$1];
291
- const oldSession = session ? {
292
- ...session,
293
- data: sanitizeObjectCopy(session.data)
294
- } : void 0;
295
- if (event.context.sessions?.[sessionName]) delete event.context.sessions[sessionName];
296
- if (config.cookie !== false && canWriteCookie) setCookie(event, sessionName, "", {
297
- ...DEFAULT_COOKIE$1,
298
- ...config.cookie,
299
- expires: /* @__PURE__ */ new Date(0),
300
- maxAge: void 0
301
- });
302
- await config.hooks?.onClear?.({
303
- oldSession,
304
- event,
305
- config
306
- });
307
- }
308
- function getEncryptKey(key) {
309
- if (!key) throw new Error("Session: JWE key is required.");
310
- let _key;
311
- if (typeof key === "string") _key = key;
312
- else if (isSymmetricJWK(key)) _key = key;
313
- else if ("publicKey" in key && key.publicKey && isPublicJWK(key.publicKey)) _key = key.publicKey;
314
- else if ("privateKey" in key && isPrivateJWK(key.privateKey)) _key = key.privateKey;
315
- if (!_key) throw new Error("Session: Invalid JWE key. It must be a password string or valid JWK.", { cause: key });
316
- return _key;
101
+ return dropSession(bind$1(event, config));
317
102
  }
318
- function getDecryptKey(key) {
319
- if (!key) throw new Error("Session: JWE key is required.");
320
- let _key = void 0;
321
- if (typeof key === "string") _key = key;
322
- else if (isSymmetricJWK(key)) _key = key;
323
- else if ("privateKey" in key) _key = key.privateKey;
324
- if (!_key) throw new Error("Session: Invalid JWE key. It must be a password string or a valid private JWK.", { cause: key });
325
- return _key;
326
- }
327
- function findSetCookie$1(setCookie, name) {
328
- const regex = new RegExp(`(?:^|,\\s*)${name}=([^;]+)`);
329
- const match = setCookie.match(regex);
330
- return match ? match[1] : void 0;
103
+ function bind$1(event, config) {
104
+ return bindSession(event, config, {
105
+ port: h3SessionPort,
106
+ codec: codec$1,
107
+ actions: sessionActions()
108
+ });
331
109
  }
332
- const kGetSessionPromise = Symbol("h3_jws_getSession");
333
- const DEFAULT_NAME = "h3-jws";
334
- const DEFAULT_COOKIE = {
335
- path: "/",
336
- secure: true,
337
- httpOnly: false
338
- };
110
+ const codec = createJWSCodec({
111
+ prefix: PREFIX,
112
+ defaultName: "h3-jws"
113
+ });
339
114
  async function useJWSSession(event, config) {
340
- const sessionName = config.name || DEFAULT_NAME;
341
- await getJWSSession(event, config);
342
- const sessionManager = {
343
- get id() {
344
- return event.context.sessions?.[sessionName]?.id;
345
- },
346
- get createdAt() {
347
- return event.context.sessions?.[sessionName]?.createdAt || Date.now();
348
- },
349
- get expiresAt() {
350
- return event.context.sessions?.[sessionName]?.expiresAt;
351
- },
352
- get data() {
353
- return event.context.sessions?.[sessionName]?.data || {};
354
- },
355
- get token() {
356
- return (event.context.sessions?.[sessionName])?.token ?? getJWSSessionToken(event, config);
357
- },
358
- update: async (update) => {
359
- await updateJWSSession(event, config, update);
360
- return sessionManager;
361
- },
362
- clear: async () => {
363
- await clearJWSSession(event, config);
364
- return sessionManager;
365
- }
366
- };
367
- return sessionManager;
115
+ const binding = bind(event, config);
116
+ await loadSession(binding);
117
+ return sessionManager(binding);
368
118
  }
369
119
  async function getJWSSession(event, config) {
370
- const sessionName = config.name || DEFAULT_NAME;
371
- if (!event.context.sessions) event.context.sessions = Object.create(null);
372
- const existingSession = event.context.sessions[sessionName];
373
- if (existingSession) {
374
- const session = existingSession[kGetSessionPromise] ? await existingSession[kGetSessionPromise] : existingSession;
375
- if (session.expiresAt !== void 0 && session.expiresAt < Date.now()) {
376
- await config.hooks?.onExpire?.({
377
- session: {
378
- id: session.id,
379
- createdAt: session.createdAt,
380
- expiresAt: session.expiresAt,
381
- token: session.token
382
- },
383
- event,
384
- error: new JWTError(`JWT "exp" (Expiration Time) Claim validation failed: Token has expired (exp: ${(/* @__PURE__ */ new Date(session.expiresAt * 1e3)).toISOString()})`, "ERR_JWT_EXPIRED", {
385
- jti: session.id,
386
- exp: session.expiresAt / 1e3
387
- }),
388
- config
389
- });
390
- delete event.context.sessions[sessionName];
391
- if (config.cookie !== false) if (isEvent(event)) setCookie(event, sessionName, "", {
392
- ...DEFAULT_COOKIE,
393
- ...config.cookie,
394
- expires: /* @__PURE__ */ new Date(0),
395
- maxAge: void 0
396
- });
397
- else console.warn("[unjwt/h3] Session expired but cookie cannot be cleared on a read-only event.");
398
- const freshNow = config.jws?.signOptions?.currentDate?.getTime() ?? Date.now();
399
- const freshCreatedAt = freshNow - freshNow % 1e3;
400
- const freshSession = {
401
- id: void 0,
402
- createdAt: freshCreatedAt,
403
- expiresAt: config.maxAge === void 0 ? void 0 : freshCreatedAt + computeDurationInSeconds(config.maxAge) * 1e3,
404
- data: Object.create(null),
405
- token: void 0
406
- };
407
- event.context.sessions[sessionName] = freshSession;
408
- return freshSession;
409
- }
410
- if (session.id !== void 0 && session.token !== void 0) await config.hooks?.onRead?.({
411
- session,
412
- event,
413
- config
414
- });
415
- return session;
416
- }
417
- const now = config.jws?.signOptions?.currentDate?.getTime() ?? Date.now();
418
- const createdAt = now - now % 1e3;
419
- const session = {
420
- id: void 0,
421
- createdAt,
422
- expiresAt: config.maxAge === void 0 ? void 0 : createdAt + computeDurationInSeconds(config.maxAge) * 1e3,
423
- data: Object.create(null),
424
- token: void 0
425
- };
426
- event.context.sessions[sessionName] = session;
427
- const token = getJWSSessionToken(event, config);
428
- let exclusiveHookFired = false;
429
- if (token) {
430
- session.token = token;
431
- const promise = verifyJWSSession(event, config, token).catch(async (error_) => {
432
- exclusiveHookFired = true;
433
- if (isJWTError(error_, "ERR_JWT_EXPIRED")) await config.hooks?.onExpire?.({
434
- session: {
435
- id: error_.cause.jti,
436
- createdAt: error_.cause.iat ? error_.cause.iat * 1e3 : void 0,
437
- expiresAt: error_.cause.exp ? error_.cause.exp * 1e3 : void 0,
438
- token
439
- },
440
- error: error_,
441
- event,
442
- config
443
- });
444
- else await config.hooks?.onError?.({
445
- session,
446
- error: error_,
447
- event,
448
- config
449
- });
450
- }).then((unsealed) => {
451
- if (unsealed) Object.assign(session, unsealed);
452
- delete event.context.sessions[sessionName][kGetSessionPromise];
453
- return session;
454
- });
455
- session[kGetSessionPromise] = promise;
456
- await promise;
457
- }
458
- if (!exclusiveHookFired && session.id !== void 0 && session.token !== void 0) await config.hooks?.onRead?.({
459
- session,
460
- event,
461
- config
462
- });
463
- return session;
120
+ return loadSession(bind(event, config));
464
121
  }
465
122
  function getJWSSessionToken(event, config) {
466
- const sessionName = config.name || DEFAULT_NAME;
467
- let token;
468
- if (config.sessionHeader !== false) {
469
- const headerValue = _getReqHeader(event, typeof config.sessionHeader === "string" ? config.sessionHeader.toLowerCase() : `x-${sessionName.toLowerCase()}-session`);
470
- if (typeof headerValue === "string") token = headerValue.startsWith("Bearer ") ? headerValue.slice(7).trim() : headerValue;
471
- }
472
- if (config.cookie !== false) {
473
- const setCookie = _getResHeader(event, "set-cookie");
474
- if (typeof setCookie === "string") token = findSetCookie(setCookie, sessionName);
475
- else if (Array.isArray(setCookie)) for (const sc of setCookie) {
476
- token = findSetCookie(sc, sessionName);
477
- if (token) break;
478
- }
479
- }
480
- if (!token) {
481
- const cookieHeader = _getReqHeader(event, "cookie");
482
- if (cookieHeader) token = parse(String(cookieHeader))[sessionName];
483
- }
484
- return token;
485
- }
486
- function _getReqHeader(event, name) {
487
- if (event.node) return event.node.req.headers[name];
488
- if (event.request) return event.request.headers.get(name);
489
- if (event.headers) return event.headers.get(name);
123
+ return sessionToken(bind(event, config));
490
124
  }
491
- function _getResHeader(event, name) {
492
- if (event.node) return event.node?.res.getHeader(name);
493
- if (event.response) return event.response.headers?.get(name);
494
- }
495
- async function updateJWSSession(event, config, update) {
496
- const canWriteCookie = isEvent(event);
497
- if (config.cookie !== false && !canWriteCookie) throw new Error("[unjwt/h3] Cannot update session on read-only event.");
498
- const sessionName = config.name || DEFAULT_NAME;
499
- const session = event.context.sessions?.[sessionName] || await getJWSSession(event, config);
500
- const oldSession = {
501
- ...session,
502
- data: sanitizeObjectCopy(session.data)
503
- };
504
- if (typeof update === "function") update = update(session.data);
505
- if (update) Object.assign(session.data, update);
506
- const now = config.jws?.signOptions?.currentDate?.getTime() ?? Date.now();
507
- const createdAt = now - now % 1e3;
508
- Object.assign(session, {
509
- id: config.generateId?.() || crypto.randomUUID(),
510
- createdAt,
511
- expiresAt: config.maxAge === void 0 ? void 0 : createdAt + computeDurationInSeconds(config.maxAge) * 1e3
512
- });
513
- let token;
514
- try {
515
- token = await signJWSSession(event, config);
516
- } catch (error_) {
517
- Object.assign(session, {
518
- id: oldSession.id,
519
- createdAt: oldSession.createdAt,
520
- expiresAt: oldSession.expiresAt,
521
- data: oldSession.data
522
- });
523
- session.token = oldSession.token;
524
- await config.hooks?.onError?.({
525
- session,
526
- event,
527
- error: error_,
528
- config
529
- });
530
- throw error_;
531
- }
532
- session.token = token;
533
- if (config.cookie !== false && canWriteCookie) setCookie(event, sessionName, token, {
534
- ...DEFAULT_COOKIE,
535
- ...config.cookie,
536
- expires: config.maxAge === void 0 ? void 0 : new Date(session.createdAt + computeDurationInSeconds(config.maxAge) * 1e3)
537
- });
538
- await config.hooks?.onUpdate?.({
539
- session,
540
- oldSession,
541
- event,
542
- config
543
- });
544
- return session;
125
+ async function updateJWSSession(event, config, update, options) {
126
+ return issueSession(bind(event, config), update, options);
545
127
  }
546
128
  async function signJWSSession(event, config) {
547
- const key = getSignKey(config.key);
548
- const sessionName = config.name || DEFAULT_NAME;
549
- const session = event.context.sessions?.[sessionName] || await getJWSSession(event, config);
550
- const iat = Math.floor(session.createdAt / 1e3);
551
- const exp = session.expiresAt ? Math.floor(session.expiresAt / 1e3) : void 0;
552
- const payload = {
553
- ...session.data,
554
- jti: session.id,
555
- iat
556
- };
557
- if (exp) payload.exp = exp;
558
- let typ = void 0;
559
- 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;
560
- return await sign(payload, key, {
561
- ...config.jws?.signOptions,
562
- expiresIn: void 0,
563
- protectedHeader: {
564
- ...config.jws?.signOptions?.protectedHeader,
565
- kid: key.kid,
566
- typ: typ || "JWT",
567
- cty: "application/json"
568
- }
569
- });
129
+ return encodeSession(bind(event, config));
570
130
  }
571
131
  async function verifyJWSSession(event, config, token) {
572
- const alg = config.jws?.signOptions?.alg;
573
- const jwk = config.hooks?.onVerifyKeyLookup ? (header) => config.hooks.onVerifyKeyLookup({
574
- header,
575
- event,
576
- config
577
- }) : getVerifyKey(config.key);
578
- let typ = void 0;
579
- 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;
580
- const { payload } = await verify(token, jwk, {
581
- ...config.jws?.verifyOptions,
582
- requiredClaims: [...new Set([
583
- ...config.jws?.verifyOptions?.requiredClaims || [],
584
- "jti",
585
- "iat"
586
- ])],
587
- typ: typ || "JWT",
588
- algorithms: alg ? [alg] : void 0,
589
- forceUint8Array: false,
590
- validateClaims: true
591
- }).catch((error_) => {
592
- if (isJWTError(error_, "ERR_JWT_EXPIRED")) throw error_;
593
- const message = error_ instanceof Error ? error_.message : String(error_);
594
- throw new Error(`Invalid session token: ${message}`);
132
+ return openSessionToken(codec, config, {
133
+ token,
134
+ carrier: { event }
595
135
  });
596
- const { jti, iat, exp, ...data } = payload;
597
- return {
598
- id: jti,
599
- createdAt: iat * 1e3,
600
- expiresAt: exp ? exp * 1e3 : void 0,
601
- data: data && typeof data === "object" ? data : Object.create(null)
602
- };
603
136
  }
604
137
  async function clearJWSSession(event, config) {
605
- const canWriteCookie = isEvent(event);
606
- if (config.cookie !== false && !canWriteCookie) throw new Error("[unjwt/h3] Cannot clear session on read-only event.");
607
- const sessionName = config.name || DEFAULT_NAME;
608
- let session = event.context.sessions?.[sessionName];
609
- if (session && session[kGetSessionPromise]) session = await session[kGetSessionPromise];
610
- const oldSession = session ? {
611
- ...session,
612
- data: sanitizeObjectCopy(session.data)
613
- } : void 0;
614
- if (event.context.sessions?.[sessionName]) delete event.context.sessions[sessionName];
615
- if (config.cookie !== false && canWriteCookie) setCookie(event, sessionName, "", {
616
- ...DEFAULT_COOKIE,
617
- ...config.cookie,
618
- expires: /* @__PURE__ */ new Date(0),
619
- maxAge: void 0
620
- });
621
- await config.hooks?.onClear?.({
622
- oldSession,
623
- event,
624
- config
625
- });
626
- }
627
- function getSignKey(key) {
628
- if (!key) throw new Error("Session: JWS key is required.");
629
- let _key = void 0;
630
- if (isSymmetricJWK(key)) _key = key;
631
- else if ("privateKey" in key && isPrivateJWK(key.privateKey)) _key = key.privateKey;
632
- if (!_key) throw new Error("Session: Invalid JWS key. It must be a symmetric JWK or a private JWK.", { cause: key });
633
- return _key;
138
+ return dropSession(bind(event, config));
634
139
  }
635
- function getVerifyKey(key) {
636
- if (!key) throw new Error("Session: JWS key is required.");
637
- let _key = void 0;
638
- if (isSymmetricJWK(key)) _key = key;
639
- else if ("publicKey" in key && isPublicJWK(key.publicKey)) _key = key.publicKey;
640
- else if ("publicKey" in key && Array.isArray(key.publicKey)) {
641
- const keys = key.publicKey.filter((k) => isPublicJWK(k));
642
- if (keys && keys.length > 0) _key = { keys };
643
- }
644
- if (!_key) throw new Error("Session: Invalid JWS key. It must be a symmetric JWK or a public JWK/set.", { cause: key });
645
- return _key;
646
- }
647
- function findSetCookie(setCookie, name) {
648
- const regex = new RegExp(`(?:^|,\\s*)${name}=([^;]+)`);
649
- const match = setCookie.match(regex);
650
- return match ? match[1] : void 0;
140
+ function bind(event, config) {
141
+ return bindSession(event, config, {
142
+ port: h3SessionPort,
143
+ codec,
144
+ actions: sessionActions()
145
+ });
651
146
  }
652
- export { clearJWESession, clearJWSSession, deriveJWKFromPassword, exportPEM, exportToPEM, generateJWK, getJWESession, getJWESessionToken, getJWSSession, getJWSSessionToken, importFromPEM, importPEM, sealJWESession, signJWSSession, unsealJWESession, updateJWESession, updateJWSSession, useJWESession, useJWSSession, verifyJWSSession };
147
+ export { clearJWESession, clearJWSSession, deriveJWKFromPassword, exportPEM, generateJWK, getJWESession, getJWESessionToken, getJWSSession, getJWSSessionToken, importPEM, sealJWESession, signJWSSession, thumbprint, unsealJWESession, updateJWESession, updateJWSSession, useJWESession, useJWSSession, verifyJWSSession };