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
|
@@ -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/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { i as utils } from './shared/unjwt.
|
|
2
|
-
export { j as jwe } from './shared/unjwt.
|
|
3
|
-
export { j as jwk } from './shared/unjwt.
|
|
4
|
-
export { j as jws } from './shared/unjwt.
|
|
1
|
+
export { i as utils } from './shared/unjwt.C86hA0o-.mjs';
|
|
2
|
+
export { j as jwe } from './shared/unjwt.CiBEuvtQ.mjs';
|
|
3
|
+
export { j as jwk } from './shared/unjwt.epPK1KFr.mjs';
|
|
4
|
+
export { j as jws } from './shared/unjwt.D-FATaw1.mjs';
|
package/dist/jwe.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import './shared/unjwt.
|
|
2
|
-
import './shared/unjwt.
|
|
3
|
-
export { d as decrypt, e as encrypt } from './shared/unjwt.
|
|
1
|
+
import './shared/unjwt.epPK1KFr.mjs';
|
|
2
|
+
import './shared/unjwt.C86hA0o-.mjs';
|
|
3
|
+
export { d as decrypt, e as encrypt } from './shared/unjwt.CiBEuvtQ.mjs';
|
package/dist/jwk.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import './shared/unjwt.
|
|
2
|
-
export { k as deriveJWKFromPassword, m as deriveKeyFromPassword, h as exportJWKToPEM, n as exportKey, c as generateJWK, l as generateKey, b as getJWKFromSet, f as importJWKFromPEM, i as importKey, u as unwrapKey, w as wrapKey } from './shared/unjwt.
|
|
1
|
+
import './shared/unjwt.C86hA0o-.mjs';
|
|
2
|
+
export { k as deriveJWKFromPassword, m as deriveKeyFromPassword, h as exportJWKToPEM, n as exportKey, c as generateJWK, l as generateKey, b as getJWKFromSet, f as importJWKFromPEM, i as importKey, u as unwrapKey, w as wrapKey } from './shared/unjwt.epPK1KFr.mjs';
|
package/dist/jws.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import './shared/unjwt.
|
|
2
|
-
import './shared/unjwt.
|
|
3
|
-
export { s as sign, v as verify } from './shared/unjwt.
|
|
1
|
+
import './shared/unjwt.epPK1KFr.mjs';
|
|
2
|
+
import './shared/unjwt.C86hA0o-.mjs';
|
|
3
|
+
export { s as sign, v as verify } from './shared/unjwt.D-FATaw1.mjs';
|
|
@@ -435,4 +435,4 @@ const index = {
|
|
|
435
435
|
validateJwtClaims: validateJwtClaims
|
|
436
436
|
};
|
|
437
437
|
|
|
438
|
-
export {
|
|
438
|
+
export { isAsymmetricJWK as A, maybeArray as B, isJWTContent as C, decodeMaybeJWTString as D, computeExpiresInSeconds as E, computeMaxTokenAgeSeconds as F, isJWK as a, applyTypCtyDefaults as b, computeJwtTimeClaims as c, base64UrlEncode as d, base64UrlDecode as e, decodePayloadFromBytes as f, getPlaintextBytes as g, validateJwtClaims as h, index as i, isCryptoKeyPair as j, isCryptoKey as k, textDecoder as l, isJWKSet as m, decodePayloadFromB64UrlSegment as n, validateCriticalHeadersJWS as o, isSymmetricJWK as p, isPrivateJWK as q, randomBytes as r, sanitizeObject as s, textEncoder as t, isPublicJWK as u, validateCriticalHeadersJWE as v, concatUint8Arrays as w, base64Decode as x, base64Encode as y, assertCryptoKey as z };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as importKey, e as encryptKey, g as generateIV, a as encrypt$1, u as unwrapKey, d as decrypt$1 } from './unjwt.
|
|
2
|
-
import { r as randomBytes, a as isJWK, s as sanitizeObject, b as applyTypCtyDefaults, c as computeJwtTimeClaims, g as getPlaintextBytes, d as base64UrlEncode, t as textEncoder, e as base64UrlDecode, f as decodePayloadFromBytes, v as validateCriticalHeadersJWE, h as validateJwtClaims, j as isCryptoKeyPair, k as isCryptoKey } from './unjwt.
|
|
1
|
+
import { i as importKey, e as encryptKey, g as generateIV, a as encrypt$1, u as unwrapKey, d as decrypt$1 } from './unjwt.epPK1KFr.mjs';
|
|
2
|
+
import { r as randomBytes, a as isJWK, s as sanitizeObject, b as applyTypCtyDefaults, c as computeJwtTimeClaims, g as getPlaintextBytes, d as base64UrlEncode, t as textEncoder, e as base64UrlDecode, f as decodePayloadFromBytes, v as validateCriticalHeadersJWE, h as validateJwtClaims, j as isCryptoKeyPair, k as isCryptoKey } from './unjwt.C86hA0o-.mjs';
|
|
3
3
|
|
|
4
4
|
async function encrypt(payload, key, options = {}) {
|
|
5
5
|
const {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as importKey, s as sign$1, b as getJWKFromSet, v as verify$1 } from './unjwt.
|
|
2
|
-
import { s as sanitizeObject, a as isJWK, b as applyTypCtyDefaults, c as computeJwtTimeClaims, d as base64UrlEncode, t as textEncoder, l as textDecoder, e as base64UrlDecode, m as isJWKSet, n as decodePayloadFromB64UrlSegment, o as validateCriticalHeadersJWS, h as validateJwtClaims } from './unjwt.
|
|
1
|
+
import { i as importKey, s as sign$1, b as getJWKFromSet, v as verify$1 } from './unjwt.epPK1KFr.mjs';
|
|
2
|
+
import { s as sanitizeObject, a as isJWK, b as applyTypCtyDefaults, c as computeJwtTimeClaims, d as base64UrlEncode, t as textEncoder, l as textDecoder, e as base64UrlDecode, m as isJWKSet, n as decodePayloadFromB64UrlSegment, o as validateCriticalHeadersJWS, h as validateJwtClaims } from './unjwt.C86hA0o-.mjs';
|
|
3
3
|
|
|
4
4
|
async function sign(payload, key, options = {}) {
|
|
5
5
|
const { protectedHeader: additionalProtectedHeader } = options;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { k as isCryptoKey, w as concatUint8Arrays, d as base64UrlEncode, x as base64Decode, y as base64Encode, t as textEncoder, s as sanitizeObject, z as assertCryptoKey, a as isJWK, e as base64UrlDecode,
|
|
1
|
+
import { k as isCryptoKey, w as concatUint8Arrays, d as base64UrlEncode, x as base64Decode, y as base64Encode, t as textEncoder, s as sanitizeObject, z as assertCryptoKey, a as isJWK, e as base64UrlDecode, A as isAsymmetricJWK, r as randomBytes, m as isJWKSet } from './unjwt.C86hA0o-.mjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* This is a fork of Jose library's utility functions.
|
|
@@ -1482,7 +1482,10 @@ async function generateJWK(alg, jwkParams, options = {}) {
|
|
|
1482
1482
|
} = options;
|
|
1483
1483
|
return generateKey(alg, {
|
|
1484
1484
|
...opts,
|
|
1485
|
-
toJWK:
|
|
1485
|
+
toJWK: {
|
|
1486
|
+
kid: typeof jwkParams?.kid === "string" ? jwkParams.kid : crypto.randomUUID(),
|
|
1487
|
+
...jwkParams
|
|
1488
|
+
}
|
|
1486
1489
|
});
|
|
1487
1490
|
}
|
|
1488
1491
|
async function deriveKeyFromPassword(password, alg, options) {
|