unjwt 0.5.40 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +147 -6
  2. package/dist/_chunks/_crypto.mjs +1014 -0
  3. package/dist/_chunks/error.d.mts +67 -0
  4. package/dist/_chunks/index.d.mts +149 -45
  5. package/dist/_chunks/jwk.d.mts +154 -57
  6. package/dist/_chunks/jws.mjs +729 -683
  7. package/dist/_chunks/utils.mjs +74 -70
  8. package/dist/{_chunks/jws.d.mts → adapters/h3v1/index.d.mts} +98 -58
  9. package/dist/{_chunks/jws2.mjs → adapters/h3v1/index.mjs} +198 -85
  10. package/dist/adapters/h3v2/index.d.mts +213 -0
  11. package/dist/adapters/{h3v2.mjs → h3v2/index.mjs} +195 -89
  12. package/dist/core/_crypto/index.d.mts +71 -0
  13. package/dist/core/_crypto/index.mjs +2 -0
  14. package/dist/core/jwe.d.mts +40 -0
  15. package/dist/{_chunks → core}/jwe.mjs +61 -32
  16. package/dist/core/jwk.d.mts +231 -0
  17. package/dist/core/jwk.mjs +686 -0
  18. package/dist/core/jws.d.mts +31 -0
  19. package/dist/core/jws.mjs +104 -0
  20. package/dist/{_chunks/index2.d.mts → core/utils/index.d.mts} +13 -12
  21. package/dist/core/utils/index.mjs +2 -0
  22. package/dist/index.d.mts +7 -6
  23. package/dist/index.mjs +5 -5
  24. package/package.json +48 -40
  25. package/dist/_chunks/chunk.mjs +0 -13
  26. package/dist/_chunks/jwe.d.mts +0 -43
  27. package/dist/_chunks/jwk.mjs +0 -1592
  28. package/dist/adapters/h3.d.mts +0 -3
  29. package/dist/adapters/h3.mjs +0 -3
  30. package/dist/adapters/h3v1.d.mts +0 -3
  31. package/dist/adapters/h3v1.mjs +0 -3
  32. package/dist/adapters/h3v2.d.mts +0 -183
  33. package/dist/jwe.d.mts +0 -3
  34. package/dist/jwe.mjs +0 -2
  35. package/dist/jwk.d.mts +0 -3
  36. package/dist/jwk.mjs +0 -2
  37. package/dist/jws.d.mts +0 -3
  38. package/dist/jws.mjs +0 -2
  39. package/dist/utils.d.mts +0 -3
  40. package/dist/utils.mjs +0 -2
@@ -0,0 +1,213 @@
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";
2
+ import { H3Event, HTTPEvent } from "h3";
3
+ import { CookieSerializeOptions } from "cookie-es";
4
+
5
+ //#region src/adapters/h3v2/session/types.d.ts
6
+ interface SessionClaims extends Required<Pick<JWTClaims, "jti" | "iat">>, StrictOmit<JWTClaims, "jti" | "iat"> {}
7
+ type SessionData<T extends Record<string, any> = SessionClaims> = StrictOmit<T, "jti" | "iat" | "exp">;
8
+ type SessionUpdate<T extends Record<string, any> = SessionClaims> = Partial<SessionData<T>> | ((oldData: SessionData<T>) => Partial<SessionData<T>> | undefined);
9
+ interface SessionManager<T extends Record<string, any> = SessionClaims, ConfigMaxAge extends ExpiresIn | undefined = ExpiresIn | undefined> {
10
+ readonly id: string | undefined;
11
+ readonly createdAt: number;
12
+ readonly expiresAt: ConfigMaxAge extends ExpiresIn ? number : "exp" extends keyof T ? T["exp"] : number | undefined;
13
+ readonly data: SessionData<T>;
14
+ readonly token: string | undefined;
15
+ update: (update?: SessionUpdate<T>) => Promise<SessionManager<T, ConfigMaxAge>>;
16
+ clear: () => Promise<SessionManager<T, ConfigMaxAge>>;
17
+ }
18
+ //#endregion
19
+ //#region src/adapters/h3v2/session/jwe.d.ts
20
+ declare const kGetSessionPromise$1: unique symbol;
21
+ interface SessionJWE<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined> {
22
+ id: string | undefined;
23
+ createdAt: number;
24
+ expiresAt: MaxAge extends ExpiresIn ? number : T["exp"];
25
+ data: SessionData<T>;
26
+ token: string | undefined;
27
+ [kGetSessionPromise$1]?: Promise<SessionJWE<T, MaxAge>>;
28
+ }
29
+ interface SessionHooksJWE<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends HTTPEvent | H3Event = HTTPEvent | H3Event> {
30
+ onRead?: (args: {
31
+ session: SessionJWE<T, MaxAge> & {
32
+ id: string;
33
+ token: string;
34
+ };
35
+ event: TEvent;
36
+ config: SessionConfigJWE<T, MaxAge, TEvent>;
37
+ }) => void | Promise<void>;
38
+ onUpdate?: (args: {
39
+ /** Session after it has been updated.. */session: SessionJWE<T, MaxAge> & {
40
+ id: string;
41
+ token: string;
42
+ }; /** Snapshot of the session before was updated. */
43
+ oldSession: SessionJWE<T, MaxAge>;
44
+ event: TEvent;
45
+ config: SessionConfigJWE<T, MaxAge, TEvent>;
46
+ }) => void | Promise<void>;
47
+ onClear?: (args: {
48
+ oldSession: SessionJWE<T, MaxAge> | undefined;
49
+ event: TEvent;
50
+ config: SessionConfigJWE<T, MaxAge, TEvent>;
51
+ }) => void | Promise<void>;
52
+ onExpire?: (args: {
53
+ session: {
54
+ id: string | undefined;
55
+ createdAt: number | undefined;
56
+ expiresAt: number | undefined;
57
+ token: string;
58
+ };
59
+ event: TEvent;
60
+ error: Error;
61
+ config: SessionConfigJWE<T, MaxAge, TEvent>;
62
+ }) => void | Promise<void>;
63
+ onError?: (args: {
64
+ /**
65
+ * The session involved in the error.
66
+ */
67
+ session: SessionJWE<T, MaxAge>;
68
+ event: TEvent;
69
+ error: any;
70
+ config: SessionConfigJWE<T, MaxAge, TEvent>;
71
+ }) => void | Promise<void>;
72
+ onUnsealKeyLookup?: (args: {
73
+ header: JWEHeaderParameters;
74
+ event: TEvent;
75
+ config: SessionConfigJWE<T, MaxAge, TEvent>;
76
+ }) => JWK_Symmetric | JWK_Private | Promise<JWK_Symmetric | JWK_Private>;
77
+ }
78
+ interface SessionConfigJWE<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends HTTPEvent | H3Event = HTTPEvent | H3Event> {
79
+ /** Shared secret used, string for PBES2 or Json Web Key (JWK) */
80
+ key: string | JWK_Symmetric | {
81
+ privateKey: JWK_Private | JWK_Symmetric;
82
+ publicKey?: JWK_Public;
83
+ };
84
+ /** Session lifetime in seconds (used to derive exp from iat) */
85
+ maxAge?: MaxAge;
86
+ /** Default is "h3" */
87
+ name?: string;
88
+ /** Default is secure, httpOnly, path="/" */
89
+ cookie?: false | (CookieSerializeOptions & {
90
+ chunkMaxLength?: number;
91
+ });
92
+ /** Default is x-h3-session / x-{name}-session */
93
+ sessionHeader?: false | string;
94
+ /** Default is crypto.randomUUID */
95
+ generateId?: () => string;
96
+ /** JWE configuration overrides */
97
+ jwe?: {
98
+ encryptOptions?: Omit<JWEEncryptOptions, "expiresIn">;
99
+ decryptOptions?: JWTClaimValidationOptions;
100
+ };
101
+ hooks?: SessionHooksJWE<T, MaxAge, TEvent>;
102
+ }
103
+ /**
104
+ * Create a session manager for the current request.
105
+ */
106
+ declare function useJWESession<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends HTTPEvent = HTTPEvent>(event: TEvent, config: SessionConfigJWE<T, MaxAge, TEvent>): Promise<SessionManager<T, MaxAge>>;
107
+ /**
108
+ * Get the session for the current request.
109
+ */
110
+ declare function getJWESession<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends HTTPEvent = HTTPEvent>(event: TEvent, config: SessionConfigJWE<T, MaxAge, TEvent>): Promise<SessionJWE<T, MaxAge>>;
111
+ declare function getJWESessionToken<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends HTTPEvent = HTTPEvent>(event: TEvent, config: SessionConfigJWE<T, MaxAge, TEvent>): string | undefined;
112
+ /**
113
+ * Update the session data for the current request.
114
+ */
115
+ declare function updateJWESession<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends HTTPEvent = HTTPEvent>(event: TEvent, config: SessionConfigJWE<T, MaxAge, TEvent>, update?: SessionUpdate<T>): Promise<SessionJWE<T, MaxAge>>;
116
+ /**
117
+ * Produce a JWE for the current session.
118
+ */
119
+ declare function sealJWESession<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends HTTPEvent = HTTPEvent>(event: TEvent, config: SessionConfigJWE<T, MaxAge, TEvent>): Promise<string>;
120
+ /**
121
+ * Decrypt the JWE and return a Session-compatible object.
122
+ */
123
+ declare function unsealJWESession<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends HTTPEvent = HTTPEvent>(event: TEvent, config: SessionConfigJWE<T, MaxAge, TEvent>, sealed: string): Promise<Partial<SessionJWE<T, MaxAge>>>;
124
+ /**
125
+ * Clear the session (delete from context and drop cookie).
126
+ */
127
+ declare function clearJWESession<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends HTTPEvent = HTTPEvent>(event: TEvent, config: SessionConfigJWE<T, MaxAge, TEvent>): Promise<void>;
128
+ //#endregion
129
+ //#region src/adapters/h3v2/session/jws.d.ts
130
+ declare const kGetSessionPromise: unique symbol;
131
+ interface SessionJWS<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined> {
132
+ id: string | undefined;
133
+ createdAt: number;
134
+ expiresAt: MaxAge extends ExpiresIn ? number : T["exp"];
135
+ data: SessionData<T>;
136
+ token: string | undefined;
137
+ [kGetSessionPromise]?: Promise<SessionJWS<T, MaxAge>>;
138
+ }
139
+ interface SessionHooksJWS<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends HTTPEvent = HTTPEvent> {
140
+ onRead?: (args: {
141
+ session: SessionJWS<T, MaxAge> & {
142
+ id: string;
143
+ token: string;
144
+ };
145
+ event: TEvent;
146
+ config: SessionConfigJWS<T, MaxAge, TEvent>;
147
+ }) => void | Promise<void>;
148
+ onUpdate?: (args: {
149
+ /** Session after it has been updated.. */session: SessionJWS<T, MaxAge> & {
150
+ id: string;
151
+ token: string;
152
+ }; /** Snapshot of the session before was updated. */
153
+ oldSession: SessionJWS<T, MaxAge>;
154
+ event: TEvent;
155
+ config: SessionConfigJWS<T, MaxAge, TEvent>;
156
+ }) => void | Promise<void>;
157
+ onClear?: (args: {
158
+ oldSession: SessionJWS<T, MaxAge> | undefined;
159
+ event: TEvent;
160
+ config: SessionConfigJWS<T, MaxAge, TEvent>;
161
+ }) => void | Promise<void>;
162
+ onExpire?: (args: {
163
+ session: {
164
+ id: string | undefined;
165
+ createdAt: number | undefined;
166
+ expiresAt: number | undefined;
167
+ token: string;
168
+ };
169
+ event: TEvent;
170
+ error: Error;
171
+ config: SessionConfigJWS<T, MaxAge, TEvent>;
172
+ }) => void | Promise<void>;
173
+ onError?: (args: {
174
+ /**
175
+ * The session involved in the error.
176
+ */
177
+ session: SessionJWS<T, MaxAge>;
178
+ event: TEvent;
179
+ error: any;
180
+ config: SessionConfigJWS<T, MaxAge, TEvent>;
181
+ }) => void | Promise<void>;
182
+ onVerifyKeyLookup?: (args: {
183
+ header: JWKLookupFunctionHeader;
184
+ event: TEvent;
185
+ config: SessionConfigJWS<T, MaxAge, TEvent>;
186
+ }) => JWKSet | JWK_Symmetric | JWK_Public | Promise<JWKSet | JWK_Symmetric | JWK_Public>;
187
+ }
188
+ interface SessionConfigJWS<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends HTTPEvent = HTTPEvent> {
189
+ key: JWK_Symmetric | {
190
+ privateKey: JWK_Private;
191
+ publicKey: JWK_Public | JWK_Public[] | JWKSet;
192
+ };
193
+ maxAge?: MaxAge;
194
+ name?: string;
195
+ cookie?: false | (CookieSerializeOptions & {
196
+ chunkMaxLength?: number;
197
+ });
198
+ sessionHeader?: false | string;
199
+ generateId?: () => string;
200
+ jws?: {
201
+ signOptions?: Omit<JWSSignOptions, "expiresIn">;
202
+ verifyOptions?: JWTClaimValidationOptions;
203
+ };
204
+ hooks?: SessionHooksJWS<T, MaxAge, TEvent>;
205
+ }
206
+ declare function useJWSSession<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends HTTPEvent = HTTPEvent>(event: TEvent, config: SessionConfigJWS<T, MaxAge, TEvent>): Promise<SessionManager<T, MaxAge>>;
207
+ declare function getJWSSession<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends HTTPEvent = HTTPEvent>(event: TEvent, config: SessionConfigJWS<T, MaxAge, TEvent>): Promise<SessionJWS<T, MaxAge>>;
208
+ declare function updateJWSSession<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends HTTPEvent = HTTPEvent>(event: TEvent, config: SessionConfigJWS<T, MaxAge, TEvent>, update?: SessionUpdate<T>): Promise<SessionJWS<T, MaxAge>>;
209
+ declare function signJWSSession<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends HTTPEvent = HTTPEvent>(event: TEvent, config: SessionConfigJWS<T, MaxAge, TEvent>): Promise<string>;
210
+ declare function verifyJWSSession<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends HTTPEvent = HTTPEvent>(event: TEvent, config: SessionConfigJWS<T, MaxAge, TEvent>, token: string): Promise<Partial<SessionJWS<T, MaxAge>>>;
211
+ declare function clearJWSSession<T extends Record<string, any> = SessionClaims, MaxAge extends ExpiresIn | undefined = ExpiresIn | undefined, TEvent extends HTTPEvent = HTTPEvent>(event: TEvent, config: SessionConfigJWS<T, MaxAge, TEvent>): Promise<void>;
212
+ //#endregion
213
+ 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 };
@@ -1,9 +1,8 @@
1
- import { a as deriveJWKFromPassword, c as importJWKFromPEM, d as isSymmetricJWK, f as computeExpiresInSeconds, i as encrypt, l as isPrivateJWK, n as verify, o as exportJWKToPEM, r as decrypt, s as generateJWK, t as sign, u as isPublicJWK } from "../_chunks/jws.mjs";
1
+ import { a as deriveJWKFromPassword, c as importFromPEM, d as isSymmetricJWK, f as computeExpiresInSeconds, i as encrypt, l as isPrivateJWK, m as isJWTError, n as verify, o as exportToPEM, p as JWTError, r as decrypt, s as generateJWK, t as sign, u as isPublicJWK } from "../../_chunks/jws.mjs";
2
2
  import { getChunkedCookie, getEventContext, setChunkedCookie } from "h3";
3
3
  import { NullProtoObj } from "rou3";
4
4
  //#region src/adapters/h3v2/session/jwe.ts
5
5
  const kGetSessionPromise$1 = Symbol("h3_jwe_getSession");
6
- const kSessionToken$1 = Symbol("h3_jwe_sessionToken");
7
6
  const DEFAULT_NAME$1 = "h3-jwe";
8
7
  const DEFAULT_COOKIE$1 = {
9
8
  path: "/",
@@ -30,7 +29,7 @@ async function useJWESession(event, config) {
30
29
  return getSessionFromContext$1(event, sessionName)?.data || {};
31
30
  },
32
31
  get token() {
33
- return getSessionFromContext$1(event, sessionName)?.[kSessionToken$1] ?? getJWESessionToken(event, config);
32
+ return getSessionFromContext$1(event, sessionName)?.token ?? getJWESessionToken(event, config);
34
33
  },
35
34
  update: async (update) => {
36
35
  await updateJWESession(event, config, update);
@@ -60,15 +59,42 @@ async function getJWESession(event, config) {
60
59
  */
61
60
  if (session.expiresAt !== void 0 && session.expiresAt < Date.now()) {
62
61
  await config.hooks?.onExpire?.({
62
+ session: {
63
+ id: session.id,
64
+ createdAt: session.createdAt,
65
+ expiresAt: session.expiresAt,
66
+ token: session.token
67
+ },
63
68
  event,
64
- error: /* @__PURE__ */ new Error(`JWT "exp" (Expiration Time) Claim validation failed: Token has expired (exp: ${(/* @__PURE__ */ new Date(session.expiresAt * 1e3)).toISOString()})`),
69
+ error: new JWTError(`JWT "exp" (Expiration Time) Claim validation failed: Token has expired (exp: ${(/* @__PURE__ */ new Date(session.expiresAt * 1e3)).toISOString()})`, "ERR_JWT_EXPIRED", {
70
+ jti: session.id,
71
+ exp: session.expiresAt / 1e3
72
+ }),
65
73
  config
66
74
  });
67
- return clearJWESession(event, config).then(() => getJWESession(event, config));
75
+ delete context.sessions[sessionName];
76
+ if (config.cookie !== false) if (hasWritableResponse$1(event)) setChunkedCookie(event, sessionName, "", {
77
+ ...DEFAULT_COOKIE$1,
78
+ ...config.cookie,
79
+ expires: /* @__PURE__ */ new Date(0),
80
+ maxAge: void 0
81
+ });
82
+ else console.warn("[unjwt/h3] Session expired but cookie cannot be cleared on a read-only event.");
83
+ const freshNow = config.jwe?.encryptOptions?.currentDate?.getTime() ?? Date.now();
84
+ const freshCreatedAt = freshNow - freshNow % 1e3;
85
+ const freshSession = {
86
+ id: void 0,
87
+ createdAt: freshCreatedAt,
88
+ expiresAt: config.maxAge === void 0 ? void 0 : freshCreatedAt + computeExpiresInSeconds(config.maxAge) * 1e3,
89
+ data: new NullProtoObj(),
90
+ token: void 0
91
+ };
92
+ context.sessions[sessionName] = freshSession;
93
+ return freshSession;
68
94
  }
69
- await config.hooks?.onRead?.({
70
- event,
95
+ if (session.id !== void 0 && session.token !== void 0) await config.hooks?.onRead?.({
71
96
  session,
97
+ event,
72
98
  config
73
99
  });
74
100
  return session;
@@ -79,25 +105,29 @@ async function getJWESession(event, config) {
79
105
  id: void 0,
80
106
  createdAt,
81
107
  expiresAt: config.maxAge === void 0 ? void 0 : createdAt + computeExpiresInSeconds(config.maxAge) * 1e3,
82
- data: new NullProtoObj()
108
+ data: new NullProtoObj(),
109
+ token: void 0
83
110
  };
84
111
  context.sessions[sessionName] = session;
85
112
  const token = getJWESessionToken(event, config);
113
+ let exclusiveHookFired = false;
86
114
  if (token) {
87
- session[kSessionToken$1] = token;
115
+ session.token = token;
88
116
  const promise = unsealJWESession(event, config, token).catch(async (error_) => {
89
- if (error_ instanceof Error) {
90
- const message = error_.message;
91
- if (message.includes("Token has expired") || message.includes("Token is too old")) {
92
- await config.hooks?.onExpire?.({
93
- event,
94
- error: error_,
95
- config
96
- });
97
- return;
98
- }
99
- }
100
- await config.hooks?.onError?.({
117
+ exclusiveHookFired = true;
118
+ if (isJWTError(error_, "ERR_JWT_EXPIRED")) await config.hooks?.onExpire?.({
119
+ session: {
120
+ id: error_.cause.jti,
121
+ createdAt: error_.cause.iat ? error_.cause.iat * 1e3 : void 0,
122
+ expiresAt: error_.cause.exp ? error_.cause.exp * 1e3 : void 0,
123
+ token
124
+ },
125
+ event,
126
+ error: error_,
127
+ config
128
+ });
129
+ else await config.hooks?.onError?.({
130
+ session,
101
131
  event,
102
132
  error: error_,
103
133
  config
@@ -110,9 +140,9 @@ async function getJWESession(event, config) {
110
140
  session[kGetSessionPromise$1] = promise;
111
141
  await promise;
112
142
  }
113
- await config.hooks?.onRead?.({
114
- event,
143
+ if (!exclusiveHookFired && session.id !== void 0 && session.token !== void 0) await config.hooks?.onRead?.({
115
144
  session,
145
+ event,
116
146
  config
117
147
  });
118
148
  return session;
@@ -136,6 +166,8 @@ function getJWESessionToken(event, config) {
136
166
  * Update the session data for the current request.
137
167
  */
138
168
  async function updateJWESession(event, config, update) {
169
+ const canWriteCookie = hasWritableResponse$1(event);
170
+ if (config.cookie !== false && !canWriteCookie) throw new Error("[unjwt/h3] Cannot update session on read-only event.");
139
171
  const sessionName = config.name || DEFAULT_NAME$1;
140
172
  const session = getEventContext(event).sessions?.[sessionName] || await getJWESession(event, config);
141
173
  if (typeof update === "function") update = update(session.data);
@@ -151,21 +183,36 @@ async function updateJWESession(event, config, update) {
151
183
  createdAt,
152
184
  expiresAt: config.maxAge === void 0 ? void 0 : createdAt + computeExpiresInSeconds(config.maxAge) * 1e3
153
185
  });
154
- if (config.cookie !== false && hasWritableResponse$1(event)) {
155
- const sealed = await sealJWESession(event, config);
156
- session[kSessionToken$1] = sealed;
157
- setChunkedCookie(event, sessionName, sealed, {
158
- ...DEFAULT_COOKIE$1,
159
- expires: config.maxAge === void 0 ? void 0 : new Date(session.createdAt + computeExpiresInSeconds(config.maxAge) * 1e3),
160
- ...config.cookie
186
+ let sealed;
187
+ try {
188
+ sealed = await sealJWESession(event, config);
189
+ } catch (error_) {
190
+ Object.assign(session, {
191
+ id: oldSession.id,
192
+ createdAt: oldSession.createdAt,
193
+ expiresAt: oldSession.expiresAt
161
194
  });
162
- await config.hooks?.onUpdate?.({
163
- oldSession,
195
+ session.token = oldSession.token;
196
+ await config.hooks?.onError?.({
164
197
  session,
165
198
  event,
199
+ error: error_,
166
200
  config
167
201
  });
202
+ throw error_;
168
203
  }
204
+ session.token = sealed;
205
+ if (config.cookie !== false && canWriteCookie) setChunkedCookie(event, sessionName, sealed, {
206
+ ...DEFAULT_COOKIE$1,
207
+ expires: config.maxAge === void 0 ? void 0 : new Date(session.createdAt + computeExpiresInSeconds(config.maxAge) * 1e3),
208
+ ...config.cookie
209
+ });
210
+ await config.hooks?.onUpdate?.({
211
+ session,
212
+ oldSession,
213
+ event,
214
+ config
215
+ });
169
216
  return session;
170
217
  }
171
218
  /**
@@ -222,7 +269,7 @@ async function unsealJWESession(event, config, sealed) {
222
269
  unwrappedKeyAlgorithm: void 0,
223
270
  keyUsage: void 0,
224
271
  forceUint8Array: false,
225
- validateJWT: true
272
+ validateClaims: true
226
273
  }).catch((error_) => {
227
274
  if (error_ instanceof Error) throw error_;
228
275
  throw new Error(String(error_));
@@ -239,24 +286,28 @@ async function unsealJWESession(event, config, sealed) {
239
286
  * Clear the session (delete from context and drop cookie).
240
287
  */
241
288
  async function clearJWESession(event, config) {
289
+ const canWriteCookie = hasWritableResponse$1(event);
290
+ if (config.cookie !== false && !canWriteCookie) throw new Error("[unjwt/h3] Cannot clear session on read-only event.");
242
291
  const context = getEventContext(event);
243
292
  const sessionName = config.name || DEFAULT_NAME$1;
244
293
  let session = context.sessions?.[sessionName];
245
294
  if (session && session[kGetSessionPromise$1]) session = await session[kGetSessionPromise$1];
295
+ const oldSession = session ? {
296
+ ...session,
297
+ data: { ...session.data }
298
+ } : void 0;
246
299
  if (context.sessions?.[sessionName]) delete context.sessions[sessionName];
247
- if (config.cookie !== false && hasWritableResponse$1(event)) {
248
- setChunkedCookie(event, sessionName, "", {
249
- ...DEFAULT_COOKIE$1,
250
- ...config.cookie,
251
- expires: /* @__PURE__ */ new Date(0),
252
- maxAge: void 0
253
- });
254
- await config.hooks?.onClear?.({
255
- session,
256
- event,
257
- config
258
- });
259
- }
300
+ if (config.cookie !== false && canWriteCookie) setChunkedCookie(event, sessionName, "", {
301
+ ...DEFAULT_COOKIE$1,
302
+ ...config.cookie,
303
+ expires: /* @__PURE__ */ new Date(0),
304
+ maxAge: void 0
305
+ });
306
+ await config.hooks?.onClear?.({
307
+ oldSession,
308
+ event,
309
+ config
310
+ });
260
311
  }
261
312
  function getSessionFromContext$1(event, sessionName) {
262
313
  return getEventContext(event).sessions?.[sessionName];
@@ -291,7 +342,6 @@ function findSetCookie$1(setCookie, name) {
291
342
  //#endregion
292
343
  //#region src/adapters/h3v2/session/jws.ts
293
344
  const kGetSessionPromise = Symbol("h3_jws_getSession");
294
- const kSessionToken = Symbol("h3_jws_sessionToken");
295
345
  const DEFAULT_NAME = "h3-jws";
296
346
  const DEFAULT_COOKIE = {
297
347
  path: "/",
@@ -315,7 +365,7 @@ async function useJWSSession(event, config) {
315
365
  return getSessionFromContext(event, sessionName)?.data || {};
316
366
  },
317
367
  get token() {
318
- return getSessionFromContext(event, sessionName)?.[kSessionToken] ?? getJWSSessionToken(event, config);
368
+ return getSessionFromContext(event, sessionName)?.token ?? getJWSSessionToken(event, config);
319
369
  },
320
370
  update: async (update) => {
321
371
  await updateJWSSession(event, config, update);
@@ -337,15 +387,42 @@ async function getJWSSession(event, config) {
337
387
  const session = existingSession[kGetSessionPromise] ? await existingSession[kGetSessionPromise] : existingSession;
338
388
  if (session.expiresAt !== void 0 && session.expiresAt < Date.now()) {
339
389
  await config.hooks?.onExpire?.({
390
+ session: {
391
+ id: session.id,
392
+ createdAt: session.createdAt,
393
+ expiresAt: session.expiresAt,
394
+ token: session.token
395
+ },
340
396
  event,
341
- error: /* @__PURE__ */ new Error(`JWT "exp" (Expiration Time) Claim validation failed: Token has expired (exp: ${(/* @__PURE__ */ new Date(session.expiresAt * 1e3)).toISOString()})`),
397
+ error: new JWTError(`JWT "exp" (Expiration Time) Claim validation failed: Token has expired (exp: ${(/* @__PURE__ */ new Date(session.expiresAt * 1e3)).toISOString()})`, "ERR_JWT_EXPIRED", {
398
+ jti: session.id,
399
+ exp: session.expiresAt / 1e3
400
+ }),
342
401
  config
343
402
  });
344
- return clearJWSSession(event, config).then(() => getJWSSession(event, config));
403
+ delete context.sessions[sessionName];
404
+ if (config.cookie !== false) if (hasWritableResponse(event)) setChunkedCookie(event, sessionName, "", {
405
+ ...DEFAULT_COOKIE,
406
+ ...config.cookie,
407
+ expires: /* @__PURE__ */ new Date(0),
408
+ maxAge: void 0
409
+ });
410
+ else console.warn("[unjwt/h3] Session expired but cookie cannot be cleared on a read-only event.");
411
+ const freshNow = config.jws?.signOptions?.currentDate?.getTime() ?? Date.now();
412
+ const freshCreatedAt = freshNow - freshNow % 1e3;
413
+ const freshSession = {
414
+ id: void 0,
415
+ createdAt: freshCreatedAt,
416
+ expiresAt: config.maxAge === void 0 ? void 0 : freshCreatedAt + computeExpiresInSeconds(config.maxAge) * 1e3,
417
+ data: new NullProtoObj(),
418
+ token: void 0
419
+ };
420
+ context.sessions[sessionName] = freshSession;
421
+ return freshSession;
345
422
  }
346
- await config.hooks?.onRead?.({
347
- event,
423
+ if (session.id !== void 0 && session.token !== void 0) await config.hooks?.onRead?.({
348
424
  session,
425
+ event,
349
426
  config
350
427
  });
351
428
  return session;
@@ -356,22 +433,29 @@ async function getJWSSession(event, config) {
356
433
  id: void 0,
357
434
  createdAt,
358
435
  expiresAt: config.maxAge === void 0 ? void 0 : createdAt + computeExpiresInSeconds(config.maxAge) * 1e3,
359
- data: new NullProtoObj()
436
+ data: new NullProtoObj(),
437
+ token: void 0
360
438
  };
361
439
  context.sessions[sessionName] = session;
362
440
  const token = getJWSSessionToken(event, config);
441
+ let exclusiveHookFired = false;
363
442
  if (token) {
364
- session[kSessionToken] = token;
443
+ session.token = token;
365
444
  const promise = verifyJWSSession(event, config, token).catch(async (error_) => {
366
- if (error_ instanceof Error && (error_.message.includes("Token has expired") || error_.message.includes("Token is too old"))) {
367
- await config.hooks?.onExpire?.({
368
- event,
369
- error: error_,
370
- config
371
- });
372
- return;
373
- }
374
- await config.hooks?.onError?.({
445
+ exclusiveHookFired = true;
446
+ if (isJWTError(error_, "ERR_JWT_EXPIRED")) await config.hooks?.onExpire?.({
447
+ session: {
448
+ id: error_.cause.jti,
449
+ createdAt: error_.cause.iat ? error_.cause.iat * 1e3 : void 0,
450
+ expiresAt: error_.cause.exp ? error_.cause.exp * 1e3 : void 0,
451
+ token
452
+ },
453
+ event,
454
+ error: error_,
455
+ config
456
+ });
457
+ else await config.hooks?.onError?.({
458
+ session,
375
459
  event,
376
460
  error: error_,
377
461
  config
@@ -384,9 +468,9 @@ async function getJWSSession(event, config) {
384
468
  session[kGetSessionPromise] = promise;
385
469
  await promise;
386
470
  }
387
- await config.hooks?.onRead?.({
388
- event,
471
+ if (!exclusiveHookFired && session.id !== void 0 && session.token !== void 0) await config.hooks?.onRead?.({
389
472
  session,
473
+ event,
390
474
  config
391
475
  });
392
476
  return session;
@@ -407,6 +491,8 @@ function getJWSSessionToken(event, config) {
407
491
  return token;
408
492
  }
409
493
  async function updateJWSSession(event, config, update) {
494
+ const canWriteCookie = hasWritableResponse(event);
495
+ if (config.cookie !== false && !canWriteCookie) throw new Error("[unjwt/h3] Cannot update session on read-only event.");
410
496
  const sessionName = config.name || DEFAULT_NAME;
411
497
  const session = getEventContext(event).sessions?.[sessionName] || await getJWSSession(event, config);
412
498
  if (typeof update === "function") update = update(session.data);
@@ -422,21 +508,36 @@ async function updateJWSSession(event, config, update) {
422
508
  createdAt,
423
509
  expiresAt: config.maxAge === void 0 ? void 0 : createdAt + computeExpiresInSeconds(config.maxAge) * 1e3
424
510
  });
425
- if (config.cookie !== false && hasWritableResponse(event)) {
426
- const token = await signJWSSession(event, config);
427
- session[kSessionToken] = token;
428
- setChunkedCookie(event, sessionName, token, {
429
- ...DEFAULT_COOKIE,
430
- expires: config.maxAge === void 0 ? void 0 : new Date(session.createdAt + computeExpiresInSeconds(config.maxAge) * 1e3),
431
- ...config.cookie
511
+ let token;
512
+ try {
513
+ token = await signJWSSession(event, config);
514
+ } catch (error_) {
515
+ Object.assign(session, {
516
+ id: oldSession.id,
517
+ createdAt: oldSession.createdAt,
518
+ expiresAt: oldSession.expiresAt
432
519
  });
433
- await config.hooks?.onUpdate?.({
520
+ session.token = oldSession.token;
521
+ await config.hooks?.onError?.({
434
522
  session,
435
- oldSession,
436
523
  event,
524
+ error: error_,
437
525
  config
438
526
  });
527
+ throw error_;
439
528
  }
529
+ session.token = token;
530
+ if (config.cookie !== false && canWriteCookie) setChunkedCookie(event, sessionName, token, {
531
+ ...DEFAULT_COOKIE,
532
+ expires: config.maxAge === void 0 ? void 0 : new Date(session.createdAt + computeExpiresInSeconds(config.maxAge) * 1e3),
533
+ ...config.cookie
534
+ });
535
+ await config.hooks?.onUpdate?.({
536
+ session,
537
+ oldSession,
538
+ event,
539
+ config
540
+ });
440
541
  return session;
441
542
  }
442
543
  async function signJWSSession(event, config) {
@@ -483,8 +584,9 @@ async function verifyJWSSession(event, config, token) {
483
584
  typ: typ || "JWT",
484
585
  algorithms: alg ? [alg] : void 0,
485
586
  forceUint8Array: false,
486
- validateJWT: true
587
+ validateClaims: true
487
588
  }).catch((error_) => {
589
+ if (isJWTError(error_, "ERR_JWT_EXPIRED")) throw error_;
488
590
  const message = error_ instanceof Error ? error_.message : String(error_);
489
591
  throw new Error(`Invalid session token: ${message}`);
490
592
  });
@@ -497,24 +599,28 @@ async function verifyJWSSession(event, config, token) {
497
599
  };
498
600
  }
499
601
  async function clearJWSSession(event, config) {
602
+ const canWriteCookie = hasWritableResponse(event);
603
+ if (config.cookie !== false && !canWriteCookie) throw new Error("[unjwt/h3] Cannot clear session on read-only event.");
500
604
  const context = getEventContext(event);
501
605
  const sessionName = config.name || DEFAULT_NAME;
502
606
  let session = context.sessions?.[sessionName];
503
607
  if (session && session[kGetSessionPromise]) session = await session[kGetSessionPromise];
608
+ const oldSession = session ? {
609
+ ...session,
610
+ data: { ...session.data }
611
+ } : void 0;
504
612
  if (context.sessions?.[sessionName]) delete context.sessions[sessionName];
505
- if (config.cookie !== false && hasWritableResponse(event)) {
506
- setChunkedCookie(event, sessionName, "", {
507
- ...DEFAULT_COOKIE,
508
- ...config.cookie,
509
- expires: /* @__PURE__ */ new Date(0),
510
- maxAge: void 0
511
- });
512
- await config.hooks?.onClear?.({
513
- session,
514
- event,
515
- config
516
- });
517
- }
613
+ if (config.cookie !== false && canWriteCookie) setChunkedCookie(event, sessionName, "", {
614
+ ...DEFAULT_COOKIE,
615
+ ...config.cookie,
616
+ expires: /* @__PURE__ */ new Date(0),
617
+ maxAge: void 0
618
+ });
619
+ await config.hooks?.onClear?.({
620
+ oldSession,
621
+ event,
622
+ config
623
+ });
518
624
  }
519
625
  function getSessionFromContext(event, sessionName) {
520
626
  return getEventContext(event).sessions?.[sessionName];
@@ -554,4 +660,4 @@ function findSetCookie(setCookie, name) {
554
660
  return match ? match[1] : void 0;
555
661
  }
556
662
  //#endregion
557
- export { clearJWESession, clearJWSSession, deriveJWKFromPassword, exportJWKToPEM, generateJWK, getJWESession, getJWESessionToken, getJWSSession, importJWKFromPEM, sealJWESession, signJWSSession, unsealJWESession, updateJWESession, updateJWSSession, useJWESession, useJWSSession, verifyJWSSession };
663
+ export { clearJWESession, clearJWSSession, deriveJWKFromPassword, exportToPEM, generateJWK, getJWESession, getJWESessionToken, getJWSSession, importFromPEM, sealJWESession, signJWSSession, unsealJWESession, updateJWESession, updateJWSSession, useJWESession, useJWSSession, verifyJWSSession };