@parsrun/auth 0.1.0

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.
Files changed (54) hide show
  1. package/README.md +133 -0
  2. package/dist/adapters/hono.d.ts +9 -0
  3. package/dist/adapters/hono.js +6 -0
  4. package/dist/adapters/hono.js.map +1 -0
  5. package/dist/adapters/index.d.ts +9 -0
  6. package/dist/adapters/index.js +7 -0
  7. package/dist/adapters/index.js.map +1 -0
  8. package/dist/authorization-By1Xp8Za.d.ts +213 -0
  9. package/dist/base-BKyR8rcE.d.ts +646 -0
  10. package/dist/chunk-42MGHABB.js +263 -0
  11. package/dist/chunk-42MGHABB.js.map +1 -0
  12. package/dist/chunk-7GOBAL4G.js +3 -0
  13. package/dist/chunk-7GOBAL4G.js.map +1 -0
  14. package/dist/chunk-G5I3T73A.js +152 -0
  15. package/dist/chunk-G5I3T73A.js.map +1 -0
  16. package/dist/chunk-IB4WUQDZ.js +410 -0
  17. package/dist/chunk-IB4WUQDZ.js.map +1 -0
  18. package/dist/chunk-MOG4Y6I7.js +415 -0
  19. package/dist/chunk-MOG4Y6I7.js.map +1 -0
  20. package/dist/chunk-NK4TJV2W.js +295 -0
  21. package/dist/chunk-NK4TJV2W.js.map +1 -0
  22. package/dist/chunk-RHNVRCF3.js +838 -0
  23. package/dist/chunk-RHNVRCF3.js.map +1 -0
  24. package/dist/chunk-YTCPXJR5.js +570 -0
  25. package/dist/chunk-YTCPXJR5.js.map +1 -0
  26. package/dist/cloudflare-kv-L64CZKDK.js +105 -0
  27. package/dist/cloudflare-kv-L64CZKDK.js.map +1 -0
  28. package/dist/deno-kv-F55HKKP6.js +111 -0
  29. package/dist/deno-kv-F55HKKP6.js.map +1 -0
  30. package/dist/index-C3kz9XqE.d.ts +226 -0
  31. package/dist/index-DOGcetyD.d.ts +1041 -0
  32. package/dist/index.d.ts +1579 -0
  33. package/dist/index.js +4294 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/jwt-manager-CH8H0kmm.d.ts +182 -0
  36. package/dist/providers/index.d.ts +90 -0
  37. package/dist/providers/index.js +3 -0
  38. package/dist/providers/index.js.map +1 -0
  39. package/dist/providers/otp/index.d.ts +3 -0
  40. package/dist/providers/otp/index.js +4 -0
  41. package/dist/providers/otp/index.js.map +1 -0
  42. package/dist/redis-5TIS6XCA.js +121 -0
  43. package/dist/redis-5TIS6XCA.js.map +1 -0
  44. package/dist/security/index.d.ts +301 -0
  45. package/dist/security/index.js +5 -0
  46. package/dist/security/index.js.map +1 -0
  47. package/dist/session/index.d.ts +117 -0
  48. package/dist/session/index.js +4 -0
  49. package/dist/session/index.js.map +1 -0
  50. package/dist/storage/index.d.ts +97 -0
  51. package/dist/storage/index.js +3 -0
  52. package/dist/storage/index.js.map +1 -0
  53. package/dist/types-DSjafxJ4.d.ts +193 -0
  54. package/package.json +102 -0
@@ -0,0 +1,415 @@
1
+ import { StorageKeys } from './chunk-42MGHABB.js';
2
+ import * as jose from 'jose';
3
+
4
+ function parseDuration(duration) {
5
+ const match = duration.match(/^(\d+)(s|m|h|d|w)$/);
6
+ if (!match) {
7
+ throw new Error(`Invalid duration format: ${duration}. Use format like '15m', '1h', '7d'`);
8
+ }
9
+ const value = parseInt(match[1], 10);
10
+ const unit = match[2];
11
+ switch (unit) {
12
+ case "s":
13
+ return value;
14
+ case "m":
15
+ return value * 60;
16
+ case "h":
17
+ return value * 60 * 60;
18
+ case "d":
19
+ return value * 60 * 60 * 24;
20
+ case "w":
21
+ return value * 60 * 60 * 24 * 7;
22
+ default:
23
+ throw new Error(`Unknown duration unit: ${unit}`);
24
+ }
25
+ }
26
+ var DEFAULT_CONFIG = {
27
+ accessTokenTTL: "15m",
28
+ refreshTokenTTL: "7d",
29
+ keyVersion: 1
30
+ };
31
+ var JwtManager = class {
32
+ secret;
33
+ previousSecrets;
34
+ config;
35
+ keyVersion;
36
+ constructor(config) {
37
+ this.config = {
38
+ accessTokenTTL: DEFAULT_CONFIG.accessTokenTTL,
39
+ refreshTokenTTL: DEFAULT_CONFIG.refreshTokenTTL,
40
+ previousSecrets: [],
41
+ keyVersion: DEFAULT_CONFIG.keyVersion,
42
+ ...config
43
+ };
44
+ this.secret = new TextEncoder().encode(config.secret);
45
+ this.keyVersion = this.config.keyVersion;
46
+ this.previousSecrets = this.config.previousSecrets.map(
47
+ (s) => new TextEncoder().encode(s)
48
+ );
49
+ }
50
+ /**
51
+ * Get current key version
52
+ */
53
+ getKeyVersion() {
54
+ return this.keyVersion;
55
+ }
56
+ /**
57
+ * Rotate the signing key
58
+ * Moves current secret to previousSecrets and sets new secret
59
+ */
60
+ rotateKey(newSecret, options) {
61
+ const maxPrevious = options?.maxPreviousSecrets ?? 2;
62
+ const previousSecret = this.config.secret;
63
+ this.previousSecrets.unshift(this.secret);
64
+ if (this.previousSecrets.length > maxPrevious) {
65
+ this.previousSecrets = this.previousSecrets.slice(0, maxPrevious);
66
+ }
67
+ this.secret = new TextEncoder().encode(newSecret);
68
+ this.config.secret = newSecret;
69
+ this.keyVersion++;
70
+ this.config.previousSecrets = [
71
+ previousSecret,
72
+ ...this.config.previousSecrets.slice(0, maxPrevious - 1)
73
+ ];
74
+ this.config.keyVersion = this.keyVersion;
75
+ return {
76
+ previousSecret,
77
+ newSecret,
78
+ keyVersion: this.keyVersion,
79
+ rotatedAt: /* @__PURE__ */ new Date()
80
+ };
81
+ }
82
+ /**
83
+ * Get current configuration (for persistence)
84
+ */
85
+ getConfig() {
86
+ return { ...this.config };
87
+ }
88
+ /**
89
+ * Generate access token
90
+ */
91
+ async generateAccessToken(payload) {
92
+ const ttlSeconds = parseDuration(this.config.accessTokenTTL);
93
+ const expiresAt = new Date(Date.now() + ttlSeconds * 1e3);
94
+ const jwt = new jose.SignJWT({
95
+ sub: payload.userId,
96
+ ...payload.tenantId && { tid: payload.tenantId },
97
+ ...payload.sessionId && { sid: payload.sessionId },
98
+ roles: payload.roles ?? [],
99
+ permissions: payload.permissions ?? [],
100
+ ...payload.claims
101
+ }).setProtectedHeader({ alg: "HS256", kid: `v${this.keyVersion}` }).setIssuedAt().setExpirationTime(expiresAt).setIssuer(this.config.issuer).setAudience(this.config.audience);
102
+ const token = await jwt.sign(this.secret);
103
+ return { token, expiresAt };
104
+ }
105
+ /**
106
+ * Generate refresh token
107
+ */
108
+ async generateRefreshToken(payload) {
109
+ const ttlSeconds = parseDuration(this.config.refreshTokenTTL);
110
+ const expiresAt = new Date(Date.now() + ttlSeconds * 1e3);
111
+ const jwt = new jose.SignJWT({
112
+ sub: payload.userId,
113
+ ...payload.tenantId && { tid: payload.tenantId },
114
+ ...payload.sessionId && { sid: payload.sessionId },
115
+ type: "refresh"
116
+ }).setProtectedHeader({ alg: "HS256", kid: `v${this.keyVersion}` }).setIssuedAt().setExpirationTime(expiresAt).setIssuer(this.config.issuer).setAudience(this.config.audience);
117
+ const token = await jwt.sign(this.secret);
118
+ return { token, expiresAt };
119
+ }
120
+ /**
121
+ * Generate token pair (access + refresh)
122
+ */
123
+ async generateTokenPair(payload) {
124
+ const [access, refresh] = await Promise.all([
125
+ this.generateAccessToken(payload),
126
+ this.generateRefreshToken(payload)
127
+ ]);
128
+ return {
129
+ accessToken: access.token,
130
+ refreshToken: refresh.token,
131
+ accessExpiresAt: access.expiresAt,
132
+ refreshExpiresAt: refresh.expiresAt
133
+ };
134
+ }
135
+ /**
136
+ * Verify access token
137
+ * Tries current secret first, then falls back to previous secrets for graceful rotation
138
+ */
139
+ async verifyAccessToken(token) {
140
+ const secrets = [this.secret, ...this.previousSecrets];
141
+ for (let i = 0; i < secrets.length; i++) {
142
+ const secret = secrets[i];
143
+ if (!secret) continue;
144
+ try {
145
+ const { payload } = await jose.jwtVerify(token, secret, {
146
+ issuer: this.config.issuer,
147
+ audience: this.config.audience
148
+ });
149
+ return {
150
+ sub: payload.sub,
151
+ tid: payload["tid"],
152
+ sid: payload["sid"],
153
+ iat: payload.iat,
154
+ exp: payload.exp,
155
+ iss: payload.iss,
156
+ aud: payload.aud,
157
+ roles: payload["roles"] ?? [],
158
+ permissions: payload["permissions"] ?? []
159
+ };
160
+ } catch (error) {
161
+ if (error instanceof jose.errors.JWTExpired) {
162
+ throw new JwtError("Access token expired", "TOKEN_EXPIRED");
163
+ }
164
+ if (i === secrets.length - 1) {
165
+ if (error instanceof jose.errors.JWTInvalid) {
166
+ throw new JwtError("Invalid access token", "INVALID_TOKEN");
167
+ }
168
+ throw new JwtError("Token verification failed", "VERIFICATION_FAILED");
169
+ }
170
+ }
171
+ }
172
+ throw new JwtError("Token verification failed", "VERIFICATION_FAILED");
173
+ }
174
+ /**
175
+ * Verify refresh token
176
+ */
177
+ async verifyRefreshToken(token) {
178
+ const secrets = [this.secret, ...this.previousSecrets];
179
+ for (let i = 0; i < secrets.length; i++) {
180
+ const secret = secrets[i];
181
+ if (!secret) continue;
182
+ try {
183
+ const { payload } = await jose.jwtVerify(token, secret, {
184
+ issuer: this.config.issuer,
185
+ audience: this.config.audience
186
+ });
187
+ if (payload["type"] !== "refresh") {
188
+ throw new JwtError("Invalid token type", "INVALID_TOKEN_TYPE");
189
+ }
190
+ return {
191
+ userId: payload.sub,
192
+ tenantId: payload["tid"],
193
+ sessionId: payload["sid"]
194
+ };
195
+ } catch (error) {
196
+ if (error instanceof jose.errors.JWTExpired) {
197
+ throw new JwtError("Refresh token expired", "TOKEN_EXPIRED");
198
+ }
199
+ if (error instanceof JwtError) {
200
+ throw error;
201
+ }
202
+ if (i === secrets.length - 1) {
203
+ if (error instanceof jose.errors.JWTInvalid) {
204
+ throw new JwtError("Invalid refresh token", "INVALID_TOKEN");
205
+ }
206
+ throw new JwtError("Token verification failed", "VERIFICATION_FAILED");
207
+ }
208
+ }
209
+ }
210
+ throw new JwtError("Token verification failed", "VERIFICATION_FAILED");
211
+ }
212
+ /**
213
+ * Decode token without verification (for inspection)
214
+ */
215
+ decodeToken(token) {
216
+ try {
217
+ return jose.decodeJwt(token);
218
+ } catch {
219
+ return null;
220
+ }
221
+ }
222
+ /**
223
+ * Check if token is expired (without signature verification)
224
+ */
225
+ isTokenExpired(token) {
226
+ const payload = this.decodeToken(token);
227
+ if (!payload?.exp) return true;
228
+ return payload.exp * 1e3 < Date.now();
229
+ }
230
+ /**
231
+ * Get token expiration date (without signature verification)
232
+ */
233
+ getTokenExpiration(token) {
234
+ const payload = this.decodeToken(token);
235
+ if (!payload?.exp) return null;
236
+ return new Date(payload.exp * 1e3);
237
+ }
238
+ /**
239
+ * Get time until token expires in seconds
240
+ */
241
+ getTokenTTL(token) {
242
+ const exp = this.getTokenExpiration(token);
243
+ if (!exp) return 0;
244
+ return Math.max(0, Math.floor((exp.getTime() - Date.now()) / 1e3));
245
+ }
246
+ };
247
+ var JwtError = class extends Error {
248
+ constructor(message, code) {
249
+ super(message);
250
+ this.code = code;
251
+ this.name = "JwtError";
252
+ }
253
+ };
254
+ function extractBearerToken(authHeader) {
255
+ if (!authHeader) return null;
256
+ const parts = authHeader.split(" ");
257
+ if (parts.length !== 2 || parts[0] !== "Bearer") return null;
258
+ return parts[1] ?? null;
259
+ }
260
+ function createJwtManager(config) {
261
+ return new JwtManager(config);
262
+ }
263
+
264
+ // src/session/blocklist.ts
265
+ var SessionBlocklist = class {
266
+ storage;
267
+ constructor(storage, _config) {
268
+ this.storage = storage;
269
+ }
270
+ /**
271
+ * Get storage key for blocklist entry
272
+ */
273
+ getKey(sessionId) {
274
+ return StorageKeys.blocklist(sessionId);
275
+ }
276
+ /**
277
+ * Block a session (revoke it)
278
+ */
279
+ async blockSession(sessionId, expiresAt, options) {
280
+ const ttlMs = expiresAt.getTime() - Date.now();
281
+ if (ttlMs <= 0) return;
282
+ const ttlSeconds = Math.ceil(ttlMs / 1e3);
283
+ const key = this.getKey(sessionId);
284
+ const entry = {
285
+ id: sessionId,
286
+ expiresAt: expiresAt.toISOString(),
287
+ reason: options?.reason,
288
+ blockedAt: (/* @__PURE__ */ new Date()).toISOString(),
289
+ userId: options?.userId
290
+ };
291
+ await this.storage.set(key, entry, ttlSeconds);
292
+ }
293
+ /**
294
+ * Check if a session is blocked
295
+ */
296
+ async isBlocked(sessionId) {
297
+ const key = this.getKey(sessionId);
298
+ const entry = await this.storage.get(key);
299
+ if (!entry) return false;
300
+ if (new Date(entry.expiresAt) < /* @__PURE__ */ new Date()) {
301
+ await this.storage.delete(key);
302
+ return false;
303
+ }
304
+ return true;
305
+ }
306
+ /**
307
+ * Get blocklist entry details
308
+ */
309
+ async getEntry(sessionId) {
310
+ const key = this.getKey(sessionId);
311
+ return this.storage.get(key);
312
+ }
313
+ /**
314
+ * Unblock a session (if needed)
315
+ */
316
+ async unblockSession(sessionId) {
317
+ const key = this.getKey(sessionId);
318
+ await this.storage.delete(key);
319
+ }
320
+ /**
321
+ * Block multiple sessions (logout all devices)
322
+ */
323
+ async blockMultipleSessions(sessions) {
324
+ await Promise.all(
325
+ sessions.map(
326
+ (s) => this.blockSession(s.id, s.expiresAt, {
327
+ reason: s.reason,
328
+ userId: s.userId
329
+ })
330
+ )
331
+ );
332
+ }
333
+ /**
334
+ * Block all sessions for a user
335
+ * Requires session IDs to be provided (from session store)
336
+ */
337
+ async blockAllUserSessions(userId, sessionIds, tokenExpiresAt, reason = "User logout all") {
338
+ await this.blockMultipleSessions(
339
+ sessionIds.map((id) => ({
340
+ id,
341
+ expiresAt: tokenExpiresAt,
342
+ reason,
343
+ userId
344
+ }))
345
+ );
346
+ }
347
+ };
348
+ var TokenBlocklist = class {
349
+ storage;
350
+ constructor(storage) {
351
+ this.storage = storage;
352
+ }
353
+ /**
354
+ * Get storage key for token blocklist entry
355
+ */
356
+ getKey(tokenHash) {
357
+ return `blocklist:token:${tokenHash}`;
358
+ }
359
+ /**
360
+ * Hash a token for storage (don't store raw tokens)
361
+ */
362
+ async hashToken(token) {
363
+ const encoder = new TextEncoder();
364
+ const data = encoder.encode(token);
365
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
366
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
367
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
368
+ }
369
+ /**
370
+ * Block a token
371
+ */
372
+ async blockToken(token, expiresAt, reason) {
373
+ const ttlMs = expiresAt.getTime() - Date.now();
374
+ if (ttlMs <= 0) return;
375
+ const ttlSeconds = Math.ceil(ttlMs / 1e3);
376
+ const hash = await this.hashToken(token);
377
+ const key = this.getKey(hash);
378
+ await this.storage.set(
379
+ key,
380
+ {
381
+ hash,
382
+ expiresAt: expiresAt.toISOString(),
383
+ reason,
384
+ blockedAt: (/* @__PURE__ */ new Date()).toISOString()
385
+ },
386
+ ttlSeconds
387
+ );
388
+ }
389
+ /**
390
+ * Check if a token is blocked
391
+ */
392
+ async isBlocked(token) {
393
+ const hash = await this.hashToken(token);
394
+ const key = this.getKey(hash);
395
+ return this.storage.has(key);
396
+ }
397
+ /**
398
+ * Unblock a token
399
+ */
400
+ async unblockToken(token) {
401
+ const hash = await this.hashToken(token);
402
+ const key = this.getKey(hash);
403
+ await this.storage.delete(key);
404
+ }
405
+ };
406
+ function createSessionBlocklist(storage, config) {
407
+ return new SessionBlocklist(storage, config);
408
+ }
409
+ function createTokenBlocklist(storage) {
410
+ return new TokenBlocklist(storage);
411
+ }
412
+
413
+ export { JwtError, JwtManager, SessionBlocklist, TokenBlocklist, createJwtManager, createSessionBlocklist, createTokenBlocklist, extractBearerToken, parseDuration };
414
+ //# sourceMappingURL=chunk-MOG4Y6I7.js.map
415
+ //# sourceMappingURL=chunk-MOG4Y6I7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/session/jwt-manager.ts","../src/session/blocklist.ts"],"names":[],"mappings":"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","file":"chunk-MOG4Y6I7.js","sourcesContent":["/**\n * JWT Manager with Key Rotation Support\n * Uses jose library for multi-runtime compatibility (Node, Deno, CF Workers, Bun)\n */\n\nimport * as jose from 'jose';\n\n/**\n * JWT configuration\n */\nexport interface JwtConfig {\n /** Secret key for signing tokens */\n secret: string;\n /** Token issuer */\n issuer: string;\n /** Token audience */\n audience: string;\n /** Access token TTL (e.g., '15m', '1h') */\n accessTokenTTL?: string;\n /** Refresh token TTL (e.g., '7d', '12h') */\n refreshTokenTTL?: string;\n /** Previous secrets for key rotation */\n previousSecrets?: string[];\n /** Current key version */\n keyVersion?: number;\n}\n\n/**\n * JWT payload structure\n */\nexport interface JwtPayload {\n /** User ID */\n sub: string;\n /** Tenant ID */\n tid?: string;\n /** Session ID */\n sid?: string;\n /** Issued at timestamp */\n iat: number;\n /** Expiration timestamp */\n exp: number;\n /** Issuer */\n iss: string;\n /** Audience */\n aud: string | string[];\n /** User roles */\n roles?: string[];\n /** User permissions */\n permissions?: string[];\n /** Additional claims */\n [key: string]: unknown;\n}\n\n/**\n * Token pair (access + refresh)\n */\nexport interface TokenPair {\n accessToken: string;\n refreshToken: string;\n accessExpiresAt: Date;\n refreshExpiresAt: Date;\n}\n\n/**\n * Key rotation result\n */\nexport interface KeyRotationResult {\n previousSecret: string;\n newSecret: string;\n keyVersion: number;\n rotatedAt: Date;\n}\n\n/**\n * Parse duration string to seconds\n * Supports: s (seconds), m (minutes), h (hours), d (days), w (weeks)\n */\nexport function parseDuration(duration: string): number {\n const match = duration.match(/^(\\d+)(s|m|h|d|w)$/);\n if (!match) {\n throw new Error(`Invalid duration format: ${duration}. Use format like '15m', '1h', '7d'`);\n }\n\n const value = parseInt(match[1]!, 10);\n const unit = match[2];\n\n switch (unit) {\n case 's':\n return value;\n case 'm':\n return value * 60;\n case 'h':\n return value * 60 * 60;\n case 'd':\n return value * 60 * 60 * 24;\n case 'w':\n return value * 60 * 60 * 24 * 7;\n default:\n throw new Error(`Unknown duration unit: ${unit}`);\n }\n}\n\n/**\n * Default JWT configuration\n */\nconst DEFAULT_CONFIG = {\n accessTokenTTL: '15m',\n refreshTokenTTL: '7d',\n keyVersion: 1,\n};\n\n/**\n * JWT Manager\n * Handles token generation, verification, and key rotation\n */\nexport class JwtManager {\n private secret: Uint8Array;\n private previousSecrets: Uint8Array[];\n private config: Required<JwtConfig>;\n private keyVersion: number;\n\n constructor(config: JwtConfig) {\n this.config = {\n accessTokenTTL: DEFAULT_CONFIG.accessTokenTTL,\n refreshTokenTTL: DEFAULT_CONFIG.refreshTokenTTL,\n previousSecrets: [],\n keyVersion: DEFAULT_CONFIG.keyVersion,\n ...config,\n } as Required<JwtConfig>;\n\n this.secret = new TextEncoder().encode(config.secret);\n this.keyVersion = this.config.keyVersion;\n this.previousSecrets = (this.config.previousSecrets).map(\n (s) => new TextEncoder().encode(s)\n );\n }\n\n /**\n * Get current key version\n */\n getKeyVersion(): number {\n return this.keyVersion;\n }\n\n /**\n * Rotate the signing key\n * Moves current secret to previousSecrets and sets new secret\n */\n rotateKey(\n newSecret: string,\n options?: { maxPreviousSecrets?: number }\n ): KeyRotationResult {\n const maxPrevious = options?.maxPreviousSecrets ?? 2;\n const previousSecret = this.config.secret;\n\n // Move current secret to previous secrets\n this.previousSecrets.unshift(this.secret);\n\n // Limit the number of previous secrets\n if (this.previousSecrets.length > maxPrevious) {\n this.previousSecrets = this.previousSecrets.slice(0, maxPrevious);\n }\n\n // Set new secret\n this.secret = new TextEncoder().encode(newSecret);\n this.config.secret = newSecret;\n this.keyVersion++;\n\n // Update config's previous secrets\n this.config.previousSecrets = [\n previousSecret,\n ...this.config.previousSecrets.slice(0, maxPrevious - 1),\n ];\n this.config.keyVersion = this.keyVersion;\n\n return {\n previousSecret,\n newSecret,\n keyVersion: this.keyVersion,\n rotatedAt: new Date(),\n };\n }\n\n /**\n * Get current configuration (for persistence)\n */\n getConfig(): JwtConfig {\n return { ...this.config };\n }\n\n /**\n * Generate access token\n */\n async generateAccessToken(payload: {\n userId: string;\n tenantId?: string;\n sessionId?: string;\n roles?: string[];\n permissions?: string[];\n claims?: Record<string, unknown>;\n }): Promise<{ token: string; expiresAt: Date }> {\n const ttlSeconds = parseDuration(this.config.accessTokenTTL);\n const expiresAt = new Date(Date.now() + ttlSeconds * 1000);\n\n const jwt = new jose.SignJWT({\n sub: payload.userId,\n ...(payload.tenantId && { tid: payload.tenantId }),\n ...(payload.sessionId && { sid: payload.sessionId }),\n roles: payload.roles ?? [],\n permissions: payload.permissions ?? [],\n ...payload.claims,\n })\n .setProtectedHeader({ alg: 'HS256', kid: `v${this.keyVersion}` })\n .setIssuedAt()\n .setExpirationTime(expiresAt)\n .setIssuer(this.config.issuer)\n .setAudience(this.config.audience);\n\n const token = await jwt.sign(this.secret);\n\n return { token, expiresAt };\n }\n\n /**\n * Generate refresh token\n */\n async generateRefreshToken(payload: {\n userId: string;\n tenantId?: string;\n sessionId?: string;\n }): Promise<{ token: string; expiresAt: Date }> {\n const ttlSeconds = parseDuration(this.config.refreshTokenTTL);\n const expiresAt = new Date(Date.now() + ttlSeconds * 1000);\n\n const jwt = new jose.SignJWT({\n sub: payload.userId,\n ...(payload.tenantId && { tid: payload.tenantId }),\n ...(payload.sessionId && { sid: payload.sessionId }),\n type: 'refresh',\n })\n .setProtectedHeader({ alg: 'HS256', kid: `v${this.keyVersion}` })\n .setIssuedAt()\n .setExpirationTime(expiresAt)\n .setIssuer(this.config.issuer)\n .setAudience(this.config.audience);\n\n const token = await jwt.sign(this.secret);\n\n return { token, expiresAt };\n }\n\n /**\n * Generate token pair (access + refresh)\n */\n async generateTokenPair(payload: {\n userId: string;\n tenantId?: string;\n sessionId?: string;\n roles?: string[];\n permissions?: string[];\n claims?: Record<string, unknown>;\n }): Promise<TokenPair> {\n const [access, refresh] = await Promise.all([\n this.generateAccessToken(payload),\n this.generateRefreshToken(payload),\n ]);\n\n return {\n accessToken: access.token,\n refreshToken: refresh.token,\n accessExpiresAt: access.expiresAt,\n refreshExpiresAt: refresh.expiresAt,\n };\n }\n\n /**\n * Verify access token\n * Tries current secret first, then falls back to previous secrets for graceful rotation\n */\n async verifyAccessToken(token: string): Promise<JwtPayload> {\n const secrets = [this.secret, ...this.previousSecrets];\n\n for (let i = 0; i < secrets.length; i++) {\n const secret = secrets[i];\n if (!secret) continue;\n\n try {\n const { payload } = await jose.jwtVerify(token, secret, {\n issuer: this.config.issuer,\n audience: this.config.audience,\n });\n\n return {\n sub: payload.sub as string,\n tid: payload['tid'] as string | undefined,\n sid: payload['sid'] as string | undefined,\n iat: payload.iat as number,\n exp: payload.exp as number,\n iss: payload.iss as string,\n aud: payload.aud as string | string[],\n roles: (payload['roles'] as string[]) ?? [],\n permissions: (payload['permissions'] as string[]) ?? [],\n };\n } catch (error) {\n // If it's an expiration error, don't try other secrets\n if (error instanceof jose.errors.JWTExpired) {\n throw new JwtError('Access token expired', 'TOKEN_EXPIRED');\n }\n // Try next secret on signature mismatch\n if (i === secrets.length - 1) {\n if (error instanceof jose.errors.JWTInvalid) {\n throw new JwtError('Invalid access token', 'INVALID_TOKEN');\n }\n throw new JwtError('Token verification failed', 'VERIFICATION_FAILED');\n }\n }\n }\n\n throw new JwtError('Token verification failed', 'VERIFICATION_FAILED');\n }\n\n /**\n * Verify refresh token\n */\n async verifyRefreshToken(token: string): Promise<{\n userId: string;\n tenantId?: string;\n sessionId?: string;\n }> {\n const secrets = [this.secret, ...this.previousSecrets];\n\n for (let i = 0; i < secrets.length; i++) {\n const secret = secrets[i];\n if (!secret) continue;\n\n try {\n const { payload } = await jose.jwtVerify(token, secret, {\n issuer: this.config.issuer,\n audience: this.config.audience,\n });\n\n if (payload['type'] !== 'refresh') {\n throw new JwtError('Invalid token type', 'INVALID_TOKEN_TYPE');\n }\n\n return {\n userId: payload.sub as string,\n tenantId: payload['tid'] as string | undefined,\n sessionId: payload['sid'] as string | undefined,\n };\n } catch (error) {\n if (error instanceof jose.errors.JWTExpired) {\n throw new JwtError('Refresh token expired', 'TOKEN_EXPIRED');\n }\n if (error instanceof JwtError) {\n throw error;\n }\n if (i === secrets.length - 1) {\n if (error instanceof jose.errors.JWTInvalid) {\n throw new JwtError('Invalid refresh token', 'INVALID_TOKEN');\n }\n throw new JwtError('Token verification failed', 'VERIFICATION_FAILED');\n }\n }\n }\n\n throw new JwtError('Token verification failed', 'VERIFICATION_FAILED');\n }\n\n /**\n * Decode token without verification (for inspection)\n */\n decodeToken(token: string): jose.JWTPayload | null {\n try {\n return jose.decodeJwt(token);\n } catch {\n return null;\n }\n }\n\n /**\n * Check if token is expired (without signature verification)\n */\n isTokenExpired(token: string): boolean {\n const payload = this.decodeToken(token);\n if (!payload?.exp) return true;\n return payload.exp * 1000 < Date.now();\n }\n\n /**\n * Get token expiration date (without signature verification)\n */\n getTokenExpiration(token: string): Date | null {\n const payload = this.decodeToken(token);\n if (!payload?.exp) return null;\n return new Date(payload.exp * 1000);\n }\n\n /**\n * Get time until token expires in seconds\n */\n getTokenTTL(token: string): number {\n const exp = this.getTokenExpiration(token);\n if (!exp) return 0;\n return Math.max(0, Math.floor((exp.getTime() - Date.now()) / 1000));\n }\n}\n\n/**\n * JWT Error class\n */\nexport class JwtError extends Error {\n constructor(\n message: string,\n public readonly code:\n | 'TOKEN_EXPIRED'\n | 'INVALID_TOKEN'\n | 'INVALID_TOKEN_TYPE'\n | 'VERIFICATION_FAILED'\n ) {\n super(message);\n this.name = 'JwtError';\n }\n}\n\n/**\n * Extract token from Authorization header\n */\nexport function extractBearerToken(\n authHeader: string | null | undefined\n): string | null {\n if (!authHeader) return null;\n const parts = authHeader.split(' ');\n if (parts.length !== 2 || parts[0] !== 'Bearer') return null;\n return parts[1] ?? null;\n}\n\n/**\n * Create a JwtManager instance\n */\nexport function createJwtManager(config: JwtConfig): JwtManager {\n return new JwtManager(config);\n}\n","/**\n * Session Blocklist\n * Uses KVStorage interface for multi-runtime support\n * Supports token/session revocation for logout\n */\n\nimport type { KVStorage } from '../storage/types.js';\nimport { StorageKeys } from '../storage/index.js';\n\n/**\n * Blocklist configuration\n */\nexport interface BlocklistConfig {\n /** Key prefix for blocked sessions */\n prefix?: string;\n /** Default TTL in seconds (should match max token lifetime) */\n defaultTTL?: number;\n}\n\n/**\n * Blocklist entry\n */\ninterface BlocklistEntry {\n /** Session or token ID */\n id: string;\n /** When the entry expires */\n expiresAt: string;\n /** Why was it blocked */\n reason?: string;\n /** When it was blocked */\n blockedAt: string;\n /** User ID (for audit) */\n userId?: string;\n}\n\n/**\n * Session Blocklist\n * Manages blocked/revoked sessions and tokens\n */\nexport class SessionBlocklist {\n private storage: KVStorage;\n\n constructor(storage: KVStorage, _config?: BlocklistConfig) {\n this.storage = storage;\n // Config is reserved for future use (custom prefix, TTL settings)\n }\n\n /**\n * Get storage key for blocklist entry\n */\n private getKey(sessionId: string): string {\n return StorageKeys.blocklist(sessionId);\n }\n\n /**\n * Block a session (revoke it)\n */\n async blockSession(\n sessionId: string,\n expiresAt: Date,\n options?: { reason?: string; userId?: string }\n ): Promise<void> {\n const ttlMs = expiresAt.getTime() - Date.now();\n if (ttlMs <= 0) return; // Already expired, no need to block\n\n const ttlSeconds = Math.ceil(ttlMs / 1000);\n const key = this.getKey(sessionId);\n\n const entry: BlocklistEntry = {\n id: sessionId,\n expiresAt: expiresAt.toISOString(),\n reason: options?.reason,\n blockedAt: new Date().toISOString(),\n userId: options?.userId,\n };\n\n await this.storage.set(key, entry, ttlSeconds);\n }\n\n /**\n * Check if a session is blocked\n */\n async isBlocked(sessionId: string): Promise<boolean> {\n const key = this.getKey(sessionId);\n const entry = await this.storage.get<BlocklistEntry>(key);\n\n if (!entry) return false;\n\n // Double-check expiration (storage should handle this, but be safe)\n if (new Date(entry.expiresAt) < new Date()) {\n await this.storage.delete(key);\n return false;\n }\n\n return true;\n }\n\n /**\n * Get blocklist entry details\n */\n async getEntry(sessionId: string): Promise<BlocklistEntry | null> {\n const key = this.getKey(sessionId);\n return this.storage.get<BlocklistEntry>(key);\n }\n\n /**\n * Unblock a session (if needed)\n */\n async unblockSession(sessionId: string): Promise<void> {\n const key = this.getKey(sessionId);\n await this.storage.delete(key);\n }\n\n /**\n * Block multiple sessions (logout all devices)\n */\n async blockMultipleSessions(\n sessions: Array<{\n id: string;\n expiresAt: Date;\n reason?: string;\n userId?: string;\n }>\n ): Promise<void> {\n await Promise.all(\n sessions.map((s) =>\n this.blockSession(s.id, s.expiresAt, {\n reason: s.reason,\n userId: s.userId,\n })\n )\n );\n }\n\n /**\n * Block all sessions for a user\n * Requires session IDs to be provided (from session store)\n */\n async blockAllUserSessions(\n userId: string,\n sessionIds: string[],\n tokenExpiresAt: Date,\n reason = 'User logout all'\n ): Promise<void> {\n await this.blockMultipleSessions(\n sessionIds.map((id) => ({\n id,\n expiresAt: tokenExpiresAt,\n reason,\n userId,\n }))\n );\n }\n}\n\n/**\n * Token Blocklist\n * For revoking individual tokens (separate from sessions)\n */\nexport class TokenBlocklist {\n private storage: KVStorage;\n\n constructor(storage: KVStorage) {\n this.storage = storage;\n }\n\n /**\n * Get storage key for token blocklist entry\n */\n private getKey(tokenHash: string): string {\n return `blocklist:token:${tokenHash}`;\n }\n\n /**\n * Hash a token for storage (don't store raw tokens)\n */\n private async hashToken(token: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(token);\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');\n }\n\n /**\n * Block a token\n */\n async blockToken(token: string, expiresAt: Date, reason?: string): Promise<void> {\n const ttlMs = expiresAt.getTime() - Date.now();\n if (ttlMs <= 0) return;\n\n const ttlSeconds = Math.ceil(ttlMs / 1000);\n const hash = await this.hashToken(token);\n const key = this.getKey(hash);\n\n await this.storage.set(\n key,\n {\n hash,\n expiresAt: expiresAt.toISOString(),\n reason,\n blockedAt: new Date().toISOString(),\n },\n ttlSeconds\n );\n }\n\n /**\n * Check if a token is blocked\n */\n async isBlocked(token: string): Promise<boolean> {\n const hash = await this.hashToken(token);\n const key = this.getKey(hash);\n return this.storage.has(key);\n }\n\n /**\n * Unblock a token\n */\n async unblockToken(token: string): Promise<void> {\n const hash = await this.hashToken(token);\n const key = this.getKey(hash);\n await this.storage.delete(key);\n }\n}\n\n/**\n * Create session blocklist\n */\nexport function createSessionBlocklist(\n storage: KVStorage,\n config?: BlocklistConfig\n): SessionBlocklist {\n return new SessionBlocklist(storage, config);\n}\n\n/**\n * Create token blocklist\n */\nexport function createTokenBlocklist(storage: KVStorage): TokenBlocklist {\n return new TokenBlocklist(storage);\n}\n"]}