unjwt 0.5.22 → 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 +119 -4
- package/dist/{h3.d.mts → adapters/h3.d.mts} +25 -24
- package/dist/{h3.mjs → adapters/h3.mjs} +143 -105
- package/dist/adapters/h3v1.d.mts +7 -0
- package/dist/adapters/h3v1.mjs +7 -0
- package/dist/adapters/h3v2.d.mts +129 -0
- package/dist/adapters/h3v2.mjs +618 -0
- package/dist/index.mjs +4 -4
- package/dist/jwe.mjs +3 -3
- package/dist/jwk.mjs +2 -2
- package/dist/jws.mjs +3 -3
- package/dist/shared/{unjwt.DCiTNyCM.mjs → unjwt.C86hA0o-.mjs} +1 -1
- package/dist/shared/{unjwt.CXl9FD-V.mjs → unjwt.CiBEuvtQ.mjs} +2 -2
- package/dist/shared/{unjwt.DmRAjMp8.mjs → unjwt.D-FATaw1.mjs} +2 -2
- package/dist/shared/{unjwt.Vg02PZWw.mjs → unjwt.epPK1KFr.mjs} +5 -2
- package/dist/utils.mjs +1 -1
- package/package.json +19 -6
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 `
|
|
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/
|
|
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,15 +1,15 @@
|
|
|
1
|
-
import { b as JWEEncryptOptions } from '
|
|
2
|
-
export { d as JWEDecryptOptions, e as JWEDecryptResult, J as JWEHeaderParameters, c as JWEKeyLookupFunction, a as JWEKeyManagementHeaderParameters } from '
|
|
3
|
-
import { X as JWTClaims, r as JWK_Symmetric, u as JWK_Private, t as JWK_Public, Z as JWTClaimValidationOptions, w as JWKSet } from '
|
|
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 '
|
|
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 '
|
|
8
|
-
export { d as deriveJWKFromPassword, e as exportJWKToPEM, g as generateJWK, i as importJWKFromPEM } from '
|
|
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.
|
|
12
|
-
* @source https://github.com/h3js/h3/blob/b4dce71c256911335f3402d09f30ffad120ad61a/
|
|
12
|
+
* @source https://github.com/h3js/h3/blob/b4dce71c256911335f3402d09f30ffad120ad61a/src/utils/session.ts
|
|
13
13
|
* @license MIT https://github.com/h3js/h3/blob/b4dce71c256911335f3402d09f30ffad120ad61a/LICENSE
|
|
14
14
|
*/
|
|
15
15
|
|
|
@@ -24,11 +24,11 @@ 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) => void | Promise<void>;
|
|
28
|
-
onUpdate?: (session: SessionJWE, event: H3Event) => void | Promise<void>;
|
|
29
|
-
onClear?: (event: H3Event) => void | Promise<void>;
|
|
30
|
-
onExpire?: (event: H3Event, error: any | undefined) => void | Promise<void>;
|
|
31
|
-
onError?: (event: H3Event, error: any) => void | Promise<void>;
|
|
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>;
|
|
32
32
|
}
|
|
33
33
|
interface SessionManager<T extends SessionDataT$1 = SessionDataT$1> {
|
|
34
34
|
readonly id: string | undefined;
|
|
@@ -39,8 +39,13 @@ interface SessionManager<T extends SessionDataT$1 = SessionDataT$1> {
|
|
|
39
39
|
clear: () => Promise<SessionManager<T>>;
|
|
40
40
|
}
|
|
41
41
|
interface SessionConfigJWE {
|
|
42
|
-
/**
|
|
43
|
-
secret
|
|
42
|
+
/** @deprecated use `key` instead */
|
|
43
|
+
secret?: string | JWK_Symmetric | {
|
|
44
|
+
privateKey: JWK_Private | JWK_Symmetric;
|
|
45
|
+
publicKey?: JWK_Public;
|
|
46
|
+
};
|
|
47
|
+
/** Shared key used, string for PBES2 or Json Web Key (JWK) */
|
|
48
|
+
key: string | JWK_Symmetric | JWK_Private | {
|
|
44
49
|
privateKey: JWK_Private | JWK_Symmetric;
|
|
45
50
|
publicKey?: JWK_Public;
|
|
46
51
|
};
|
|
@@ -52,8 +57,6 @@ interface SessionConfigJWE {
|
|
|
52
57
|
cookie?: false | CookieSerializeOptions;
|
|
53
58
|
/** Default is x-h3-session / x-{name}-session */
|
|
54
59
|
sessionHeader?: false | string;
|
|
55
|
-
/** Optional WebCrypto implementation override */
|
|
56
|
-
crypto?: Crypto;
|
|
57
60
|
/** Default is crypto.randomUUID */
|
|
58
61
|
generateId?: () => string;
|
|
59
62
|
/** JWE configuration overrides */
|
|
@@ -111,7 +114,7 @@ declare function clearJWESession(event: H3Event, config: Partial<SessionConfigJW
|
|
|
111
114
|
|
|
112
115
|
/**
|
|
113
116
|
* This is a fork of h3 library's session utility functions.
|
|
114
|
-
* @source https://github.com/h3js/h3/blob/b4dce71c256911335f3402d09f30ffad120ad61a/
|
|
117
|
+
* @source https://github.com/h3js/h3/blob/b4dce71c256911335f3402d09f30ffad120ad61a/src/utils/session.ts
|
|
115
118
|
* @license MIT https://github.com/h3js/h3/blob/b4dce71c256911335f3402d09f30ffad120ad61a/LICENSE
|
|
116
119
|
*/
|
|
117
120
|
|
|
@@ -125,11 +128,11 @@ interface SessionJWS<T extends SessionDataT = SessionDataT> {
|
|
|
125
128
|
[kGetSessionPromise]?: Promise<SessionJWS<T>>;
|
|
126
129
|
}
|
|
127
130
|
interface SessionHooksJWS {
|
|
128
|
-
onRead?: (session: SessionJWS, event: H3Event) => void | Promise<void>;
|
|
129
|
-
onUpdate?: (session: SessionJWS, event: H3Event) => void | Promise<void>;
|
|
130
|
-
onClear?: (event: H3Event) => void | Promise<void>;
|
|
131
|
-
onExpire?: (event: H3Event, error: any | undefined) => void | Promise<void>;
|
|
132
|
-
onError?: (event: H3Event, error: any) => void | Promise<void>;
|
|
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>;
|
|
133
136
|
}
|
|
134
137
|
interface SessionConfigJWS {
|
|
135
138
|
/**
|
|
@@ -147,8 +150,6 @@ interface SessionConfigJWS {
|
|
|
147
150
|
cookie?: false | CookieSerializeOptions;
|
|
148
151
|
/** Custom header (false to disable header lookup) */
|
|
149
152
|
sessionHeader?: false | string;
|
|
150
|
-
/** Provide a custom WebCrypto (if needed for algorithm operations) */
|
|
151
|
-
crypto?: Crypto;
|
|
152
153
|
/** Custom ID generator (defaults to crypto.randomUUID) */
|
|
153
154
|
generateId?: () => string;
|
|
154
155
|
/** JWS customization */
|