unjwt 0.5.24 → 0.5.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,7 +15,8 @@ A collection of low-level JWT ([RFC 7519](https://datatracker.ietf.org/doc/html/
15
15
  - **JWK (JSON Web Key, [RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517))**: generate, import, export, wrap, and unwrap keys in JWK format or as `CryptoKey` objects.
16
16
 
17
17
  - **Adapters** for popular frameworks (PRs welcome for more!):
18
- - [H3 (Nuxt, Nitro)](./src/adapters/h3.ts): `useJWSSession()`, `useJWESession()`
18
+ - [H3 v1 (Nuxt v4, Nitro v2)](./src/adapters/h3v1/): `useJWSSession()`, `useJWESession()`
19
+ - [H3 v2 (Nuxt v5, Nitro v3)](./src/adapters/h3v2/): `useJWSSession()`, `useJWESession()`
19
20
 
20
21
  ## Table of Contents
21
22
 
@@ -25,7 +26,8 @@ A collection of low-level JWT ([RFC 7519](https://datatracker.ietf.org/doc/html/
25
26
  - [JWK](#jwk-json-web-key-rfc-7517)
26
27
  - [Utility Functions](#utility-functions)
27
28
  - [Adapters](#adapters)
28
- - [H3 (Nuxt, Nitro)](#h3-nuxt-nitro)
29
+ - [H3 v1 (Nuxt up to v4, Nitro up to v2)](#h3-v1-nuxt-v4-nitro-v2)
30
+ - [H3 v2 (Nuxt v5, Nitro v3)](#h3-v2-nuxt-v5-nitro-v3)
29
31
  - [Development](#development)
30
32
  - [Credits](#credits)
31
33
  - [License](#license)
@@ -616,7 +618,7 @@ Both helpers expose the same API: read `session.id` / `session.data`, call `sess
616
618
  import { defineEventHandler } from "h3";
617
619
  import { useJWESession, useJWSSession, generateJWK } from "unjwt/adapters/h3v1";
618
620
 
619
- const keys = await generateJWK("RS256");
621
+ const keys = await generateJWK("RS256"); // Make sure to persist these keys somewhere!
620
622
 
621
623
  export default defineEventHandler(async (event) => {
622
624
  const privateSession = await useJWESession(event, {
@@ -663,7 +665,7 @@ import {
663
665
  generateJWK,
664
666
  } from "unjwt/adapters/h3v2";
665
667
 
666
- const atJwk = await generateJWK("RS256");
668
+ const atJwk = await generateJWK("RS256"); // Make sure to persist these keys somewhere!
667
669
 
668
670
  const jweOptions = {
669
671
  key: "refresh_token_secret",
@@ -675,7 +677,7 @@ const jwsOptions = {
675
677
  name: "access_token",
676
678
  maxAge: 15 * 60, // 15 minutes
677
679
  hooks: {
678
- async onExpire(event, _error, config) {
680
+ async onExpire({ event, config }) {
679
681
  const refreshSession = await getJWESession(event, jweOptions);
680
682
  if (!refreshSession.data.sub) {
681
683
  // no valid refresh session, nothing to do
@@ -24,11 +24,30 @@ interface SessionJWE<T extends SessionDataT$1 = SessionDataT$1> {
24
24
  [kGetSessionPromise$1]?: Promise<SessionJWE<T>>;
25
25
  }
26
26
  interface SessionHooksJWE {
27
- onRead?: (session: SessionJWE, event: H3Event, config: SessionConfigJWE) => void | Promise<void>;
28
- onUpdate?: (session: SessionJWE, event: H3Event, config: SessionConfigJWE) => void | Promise<void>;
29
- onClear?: (event: H3Event, config: Partial<SessionConfigJWE>) => void | Promise<void>;
30
- onExpire?: (event: H3Event, error: any | undefined, config: SessionConfigJWE) => void | Promise<void>;
31
- onError?: (event: H3Event, error: any, config: SessionConfigJWE) => void | Promise<void>;
27
+ onRead?: (args: {
28
+ session: SessionJWE;
29
+ event: H3Event;
30
+ config: SessionConfigJWE;
31
+ }) => void | Promise<void>;
32
+ onUpdate?: (args: {
33
+ session: SessionJWE;
34
+ event: H3Event;
35
+ config: SessionConfigJWE;
36
+ }) => void | Promise<void>;
37
+ onClear?: (args: {
38
+ event: H3Event;
39
+ config: Partial<SessionConfigJWE>;
40
+ }) => void | Promise<void>;
41
+ onExpire?: (args: {
42
+ event: H3Event;
43
+ error: Error;
44
+ config: SessionConfigJWE;
45
+ }) => void | Promise<void>;
46
+ onError?: (args: {
47
+ event: H3Event;
48
+ error: any;
49
+ config: SessionConfigJWE;
50
+ }) => void | Promise<void>;
32
51
  }
33
52
  interface SessionManager<T extends SessionDataT$1 = SessionDataT$1> {
34
53
  readonly id: string | undefined;
@@ -128,11 +147,30 @@ interface SessionJWS<T extends SessionDataT = SessionDataT> {
128
147
  [kGetSessionPromise]?: Promise<SessionJWS<T>>;
129
148
  }
130
149
  interface SessionHooksJWS {
131
- onRead?: (session: SessionJWS, event: H3Event, config: SessionConfigJWS) => void | Promise<void>;
132
- onUpdate?: (session: SessionJWS, event: H3Event, config: SessionConfigJWS) => void | Promise<void>;
133
- onClear?: (event: H3Event, config: Partial<SessionConfigJWS>) => void | Promise<void>;
134
- onExpire?: (event: H3Event, error: any | undefined, config: SessionConfigJWS) => void | Promise<void>;
135
- onError?: (event: H3Event, error: any, config: SessionConfigJWS) => void | Promise<void>;
150
+ onRead?: (args: {
151
+ session: SessionJWS;
152
+ event: H3Event;
153
+ config: SessionConfigJWS;
154
+ }) => void | Promise<void>;
155
+ onUpdate?: (args: {
156
+ session: SessionJWS;
157
+ event: H3Event;
158
+ config: SessionConfigJWS;
159
+ }) => void | Promise<void>;
160
+ onClear?: (args: {
161
+ event: H3Event;
162
+ config: Partial<SessionConfigJWS>;
163
+ }) => void | Promise<void>;
164
+ onExpire?: (args: {
165
+ event: H3Event;
166
+ error: Error;
167
+ config: SessionConfigJWS;
168
+ }) => void | Promise<void>;
169
+ onError?: (args: {
170
+ event: H3Event;
171
+ error: any;
172
+ config: SessionConfigJWS;
173
+ }) => void | Promise<void>;
136
174
  }
137
175
  interface SessionConfigJWS {
138
176
  /**
@@ -59,12 +59,22 @@ async function getJWESession(event, config) {
59
59
  if (existingSession) {
60
60
  const session2 = existingSession[kGetSessionPromise$1] ? await existingSession[kGetSessionPromise$1] : existingSession;
61
61
  if (session2.expiresAt !== void 0 && session2.expiresAt < Date.now() && isEvent(event)) {
62
- await config.hooks?.onExpire?.(event, void 0, config);
62
+ await config.hooks?.onExpire?.({
63
+ event,
64
+ error: new Error(
65
+ `JWT "exp" (Expiration Time) Claim validation failed: Token has expired (exp: ${new Date(session2.expiresAt * 1e3).toISOString()})`
66
+ ),
67
+ config
68
+ });
63
69
  return clearJWESession(event, config).then(
64
70
  () => getJWESession(event, config)
65
71
  );
66
72
  }
67
- await config.hooks?.onRead?.(session2, event, config);
73
+ await config.hooks?.onRead?.({
74
+ event,
75
+ session: session2,
76
+ config
77
+ });
68
78
  return session2;
69
79
  }
70
80
  const session = {
@@ -74,27 +84,35 @@ async function getJWESession(event, config) {
74
84
  data: /* @__PURE__ */ Object.create(null)
75
85
  };
76
86
  event.context.sessions[sessionName] = session;
77
- let jweToken;
87
+ let token;
78
88
  if (config.sessionHeader !== false) {
79
89
  const headerName = typeof config.sessionHeader === "string" ? config.sessionHeader.toLowerCase() : `x-${sessionName.toLowerCase()}-session`;
80
90
  const headerValue = _getReqHeader$1(event, headerName);
81
91
  if (typeof headerValue === "string") {
82
- jweToken = headerValue;
92
+ token = headerValue.startsWith("Bearer ") ? headerValue.slice(7).trim() : headerValue;
83
93
  }
84
94
  }
85
- if (!jweToken) {
95
+ if (!token) {
86
96
  const cookieHeader = _getReqHeader$1(event, "cookie");
87
97
  if (cookieHeader) {
88
- jweToken = parse(String(cookieHeader))[sessionName];
98
+ token = parse(String(cookieHeader))[sessionName];
89
99
  }
90
100
  }
91
- if (jweToken) {
92
- const promise = unsealJWESession(event, config, jweToken).catch(async (error_) => {
101
+ if (token) {
102
+ const promise = unsealJWESession(event, config, token).catch(async (error_) => {
93
103
  if (error_ instanceof Error && (error_.message.includes("Token has expired") || error_.message.includes("Token is too old"))) {
94
- await config.hooks?.onExpire?.(event, error_, config);
104
+ await config.hooks?.onExpire?.({
105
+ event,
106
+ error: error_,
107
+ config
108
+ });
95
109
  return void 0;
96
110
  }
97
- await config.hooks?.onError?.(event, error_, config);
111
+ await config.hooks?.onError?.({
112
+ event,
113
+ error: error_,
114
+ config
115
+ });
98
116
  return void 0;
99
117
  }).then((unsealed) => {
100
118
  if (unsealed) {
@@ -117,7 +135,11 @@ async function getJWESession(event, config) {
117
135
  session.expiresAt = config.maxAge ? session.createdAt + config.maxAge * 1e3 : void 0;
118
136
  await updateJWESession(event, config);
119
137
  }
120
- await config.hooks?.onRead?.(session, event, config);
138
+ await config.hooks?.onRead?.({
139
+ event,
140
+ session,
141
+ config
142
+ });
121
143
  return session;
122
144
  }
123
145
  function _getReqHeader$1(event, name) {
@@ -139,7 +161,11 @@ async function updateJWESession(event, config, update) {
139
161
  }
140
162
  if (update) {
141
163
  Object.assign(session.data, update);
142
- await config.hooks?.onUpdate?.(session, event, config);
164
+ await config.hooks?.onUpdate?.({
165
+ event,
166
+ session,
167
+ config
168
+ });
143
169
  }
144
170
  if (config.cookie !== false) {
145
171
  const sealed = await sealJWESession(event, config);
@@ -195,11 +221,11 @@ async function unsealJWESession(_event, config, sealed) {
195
221
  const { payload } = await decrypt(sealed, key, {
196
222
  ...config.jwe?.decryptOptions,
197
223
  requiredClaims: [
198
- ...config.jwe?.decryptOptions?.requiredClaims?.filter(
199
- (claim) => claim !== "jti" && claim !== "iat"
200
- ) || [],
201
- "jti",
202
- "iat"
224
+ .../* @__PURE__ */ new Set([
225
+ ...config.jwe?.decryptOptions?.requiredClaims || [],
226
+ "jti",
227
+ "iat"
228
+ ])
203
229
  ],
204
230
  maxTokenAge: config.maxAge,
205
231
  algorithms: alg ? [alg] : void 0,
@@ -235,7 +261,10 @@ async function clearJWESession(event, config) {
235
261
  expires: /* @__PURE__ */ new Date(0),
236
262
  maxAge: void 0
237
263
  });
238
- await config.hooks?.onClear?.(event, config);
264
+ await config.hooks?.onClear?.({
265
+ event,
266
+ config
267
+ });
239
268
  }
240
269
  function getEncryptKey(key) {
241
270
  if (!key) {
@@ -334,12 +363,22 @@ async function getJWSSession(event, config) {
334
363
  if (existingSession) {
335
364
  const session2 = existingSession[kGetSessionPromise] ? await existingSession[kGetSessionPromise] : existingSession;
336
365
  if (session2.expiresAt !== void 0 && session2.expiresAt < Date.now() && isEvent(event)) {
337
- await config.hooks?.onExpire?.(event, void 0, config);
366
+ await config.hooks?.onExpire?.({
367
+ event,
368
+ error: new Error(
369
+ `JWT "exp" (Expiration Time) Claim validation failed: Token has expired (exp: ${new Date(session2.expiresAt * 1e3).toISOString()})`
370
+ ),
371
+ config
372
+ });
338
373
  return clearJWSSession(event, config).then(
339
374
  () => getJWSSession(event, config)
340
375
  );
341
376
  }
342
- await config.hooks?.onRead?.(session2, event, config);
377
+ await config.hooks?.onRead?.({
378
+ event,
379
+ session: session2,
380
+ config
381
+ });
343
382
  return session2;
344
383
  }
345
384
  const session = {
@@ -354,7 +393,7 @@ async function getJWSSession(event, config) {
354
393
  const headerName = typeof config.sessionHeader === "string" ? config.sessionHeader.toLowerCase() : `x-${sessionName.toLowerCase()}-session`;
355
394
  const headerValue = _getReqHeader(event, headerName);
356
395
  if (typeof headerValue === "string") {
357
- token = headerValue;
396
+ token = headerValue.startsWith("Bearer ") ? headerValue.slice(7).trim() : headerValue;
358
397
  }
359
398
  }
360
399
  if (!token) {
@@ -366,10 +405,18 @@ async function getJWSSession(event, config) {
366
405
  if (token) {
367
406
  const promise = verifyJWSSession(event, config, token).catch(async (error_) => {
368
407
  if (error_ instanceof Error && (error_.message.includes("Token has expired") || error_.message.includes("Token is too old"))) {
369
- await config.hooks?.onExpire?.(event, error_, config);
408
+ await config.hooks?.onExpire?.({
409
+ event,
410
+ error: error_,
411
+ config
412
+ });
370
413
  return void 0;
371
414
  }
372
- await config.hooks?.onError?.(event, error_, config);
415
+ await config.hooks?.onError?.({
416
+ event,
417
+ error: error_,
418
+ config
419
+ });
373
420
  return void 0;
374
421
  }).then((unsealed) => {
375
422
  if (unsealed) {
@@ -392,7 +439,11 @@ async function getJWSSession(event, config) {
392
439
  session.expiresAt = config.maxAge ? session.createdAt + config.maxAge * 1e3 : void 0;
393
440
  await updateJWSSession(event, config);
394
441
  }
395
- await config.hooks?.onRead?.(session, event, config);
442
+ await config.hooks?.onRead?.({
443
+ event,
444
+ session,
445
+ config
446
+ });
396
447
  return session;
397
448
  }
398
449
  function _getReqHeader(event, name) {
@@ -414,7 +465,11 @@ async function updateJWSSession(event, config, update) {
414
465
  }
415
466
  if (update) {
416
467
  Object.assign(session.data, update);
417
- await config.hooks?.onUpdate?.(session, event, config);
468
+ await config.hooks?.onUpdate?.({
469
+ event,
470
+ session,
471
+ config
472
+ });
418
473
  }
419
474
  if (config.cookie !== false) {
420
475
  const token = await signJWSSession(event, config);
@@ -467,11 +522,11 @@ async function verifyJWSSession(_event, config, token) {
467
522
  const { payload } = await verify(token, jwk, {
468
523
  ...config.jws?.verifyOptions,
469
524
  requiredClaims: [
470
- ...config.jws?.verifyOptions?.requiredClaims?.filter(
471
- (claim) => claim !== "jti" && claim !== "iat"
472
- ) || [],
473
- "jti",
474
- "iat"
525
+ .../* @__PURE__ */ new Set([
526
+ ...config.jws?.verifyOptions?.requiredClaims || [],
527
+ "jti",
528
+ "iat"
529
+ ])
475
530
  ],
476
531
  typ: typ || "JWT",
477
532
  algorithms: alg ? [alg] : void 0,
@@ -501,7 +556,10 @@ async function clearJWSSession(event, config) {
501
556
  expires: /* @__PURE__ */ new Date(0),
502
557
  maxAge: void 0
503
558
  });
504
- await config.hooks?.onClear?.(event, config);
559
+ await config.hooks?.onClear?.({
560
+ event,
561
+ config
562
+ });
505
563
  }
506
564
  function getSignKey(key) {
507
565
  if (!key) {
@@ -18,11 +18,30 @@ interface SessionJWE<T extends SessionDataT$1 = SessionDataT$1> {
18
18
  [kGetSessionPromise$1]?: Promise<SessionJWE<T>>;
19
19
  }
20
20
  interface SessionHooksJWE {
21
- onRead?: (session: SessionJWE, event: HTTPEvent, config: SessionConfigJWE) => void | Promise<void>;
22
- onUpdate?: (session: SessionJWE, event: HTTPEvent, config: SessionConfigJWE) => void | Promise<void>;
23
- onClear?: (event: HTTPEvent, config: Partial<SessionConfigJWE>) => void | Promise<void>;
24
- onExpire?: (event: HTTPEvent, error: any | undefined, config: SessionConfigJWE) => void | Promise<void>;
25
- onError?: (event: HTTPEvent, error: any, config: SessionConfigJWE) => void | Promise<void>;
21
+ onRead?: (args: {
22
+ session: SessionJWE;
23
+ event: HTTPEvent;
24
+ config: SessionConfigJWE;
25
+ }) => void | Promise<void>;
26
+ onUpdate?: (args: {
27
+ session: SessionJWE;
28
+ event: HTTPEvent;
29
+ config: SessionConfigJWE;
30
+ }) => void | Promise<void>;
31
+ onClear?: (args: {
32
+ event: HTTPEvent;
33
+ config: Partial<SessionConfigJWE>;
34
+ }) => void | Promise<void>;
35
+ onExpire?: (args: {
36
+ event: HTTPEvent;
37
+ error: Error;
38
+ config: SessionConfigJWE;
39
+ }) => void | Promise<void>;
40
+ onError?: (args: {
41
+ event: HTTPEvent;
42
+ error: any;
43
+ config: SessionConfigJWE;
44
+ }) => void | Promise<void>;
26
45
  }
27
46
  interface SessionManager<T extends SessionDataT$1 = SessionDataT$1> {
28
47
  readonly id: string | undefined;
@@ -93,11 +112,30 @@ interface SessionJWS<T extends SessionDataT = SessionDataT> {
93
112
  [kGetSessionPromise]?: Promise<SessionJWS<T>>;
94
113
  }
95
114
  interface SessionHooksJWS {
96
- onRead?: (session: SessionJWS, event: HTTPEvent, config: SessionConfigJWS) => void | Promise<void>;
97
- onUpdate?: (session: SessionJWS, event: HTTPEvent, config: SessionConfigJWS) => void | Promise<void>;
98
- onClear?: (event: HTTPEvent, config: Partial<SessionConfigJWS>) => void | Promise<void>;
99
- onExpire?: (event: HTTPEvent, error: any | undefined, config: SessionConfigJWS) => void | Promise<void>;
100
- onError?: (event: HTTPEvent, error: any, config: SessionConfigJWS) => void | Promise<void>;
115
+ onRead?: (args: {
116
+ session: SessionJWS;
117
+ event: HTTPEvent;
118
+ config: SessionConfigJWS;
119
+ }) => void | Promise<void>;
120
+ onUpdate?: (args: {
121
+ session: SessionJWS;
122
+ event: HTTPEvent;
123
+ config: SessionConfigJWS;
124
+ }) => void | Promise<void>;
125
+ onClear?: (args: {
126
+ event: HTTPEvent;
127
+ config: Partial<SessionConfigJWS>;
128
+ }) => void | Promise<void>;
129
+ onExpire?: (args: {
130
+ event: HTTPEvent;
131
+ error: Error;
132
+ config: SessionConfigJWS;
133
+ }) => void | Promise<void>;
134
+ onError?: (args: {
135
+ event: HTTPEvent;
136
+ error: any;
137
+ config: SessionConfigJWS;
138
+ }) => void | Promise<void>;
101
139
  }
102
140
  interface SessionConfigJWS {
103
141
  key: JWK_Symmetric | {
@@ -1,106 +1,10 @@
1
1
  export { k as deriveJWKFromPassword, h as exportJWKToPEM, c as generateJWK, f as importJWKFromPEM } from '../shared/unjwt.epPK1KFr.mjs';
2
- import { getEventContext } from 'h3';
3
- import { serialize, parseSetCookie, parse } from 'cookie-es';
2
+ import { getEventContext, getChunkedCookie, setChunkedCookie } from 'h3';
4
3
  import { NullProtoObj } from 'rou3';
5
4
  import { e as encrypt, d as decrypt } from '../shared/unjwt.CiBEuvtQ.mjs';
6
5
  import { p as isSymmetricJWK, q as isPrivateJWK, u as isPublicJWK } from '../shared/unjwt.C86hA0o-.mjs';
7
6
  import { s as sign, v as verify } from '../shared/unjwt.D-FATaw1.mjs';
8
7
 
9
- const CHUNKED_COOKIE = "__chunked__";
10
- const CHUNKS_MAX_LENGTH = 4e3;
11
- function parseCookies(event) {
12
- return parse(event.req.headers.get("cookie") || "");
13
- }
14
- function getCookie(event, name) {
15
- return parseCookies(event)[name];
16
- }
17
- function setCookie(event, name, value, options) {
18
- const newCookie = serialize(name, value, { path: "/", ...options });
19
- const currentCookies = event.res.headers.getSetCookie();
20
- if (currentCookies.length === 0) {
21
- event.res.headers.set("set-cookie", newCookie);
22
- return;
23
- }
24
- const newCookieKey = _getDistinctCookieKey(
25
- name,
26
- options || {}
27
- );
28
- event.res.headers.delete("set-cookie");
29
- for (const cookie of currentCookies) {
30
- const _key = _getDistinctCookieKey(
31
- cookie.split("=")[0],
32
- parseSetCookie(cookie)
33
- );
34
- if (_key === newCookieKey) {
35
- continue;
36
- }
37
- event.res.headers.append("set-cookie", cookie);
38
- }
39
- event.res.headers.append("set-cookie", newCookie);
40
- }
41
- function deleteCookie(event, name, serializeOptions) {
42
- setCookie(event, name, "", {
43
- ...serializeOptions,
44
- maxAge: 0
45
- });
46
- }
47
- function getChunkedCookie(event, name) {
48
- const mainCookie = getCookie(event, name);
49
- if (!mainCookie || !mainCookie.startsWith(CHUNKED_COOKIE)) {
50
- return mainCookie;
51
- }
52
- const chunksCount = getChunkedCookieCount(mainCookie);
53
- if (chunksCount === 0) {
54
- return void 0;
55
- }
56
- const chunks = [];
57
- for (let i = 1; i <= chunksCount; i++) {
58
- const chunk = getCookie(event, chunkCookieName(name, i));
59
- if (!chunk) {
60
- return void 0;
61
- }
62
- chunks.push(chunk);
63
- }
64
- return chunks.join("");
65
- }
66
- function setChunkedCookie(event, name, value, options) {
67
- const chunkMaxLength = options?.chunkMaxLength || CHUNKS_MAX_LENGTH;
68
- const chunkCount = Math.ceil(value.length / chunkMaxLength);
69
- const previousCookie = getCookie(event, name);
70
- if (previousCookie?.startsWith(CHUNKED_COOKIE)) {
71
- const previousChunkCount = getChunkedCookieCount(previousCookie);
72
- if (previousChunkCount > chunkCount) {
73
- for (let i = chunkCount; i <= previousChunkCount; i++) {
74
- deleteCookie(event, chunkCookieName(name, i), options);
75
- }
76
- }
77
- }
78
- if (chunkCount <= 1) {
79
- setCookie(event, name, value, options);
80
- return;
81
- }
82
- const mainCookieValue = `${CHUNKED_COOKIE}${chunkCount}`;
83
- setCookie(event, name, mainCookieValue, options);
84
- for (let i = 1; i <= chunkCount; i++) {
85
- const start = (i - 1) * chunkMaxLength;
86
- const end = start + chunkMaxLength;
87
- const chunkValue = value.slice(start, end);
88
- setCookie(event, chunkCookieName(name, i), chunkValue, options);
89
- }
90
- }
91
- function _getDistinctCookieKey(name, options) {
92
- return [name, options.domain || "", options.path || "/"].join(";");
93
- }
94
- function getChunkedCookieCount(cookie) {
95
- if (!cookie?.startsWith(CHUNKED_COOKIE)) {
96
- return Number.NaN;
97
- }
98
- return Number.parseInt(cookie.slice(CHUNKED_COOKIE.length));
99
- }
100
- function chunkCookieName(name, chunkNumber) {
101
- return `${name}.${chunkNumber}`;
102
- }
103
-
104
8
  const kGetSessionPromise$1 = Symbol("h3_jwe_getSession");
105
9
  const DEFAULT_NAME$1 = "h3-jwe";
106
10
  const DEFAULT_COOKIE$1 = {
@@ -145,12 +49,22 @@ async function getJWESession(event, config) {
145
49
  if (existingSession) {
146
50
  const session2 = existingSession[kGetSessionPromise$1] ? await existingSession[kGetSessionPromise$1] : existingSession;
147
51
  if (session2.expiresAt !== void 0 && session2.expiresAt < Date.now() && hasWritableResponse$1(event)) {
148
- await config.hooks?.onExpire?.(event, void 0, config);
52
+ await config.hooks?.onExpire?.({
53
+ event,
54
+ error: new Error(
55
+ `JWT "exp" (Expiration Time) Claim validation failed: Token has expired (exp: ${new Date(session2.expiresAt * 1e3).toISOString()})`
56
+ ),
57
+ config
58
+ });
149
59
  return clearJWESession(event, config).then(
150
60
  () => getJWESession(event, config)
151
61
  );
152
62
  }
153
- await config.hooks?.onRead?.(session2, event, config);
63
+ await config.hooks?.onRead?.({
64
+ event,
65
+ session: session2,
66
+ config
67
+ });
154
68
  return session2;
155
69
  }
156
70
  const session = {
@@ -160,27 +74,35 @@ async function getJWESession(event, config) {
160
74
  data: new NullProtoObj()
161
75
  };
162
76
  context.sessions[sessionName] = session;
163
- let jweToken;
77
+ let token;
164
78
  if (config.sessionHeader !== false) {
165
79
  const headerName = typeof config.sessionHeader === "string" ? config.sessionHeader.toLowerCase() : `x-${sessionName.toLowerCase()}-session`;
166
80
  const headerValue = event.req.headers.get(headerName);
167
81
  if (typeof headerValue === "string") {
168
- jweToken = headerValue;
82
+ token = headerValue.startsWith("Bearer ") ? headerValue.slice(7).trim() : headerValue;
169
83
  }
170
84
  }
171
- if (!jweToken) {
172
- jweToken = getChunkedCookie(event, sessionName);
85
+ if (!token) {
86
+ token = getChunkedCookie(event, sessionName);
173
87
  }
174
- if (jweToken) {
175
- const promise = unsealJWESession(event, config, jweToken).catch(async (error_) => {
88
+ if (token) {
89
+ const promise = unsealJWESession(event, config, token).catch(async (error_) => {
176
90
  if (error_ instanceof Error) {
177
91
  const message = error_.message;
178
92
  if (message.includes("Token has expired") || message.includes("Token is too old")) {
179
- await config.hooks?.onExpire?.(event, error_, config);
93
+ await config.hooks?.onExpire?.({
94
+ event,
95
+ error: error_,
96
+ config
97
+ });
180
98
  return void 0;
181
99
  }
182
100
  }
183
- await config.hooks?.onError?.(event, error_, config);
101
+ await config.hooks?.onError?.({
102
+ event,
103
+ error: error_,
104
+ config
105
+ });
184
106
  return void 0;
185
107
  }).then((unsealed) => {
186
108
  if (unsealed) {
@@ -198,7 +120,11 @@ async function getJWESession(event, config) {
198
120
  session.expiresAt = config.maxAge ? session.createdAt + config.maxAge * 1e3 : void 0;
199
121
  await updateJWESession(event, config);
200
122
  }
201
- await config.hooks?.onRead?.(session, event, config);
123
+ await config.hooks?.onRead?.({
124
+ event,
125
+ session,
126
+ config
127
+ });
202
128
  return session;
203
129
  }
204
130
  async function updateJWESession(event, config, update) {
@@ -210,7 +136,11 @@ async function updateJWESession(event, config, update) {
210
136
  }
211
137
  if (update) {
212
138
  Object.assign(session.data, update);
213
- await config.hooks?.onUpdate?.(session, event, config);
139
+ await config.hooks?.onUpdate?.({
140
+ event,
141
+ session,
142
+ config
143
+ });
214
144
  }
215
145
  if (config.cookie !== false && hasWritableResponse$1(event)) {
216
146
  const sealed = await sealJWESession(event, config);
@@ -263,11 +193,11 @@ async function unsealJWESession(_event, config, sealed) {
263
193
  const { payload } = await decrypt(sealed, key, {
264
194
  ...config.jwe?.decryptOptions,
265
195
  requiredClaims: [
266
- ...config.jwe?.decryptOptions?.requiredClaims?.filter(
267
- (claim) => claim !== "jti" && claim !== "iat"
268
- ) || [],
269
- "jti",
270
- "iat"
196
+ .../* @__PURE__ */ new Set([
197
+ ...config.jwe?.decryptOptions?.requiredClaims || [],
198
+ "jti",
199
+ "iat"
200
+ ])
271
201
  ],
272
202
  maxTokenAge: config.maxAge,
273
203
  algorithms: alg ? [alg] : void 0,
@@ -303,7 +233,10 @@ async function clearJWESession(event, config) {
303
233
  expires: /* @__PURE__ */ new Date(0),
304
234
  maxAge: void 0
305
235
  });
306
- await config.hooks?.onClear?.(event, config);
236
+ await config.hooks?.onClear?.({
237
+ event,
238
+ config
239
+ });
307
240
  }
308
241
  }
309
242
  function getSessionFromContext$1(event, sessionName) {
@@ -400,12 +333,22 @@ async function getJWSSession(event, config) {
400
333
  if (existingSession) {
401
334
  const session2 = existingSession[kGetSessionPromise] ? await existingSession[kGetSessionPromise] : existingSession;
402
335
  if (session2.expiresAt !== void 0 && session2.expiresAt < Date.now() && hasWritableResponse(event)) {
403
- await config.hooks?.onExpire?.(event, void 0, config);
336
+ await config.hooks?.onExpire?.({
337
+ event,
338
+ error: new Error(
339
+ `JWT "exp" (Expiration Time) Claim validation failed: Token has expired (exp: ${new Date(session2.expiresAt * 1e3).toISOString()})`
340
+ ),
341
+ config
342
+ });
404
343
  return clearJWSSession(event, config).then(
405
344
  () => getJWSSession(event, config)
406
345
  );
407
346
  }
408
- await config.hooks?.onRead?.(session2, event, config);
347
+ await config.hooks?.onRead?.({
348
+ event,
349
+ session: session2,
350
+ config
351
+ });
409
352
  return session2;
410
353
  }
411
354
  const session = {
@@ -420,7 +363,7 @@ async function getJWSSession(event, config) {
420
363
  const headerName = typeof config.sessionHeader === "string" ? config.sessionHeader.toLowerCase() : `x-${sessionName.toLowerCase()}-session`;
421
364
  const headerValue = event.req.headers.get(headerName);
422
365
  if (typeof headerValue === "string") {
423
- token = headerValue;
366
+ token = headerValue.startsWith("Bearer ") ? headerValue.slice(7).trim() : headerValue;
424
367
  }
425
368
  }
426
369
  if (!token) {
@@ -429,10 +372,18 @@ async function getJWSSession(event, config) {
429
372
  if (token) {
430
373
  const promise = verifyJWSSession(event, config, token).catch(async (error_) => {
431
374
  if (error_ instanceof Error && (error_.message.includes("Token has expired") || error_.message.includes("Token is too old"))) {
432
- await config.hooks?.onExpire?.(event, error_, config);
375
+ await config.hooks?.onExpire?.({
376
+ event,
377
+ error: error_,
378
+ config
379
+ });
433
380
  return void 0;
434
381
  }
435
- await config.hooks?.onError?.(event, error_, config);
382
+ await config.hooks?.onError?.({
383
+ event,
384
+ error: error_,
385
+ config
386
+ });
436
387
  return void 0;
437
388
  }).then((unsealed) => {
438
389
  if (unsealed) {
@@ -450,7 +401,11 @@ async function getJWSSession(event, config) {
450
401
  session.expiresAt = config.maxAge ? session.createdAt + config.maxAge * 1e3 : void 0;
451
402
  await updateJWSSession(event, config);
452
403
  }
453
- await config.hooks?.onRead?.(session, event, config);
404
+ await config.hooks?.onRead?.({
405
+ event,
406
+ session,
407
+ config
408
+ });
454
409
  return session;
455
410
  }
456
411
  async function updateJWSSession(event, config, update) {
@@ -462,7 +417,11 @@ async function updateJWSSession(event, config, update) {
462
417
  }
463
418
  if (update) {
464
419
  Object.assign(session.data, update);
465
- await config.hooks?.onUpdate?.(session, event, config);
420
+ await config.hooks?.onUpdate?.({
421
+ event,
422
+ session,
423
+ config
424
+ });
466
425
  }
467
426
  if (config.cookie !== false && hasWritableResponse(event)) {
468
427
  const token = await signJWSSession(event, config);
@@ -516,11 +475,11 @@ async function verifyJWSSession(_event, config, token) {
516
475
  const { payload } = await verify(token, jwk, {
517
476
  ...config.jws?.verifyOptions,
518
477
  requiredClaims: [
519
- ...config.jws?.verifyOptions?.requiredClaims?.filter(
520
- (claim) => claim !== "jti" && claim !== "iat"
521
- ) || [],
522
- "jti",
523
- "iat"
478
+ .../* @__PURE__ */ new Set([
479
+ ...config.jws?.verifyOptions?.requiredClaims || [],
480
+ "jti",
481
+ "iat"
482
+ ])
524
483
  ],
525
484
  typ: typ || "JWT",
526
485
  algorithms: alg ? [alg] : void 0,
@@ -551,7 +510,10 @@ async function clearJWSSession(event, config) {
551
510
  expires: /* @__PURE__ */ new Date(0),
552
511
  maxAge: void 0
553
512
  });
554
- await config.hooks?.onClear?.(event, config);
513
+ await config.hooks?.onClear?.({
514
+ event,
515
+ config
516
+ });
555
517
  }
556
518
  }
557
519
  function getSessionFromContext(event, sessionName) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unjwt",
3
- "version": "0.5.24",
3
+ "version": "0.5.26",
4
4
  "description": "Low-level JWT utilities",
5
5
  "repository": "sandros94/unjwt",
6
6
  "license": "MIT",
@@ -73,7 +73,7 @@
73
73
  "eslint": "^9.36.0",
74
74
  "eslint-config-unjs": "^0.5.0",
75
75
  "h3v1": "npm:h3@^1.15.4",
76
- "h3v2": "npm:h3@^2.0.0-beta.5",
76
+ "h3v2": "npm:h3@^2.0.1-rc.5",
77
77
  "jose": "^6.1.0",
78
78
  "prettier": "^3.6.2",
79
79
  "supertest": "^7.1.4",
@@ -83,8 +83,8 @@
83
83
  },
84
84
  "peerDependencies": {
85
85
  "cookie-es": "^1.2.2 || ^2.0.0",
86
- "h3": "^1.15.4 || >=2.0.0-beta.5 <2.0.0 || ^2.0.1",
87
- "rou3": "^0.7.7"
86
+ "h3": "^1.15.4 || >=2.0.1-rc.5 || ^2.0.1",
87
+ "rou3": "^0.7.10"
88
88
  },
89
89
  "peerDependenciesMeta": {
90
90
  "cookie-es": {
@@ -92,7 +92,13 @@
92
92
  },
93
93
  "h3": {
94
94
  "optional": true
95
+ },
96
+ "rou3": {
97
+ "optional": true
95
98
  }
96
99
  },
97
- "packageManager": "pnpm@10.17.1"
100
+ "resolutions": {
101
+ "@eslint/plugin-kit": "^0.3.4"
102
+ },
103
+ "packageManager": "pnpm@10.19.0"
98
104
  }