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