@plyaz/auth 1.0.0 → 1.0.2

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 (99) hide show
  1. package/commits.txt +3 -3
  2. package/dist/common/index.cjs +3 -1
  3. package/dist/common/index.cjs.map +1 -1
  4. package/dist/common/index.mjs +3 -1
  5. package/dist/common/index.mjs.map +1 -1
  6. package/dist/index.cjs +424 -154
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.mjs +421 -152
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +2 -1
  11. package/release_message.txt +28 -0
  12. package/src/adapters/auth-adapter-factory.ts +4 -3
  13. package/src/adapters/auth-adapter.mapper.ts +2 -2
  14. package/src/adapters/base-auth.adapter.ts +17 -9
  15. package/src/adapters/clerk/clerk.adapter.ts +9 -12
  16. package/src/adapters/custom/custom.adapter.ts +19 -10
  17. package/src/adapters/index.ts +0 -1
  18. package/src/adapters/next-auth/authOptions.ts +20 -16
  19. package/src/adapters/next-auth/next-auth.adapter.ts +13 -15
  20. package/src/api/client.ts +4 -6
  21. package/src/audit/audit.logger.ts +19 -10
  22. package/src/client/components/ProtectedRoute.tsx +15 -11
  23. package/src/client/hooks/useAuth.ts +23 -21
  24. package/src/client/hooks/useConnectedAccounts.ts +57 -45
  25. package/src/client/hooks/usePermissions.ts +1 -1
  26. package/src/client/hooks/useRBAC.ts +6 -6
  27. package/src/client/hooks/useSession.ts +5 -5
  28. package/src/client/providers/AuthProvider.tsx +23 -17
  29. package/src/client/store/auth.store.ts +71 -62
  30. package/src/client/utils/storage.ts +45 -18
  31. package/src/common/constants/oauth-providers.ts +10 -7
  32. package/src/common/errors/auth.errors.ts +4 -4
  33. package/src/common/errors/specific-auth-errors.ts +5 -9
  34. package/src/common/regex/index.ts +6 -4
  35. package/src/common/types/auth.types.ts +47 -38
  36. package/src/common/types/index.ts +12 -6
  37. package/src/common/utils/index.ts +15 -11
  38. package/src/core/blacklist/token.blacklist.ts +13 -7
  39. package/src/core/index.ts +2 -2
  40. package/src/core/jwt/jwt.manager.ts +47 -22
  41. package/src/core/session/session.manager.ts +17 -14
  42. package/src/db/repositories/connected-account.repository.ts +120 -78
  43. package/src/db/repositories/role.repository.ts +41 -26
  44. package/src/db/repositories/session.repository.ts +9 -10
  45. package/src/db/repositories/user.repository.ts +105 -91
  46. package/src/flows/index.ts +2 -2
  47. package/src/flows/sign-in.flow.ts +28 -14
  48. package/src/flows/sign-up.flow.ts +31 -20
  49. package/src/index.ts +36 -37
  50. package/src/libs/clerk.helper.ts +6 -7
  51. package/src/libs/supabase.helper.ts +79 -61
  52. package/src/libs/supabaseClient.ts +3 -3
  53. package/src/providers/base/auth-provider.interface.ts +13 -11
  54. package/src/providers/base/index.ts +1 -1
  55. package/src/providers/index.ts +1 -1
  56. package/src/providers/oauth/facebook.provider.ts +63 -39
  57. package/src/providers/oauth/github.provider.ts +14 -10
  58. package/src/providers/oauth/google.provider.ts +39 -28
  59. package/src/providers/oauth/index.ts +1 -1
  60. package/src/rbac/dynamic-roles.ts +88 -54
  61. package/src/rbac/index.ts +4 -4
  62. package/src/rbac/permission-checker.ts +147 -75
  63. package/src/rbac/role-hierarchy.ts +8 -8
  64. package/src/rbac/role.manager.ts +11 -8
  65. package/src/security/csrf/csrf.protection.ts +9 -7
  66. package/src/security/index.ts +2 -2
  67. package/src/security/rate-limiting/auth/auth.controller.ts +2 -4
  68. package/src/security/rate-limiting/auth/rate-limiting.interface.ts +26 -6
  69. package/src/security/rate-limiting/auth.module.ts +1 -2
  70. package/src/server/auth.module.ts +55 -52
  71. package/src/server/decorators/auth.decorator.ts +9 -11
  72. package/src/server/decorators/auth.decorators.ts +8 -9
  73. package/src/server/decorators/current-user.decorator.ts +6 -6
  74. package/src/server/decorators/permission.decorator.ts +17 -9
  75. package/src/server/guards/auth.guard.ts +21 -16
  76. package/src/server/guards/custom-throttler.guard.ts +4 -9
  77. package/src/server/guards/permissions.guard.ts +32 -23
  78. package/src/server/guards/roles.guard.ts +14 -12
  79. package/src/server/middleware/auth.middleware.ts +4 -4
  80. package/src/server/middleware/session.middleware.ts +4 -4
  81. package/src/server/services/account.service.ts +96 -48
  82. package/src/server/services/auth.service.ts +57 -28
  83. package/src/server/services/brute-force.service.ts +24 -19
  84. package/src/server/services/index.ts +1 -1
  85. package/src/server/services/rate-limiter.service.ts +9 -4
  86. package/src/server/services/session.service.ts +84 -48
  87. package/src/server/services/token.service.ts +71 -51
  88. package/src/session/cookie-store.ts +47 -34
  89. package/src/session/enhanced-session-manager.ts +69 -48
  90. package/src/session/index.ts +5 -5
  91. package/src/session/memory-store.ts +37 -30
  92. package/src/session/redis-store.ts +105 -72
  93. package/src/strategies/oauth.strategy.ts +10 -9
  94. package/src/strategies/traditional-auth.strategy.ts +41 -29
  95. package/src/tokens/index.ts +4 -4
  96. package/src/tokens/refresh-token-manager.ts +70 -55
  97. package/src/tokens/token-validator.ts +109 -53
  98. package/vitest.setup.d.ts +2 -2
  99. package/vitest.setup.ts +1 -1
@@ -1,35 +1,35 @@
1
- import { randomBytes, pbkdf2Sync } from 'crypto';
2
- import type { UserRepository } from '../db/repositories/user.repository';
3
- import type { SessionManager } from '../core/session/session.manager';
4
- import type { AuthTokens, AuthUser, Session } from '@plyaz/types';
5
- import { AuthenticationError } from '@plyaz/errors';
6
- import { NUMERIX } from '@plyaz/config';
7
- import type { JwtManager } from '@/core/jwt/jwt.manager';
1
+ import { randomBytes, pbkdf2Sync } from "crypto";
2
+ import type { UserRepository } from "../db/repositories/user.repository";
3
+ import type { SessionManager } from "../core/session/session.manager";
4
+ import type { AuthTokens, AuthUser, Session } from "@plyaz/types";
5
+ import { AuthenticationError } from "@plyaz/errors";
6
+ import { NUMERIX } from "@plyaz/config";
7
+ import type { JwtManager } from "@/core/jwt/jwt.manager";
8
8
 
9
9
  export class TraditionalAuthStrategy {
10
10
  constructor(
11
11
  private userRepo: UserRepository,
12
12
  private jwtManager: JwtManager,
13
- private sessionManager: SessionManager
13
+ private sessionManager: SessionManager,
14
14
  ) {}
15
15
 
16
16
  async authenticate(email: string, password: string): Promise<AuthUser> {
17
17
  const user = await this.userRepo.findByEmail(email);
18
18
  if (!user?.passwordHash) {
19
- throw new AuthenticationError('AUTH_INVALID_CREDENTIALS');
19
+ throw new AuthenticationError("AUTH_INVALID_CREDENTIALS");
20
20
  }
21
21
 
22
22
  const isValid = await this.verifyPassword(password, user.passwordHash);
23
23
  if (!isValid) {
24
- throw new AuthenticationError('AUTH_INVALID_CREDENTIALS');
24
+ throw new AuthenticationError("AUTH_INVALID_CREDENTIALS");
25
25
  }
26
26
 
27
27
  if (!user.isActive) {
28
- throw new AuthenticationError('AUTH_ACCOUNT_LOCKED');
28
+ throw new AuthenticationError("AUTH_ACCOUNT_LOCKED");
29
29
  }
30
30
 
31
31
  if (user.isSuspended) {
32
- throw new AuthenticationError('AUTH_ACCOUNT_SUSPENDED');
32
+ throw new AuthenticationError("AUTH_ACCOUNT_SUSPENDED");
33
33
  }
34
34
 
35
35
  return user;
@@ -38,16 +38,28 @@ export class TraditionalAuthStrategy {
38
38
  async hashPassword(password: string): Promise<string> {
39
39
  const SALT_LENGTH = 32;
40
40
  const HASH_LENGTH = 64;
41
- const salt = randomBytes(SALT_LENGTH).toString('hex');
42
- const hash = pbkdf2Sync(password, salt, NUMERIX.THOUSAND, HASH_LENGTH, 'sha512').toString('hex');
41
+ const salt = randomBytes(SALT_LENGTH).toString("hex");
42
+ const hash = pbkdf2Sync(
43
+ password,
44
+ salt,
45
+ NUMERIX.THOUSAND,
46
+ HASH_LENGTH,
47
+ "sha512",
48
+ ).toString("hex");
43
49
  return `pbkdf2$${salt}$${hash}`;
44
50
  }
45
51
 
46
52
  async verifyPassword(password: string, hash: string): Promise<boolean> {
47
53
  const HASH_LENGTH = 64;
48
- if (hash.startsWith('pbkdf2$')) {
49
- const [, salt, storedHash] = hash.split('$');
50
- const computedHash = pbkdf2Sync(password, salt, NUMERIX.THOUSAND, HASH_LENGTH, 'sha512').toString('hex');
54
+ if (hash.startsWith("pbkdf2$")) {
55
+ const [, salt, storedHash] = hash.split("$");
56
+ const computedHash = pbkdf2Sync(
57
+ password,
58
+ salt,
59
+ NUMERIX.THOUSAND,
60
+ HASH_LENGTH,
61
+ "sha512",
62
+ ).toString("hex");
51
63
  return computedHash === storedHash;
52
64
  }
53
65
  return false;
@@ -63,7 +75,7 @@ export class TraditionalAuthStrategy {
63
75
  // Check if user already exists
64
76
  const existingUser = await this.userRepo.findByEmail(data.email);
65
77
  if (existingUser) {
66
- throw new AuthenticationError('AUTH_INVALID_CREDENTIALS');
78
+ throw new AuthenticationError("AUTH_INVALID_CREDENTIALS");
67
79
  }
68
80
 
69
81
  // Hash password
@@ -76,21 +88,21 @@ export class TraditionalAuthStrategy {
76
88
  firstName: data.firstName,
77
89
  lastName: data.lastName,
78
90
  passwordHash,
79
- authProvider: 'EMAIL',
91
+ authProvider: "EMAIL",
80
92
  isActive: true,
81
- isVerified: false
82
- } );
93
+ isVerified: false,
94
+ });
83
95
 
84
96
  // Create session
85
97
  const session = await this.sessionManager.createSession(user.id, {
86
- provider: 'EMAIL',
87
- userAgent: 'test',
88
- ipAddress: '127.0.0.1'
98
+ provider: "EMAIL",
99
+ userAgent: "test",
100
+ ipAddress: "127.0.0.1",
89
101
  });
90
102
 
91
103
  // Generate tokens
92
104
  const tokens = this.jwtManager.generateTokens(user);
93
-
105
+
94
106
  return { user, session, tokens };
95
107
  }
96
108
 
@@ -103,9 +115,9 @@ export class TraditionalAuthStrategy {
103
115
 
104
116
  // Create session
105
117
  const session = await this.sessionManager.createSession(user.id, {
106
- provider: 'EMAIL',
107
- userAgent: 'test',
108
- ipAddress: '127.0.0.1'
118
+ provider: "EMAIL",
119
+ userAgent: "test",
120
+ ipAddress: "127.0.0.1",
109
121
  });
110
122
 
111
123
  // Generate tokens
@@ -113,4 +125,4 @@ export class TraditionalAuthStrategy {
113
125
 
114
126
  return { user, session, tokens };
115
127
  }
116
- }
128
+ }
@@ -1,4 +1,4 @@
1
- export * from './token-validator';
2
- export * from './refresh-token-manager';
3
- export * from '../core/jwt/jwt.manager';
4
- export * from '../core/blacklist/token.blacklist';
1
+ export * from "./token-validator";
2
+ export * from "./refresh-token-manager";
3
+ export * from "../core/jwt/jwt.manager";
4
+ export * from "../core/blacklist/token.blacklist";
@@ -3,31 +3,30 @@
3
3
  /**
4
4
  * @fileoverview Refresh token manager for @plyaz/auth
5
5
  * @module @plyaz/auth/tokens/refresh-token-manager
6
- *
6
+ *
7
7
  * @description
8
8
  * Manages refresh token lifecycle including generation, validation, rotation,
9
9
  * and revocation. Implements security best practices like token rotation
10
10
  * and family tracking to prevent token theft and replay attacks.
11
- *
11
+ *
12
12
  * @example
13
13
  * ```typescript
14
14
  * import { RefreshTokenManager } from '@plyaz/auth';
15
- *
15
+ *
16
16
  * const manager = new RefreshTokenManager({
17
17
  * secretKey: 'your-secret-key',
18
18
  * tokenTTL: 604800, // 7 days
19
19
  * enableRotation: true
20
20
  * });
21
- *
21
+ *
22
22
  * const tokens = await manager.generateTokenPair(userId, sessionId);
23
23
  * ```
24
24
  */
25
25
 
26
- import { sign, verify } from 'jsonwebtoken';
27
- import { randomBytes } from 'crypto';
28
- import { TokenBlacklist } from '../core/blacklist/token.blacklist';
29
- import { AuthenticationError } from '@plyaz/errors';
30
-
26
+ import { sign, verify } from "jsonwebtoken";
27
+ import { randomBytes } from "crypto";
28
+ import { TokenBlacklist } from "../core/blacklist/token.blacklist";
29
+ import { AuthenticationError } from "@plyaz/errors";
31
30
 
32
31
  /**
33
32
  * Refresh token manager configuration
@@ -78,7 +77,7 @@ export interface RefreshTokenPayload {
78
77
  /** Token generation number */
79
78
  generation?: number;
80
79
  /** Token type */
81
- type: 'refresh';
80
+ type: "refresh";
82
81
  /** Issued at */
83
82
  iat: number;
84
83
  /** Expires at */
@@ -102,7 +101,7 @@ export interface AccessTokenPayload {
102
101
  /** User permissions */
103
102
  permissions?: string[];
104
103
  /** Token type */
105
- type: 'access';
104
+ type: "access";
106
105
  /** Issued at */
107
106
  iat: number;
108
107
  /** Expires at */
@@ -142,7 +141,10 @@ export class RefreshTokenManager {
142
141
 
143
142
  constructor(config: RefreshTokenManagerConfig) {
144
143
  this.config = config;
145
- this.blacklist = new TokenBlacklist({ keyPrefix: 'token:', defaultTTL: 3600 });
144
+ this.blacklist = new TokenBlacklist({
145
+ keyPrefix: "token:",
146
+ defaultTTL: 3600,
147
+ });
146
148
  }
147
149
 
148
150
  /**
@@ -157,7 +159,7 @@ export class RefreshTokenManager {
157
159
  userId: string,
158
160
  sessionId: string,
159
161
  userRoles?: string[],
160
- userPermissions?: string[]
162
+ userPermissions?: string[],
161
163
  ): Promise<TokenPair> {
162
164
  const now = Math.floor(Date.now() / 1000);
163
165
  const accessTokenExp = now + this.config.accessTokenTTL;
@@ -175,7 +177,7 @@ export class RefreshTokenManager {
175
177
  sessionId,
176
178
  generation,
177
179
  createdAt: new Date(),
178
- lastUsedAt: new Date()
180
+ lastUsedAt: new Date(),
179
181
  });
180
182
  }
181
183
 
@@ -185,11 +187,11 @@ export class RefreshTokenManager {
185
187
  sessionId,
186
188
  roles: userRoles,
187
189
  permissions: userPermissions,
188
- type: 'access',
190
+ type: "access",
189
191
  iat: now,
190
192
  exp: accessTokenExp,
191
193
  iss: this.config.issuer,
192
- aud: this.config.audience
194
+ aud: this.config.audience,
193
195
  };
194
196
 
195
197
  // Create refresh token payload
@@ -198,22 +200,26 @@ export class RefreshTokenManager {
198
200
  sessionId,
199
201
  family,
200
202
  generation,
201
- type: 'refresh',
203
+ type: "refresh",
202
204
  iat: now,
203
205
  exp: refreshTokenExp,
204
206
  iss: this.config.issuer,
205
- aud: this.config.audience
207
+ aud: this.config.audience,
206
208
  };
207
209
 
208
210
  // Sign tokens
209
- const accessToken = sign(accessPayload, this.config.secretKey, { algorithm: 'HS256' });
210
- const refreshToken = sign(refreshPayload, this.config.secretKey, { algorithm: 'HS256' });
211
+ const accessToken = sign(accessPayload, this.config.secretKey, {
212
+ algorithm: "HS256",
213
+ });
214
+ const refreshToken = sign(refreshPayload, this.config.secretKey, {
215
+ algorithm: "HS256",
216
+ });
211
217
 
212
218
  return {
213
219
  accessToken,
214
220
  refreshToken,
215
221
  accessTokenExpiresAt: new Date(accessTokenExp * 1000),
216
- refreshTokenExpiresAt: new Date(refreshTokenExp * 1000)
222
+ refreshTokenExpiresAt: new Date(refreshTokenExp * 1000),
217
223
  };
218
224
  }
219
225
 
@@ -227,7 +233,7 @@ export class RefreshTokenManager {
227
233
  async refreshTokenPair(
228
234
  refreshToken: string,
229
235
  userRoles?: string[],
230
- userPermissions?: string[]
236
+ userPermissions?: string[],
231
237
  ): Promise<TokenPair> {
232
238
  // Validate refresh token
233
239
  const payload = await this.validateRefreshToken(refreshToken);
@@ -247,7 +253,7 @@ export class RefreshTokenManager {
247
253
  payload.sub,
248
254
  payload.sessionId,
249
255
  userRoles,
250
- userPermissions
256
+ userPermissions,
251
257
  );
252
258
 
253
259
  // Update token family
@@ -256,11 +262,11 @@ export class RefreshTokenManager {
256
262
  if (family) {
257
263
  family.generation++;
258
264
  family.lastUsedAt = new Date();
259
-
265
+
260
266
  // Enforce family size limit
261
267
  if (family.generation > this.config.maxFamilySize) {
262
268
  await this.revokeTokenFamily(payload.family);
263
- throw new AuthenticationError('AUTH_TOKEN_INVALID');
269
+ throw new AuthenticationError("AUTH_TOKEN_INVALID");
264
270
  }
265
271
  }
266
272
  }
@@ -273,41 +279,41 @@ export class RefreshTokenManager {
273
279
  * @param refreshToken - Refresh token to validate
274
280
  * @returns Decoded payload
275
281
  */
276
- async validateRefreshToken(refreshToken: string): Promise<RefreshTokenPayload> {
282
+ async validateRefreshToken(
283
+ refreshToken: string,
284
+ ): Promise<RefreshTokenPayload> {
277
285
  try {
278
286
  // Check if token is blacklisted
279
287
  if (await this.blacklist.isBlacklisted(refreshToken)) {
280
- throw new AuthenticationError('AUTH_TOKEN_REVOKED');
288
+ throw new AuthenticationError("AUTH_TOKEN_REVOKED");
281
289
  }
282
290
 
283
291
  // Verify token
284
292
  const payload = verify(refreshToken, this.config.secretKey, {
285
293
  issuer: this.config.issuer,
286
294
  audience: this.config.audience,
287
- algorithms: ['HS256']
295
+ algorithms: ["HS256"],
288
296
  }) as RefreshTokenPayload;
289
297
 
290
298
  // Validate token type
291
- if (payload.type !== 'refresh') {
292
- throw new AuthenticationError('AUTH_TOKEN_INVALID');
299
+ if (payload.type !== "refresh") {
300
+ throw new AuthenticationError("AUTH_TOKEN_INVALID");
293
301
  }
294
302
 
295
303
  return payload;
296
-
297
304
  } catch (error) {
298
- if (error instanceof Error) {
299
- if (error.name === 'TokenExpiredError') {
300
- throw new AuthenticationError('AUTH_TOKEN_EXPIRED');
301
- }
302
-
303
- if (error.name === 'JsonWebTokenError') {
304
- throw new AuthenticationError('AUTH_TOKEN_INVALID');
305
- }
306
- }
305
+ if (error instanceof Error) {
306
+ if (error.name === "TokenExpiredError") {
307
+ throw new AuthenticationError("AUTH_TOKEN_EXPIRED");
308
+ }
307
309
 
308
- throw error;
309
- }
310
+ if (error.name === "JsonWebTokenError") {
311
+ throw new AuthenticationError("AUTH_TOKEN_INVALID");
312
+ }
313
+ }
310
314
 
315
+ throw error;
316
+ }
311
317
  }
312
318
 
313
319
  /**
@@ -317,7 +323,7 @@ export class RefreshTokenManager {
317
323
  async revokeRefreshToken(refreshToken: string): Promise<void> {
318
324
  try {
319
325
  const payload = await this.validateRefreshToken(refreshToken);
320
-
326
+
321
327
  // Add to blacklist
322
328
  await this.blacklist.add(refreshToken, payload.exp);
323
329
 
@@ -325,10 +331,12 @@ export class RefreshTokenManager {
325
331
  if (this.config.enableFamilyTracking && payload.family) {
326
332
  await this.revokeTokenFamily(payload.family);
327
333
  }
328
-
329
334
  } catch (error) {
330
335
  // Token might already be invalid, but still try to blacklist
331
- await this.blacklist.add(refreshToken, Math.floor(Date.now() / 1000) + 3600);
336
+ await this.blacklist.add(
337
+ refreshToken,
338
+ Math.floor(Date.now() / 1000) + 3600,
339
+ );
332
340
  }
333
341
  }
334
342
 
@@ -378,7 +386,9 @@ export class RefreshTokenManager {
378
386
 
379
387
  for (const [familyId, family] of this.tokenFamilies.entries()) {
380
388
  // Consider family expired if not used for longer than token TTL
381
- const expiredTime = new Date(family.lastUsedAt.getTime() + this.config.tokenTTL * 1000);
389
+ const expiredTime = new Date(
390
+ family.lastUsedAt.getTime() + this.config.tokenTTL * 1000,
391
+ );
382
392
  if (expiredTime < now) {
383
393
  expiredFamilies.push(familyId);
384
394
  }
@@ -397,28 +407,33 @@ export class RefreshTokenManager {
397
407
  * @param payload - Refresh token payload
398
408
  * @private
399
409
  */
400
- private async validateTokenFamily(payload: RefreshTokenPayload): Promise<void> {
410
+ private async validateTokenFamily(
411
+ payload: RefreshTokenPayload,
412
+ ): Promise<void> {
401
413
  if (!payload.family) {
402
414
  return;
403
415
  }
404
416
 
405
417
  const family = this.tokenFamilies.get(payload.family);
406
-
418
+
407
419
  if (!family) {
408
- throw new AuthenticationError('AUTH_TOKEN_INVALID');
420
+ throw new AuthenticationError("AUTH_TOKEN_INVALID");
409
421
  }
410
422
 
411
423
  // Check if token generation is valid
412
424
  if (payload.generation !== family.generation) {
413
425
  // Potential token theft - revoke entire family
414
426
  await this.revokeTokenFamily(payload.family);
415
- throw new AuthenticationError('AUTH_TOKEN_REVOKED');
427
+ throw new AuthenticationError("AUTH_TOKEN_REVOKED");
416
428
  }
417
429
 
418
430
  // Check if user and session match
419
- if (family.userId !== payload.sub || family.sessionId !== payload.sessionId) {
431
+ if (
432
+ family.userId !== payload.sub ||
433
+ family.sessionId !== payload.sessionId
434
+ ) {
420
435
  await this.revokeTokenFamily(payload.family);
421
- throw new AuthenticationError('AUTH_TOKEN_REVOKED');
436
+ throw new AuthenticationError("AUTH_TOKEN_REVOKED");
422
437
  }
423
438
  }
424
439
 
@@ -429,7 +444,7 @@ export class RefreshTokenManager {
429
444
  */
430
445
  private async revokeTokenFamily(familyId: string): Promise<void> {
431
446
  const family = this.tokenFamilies.get(familyId);
432
-
447
+
433
448
  if (family) {
434
449
  // In a real implementation, this would blacklist all tokens in the family
435
450
  // For now, we just remove the family tracking
@@ -443,6 +458,6 @@ export class RefreshTokenManager {
443
458
  * @private
444
459
  */
445
460
  private generateFamilyId(): string {
446
- return randomBytes(16).toString('hex');
461
+ return randomBytes(16).toString("hex");
447
462
  }
448
- }
463
+ }