unjwt 0.7.1 → 0.7.3

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.
@@ -0,0 +1,608 @@
1
+ import { computeDurationInSeconds, isJWTError, isPrivateJWK, isPublicJWK, isSymmetricJWK } from "../../_chunks/utils.mjs";
2
+ import { sanitizeObjectCopy } from "../../_chunks/libs/unsecure.mjs";
3
+ import { deriveJWKFromPassword, exportPEM, generateJWK, importPEM } from "../../_chunks/jwk.mjs";
4
+ import { sign, verify } from "../../_chunks/jws.mjs";
5
+ import { decrypt, encrypt } from "../../_chunks/jwe.mjs";
6
+ import { Elysia } from "elysia";
7
+ function guardName(contextKey) {
8
+ return `require${contextKey.charAt(0).toUpperCase()}${contextKey.slice(1)}`;
9
+ }
10
+ const CHUNK_MARKER = "__chunked__";
11
+ const DEFAULT_CHUNK_MAX_LENGTH = 4e3;
12
+ const MAX_CHUNK_COUNT = 100;
13
+ function readChunkedCookie(jar, name) {
14
+ const main = asString(jar[name]?.value);
15
+ if (main === void 0) return void 0;
16
+ if (!main.startsWith(CHUNK_MARKER)) return main;
17
+ const count = parseChunkCount(main);
18
+ if (count === 0) return void 0;
19
+ const parts = [];
20
+ for (let i = 1; i <= count; i++) {
21
+ const part = asString(jar[chunkName(name, i)]?.value);
22
+ if (part === void 0) return void 0;
23
+ parts.push(part);
24
+ }
25
+ return parts.join("");
26
+ }
27
+ function writeChunkedCookie(jar, name, value, options = {}) {
28
+ const { chunkMaxLength, ...attrs } = options;
29
+ const max = chunkMaxLength && chunkMaxLength > 0 ? chunkMaxLength : DEFAULT_CHUNK_MAX_LENGTH;
30
+ const chunkCount = Math.ceil(value.length / max);
31
+ if (chunkCount > MAX_CHUNK_COUNT) throw new Error(`[unjwt/elysia] Session token requires ${chunkCount} cookie chunks, exceeding the limit of ${MAX_CHUNK_COUNT}.`);
32
+ const previousCount = parseChunkCount(asString(jar[name]?.value));
33
+ const firstSurplus = chunkCount <= 1 ? 1 : chunkCount + 1;
34
+ for (let i = firstSurplus; i <= previousCount; i++) expireCookie(jar, chunkName(name, i), attrs);
35
+ if (chunkCount <= 1) {
36
+ jar[name].set({
37
+ ...attrs,
38
+ value
39
+ });
40
+ return;
41
+ }
42
+ jar[name].set({
43
+ ...attrs,
44
+ value: `${CHUNK_MARKER}${chunkCount}`
45
+ });
46
+ for (let i = 1; i <= chunkCount; i++) {
47
+ const slice = value.slice((i - 1) * max, i * max);
48
+ jar[chunkName(name, i)].set({
49
+ ...attrs,
50
+ value: slice
51
+ });
52
+ }
53
+ }
54
+ function removeChunkedCookie(jar, name, attrs = {}) {
55
+ const count = parseChunkCount(asString(jar[name]?.value));
56
+ expireCookie(jar, name, attrs);
57
+ for (let i = 1; i <= count; i++) expireCookie(jar, chunkName(name, i), attrs);
58
+ }
59
+ function expireCookie(jar, fullName, attrs) {
60
+ jar[fullName].set({
61
+ ...attrs,
62
+ value: "",
63
+ maxAge: 0,
64
+ expires: /* @__PURE__ */ new Date(0)
65
+ });
66
+ }
67
+ function chunkName(name, index) {
68
+ return `${name}.${index}`;
69
+ }
70
+ function parseChunkCount(main) {
71
+ if (main === void 0 || !main.startsWith(CHUNK_MARKER)) return 0;
72
+ const suffix = main.slice(11);
73
+ if (!/^\d+$/.test(suffix)) return 0;
74
+ const count = Number.parseInt(suffix, 10);
75
+ if (count < 1 || count > MAX_CHUNK_COUNT) return 0;
76
+ return count;
77
+ }
78
+ function asString(value) {
79
+ return typeof value === "string" ? value : void 0;
80
+ }
81
+ const DEFAULT_NAME$1 = "elysia-jws";
82
+ const DEFAULT_COOKIE$1 = {
83
+ path: "/",
84
+ secure: true,
85
+ httpOnly: false
86
+ };
87
+ async function createJWSSession(context, config) {
88
+ const state = emptySession$1(config);
89
+ await initFromToken$1(context, config, state);
90
+ return buildManager$1(context, config, state);
91
+ }
92
+ function jwsSession(config) {
93
+ const contextKey = config.contextKey ?? "session";
94
+ return new Elysia({
95
+ name: "unjwt/elysia-jws",
96
+ seed: contextKey
97
+ }).resolve({ as: "scoped" }, async ({ cookie, request }) => {
98
+ const session = await createJWSSession({
99
+ cookie,
100
+ request
101
+ }, config);
102
+ return { [contextKey]: session };
103
+ }).macro({ [guardName(contextKey)]: { resolve(ctx) {
104
+ if (!ctx[contextKey]?.id) return ctx.status(401, "Unauthorized");
105
+ return {};
106
+ } } });
107
+ }
108
+ function buildManager$1(context, config, state) {
109
+ const manager = {
110
+ get id() {
111
+ return state.id;
112
+ },
113
+ get createdAt() {
114
+ return state.createdAt;
115
+ },
116
+ get expiresAt() {
117
+ return state.expiresAt;
118
+ },
119
+ get data() {
120
+ return state.data;
121
+ },
122
+ get token() {
123
+ return state.token;
124
+ },
125
+ update: async (update) => {
126
+ await updateSession$1(context, config, state, update);
127
+ return manager;
128
+ },
129
+ clear: async () => {
130
+ await clearSession$1(context, config, state);
131
+ return manager;
132
+ }
133
+ };
134
+ return manager;
135
+ }
136
+ async function initFromToken$1(context, config, state) {
137
+ const token = readToken$1(context, config);
138
+ if (!token) return;
139
+ state.token = token;
140
+ let exclusiveHookFired = false;
141
+ try {
142
+ const unsealed = await verifySession(context, config, token);
143
+ Object.assign(state, unsealed);
144
+ } catch (error_) {
145
+ exclusiveHookFired = true;
146
+ if (isJWTError(error_, "ERR_JWT_EXPIRED")) await config.hooks?.onExpire?.({
147
+ session: {
148
+ id: error_.cause.jti,
149
+ createdAt: error_.cause.iat ? error_.cause.iat * 1e3 : void 0,
150
+ expiresAt: error_.cause.exp ? error_.cause.exp * 1e3 : void 0,
151
+ token
152
+ },
153
+ context,
154
+ error: error_,
155
+ config
156
+ });
157
+ else await config.hooks?.onError?.({
158
+ session: state,
159
+ context,
160
+ error: error_,
161
+ config
162
+ });
163
+ }
164
+ if (!exclusiveHookFired && state.id !== void 0 && state.token !== void 0) await config.hooks?.onRead?.({
165
+ session: state,
166
+ context,
167
+ config
168
+ });
169
+ }
170
+ async function updateSession$1(context, config, state, update) {
171
+ const oldSession = {
172
+ ...state,
173
+ data: sanitizeObjectCopy(state.data)
174
+ };
175
+ const resolvedUpdate = typeof update === "function" ? update(state.data) : update;
176
+ if (resolvedUpdate) Object.assign(state.data, resolvedUpdate);
177
+ const now = config.jws?.signOptions?.currentDate?.getTime() ?? Date.now();
178
+ const createdAt = now - now % 1e3;
179
+ state.id = config.generateId?.() || crypto.randomUUID();
180
+ state.createdAt = createdAt;
181
+ state.expiresAt = config.maxAge === void 0 ? void 0 : createdAt + computeDurationInSeconds(config.maxAge) * 1e3;
182
+ let token;
183
+ try {
184
+ token = await signSession(config, state);
185
+ } catch (error_) {
186
+ state.id = oldSession.id;
187
+ state.createdAt = oldSession.createdAt;
188
+ state.expiresAt = oldSession.expiresAt;
189
+ state.data = oldSession.data;
190
+ state.token = oldSession.token;
191
+ await config.hooks?.onError?.({
192
+ session: state,
193
+ context,
194
+ error: error_,
195
+ config
196
+ });
197
+ throw error_;
198
+ }
199
+ state.token = token;
200
+ if (config.cookie !== false) {
201
+ const { chunkMaxLength, ...attrs } = {
202
+ ...DEFAULT_COOKIE$1,
203
+ ...config.cookie
204
+ };
205
+ writeChunkedCookie(context.cookie, config.name || DEFAULT_NAME$1, token, {
206
+ ...attrs,
207
+ chunkMaxLength,
208
+ expires: config.maxAge === void 0 ? void 0 : new Date(createdAt + computeDurationInSeconds(config.maxAge) * 1e3)
209
+ });
210
+ }
211
+ await config.hooks?.onUpdate?.({
212
+ session: state,
213
+ oldSession,
214
+ context,
215
+ config
216
+ });
217
+ }
218
+ async function clearSession$1(context, config, state) {
219
+ const oldSession = state.id !== void 0 || state.token !== void 0 ? {
220
+ ...state,
221
+ data: sanitizeObjectCopy(state.data)
222
+ } : void 0;
223
+ const fresh = emptySession$1(config);
224
+ state.id = fresh.id;
225
+ state.createdAt = fresh.createdAt;
226
+ state.expiresAt = fresh.expiresAt;
227
+ state.data = fresh.data;
228
+ state.token = fresh.token;
229
+ if (config.cookie !== false) {
230
+ const { chunkMaxLength: _chunkMaxLength, ...attrs } = {
231
+ ...DEFAULT_COOKIE$1,
232
+ ...config.cookie
233
+ };
234
+ removeChunkedCookie(context.cookie, config.name || DEFAULT_NAME$1, attrs);
235
+ }
236
+ await config.hooks?.onClear?.({
237
+ oldSession,
238
+ context,
239
+ config
240
+ });
241
+ }
242
+ async function signSession(config, state) {
243
+ const key = getSignKey(config.key);
244
+ const iat = Math.floor(state.createdAt / 1e3);
245
+ const exp = state.expiresAt ? Math.floor(state.expiresAt / 1e3) : void 0;
246
+ const payload = {
247
+ ...state.data,
248
+ jti: state.id,
249
+ iat
250
+ };
251
+ if (exp) payload.exp = exp;
252
+ return sign(payload, key, {
253
+ ...config.jws?.signOptions,
254
+ expiresIn: void 0,
255
+ protectedHeader: {
256
+ ...config.jws?.signOptions?.protectedHeader,
257
+ kid: "kid" in key ? key.kid : void 0,
258
+ typ: resolveTyp$1(config) || "JWT",
259
+ cty: "application/json"
260
+ }
261
+ });
262
+ }
263
+ async function verifySession(context, config, token) {
264
+ const alg = config.jws?.signOptions?.alg;
265
+ const { payload } = await verify(token, config.hooks?.onVerifyKeyLookup ? (header) => config.hooks.onVerifyKeyLookup({
266
+ header,
267
+ context,
268
+ config
269
+ }) : getVerifyKey(config.key), {
270
+ ...config.jws?.verifyOptions,
271
+ requiredClaims: [.../* @__PURE__ */ new Set([
272
+ ...config.jws?.verifyOptions?.requiredClaims || [],
273
+ "jti",
274
+ "iat"
275
+ ])],
276
+ typ: resolveTyp$1(config) || "JWT",
277
+ algorithms: alg ? [alg] : void 0,
278
+ forceUint8Array: false,
279
+ validateClaims: true
280
+ }).catch((error_) => {
281
+ if (isJWTError(error_, "ERR_JWT_EXPIRED")) throw error_;
282
+ const message = error_ instanceof Error ? error_.message : String(error_);
283
+ throw new Error(`Invalid session token: ${message}`);
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 : emptyData$1()
291
+ };
292
+ }
293
+ function emptySession$1(config) {
294
+ const now = config.jws?.signOptions?.currentDate?.getTime() ?? Date.now();
295
+ const createdAt = now - now % 1e3;
296
+ return {
297
+ id: void 0,
298
+ createdAt,
299
+ expiresAt: config.maxAge === void 0 ? void 0 : createdAt + computeDurationInSeconds(config.maxAge) * 1e3,
300
+ data: emptyData$1(),
301
+ token: void 0
302
+ };
303
+ }
304
+ function emptyData$1() {
305
+ return Object.create(null);
306
+ }
307
+ function readToken$1(context, config) {
308
+ const sessionName = config.name || DEFAULT_NAME$1;
309
+ if (config.sessionHeader !== false) {
310
+ const headerName = typeof config.sessionHeader === "string" ? config.sessionHeader.toLowerCase() : `x-${sessionName.toLowerCase()}-session`;
311
+ const headerValue = context.request.headers.get(headerName);
312
+ if (typeof headerValue === "string") return headerValue.startsWith("Bearer ") ? headerValue.slice(7).trim() : headerValue;
313
+ }
314
+ if (config.cookie !== false) return readChunkedCookie(context.cookie, sessionName);
315
+ }
316
+ function resolveTyp$1(config) {
317
+ const typ = config.jws?.signOptions?.protectedHeader?.typ;
318
+ if (typeof typ === "string" && typ.toLowerCase().includes("jwt")) return typ;
319
+ }
320
+ function getSignKey(key) {
321
+ if (!key) throw new Error("[unjwt/elysia] JWS key is required.");
322
+ let _key;
323
+ if (isSymmetricJWK(key)) _key = key;
324
+ else if ("privateKey" in key && isPrivateJWK(key.privateKey)) _key = key.privateKey;
325
+ if (!_key) throw new Error("[unjwt/elysia] Invalid JWS key. It must be a symmetric JWK or a private JWK.", { cause: key });
326
+ return _key;
327
+ }
328
+ function getVerifyKey(key) {
329
+ if (!key) throw new Error("[unjwt/elysia] JWS key is required.");
330
+ let _key;
331
+ if (isSymmetricJWK(key)) _key = key;
332
+ else if ("publicKey" in key) {
333
+ const publicKey = key.publicKey;
334
+ if (isPublicJWK(publicKey)) _key = publicKey;
335
+ else if (Array.isArray(publicKey)) {
336
+ const keys = publicKey.filter((candidate) => isPublicJWK(candidate));
337
+ if (keys.length > 0) _key = { keys };
338
+ } else if (publicKey && typeof publicKey === "object" && Array.isArray(publicKey.keys)) {
339
+ const keys = publicKey.keys.filter((candidate) => isPublicJWK(candidate));
340
+ if (keys.length > 0) _key = { keys };
341
+ }
342
+ }
343
+ if (!_key) throw new Error("[unjwt/elysia] Invalid JWS key. It must be a symmetric JWK or a public JWK/set.", { cause: key });
344
+ return _key;
345
+ }
346
+ const DEFAULT_NAME = "elysia-jwe";
347
+ const DEFAULT_COOKIE = {
348
+ path: "/",
349
+ secure: true,
350
+ httpOnly: true
351
+ };
352
+ async function createJWESession(context, config) {
353
+ const state = emptySession(config);
354
+ await initFromToken(context, config, state);
355
+ return buildManager(context, config, state);
356
+ }
357
+ function jweSession(config) {
358
+ const contextKey = config.contextKey ?? "session";
359
+ return new Elysia({
360
+ name: "unjwt/elysia-jwe",
361
+ seed: contextKey
362
+ }).resolve({ as: "scoped" }, async ({ cookie, request }) => {
363
+ const session = await createJWESession({
364
+ cookie,
365
+ request
366
+ }, config);
367
+ return { [contextKey]: session };
368
+ }).macro({ [guardName(contextKey)]: { resolve(ctx) {
369
+ if (!ctx[contextKey]?.id) return ctx.status(401, "Unauthorized");
370
+ return {};
371
+ } } });
372
+ }
373
+ function buildManager(context, config, state) {
374
+ const manager = {
375
+ get id() {
376
+ return state.id;
377
+ },
378
+ get createdAt() {
379
+ return state.createdAt;
380
+ },
381
+ get expiresAt() {
382
+ return state.expiresAt;
383
+ },
384
+ get data() {
385
+ return state.data;
386
+ },
387
+ get token() {
388
+ return state.token;
389
+ },
390
+ update: async (update) => {
391
+ await updateSession(context, config, state, update);
392
+ return manager;
393
+ },
394
+ clear: async () => {
395
+ await clearSession(context, config, state);
396
+ return manager;
397
+ }
398
+ };
399
+ return manager;
400
+ }
401
+ async function initFromToken(context, config, state) {
402
+ const token = readToken(context, config);
403
+ if (!token) return;
404
+ state.token = token;
405
+ let exclusiveHookFired = false;
406
+ try {
407
+ const unsealed = await unsealSession(context, config, token);
408
+ Object.assign(state, unsealed);
409
+ } catch (error_) {
410
+ exclusiveHookFired = true;
411
+ if (isJWTError(error_, "ERR_JWT_EXPIRED")) await config.hooks?.onExpire?.({
412
+ session: {
413
+ id: error_.cause.jti,
414
+ createdAt: error_.cause.iat ? error_.cause.iat * 1e3 : void 0,
415
+ expiresAt: error_.cause.exp ? error_.cause.exp * 1e3 : void 0,
416
+ token
417
+ },
418
+ context,
419
+ error: error_,
420
+ config
421
+ });
422
+ else await config.hooks?.onError?.({
423
+ session: state,
424
+ context,
425
+ error: error_,
426
+ config
427
+ });
428
+ }
429
+ if (!exclusiveHookFired && state.id !== void 0 && state.token !== void 0) await config.hooks?.onRead?.({
430
+ session: state,
431
+ context,
432
+ config
433
+ });
434
+ }
435
+ async function updateSession(context, config, state, update) {
436
+ const oldSession = {
437
+ ...state,
438
+ data: sanitizeObjectCopy(state.data)
439
+ };
440
+ const resolvedUpdate = typeof update === "function" ? update(state.data) : update;
441
+ if (resolvedUpdate) Object.assign(state.data, resolvedUpdate);
442
+ const now = config.jwe?.encryptOptions?.currentDate?.getTime() ?? Date.now();
443
+ const createdAt = now - now % 1e3;
444
+ state.id = config.generateId?.() || crypto.randomUUID();
445
+ state.createdAt = createdAt;
446
+ state.expiresAt = config.maxAge === void 0 ? void 0 : createdAt + computeDurationInSeconds(config.maxAge) * 1e3;
447
+ let token;
448
+ try {
449
+ token = await sealSession(config, state);
450
+ } catch (error_) {
451
+ state.id = oldSession.id;
452
+ state.createdAt = oldSession.createdAt;
453
+ state.expiresAt = oldSession.expiresAt;
454
+ state.data = oldSession.data;
455
+ state.token = oldSession.token;
456
+ await config.hooks?.onError?.({
457
+ session: state,
458
+ context,
459
+ error: error_,
460
+ config
461
+ });
462
+ throw error_;
463
+ }
464
+ state.token = token;
465
+ if (config.cookie !== false) {
466
+ const { chunkMaxLength, ...attrs } = {
467
+ ...DEFAULT_COOKIE,
468
+ ...config.cookie
469
+ };
470
+ writeChunkedCookie(context.cookie, config.name || DEFAULT_NAME, token, {
471
+ ...attrs,
472
+ chunkMaxLength,
473
+ expires: config.maxAge === void 0 ? void 0 : new Date(createdAt + computeDurationInSeconds(config.maxAge) * 1e3)
474
+ });
475
+ }
476
+ await config.hooks?.onUpdate?.({
477
+ session: state,
478
+ oldSession,
479
+ context,
480
+ config
481
+ });
482
+ }
483
+ async function clearSession(context, config, state) {
484
+ const oldSession = state.id !== void 0 || state.token !== void 0 ? {
485
+ ...state,
486
+ data: sanitizeObjectCopy(state.data)
487
+ } : void 0;
488
+ const fresh = emptySession(config);
489
+ state.id = fresh.id;
490
+ state.createdAt = fresh.createdAt;
491
+ state.expiresAt = fresh.expiresAt;
492
+ state.data = fresh.data;
493
+ state.token = fresh.token;
494
+ if (config.cookie !== false) {
495
+ const { chunkMaxLength: _chunkMaxLength, ...attrs } = {
496
+ ...DEFAULT_COOKIE,
497
+ ...config.cookie
498
+ };
499
+ removeChunkedCookie(context.cookie, config.name || DEFAULT_NAME, attrs);
500
+ }
501
+ await config.hooks?.onClear?.({
502
+ oldSession,
503
+ context,
504
+ config
505
+ });
506
+ }
507
+ async function sealSession(config, state) {
508
+ const key = getEncryptKey(config.key);
509
+ const iat = Math.floor(state.createdAt / 1e3);
510
+ const exp = state.expiresAt ? Math.floor(state.expiresAt / 1e3) : void 0;
511
+ const payload = {
512
+ ...state.data,
513
+ jti: state.id,
514
+ iat
515
+ };
516
+ if (exp) payload.exp = exp;
517
+ return encrypt(payload, key, {
518
+ ...config.jwe?.encryptOptions,
519
+ expiresIn: void 0,
520
+ protectedHeader: {
521
+ ...config.jwe?.encryptOptions?.protectedHeader,
522
+ typ: resolveTyp(config) || "JWT",
523
+ cty: "application/json"
524
+ }
525
+ });
526
+ }
527
+ async function unsealSession(context, config, sealed) {
528
+ const key = config.hooks?.onUnsealKeyLookup ? (header) => config.hooks.onUnsealKeyLookup({
529
+ header,
530
+ context,
531
+ config
532
+ }) : getDecryptKey(config.key);
533
+ const alg = config.jwe?.encryptOptions?.alg;
534
+ const enc = config.jwe?.encryptOptions?.enc;
535
+ const { payload } = await decrypt(sealed, key, {
536
+ ...config.jwe?.decryptOptions,
537
+ requiredClaims: [.../* @__PURE__ */ new Set([
538
+ ...config.jwe?.decryptOptions?.requiredClaims || [],
539
+ "jti",
540
+ "iat"
541
+ ])],
542
+ typ: resolveTyp(config) || "JWT",
543
+ maxTokenAge: config.maxAge,
544
+ algorithms: alg ? [alg] : void 0,
545
+ encryptionAlgorithms: enc ? [enc] : void 0,
546
+ unwrappedKeyAlgorithm: void 0,
547
+ keyUsage: void 0,
548
+ forceUint8Array: false,
549
+ validateClaims: true
550
+ }).catch((error_) => {
551
+ if (error_ instanceof Error) throw error_;
552
+ throw new Error(String(error_));
553
+ });
554
+ const { jti, iat, exp, ...data } = payload;
555
+ return {
556
+ id: jti,
557
+ createdAt: iat * 1e3,
558
+ expiresAt: exp ? exp * 1e3 : void 0,
559
+ data: data && typeof data === "object" ? data : emptyData()
560
+ };
561
+ }
562
+ function emptySession(config) {
563
+ const now = config.jwe?.encryptOptions?.currentDate?.getTime() ?? Date.now();
564
+ const createdAt = now - now % 1e3;
565
+ return {
566
+ id: void 0,
567
+ createdAt,
568
+ expiresAt: config.maxAge === void 0 ? void 0 : createdAt + computeDurationInSeconds(config.maxAge) * 1e3,
569
+ data: emptyData(),
570
+ token: void 0
571
+ };
572
+ }
573
+ function emptyData() {
574
+ return Object.create(null);
575
+ }
576
+ function readToken(context, config) {
577
+ const sessionName = config.name || DEFAULT_NAME;
578
+ if (config.sessionHeader !== false) {
579
+ const headerName = typeof config.sessionHeader === "string" ? config.sessionHeader.toLowerCase() : `x-${sessionName.toLowerCase()}-session`;
580
+ const headerValue = context.request.headers.get(headerName);
581
+ if (typeof headerValue === "string") return headerValue.startsWith("Bearer ") ? headerValue.slice(7).trim() : headerValue;
582
+ }
583
+ if (config.cookie !== false) return readChunkedCookie(context.cookie, sessionName);
584
+ }
585
+ function resolveTyp(config) {
586
+ const typ = config.jwe?.encryptOptions?.protectedHeader?.typ;
587
+ if (typeof typ === "string" && typ.toLowerCase().includes("jwt")) return typ;
588
+ }
589
+ function getEncryptKey(key) {
590
+ if (!key) throw new Error("[unjwt/elysia] JWE key is required.");
591
+ let _key;
592
+ if (typeof key === "string") _key = key;
593
+ else if (isSymmetricJWK(key)) _key = key;
594
+ else if ("publicKey" in key && key.publicKey && isPublicJWK(key.publicKey)) _key = key.publicKey;
595
+ else if ("privateKey" in key && isPrivateJWK(key.privateKey)) _key = key.privateKey;
596
+ if (!_key) throw new Error("[unjwt/elysia] Invalid JWE key. It must be a password string or valid JWK.", { cause: key });
597
+ return _key;
598
+ }
599
+ function getDecryptKey(key) {
600
+ if (!key) throw new Error("[unjwt/elysia] JWE key is required.");
601
+ let _key;
602
+ if (typeof key === "string") _key = key;
603
+ else if (isSymmetricJWK(key)) _key = key;
604
+ else if ("privateKey" in key) _key = key.privateKey;
605
+ if (!_key) throw new Error("[unjwt/elysia] Invalid JWE key. It must be a password string or a valid private JWK.", { cause: key });
606
+ return _key;
607
+ }
608
+ export { createJWESession, createJWSSession, deriveJWKFromPassword, exportPEM, generateJWK, importPEM, jweSession, jwsSession };