unjwt 0.5.23 → 0.5.24

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
@@ -603,18 +603,18 @@ console.log(rsaPublicSpki);
603
603
 
604
604
  ### Adapters
605
605
 
606
- #### H3 (Nuxt, Nitro)
606
+ #### H3 v1 (Nuxt v4, Nitro v2)
607
607
 
608
- The `h3` adapter bundles session helpers that store data inside signed or encrypted JWTs.
608
+ The `h3v1` adapter bundles session helpers that store data inside signed or encrypted JWTs.
609
609
 
610
610
  - `useJWESession(event, config)` encrypts the session payload with the provided `secret` (password string or private/symmetric JWK). Use this when session data must remain confidential. (cookie's `httpOnly: true` by default)
611
611
  - `useJWSSession(event, config)` signs, but does not encrypt, the session payload with `config.key`. Use this when clients may read the session content but you still need tamper protection. (cookie's `httpOnly: false` by default)
612
612
 
613
- Both helpers expose the same API: read `session.id` / `session.data`, call `session.update()` to patch values, and `session.clear()` to invalidate the cookie. When setting `maxAge`, `exp` claim is automatically managed and validated.
613
+ Both helpers expose the same API: read `session.id` / `session.data`, call `session.update()` to patch values, and `session.clear()` to invalidate the cookie. When setting `maxAge`, `exp` claim is automatically managed and validated. They also support a number of `hooks` in the config for custom logic (e.g. logging, refreshing, etc).
614
614
 
615
615
  ```ts
616
616
  import { defineEventHandler } from "h3";
617
- import { useJWESession, useJWSSession, generateJWK } from "unjwt/adapters/h3";
617
+ import { useJWESession, useJWSSession, generateJWK } from "unjwt/adapters/h3v1";
618
618
 
619
619
  const keys = await generateJWK("RS256");
620
620
 
@@ -641,6 +641,121 @@ export default defineEventHandler(async (event) => {
641
641
  });
642
642
  ```
643
643
 
644
+ #### H3 v2 (Nuxt v5, Nitro v3)
645
+
646
+ The `h3v2` adapter bundles session helpers that store data inside signed or encrypted JWTs.
647
+
648
+ - `useJWESession(event, config)` encrypts the session payload with the provided `secret` (password string or private/symmetric JWK). Use this when session data must remain confidential. (cookie's `httpOnly: true` by default)
649
+ - `useJWSSession(event, config)` signs, but does not encrypt, the session payload with `config.key`. Use this when clients may read the session content but you still need tamper protection. (cookie's `httpOnly: false` by default)
650
+
651
+ Both helpers expose the same API: read `session.id` / `session.data`, call `session.update()` to patch values, and `session.clear()` to invalidate the cookie. When setting `maxAge`, `exp` claim is automatically managed and validated. They also support a number of `hooks` in the config for custom logic (e.g. logging, refreshing, etc).
652
+
653
+ ```ts
654
+ import { H3, HTTPError, serve } from "h3v2";
655
+
656
+ import {
657
+ type SessionConfigJWE,
658
+ type SessionConfigJWS,
659
+ useJWESession,
660
+ useJWSSession,
661
+ getJWESession,
662
+ updateJWSSession,
663
+ generateJWK,
664
+ } from "unjwt/adapters/h3v2";
665
+
666
+ const atJwk = await generateJWK("RS256");
667
+
668
+ const jweOptions = {
669
+ key: "refresh_token_secret",
670
+ name: "refresh_token",
671
+ } satisfies SessionConfigJWE;
672
+
673
+ const jwsOptions = {
674
+ key: atJwk,
675
+ name: "access_token",
676
+ maxAge: 15 * 60, // 15 minutes
677
+ hooks: {
678
+ async onExpire(event, _error, config) {
679
+ const refreshSession = await getJWESession(event, jweOptions);
680
+ if (!refreshSession.data.sub) {
681
+ // no valid refresh session, nothing to do
682
+ return;
683
+ }
684
+
685
+ console.log("Access token expired, refreshing...");
686
+
687
+ // refresh the access token
688
+ await updateJWSSession(event, config, {
689
+ sub: refreshSession.data.sub,
690
+ scope: refreshSession.data.scope,
691
+ });
692
+ },
693
+ },
694
+ } satisfies SessionConfigJWS;
695
+
696
+ const app = new H3();
697
+
698
+ app.post("/login", async (event) => {
699
+ const refreshSession = await useJWESession(event, jweOptions);
700
+ const accessSession = await useJWSSession(event, jwsOptions);
701
+
702
+ if (accessSession.data.sub) {
703
+ // user already logged in, return existing info
704
+ return {
705
+ accessToken: {
706
+ id: accessSession.id,
707
+ createdAt: accessSession.createdAt,
708
+ expiresAt: accessSession.expiresAt,
709
+ data: accessSession.data,
710
+ },
711
+ refreshSession: {
712
+ id: refreshSession.id,
713
+ createdAt: refreshSession.createdAt,
714
+ expiresAt: refreshSession.expiresAt,
715
+ data: refreshSession.data,
716
+ },
717
+ };
718
+ }
719
+
720
+ const data = (await event.req.json()) as {
721
+ username?: string;
722
+ password?: string;
723
+ };
724
+
725
+ if (!data.username || !data.password) {
726
+ throw new HTTPError("Username and password are required", { status: 400 });
727
+ }
728
+
729
+ // validate user credentials here
730
+
731
+ const claims = {
732
+ sub: data.username,
733
+ scope: ["read:profile"],
734
+ };
735
+
736
+ await accessSession.update(claims);
737
+
738
+ await refreshSession.update(claims);
739
+
740
+ return {
741
+ accessToken: {
742
+ id: accessSession.id,
743
+ createdAt: accessSession.createdAt,
744
+ expiresAt: accessSession.expiresAt,
745
+ data: accessSession.data,
746
+ },
747
+ refreshSession: {
748
+ id: refreshSession.id,
749
+ createdAt: refreshSession.createdAt,
750
+ expiresAt: refreshSession.expiresAt,
751
+ data: refreshSession.data,
752
+ },
753
+ };
754
+ });
755
+
756
+ serve(app);
757
+ ```
758
+
644
759
  ---
645
760
 
646
761
  ### Utility Functions
@@ -1,11 +1,11 @@
1
- import { b as JWEEncryptOptions } from './shared/unjwt.9mL1vSFP.mjs';
2
- export { d as JWEDecryptOptions, e as JWEDecryptResult, J as JWEHeaderParameters, c as JWEKeyLookupFunction, a as JWEKeyManagementHeaderParameters } from './shared/unjwt.9mL1vSFP.mjs';
3
- import { X as JWTClaims, r as JWK_Symmetric, u as JWK_Private, t as JWK_Public, Z as JWTClaimValidationOptions, w as JWKSet } from './shared/unjwt.CtHrClHc.mjs';
4
- export { C as CompositeKey, f as ContentEncryptionAlgorithm, D as DeriveKeyOptions, e as DeriveKeyReturn, c as GenerateJWKOptions, d as GenerateJWKReturn, G as GenerateKeyAlgorithm, a as GenerateKeyOptions, b as GenerateKeyReturn, v as JWK, S as JWKAlgorithm, T as JWKPEMAlgorithm, J as JWKParameters, I as JWK_AES_CBC_HMAC, L as JWK_AES_GCM, H as JWK_AES_GCM_KW, F as JWK_AES_KW, s as JWK_Asymmetric, R as JWK_Asymmetric_Algorithm, j as JWK_EC, O as JWK_ECDH_ES, A as JWK_ECDSA, i as JWK_EC_Private, h as JWK_EC_Public, x as JWK_HMAC, p as JWK_OKP, o as JWK_OKP_Private, n as JWK_OKP_Public, P as JWK_OKP_SIGN, N as JWK_PBES2, m as JWK_RSA, B as JWK_RSA_ENC, z as JWK_RSA_PSS, l as JWK_RSA_Private, k as JWK_RSA_Public, y as JWK_RSA_SIGN, Q as JWK_Symmetric_Algorithm, q as JWK_oct, Y as JWT, V as JoseHeaderParameters, K as KeyManagementAlgorithm, U as UnwrapKeyOptions, W as WrapKeyOptions, g as WrapKeyResult } from './shared/unjwt.CtHrClHc.mjs';
1
+ import { b as JWEEncryptOptions } from '../shared/unjwt.9mL1vSFP.mjs';
2
+ export { d as JWEDecryptOptions, e as JWEDecryptResult, J as JWEHeaderParameters, c as JWEKeyLookupFunction, a as JWEKeyManagementHeaderParameters } from '../shared/unjwt.9mL1vSFP.mjs';
3
+ import { X as JWTClaims, r as JWK_Symmetric, u as JWK_Private, t as JWK_Public, Z as JWTClaimValidationOptions, w as JWKSet } from '../shared/unjwt.CtHrClHc.mjs';
4
+ export { C as CompositeKey, f as ContentEncryptionAlgorithm, D as DeriveKeyOptions, e as DeriveKeyReturn, c as GenerateJWKOptions, d as GenerateJWKReturn, G as GenerateKeyAlgorithm, a as GenerateKeyOptions, b as GenerateKeyReturn, v as JWK, S as JWKAlgorithm, T as JWKPEMAlgorithm, J as JWKParameters, I as JWK_AES_CBC_HMAC, L as JWK_AES_GCM, H as JWK_AES_GCM_KW, F as JWK_AES_KW, s as JWK_Asymmetric, R as JWK_Asymmetric_Algorithm, j as JWK_EC, O as JWK_ECDH_ES, A as JWK_ECDSA, i as JWK_EC_Private, h as JWK_EC_Public, x as JWK_HMAC, p as JWK_OKP, o as JWK_OKP_Private, n as JWK_OKP_Public, P as JWK_OKP_SIGN, N as JWK_PBES2, m as JWK_RSA, B as JWK_RSA_ENC, z as JWK_RSA_PSS, l as JWK_RSA_Private, k as JWK_RSA_Public, y as JWK_RSA_SIGN, Q as JWK_Symmetric_Algorithm, q as JWK_oct, Y as JWT, V as JoseHeaderParameters, K as KeyManagementAlgorithm, U as UnwrapKeyOptions, W as WrapKeyOptions, g as WrapKeyResult } from '../shared/unjwt.CtHrClHc.mjs';
5
5
  import { CookieSerializeOptions } from 'cookie-es';
6
6
  import { H3Event } from 'h3';
7
- import { c as JWSSignOptions } from './shared/unjwt.BsLgL0DQ.mjs';
8
- export { d as deriveJWKFromPassword, e as exportJWKToPEM, g as generateJWK, i as importJWKFromPEM } from './shared/unjwt.BjP_dGnX.mjs';
7
+ import { c as JWSSignOptions } from '../shared/unjwt.BsLgL0DQ.mjs';
8
+ export { d as deriveJWKFromPassword, e as exportJWKToPEM, g as generateJWK, i as importJWKFromPEM } from '../shared/unjwt.BjP_dGnX.mjs';
9
9
 
10
10
  /**
11
11
  * This is a fork of h3 library's session utility functions.
@@ -1,9 +1,9 @@
1
- export { k as deriveJWKFromPassword, h as exportJWKToPEM, c as generateJWK, f as importJWKFromPEM } from './shared/unjwt.epPK1KFr.mjs';
1
+ export { k as deriveJWKFromPassword, h as exportJWKToPEM, c as generateJWK, f as importJWKFromPEM } from '../shared/unjwt.epPK1KFr.mjs';
2
2
  import { isEvent, setCookie } from 'h3';
3
3
  import { parse } from 'cookie-es';
4
- import { e as encrypt, d as decrypt } from './shared/unjwt.CiBEuvtQ.mjs';
5
- import { p as isSymmetricJWK, q as isPrivateJWK, u as isPublicJWK } from './shared/unjwt.C86hA0o-.mjs';
6
- import { s as sign, v as verify } from './shared/unjwt.D-FATaw1.mjs';
4
+ import { e as encrypt, d as decrypt } from '../shared/unjwt.CiBEuvtQ.mjs';
5
+ import { p as isSymmetricJWK, q as isPrivateJWK, u as isPublicJWK } from '../shared/unjwt.C86hA0o-.mjs';
6
+ import { s as sign, v as verify } from '../shared/unjwt.D-FATaw1.mjs';
7
7
 
8
8
  /**
9
9
  * This is a fork of h3 library's session utility functions.
@@ -215,12 +215,12 @@ async function unsealJWESession(_event, config, sealed) {
215
215
  }
216
216
  throw new Error(error_);
217
217
  });
218
- const { jti, iat, ...data } = payload;
218
+ const { jti, iat, exp, ...data } = payload;
219
219
  return {
220
220
  id: jti,
221
221
  createdAt: iat * 1e3,
222
222
  // Convert back to ms
223
- expiresAt: payload.exp ? payload.exp * 1e3 : void 0,
223
+ expiresAt: exp ? exp * 1e3 : void 0,
224
224
  data: data && typeof data === "object" ? data : /* @__PURE__ */ Object.create(null)
225
225
  };
226
226
  }
@@ -481,12 +481,12 @@ async function verifyJWSSession(_event, config, token) {
481
481
  const message = error_ instanceof Error ? error_.message : String(error_);
482
482
  throw new Error(`Invalid session token: ${message}`);
483
483
  });
484
- const { jti, iat, ...data } = payload;
484
+ const { jti, iat, exp, ...data } = payload;
485
485
  return {
486
486
  id: jti,
487
487
  createdAt: iat * 1e3,
488
488
  // Convert back to ms
489
- expiresAt: payload.exp ? payload.exp * 1e3 : void 0,
489
+ expiresAt: exp ? exp * 1e3 : void 0,
490
490
  data: data && typeof data === "object" ? data : /* @__PURE__ */ Object.create(null)
491
491
  };
492
492
  }
@@ -0,0 +1,7 @@
1
+ export { d as JWEDecryptOptions, e as JWEDecryptResult, b as JWEEncryptOptions, J as JWEHeaderParameters, c as JWEKeyLookupFunction, a as JWEKeyManagementHeaderParameters } from '../shared/unjwt.9mL1vSFP.mjs';
2
+ export { C as CompositeKey, f as ContentEncryptionAlgorithm, D as DeriveKeyOptions, e as DeriveKeyReturn, c as GenerateJWKOptions, d as GenerateJWKReturn, G as GenerateKeyAlgorithm, a as GenerateKeyOptions, b as GenerateKeyReturn, v as JWK, S as JWKAlgorithm, T as JWKPEMAlgorithm, J as JWKParameters, w as JWKSet, I as JWK_AES_CBC_HMAC, L as JWK_AES_GCM, H as JWK_AES_GCM_KW, F as JWK_AES_KW, s as JWK_Asymmetric, R as JWK_Asymmetric_Algorithm, j as JWK_EC, O as JWK_ECDH_ES, A as JWK_ECDSA, i as JWK_EC_Private, h as JWK_EC_Public, x as JWK_HMAC, p as JWK_OKP, o as JWK_OKP_Private, n as JWK_OKP_Public, P as JWK_OKP_SIGN, N as JWK_PBES2, u as JWK_Private, t as JWK_Public, m as JWK_RSA, B as JWK_RSA_ENC, z as JWK_RSA_PSS, l as JWK_RSA_Private, k as JWK_RSA_Public, y as JWK_RSA_SIGN, r as JWK_Symmetric, Q as JWK_Symmetric_Algorithm, q as JWK_oct, Y as JWT, Z as JWTClaimValidationOptions, X as JWTClaims, V as JoseHeaderParameters, K as KeyManagementAlgorithm, U as UnwrapKeyOptions, W as WrapKeyOptions, g as WrapKeyResult } from '../shared/unjwt.CtHrClHc.mjs';
3
+ export { SessionConfigJWE, SessionConfigJWS, SessionData, SessionHooksJWE, SessionHooksJWS, SessionJWE, SessionJWEConfig, SessionJWS, SessionJWSConfig, SessionManager, clearJWESession, clearJWSSession, getJWESession, getJWSSession, sealJWESession, signJWSSession, unsealJWESession, updateJWESession, updateJWSSession, useJWESession, useJWSSession, verifyJWSSession } from './h3.mjs';
4
+ export { d as deriveJWKFromPassword, e as exportJWKToPEM, g as generateJWK, i as importJWKFromPEM } from '../shared/unjwt.BjP_dGnX.mjs';
5
+ import 'cookie-es';
6
+ import 'h3';
7
+ import '../shared/unjwt.BsLgL0DQ.mjs';
@@ -0,0 +1,7 @@
1
+ export { k as deriveJWKFromPassword, h as exportJWKToPEM, c as generateJWK, f as importJWKFromPEM } from '../shared/unjwt.epPK1KFr.mjs';
2
+ export { clearJWESession, clearJWSSession, getJWESession, getJWSSession, sealJWESession, signJWSSession, unsealJWESession, updateJWESession, updateJWSSession, useJWESession, useJWSSession, verifyJWSSession } from './h3.mjs';
3
+ import '../shared/unjwt.C86hA0o-.mjs';
4
+ import 'h3';
5
+ import 'cookie-es';
6
+ import '../shared/unjwt.CiBEuvtQ.mjs';
7
+ import '../shared/unjwt.D-FATaw1.mjs';
@@ -0,0 +1,129 @@
1
+ import { b as JWEEncryptOptions } from '../shared/unjwt.9mL1vSFP.mjs';
2
+ export { d as JWEDecryptOptions, e as JWEDecryptResult, J as JWEHeaderParameters, c as JWEKeyLookupFunction, a as JWEKeyManagementHeaderParameters } from '../shared/unjwt.9mL1vSFP.mjs';
3
+ import { X as JWTClaims, r as JWK_Symmetric, u as JWK_Private, t as JWK_Public, Z as JWTClaimValidationOptions, w as JWKSet } from '../shared/unjwt.CtHrClHc.mjs';
4
+ export { C as CompositeKey, f as ContentEncryptionAlgorithm, D as DeriveKeyOptions, e as DeriveKeyReturn, c as GenerateJWKOptions, d as GenerateJWKReturn, G as GenerateKeyAlgorithm, a as GenerateKeyOptions, b as GenerateKeyReturn, v as JWK, S as JWKAlgorithm, T as JWKPEMAlgorithm, J as JWKParameters, I as JWK_AES_CBC_HMAC, L as JWK_AES_GCM, H as JWK_AES_GCM_KW, F as JWK_AES_KW, s as JWK_Asymmetric, R as JWK_Asymmetric_Algorithm, j as JWK_EC, O as JWK_ECDH_ES, A as JWK_ECDSA, i as JWK_EC_Private, h as JWK_EC_Public, x as JWK_HMAC, p as JWK_OKP, o as JWK_OKP_Private, n as JWK_OKP_Public, P as JWK_OKP_SIGN, N as JWK_PBES2, m as JWK_RSA, B as JWK_RSA_ENC, z as JWK_RSA_PSS, l as JWK_RSA_Private, k as JWK_RSA_Public, y as JWK_RSA_SIGN, Q as JWK_Symmetric_Algorithm, q as JWK_oct, Y as JWT, V as JoseHeaderParameters, K as KeyManagementAlgorithm, U as UnwrapKeyOptions, W as WrapKeyOptions, g as WrapKeyResult } from '../shared/unjwt.CtHrClHc.mjs';
5
+ import { HTTPEvent } from 'h3';
6
+ import { CookieSerializeOptions } from 'cookie-es';
7
+ import { c as JWSSignOptions } from '../shared/unjwt.BsLgL0DQ.mjs';
8
+ export { d as deriveJWKFromPassword, e as exportJWKToPEM, g as generateJWK, i as importJWKFromPEM } from '../shared/unjwt.BjP_dGnX.mjs';
9
+
10
+ type SessionDataT$1 = Omit<JWTClaims, "jti" | "iat" | "exp">;
11
+ type SessionData<T extends SessionDataT$1 = SessionDataT$1> = T;
12
+ declare const kGetSessionPromise$1: unique symbol;
13
+ interface SessionJWE<T extends SessionDataT$1 = SessionDataT$1> {
14
+ id: string;
15
+ createdAt: number;
16
+ expiresAt?: number;
17
+ data: SessionData<T>;
18
+ [kGetSessionPromise$1]?: Promise<SessionJWE<T>>;
19
+ }
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>;
26
+ }
27
+ interface SessionManager<T extends SessionDataT$1 = SessionDataT$1> {
28
+ readonly id: string | undefined;
29
+ readonly createdAt: number;
30
+ readonly expiresAt: number | undefined;
31
+ readonly data: SessionData<T>;
32
+ update: (update: SessionUpdate$1<T>) => Promise<SessionManager<T>>;
33
+ clear: () => Promise<SessionManager<T>>;
34
+ }
35
+ interface SessionConfigJWE {
36
+ /** Shared secret used, string for PBES2 or Json Web Key (JWK) */
37
+ key: string | JWK_Symmetric | {
38
+ privateKey: JWK_Private | JWK_Symmetric;
39
+ publicKey?: JWK_Public;
40
+ };
41
+ /** Session lifetime in seconds (used to derive exp from iat) */
42
+ maxAge?: number;
43
+ /** Default is "h3" */
44
+ name?: string;
45
+ /** Default is secure, httpOnly, path="/" */
46
+ cookie?: false | (CookieSerializeOptions & {
47
+ chunkMaxLength?: number;
48
+ });
49
+ /** Default is x-h3-session / x-{name}-session */
50
+ sessionHeader?: false | string;
51
+ /** Default is crypto.randomUUID */
52
+ generateId?: () => string;
53
+ /** JWE configuration overrides */
54
+ jwe?: {
55
+ encryptOptions?: Omit<JWEEncryptOptions, "expiresIn">;
56
+ decryptOptions?: JWTClaimValidationOptions;
57
+ };
58
+ hooks?: SessionHooksJWE;
59
+ }
60
+ type SessionUpdate$1<T extends SessionDataT$1 = SessionDataT$1> = Partial<SessionData<T>> | ((oldData: SessionData<T>) => Partial<SessionData<T>> | undefined);
61
+ /**
62
+ * Create a session manager for the current request.
63
+ */
64
+ declare function useJWESession<T extends SessionDataT$1 = SessionDataT$1>(event: HTTPEvent, config: SessionConfigJWE): Promise<SessionManager<T>>;
65
+ /**
66
+ * Get the session for the current request.
67
+ */
68
+ declare function getJWESession<T extends SessionDataT$1 = SessionDataT$1>(event: HTTPEvent, config: SessionConfigJWE): Promise<SessionJWE<T>>;
69
+ /**
70
+ * Update the session data for the current request.
71
+ */
72
+ declare function updateJWESession<T extends SessionDataT$1 = SessionDataT$1>(event: HTTPEvent, config: SessionConfigJWE, update?: SessionUpdate$1<T>): Promise<SessionJWE<T>>;
73
+ /**
74
+ * Produce a JWE for the current session.
75
+ */
76
+ declare function sealJWESession<T extends SessionDataT$1 = SessionDataT$1>(event: HTTPEvent, config: SessionConfigJWE): Promise<string>;
77
+ /**
78
+ * Decrypt the JWE and return a Session-compatible object.
79
+ */
80
+ declare function unsealJWESession(_event: HTTPEvent, config: SessionConfigJWE, sealed: string): Promise<Partial<SessionJWE>>;
81
+ /**
82
+ * Clear the session (delete from context and drop cookie).
83
+ */
84
+ declare function clearJWESession(event: HTTPEvent, config: Partial<SessionConfigJWE>): Promise<void>;
85
+
86
+ type SessionDataT = Omit<JWTClaims, "jti" | "iat" | "exp">;
87
+ declare const kGetSessionPromise: unique symbol;
88
+ interface SessionJWS<T extends SessionDataT = SessionDataT> {
89
+ id: string;
90
+ createdAt: number;
91
+ expiresAt?: number;
92
+ data: SessionData<T>;
93
+ [kGetSessionPromise]?: Promise<SessionJWS<T>>;
94
+ }
95
+ 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>;
101
+ }
102
+ interface SessionConfigJWS {
103
+ key: JWK_Symmetric | {
104
+ privateKey: JWK_Private;
105
+ publicKey: JWK_Public | JWK_Public[] | JWKSet;
106
+ };
107
+ maxAge?: number;
108
+ name?: string;
109
+ cookie?: false | (CookieSerializeOptions & {
110
+ chunkMaxLength?: number;
111
+ });
112
+ sessionHeader?: false | string;
113
+ generateId?: () => string;
114
+ jws?: {
115
+ signOptions?: Omit<JWSSignOptions, "expiresIn">;
116
+ verifyOptions?: JWTClaimValidationOptions;
117
+ };
118
+ hooks?: SessionHooksJWS;
119
+ }
120
+ type SessionUpdate<T extends SessionDataT = SessionDataT> = Partial<SessionData<T>> | ((oldData: SessionData<T>) => Partial<SessionData<T>> | undefined);
121
+ declare function useJWSSession<T extends SessionDataT = SessionDataT>(event: HTTPEvent, config: SessionConfigJWS): Promise<SessionManager<T>>;
122
+ declare function getJWSSession<T extends SessionDataT = SessionDataT>(event: HTTPEvent, config: SessionConfigJWS): Promise<SessionJWS<T>>;
123
+ declare function updateJWSSession<T extends SessionDataT = SessionDataT>(event: HTTPEvent, config: SessionConfigJWS, update?: SessionUpdate<T>): Promise<SessionJWS<T>>;
124
+ declare function signJWSSession<T extends SessionDataT = SessionDataT>(event: HTTPEvent, config: SessionConfigJWS): Promise<string>;
125
+ declare function verifyJWSSession(_event: HTTPEvent, config: SessionConfigJWS, token: string): Promise<Partial<SessionJWS>>;
126
+ declare function clearJWSSession(event: HTTPEvent, config: Partial<SessionConfigJWS>): Promise<void>;
127
+
128
+ export { JWEEncryptOptions, JWKSet, JWK_Private, JWK_Public, JWK_Symmetric, JWTClaimValidationOptions, JWTClaims, clearJWESession, clearJWSSession, getJWESession, getJWSSession, sealJWESession, signJWSSession, unsealJWESession, updateJWESession, updateJWSSession, useJWESession, useJWSSession, verifyJWSSession };
129
+ export type { SessionConfigJWE, SessionConfigJWS, SessionData, SessionHooksJWE, SessionHooksJWS, SessionJWE, SessionJWS, SessionManager };
@@ -0,0 +1,618 @@
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';
4
+ import { NullProtoObj } from 'rou3';
5
+ import { e as encrypt, d as decrypt } from '../shared/unjwt.CiBEuvtQ.mjs';
6
+ import { p as isSymmetricJWK, q as isPrivateJWK, u as isPublicJWK } from '../shared/unjwt.C86hA0o-.mjs';
7
+ import { s as sign, v as verify } from '../shared/unjwt.D-FATaw1.mjs';
8
+
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
+ const kGetSessionPromise$1 = Symbol("h3_jwe_getSession");
105
+ const DEFAULT_NAME$1 = "h3-jwe";
106
+ const DEFAULT_COOKIE$1 = {
107
+ path: "/",
108
+ secure: true,
109
+ httpOnly: true
110
+ };
111
+ async function useJWESession(event, config) {
112
+ const sessionName = config.name || DEFAULT_NAME$1;
113
+ await getJWESession(event, config);
114
+ const sessionManager = {
115
+ get id() {
116
+ return getSessionFromContext$1(event, sessionName)?.id;
117
+ },
118
+ get createdAt() {
119
+ return getSessionFromContext$1(event, sessionName)?.createdAt ?? Date.now();
120
+ },
121
+ get expiresAt() {
122
+ return getSessionFromContext$1(event, sessionName)?.expiresAt;
123
+ },
124
+ get data() {
125
+ return getSessionFromContext$1(event, sessionName)?.data || {};
126
+ },
127
+ update: async (update) => {
128
+ await updateJWESession(event, config, update);
129
+ return sessionManager;
130
+ },
131
+ clear: async () => {
132
+ await clearJWESession(event, config);
133
+ return sessionManager;
134
+ }
135
+ };
136
+ return sessionManager;
137
+ }
138
+ async function getJWESession(event, config) {
139
+ const sessionName = config.name || DEFAULT_NAME$1;
140
+ const context = getEventContext(event);
141
+ if (!context.sessions) {
142
+ context.sessions = new NullProtoObj();
143
+ }
144
+ const existingSession = context.sessions[sessionName];
145
+ if (existingSession) {
146
+ const session2 = existingSession[kGetSessionPromise$1] ? await existingSession[kGetSessionPromise$1] : existingSession;
147
+ if (session2.expiresAt !== void 0 && session2.expiresAt < Date.now() && hasWritableResponse$1(event)) {
148
+ await config.hooks?.onExpire?.(event, void 0, config);
149
+ return clearJWESession(event, config).then(
150
+ () => getJWESession(event, config)
151
+ );
152
+ }
153
+ await config.hooks?.onRead?.(session2, event, config);
154
+ return session2;
155
+ }
156
+ const session = {
157
+ id: "",
158
+ createdAt: 0,
159
+ expiresAt: void 0,
160
+ data: new NullProtoObj()
161
+ };
162
+ context.sessions[sessionName] = session;
163
+ let jweToken;
164
+ if (config.sessionHeader !== false) {
165
+ const headerName = typeof config.sessionHeader === "string" ? config.sessionHeader.toLowerCase() : `x-${sessionName.toLowerCase()}-session`;
166
+ const headerValue = event.req.headers.get(headerName);
167
+ if (typeof headerValue === "string") {
168
+ jweToken = headerValue;
169
+ }
170
+ }
171
+ if (!jweToken) {
172
+ jweToken = getChunkedCookie(event, sessionName);
173
+ }
174
+ if (jweToken) {
175
+ const promise = unsealJWESession(event, config, jweToken).catch(async (error_) => {
176
+ if (error_ instanceof Error) {
177
+ const message = error_.message;
178
+ if (message.includes("Token has expired") || message.includes("Token is too old")) {
179
+ await config.hooks?.onExpire?.(event, error_, config);
180
+ return void 0;
181
+ }
182
+ }
183
+ await config.hooks?.onError?.(event, error_, config);
184
+ return void 0;
185
+ }).then((unsealed) => {
186
+ if (unsealed) {
187
+ Object.assign(session, unsealed);
188
+ }
189
+ delete session[kGetSessionPromise$1];
190
+ return session;
191
+ });
192
+ session[kGetSessionPromise$1] = promise;
193
+ await promise;
194
+ }
195
+ if (!session.id) {
196
+ session.id = config.generateId?.() ?? crypto.randomUUID();
197
+ session.createdAt = config.jwe?.encryptOptions?.currentDate?.getTime() ?? Date.now();
198
+ session.expiresAt = config.maxAge ? session.createdAt + config.maxAge * 1e3 : void 0;
199
+ await updateJWESession(event, config);
200
+ }
201
+ await config.hooks?.onRead?.(session, event, config);
202
+ return session;
203
+ }
204
+ async function updateJWESession(event, config, update) {
205
+ const sessionName = config.name || DEFAULT_NAME$1;
206
+ const context = getEventContext(event);
207
+ const session = context.sessions?.[sessionName] || await getJWESession(event, config);
208
+ if (typeof update === "function") {
209
+ update = update(session.data);
210
+ }
211
+ if (update) {
212
+ Object.assign(session.data, update);
213
+ await config.hooks?.onUpdate?.(session, event, config);
214
+ }
215
+ if (config.cookie !== false && hasWritableResponse$1(event)) {
216
+ const sealed = await sealJWESession(event, config);
217
+ setChunkedCookie(event, sessionName, sealed, {
218
+ ...DEFAULT_COOKIE$1,
219
+ expires: config.maxAge ? new Date(session.createdAt + config.maxAge * 1e3) : void 0,
220
+ ...config.cookie
221
+ });
222
+ }
223
+ return session;
224
+ }
225
+ async function sealJWESession(event, config) {
226
+ const key = getEncryptKey(config.key);
227
+ const sessionName = config.name || DEFAULT_NAME$1;
228
+ const context = getEventContext(event);
229
+ const session = context.sessions?.[sessionName] || await getJWESession(event, config);
230
+ const iatSeconds = Math.floor(session.createdAt / 1e3);
231
+ const expSeconds = config.maxAge == null ? void 0 : iatSeconds + config.maxAge;
232
+ const payload = {
233
+ ...session.data,
234
+ jti: session.id,
235
+ iat: iatSeconds
236
+ };
237
+ if (expSeconds) {
238
+ payload.exp = expSeconds;
239
+ }
240
+ let typ = void 0;
241
+ if (config.jwe?.encryptOptions?.protectedHeader?.typ && typeof config.jwe.encryptOptions.protectedHeader.typ === "string" && config.jwe.encryptOptions.protectedHeader.typ.toLowerCase().includes("jwt")) {
242
+ typ = config.jwe.encryptOptions.protectedHeader.typ;
243
+ }
244
+ const token = await encrypt(payload, key, {
245
+ ...config.jwe?.encryptOptions,
246
+ expiresIn: void 0,
247
+ // controlled via 'exp' claim
248
+ protectedHeader: {
249
+ ...config.jwe?.encryptOptions?.protectedHeader,
250
+ typ: typ || "JWT",
251
+ cty: "application/json"
252
+ }
253
+ });
254
+ return token;
255
+ }
256
+ async function unsealJWESession(_event, config, sealed) {
257
+ const key = getDecryptKey(config.key);
258
+ const alg = config.jwe?.encryptOptions?.alg;
259
+ const enc = config.jwe?.encryptOptions?.enc;
260
+ if (config.jwe?.encryptOptions?.protectedHeader?.typ && typeof config.jwe.encryptOptions.protectedHeader.typ === "string" && config.jwe.encryptOptions.protectedHeader.typ.toLowerCase().includes("jwt")) {
261
+ config.jwe.encryptOptions.protectedHeader.typ;
262
+ }
263
+ const { payload } = await decrypt(sealed, key, {
264
+ ...config.jwe?.decryptOptions,
265
+ requiredClaims: [
266
+ ...config.jwe?.decryptOptions?.requiredClaims?.filter(
267
+ (claim) => claim !== "jti" && claim !== "iat"
268
+ ) || [],
269
+ "jti",
270
+ "iat"
271
+ ],
272
+ maxTokenAge: config.maxAge,
273
+ algorithms: alg ? [alg] : void 0,
274
+ encryptionAlgorithms: enc ? [enc] : void 0,
275
+ unwrappedKeyAlgorithm: void 0,
276
+ keyUsage: void 0,
277
+ forceUint8Array: false,
278
+ validateJWT: true
279
+ }).catch((error_) => {
280
+ if (error_ instanceof Error) {
281
+ throw error_;
282
+ }
283
+ throw new Error(String(error_));
284
+ });
285
+ const { jti, iat, exp, ...data } = payload;
286
+ return {
287
+ id: jti,
288
+ createdAt: iat * 1e3,
289
+ expiresAt: exp ? exp * 1e3 : void 0,
290
+ data: data && typeof data === "object" ? data : new NullProtoObj()
291
+ };
292
+ }
293
+ async function clearJWESession(event, config) {
294
+ const context = getEventContext(event);
295
+ const sessionName = config.name || DEFAULT_NAME$1;
296
+ if (context.sessions?.[sessionName]) {
297
+ delete context.sessions[sessionName];
298
+ }
299
+ if (config.cookie !== false && hasWritableResponse$1(event)) {
300
+ 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?.(event, config);
307
+ }
308
+ }
309
+ function getSessionFromContext$1(event, sessionName) {
310
+ const context = getEventContext(event);
311
+ return context.sessions?.[sessionName];
312
+ }
313
+ function hasWritableResponse$1(event) {
314
+ return Boolean(event.res);
315
+ }
316
+ function getEncryptKey(key) {
317
+ if (!key) {
318
+ throw new Error("Session: JWE key is required.");
319
+ }
320
+ let _key;
321
+ if (typeof key === "string") {
322
+ _key = key;
323
+ } else if (isSymmetricJWK(key) || isPrivateJWK(key)) {
324
+ _key = key;
325
+ } else if ("publicKey" in key && isPublicJWK(key.publicKey)) {
326
+ _key = key.publicKey;
327
+ } else if ("privateKey" in key && isPrivateJWK(key.privateKey)) {
328
+ _key = key.privateKey;
329
+ }
330
+ if (!_key) {
331
+ throw new Error(
332
+ "Session: Invalid JWE key. It must be a password string or valid JWK.",
333
+ { cause: key }
334
+ );
335
+ }
336
+ return _key;
337
+ }
338
+ function getDecryptKey(key) {
339
+ if (!key) {
340
+ throw new Error("Session: JWE key is required.");
341
+ }
342
+ let _key = void 0;
343
+ if (typeof key === "string") {
344
+ _key = key;
345
+ } else if (isSymmetricJWK(key)) {
346
+ _key = key;
347
+ } else if ("privateKey" in key) {
348
+ _key = key.privateKey;
349
+ }
350
+ if (!_key) {
351
+ throw new Error(
352
+ "Session: Invalid JWE key. It must be a password string or a valid private JWK.",
353
+ { cause: key }
354
+ );
355
+ }
356
+ return _key;
357
+ }
358
+
359
+ const kGetSessionPromise = Symbol("h3_jws_getSession");
360
+ const DEFAULT_NAME = "h3-jws";
361
+ const DEFAULT_COOKIE = {
362
+ path: "/",
363
+ secure: true,
364
+ httpOnly: false
365
+ };
366
+ async function useJWSSession(event, config) {
367
+ const sessionName = config.name || DEFAULT_NAME;
368
+ await getJWSSession(event, config);
369
+ const sessionManager = {
370
+ get id() {
371
+ return getSessionFromContext(event, sessionName)?.id;
372
+ },
373
+ get createdAt() {
374
+ return getSessionFromContext(event, sessionName)?.createdAt ?? Date.now();
375
+ },
376
+ get expiresAt() {
377
+ return getSessionFromContext(event, sessionName)?.expiresAt;
378
+ },
379
+ get data() {
380
+ return getSessionFromContext(event, sessionName)?.data || {};
381
+ },
382
+ update: async (update) => {
383
+ await updateJWSSession(event, config, update);
384
+ return sessionManager;
385
+ },
386
+ clear: async () => {
387
+ await clearJWSSession(event, config);
388
+ return sessionManager;
389
+ }
390
+ };
391
+ return sessionManager;
392
+ }
393
+ async function getJWSSession(event, config) {
394
+ const sessionName = config.name || DEFAULT_NAME;
395
+ const context = getEventContext(event);
396
+ if (!context.sessions) {
397
+ context.sessions = new NullProtoObj();
398
+ }
399
+ const existingSession = context.sessions[sessionName];
400
+ if (existingSession) {
401
+ const session2 = existingSession[kGetSessionPromise] ? await existingSession[kGetSessionPromise] : existingSession;
402
+ if (session2.expiresAt !== void 0 && session2.expiresAt < Date.now() && hasWritableResponse(event)) {
403
+ await config.hooks?.onExpire?.(event, void 0, config);
404
+ return clearJWSSession(event, config).then(
405
+ () => getJWSSession(event, config)
406
+ );
407
+ }
408
+ await config.hooks?.onRead?.(session2, event, config);
409
+ return session2;
410
+ }
411
+ const session = {
412
+ id: "",
413
+ createdAt: 0,
414
+ expiresAt: void 0,
415
+ data: new NullProtoObj()
416
+ };
417
+ context.sessions[sessionName] = session;
418
+ let token;
419
+ if (config.sessionHeader !== false) {
420
+ const headerName = typeof config.sessionHeader === "string" ? config.sessionHeader.toLowerCase() : `x-${sessionName.toLowerCase()}-session`;
421
+ const headerValue = event.req.headers.get(headerName);
422
+ if (typeof headerValue === "string") {
423
+ token = headerValue;
424
+ }
425
+ }
426
+ if (!token) {
427
+ token = getChunkedCookie(event, sessionName);
428
+ }
429
+ if (token) {
430
+ const promise = verifyJWSSession(event, config, token).catch(async (error_) => {
431
+ 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);
433
+ return void 0;
434
+ }
435
+ await config.hooks?.onError?.(event, error_, config);
436
+ return void 0;
437
+ }).then((unsealed) => {
438
+ if (unsealed) {
439
+ Object.assign(session, unsealed);
440
+ }
441
+ delete session[kGetSessionPromise];
442
+ return session;
443
+ });
444
+ session[kGetSessionPromise] = promise;
445
+ await promise;
446
+ }
447
+ if (!session.id) {
448
+ session.id = config.generateId?.() ?? crypto.randomUUID();
449
+ session.createdAt = config.jws?.signOptions?.currentDate?.getTime() ?? Date.now();
450
+ session.expiresAt = config.maxAge ? session.createdAt + config.maxAge * 1e3 : void 0;
451
+ await updateJWSSession(event, config);
452
+ }
453
+ await config.hooks?.onRead?.(session, event, config);
454
+ return session;
455
+ }
456
+ async function updateJWSSession(event, config, update) {
457
+ const sessionName = config.name || DEFAULT_NAME;
458
+ const context = getEventContext(event);
459
+ const session = context.sessions?.[sessionName] || await getJWSSession(event, config);
460
+ if (typeof update === "function") {
461
+ update = update(session.data);
462
+ }
463
+ if (update) {
464
+ Object.assign(session.data, update);
465
+ await config.hooks?.onUpdate?.(session, event, config);
466
+ }
467
+ if (config.cookie !== false && hasWritableResponse(event)) {
468
+ const token = await signJWSSession(event, config);
469
+ setChunkedCookie(event, sessionName, token, {
470
+ ...DEFAULT_COOKIE,
471
+ expires: config.maxAge ? new Date(session.createdAt + config.maxAge * 1e3) : void 0,
472
+ ...config.cookie
473
+ });
474
+ }
475
+ return session;
476
+ }
477
+ async function signJWSSession(event, config) {
478
+ const key = getSignKey(config.key);
479
+ const sessionName = config.name || DEFAULT_NAME;
480
+ const context = getEventContext(event);
481
+ const session = context.sessions?.[sessionName] || await getJWSSession(event, config);
482
+ const iatSeconds = Math.floor(session.createdAt / 1e3);
483
+ const expSeconds = config.maxAge == null ? void 0 : iatSeconds + config.maxAge;
484
+ const payload = {
485
+ ...session.data,
486
+ jti: session.id,
487
+ iat: iatSeconds
488
+ };
489
+ if (expSeconds) {
490
+ payload.exp = expSeconds;
491
+ }
492
+ let typ;
493
+ if (config.jws?.signOptions?.protectedHeader?.typ && typeof config.jws.signOptions.protectedHeader.typ === "string" && config.jws.signOptions.protectedHeader.typ.toLowerCase().includes("jwt")) {
494
+ typ = config.jws.signOptions.protectedHeader.typ;
495
+ }
496
+ const token = await sign(payload, key, {
497
+ ...config.jws?.signOptions,
498
+ expiresIn: void 0,
499
+ // controlled via 'exp' claim
500
+ protectedHeader: {
501
+ ...config.jws?.signOptions?.protectedHeader,
502
+ kid: "kid" in key ? key.kid : void 0,
503
+ typ: typ || "JWT",
504
+ cty: "application/json"
505
+ }
506
+ });
507
+ return token;
508
+ }
509
+ async function verifyJWSSession(_event, config, token) {
510
+ const jwk = getVerifyKey(config.key);
511
+ const alg = config.jws?.signOptions?.alg;
512
+ let typ;
513
+ if (config.jws?.signOptions?.protectedHeader?.typ && typeof config.jws.signOptions.protectedHeader.typ === "string" && config.jws.signOptions.protectedHeader.typ.toLowerCase().includes("jwt")) {
514
+ typ = config.jws.signOptions.protectedHeader.typ;
515
+ }
516
+ const { payload } = await verify(token, jwk, {
517
+ ...config.jws?.verifyOptions,
518
+ requiredClaims: [
519
+ ...config.jws?.verifyOptions?.requiredClaims?.filter(
520
+ (claim) => claim !== "jti" && claim !== "iat"
521
+ ) || [],
522
+ "jti",
523
+ "iat"
524
+ ],
525
+ typ: typ || "JWT",
526
+ algorithms: alg ? [alg] : void 0,
527
+ forceUint8Array: false,
528
+ validateJWT: true
529
+ }).catch((error_) => {
530
+ const message = error_ instanceof Error ? error_.message : String(error_);
531
+ throw new Error(`Invalid session token: ${message}`);
532
+ });
533
+ const { jti, iat, exp, ...data } = payload;
534
+ return {
535
+ id: jti,
536
+ createdAt: iat * 1e3,
537
+ expiresAt: exp ? exp * 1e3 : void 0,
538
+ data: data && typeof data === "object" ? data : new NullProtoObj()
539
+ };
540
+ }
541
+ async function clearJWSSession(event, config) {
542
+ const context = getEventContext(event);
543
+ const sessionName = config.name || DEFAULT_NAME;
544
+ if (context.sessions?.[sessionName]) {
545
+ delete context.sessions[sessionName];
546
+ }
547
+ if (config.cookie !== false && hasWritableResponse(event)) {
548
+ setChunkedCookie(event, sessionName, "", {
549
+ ...DEFAULT_COOKIE,
550
+ ...config.cookie,
551
+ expires: /* @__PURE__ */ new Date(0),
552
+ maxAge: void 0
553
+ });
554
+ await config.hooks?.onClear?.(event, config);
555
+ }
556
+ }
557
+ function getSessionFromContext(event, sessionName) {
558
+ const context = getEventContext(event);
559
+ return context.sessions?.[sessionName];
560
+ }
561
+ function hasWritableResponse(event) {
562
+ return Boolean(event.res);
563
+ }
564
+ function getSignKey(key) {
565
+ if (!key) {
566
+ throw new Error("Session: JWS key is required.");
567
+ }
568
+ let _key;
569
+ if (isSymmetricJWK(key)) {
570
+ _key = key;
571
+ } else if ("privateKey" in key && isPrivateJWK(key.privateKey)) {
572
+ _key = key.privateKey;
573
+ }
574
+ if (!_key) {
575
+ throw new Error(
576
+ "Session: Invalid JWS key. It must be a symmetric JWK or a private JWK.",
577
+ { cause: key }
578
+ );
579
+ }
580
+ return _key;
581
+ }
582
+ function getVerifyKey(key) {
583
+ if (!key) {
584
+ throw new Error("Session: JWS key is required.");
585
+ }
586
+ let _key;
587
+ if (isSymmetricJWK(key)) {
588
+ _key = key;
589
+ } else if ("publicKey" in key) {
590
+ const publicKey = key.publicKey;
591
+ if (isPublicJWK(publicKey)) {
592
+ _key = publicKey;
593
+ } else if (Array.isArray(publicKey)) {
594
+ const keys = publicKey.filter(
595
+ (candidate) => isPublicJWK(candidate)
596
+ );
597
+ if (keys.length > 0) {
598
+ _key = { keys };
599
+ }
600
+ } else if (publicKey && typeof publicKey === "object" && Array.isArray(publicKey.keys)) {
601
+ const keys = publicKey.keys.filter(
602
+ (candidate) => isPublicJWK(candidate)
603
+ );
604
+ if (keys.length > 0) {
605
+ _key = { keys };
606
+ }
607
+ }
608
+ }
609
+ if (!_key) {
610
+ throw new Error(
611
+ "Session: Invalid JWS key. It must be a symmetric JWK or a public JWK/set.",
612
+ { cause: key }
613
+ );
614
+ }
615
+ return _key;
616
+ }
617
+
618
+ export { clearJWESession, clearJWSSession, getJWESession, getJWSSession, sealJWESession, signJWSSession, unsealJWESession, updateJWESession, updateJWSSession, useJWESession, useJWSSession, verifyJWSSession };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unjwt",
3
- "version": "0.5.23",
3
+ "version": "0.5.24",
4
4
  "description": "Low-level JWT utilities",
5
5
  "repository": "sandros94/unjwt",
6
6
  "license": "MIT",
@@ -31,9 +31,17 @@
31
31
  "types": "./dist/utils.d.mts",
32
32
  "default": "./dist/utils.mjs"
33
33
  },
34
- "./h3": {
35
- "types": "./dist/h3.d.mts",
36
- "default": "./dist/h3.mjs"
34
+ "./adapters/h3": {
35
+ "types": "./dist/adapters/h3.d.mts",
36
+ "default": "./dist/adapters/h3.mjs"
37
+ },
38
+ "./adapters/h3v1": {
39
+ "types": "./dist/adapters/h3v1.d.mts",
40
+ "default": "./dist/adapters/h3v1.mjs"
41
+ },
42
+ "./adapters/h3v2": {
43
+ "types": "./dist/adapters/h3v2.d.mts",
44
+ "default": "./dist/adapters/h3v2.mjs"
37
45
  }
38
46
  },
39
47
  "main": "./dist/index.mjs",
@@ -60,8 +68,12 @@
60
68
  "@vitest/coverage-v8": "^3.2.4",
61
69
  "automd": "^0.4.2",
62
70
  "changelogen": "^0.6.2",
71
+ "cookie-esv1": "npm:cookie-es@^1.2.2",
72
+ "cookie-esv2": "npm:cookie-es@^2.0.0",
63
73
  "eslint": "^9.36.0",
64
74
  "eslint-config-unjs": "^0.5.0",
75
+ "h3v1": "npm:h3@^1.15.4",
76
+ "h3v2": "npm:h3@^2.0.0-beta.5",
65
77
  "jose": "^6.1.0",
66
78
  "prettier": "^3.6.2",
67
79
  "supertest": "^7.1.4",
@@ -71,7 +83,8 @@
71
83
  },
72
84
  "peerDependencies": {
73
85
  "cookie-es": "^1.2.2 || ^2.0.0",
74
- "h3": "^1.15.4 || ^2.0.0-rc.0 || ^2.0.0"
86
+ "h3": "^1.15.4 || >=2.0.0-beta.5 <2.0.0 || ^2.0.1",
87
+ "rou3": "^0.7.7"
75
88
  },
76
89
  "peerDependenciesMeta": {
77
90
  "cookie-es": {