@plyaz/auth 1.0.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 (113) hide show
  1. package/.github/pull_request_template.md +71 -0
  2. package/.github/workflows/deploy.yml +9 -0
  3. package/.github/workflows/publish.yml +14 -0
  4. package/.github/workflows/security.yml +20 -0
  5. package/README.md +89 -0
  6. package/commits.txt +5 -0
  7. package/dist/common/index.cjs +48 -0
  8. package/dist/common/index.cjs.map +1 -0
  9. package/dist/common/index.mjs +43 -0
  10. package/dist/common/index.mjs.map +1 -0
  11. package/dist/index.cjs +20411 -0
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.mjs +5139 -0
  14. package/dist/index.mjs.map +1 -0
  15. package/eslint.config.mjs +13 -0
  16. package/index.html +13 -0
  17. package/package.json +141 -0
  18. package/src/adapters/auth-adapter-factory.ts +26 -0
  19. package/src/adapters/auth-adapter.mapper.ts +53 -0
  20. package/src/adapters/base-auth.adapter.ts +119 -0
  21. package/src/adapters/clerk/clerk.adapter.ts +204 -0
  22. package/src/adapters/custom/custom.adapter.ts +119 -0
  23. package/src/adapters/index.ts +4 -0
  24. package/src/adapters/next-auth/authOptions.ts +81 -0
  25. package/src/adapters/next-auth/next-auth.adapter.ts +211 -0
  26. package/src/api/client.ts +37 -0
  27. package/src/audit/audit.logger.ts +52 -0
  28. package/src/client/components/ProtectedRoute.tsx +37 -0
  29. package/src/client/hooks/useAuth.ts +128 -0
  30. package/src/client/hooks/useConnectedAccounts.ts +108 -0
  31. package/src/client/hooks/usePermissions.ts +36 -0
  32. package/src/client/hooks/useRBAC.ts +36 -0
  33. package/src/client/hooks/useSession.ts +18 -0
  34. package/src/client/providers/AuthProvider.tsx +104 -0
  35. package/src/client/store/auth.store.ts +306 -0
  36. package/src/client/utils/storage.ts +70 -0
  37. package/src/common/constants/oauth-providers.ts +49 -0
  38. package/src/common/errors/auth.errors.ts +64 -0
  39. package/src/common/errors/specific-auth-errors.ts +201 -0
  40. package/src/common/index.ts +19 -0
  41. package/src/common/regex/index.ts +27 -0
  42. package/src/common/types/auth.types.ts +641 -0
  43. package/src/common/types/index.ts +297 -0
  44. package/src/common/utils/index.ts +84 -0
  45. package/src/core/blacklist/token.blacklist.ts +60 -0
  46. package/src/core/index.ts +2 -0
  47. package/src/core/jwt/jwt.manager.ts +131 -0
  48. package/src/core/session/session.manager.ts +56 -0
  49. package/src/db/repositories/connected-account.repository.ts +415 -0
  50. package/src/db/repositories/role.repository.ts +519 -0
  51. package/src/db/repositories/session.repository.ts +308 -0
  52. package/src/db/repositories/user.repository.ts +320 -0
  53. package/src/flows/index.ts +2 -0
  54. package/src/flows/sign-in.flow.ts +106 -0
  55. package/src/flows/sign-up.flow.ts +121 -0
  56. package/src/index.ts +54 -0
  57. package/src/libs/clerk.helper.ts +36 -0
  58. package/src/libs/supabase.helper.ts +255 -0
  59. package/src/libs/supabaseClient.ts +6 -0
  60. package/src/providers/base/auth-provider.interface.ts +42 -0
  61. package/src/providers/base/index.ts +1 -0
  62. package/src/providers/index.ts +2 -0
  63. package/src/providers/oauth/facebook.provider.ts +97 -0
  64. package/src/providers/oauth/github.provider.ts +148 -0
  65. package/src/providers/oauth/google.provider.ts +126 -0
  66. package/src/providers/oauth/index.ts +3 -0
  67. package/src/rbac/dynamic-roles.ts +552 -0
  68. package/src/rbac/index.ts +4 -0
  69. package/src/rbac/permission-checker.ts +464 -0
  70. package/src/rbac/role-hierarchy.ts +545 -0
  71. package/src/rbac/role.manager.ts +75 -0
  72. package/src/security/csrf/csrf.protection.ts +37 -0
  73. package/src/security/index.ts +3 -0
  74. package/src/security/rate-limiting/auth/auth.controller.ts +12 -0
  75. package/src/security/rate-limiting/auth/rate-limiting.interface.ts +67 -0
  76. package/src/security/rate-limiting/auth.module.ts +32 -0
  77. package/src/server/auth.module.ts +158 -0
  78. package/src/server/decorators/auth.decorator.ts +43 -0
  79. package/src/server/decorators/auth.decorators.ts +31 -0
  80. package/src/server/decorators/current-user.decorator.ts +49 -0
  81. package/src/server/decorators/permission.decorator.ts +49 -0
  82. package/src/server/guards/auth.guard.ts +56 -0
  83. package/src/server/guards/custom-throttler.guard.ts +46 -0
  84. package/src/server/guards/permissions.guard.ts +115 -0
  85. package/src/server/guards/roles.guard.ts +31 -0
  86. package/src/server/middleware/auth.middleware.ts +46 -0
  87. package/src/server/middleware/index.ts +2 -0
  88. package/src/server/middleware/middleware.ts +11 -0
  89. package/src/server/middleware/session.middleware.ts +255 -0
  90. package/src/server/services/account.service.ts +269 -0
  91. package/src/server/services/auth.service.ts +79 -0
  92. package/src/server/services/brute-force.service.ts +98 -0
  93. package/src/server/services/index.ts +15 -0
  94. package/src/server/services/rate-limiter.service.ts +60 -0
  95. package/src/server/services/session.service.ts +287 -0
  96. package/src/server/services/token.service.ts +262 -0
  97. package/src/session/cookie-store.ts +255 -0
  98. package/src/session/enhanced-session-manager.ts +406 -0
  99. package/src/session/index.ts +14 -0
  100. package/src/session/memory-store.ts +320 -0
  101. package/src/session/redis-store.ts +443 -0
  102. package/src/strategies/oauth.strategy.ts +128 -0
  103. package/src/strategies/traditional-auth.strategy.ts +116 -0
  104. package/src/tokens/index.ts +4 -0
  105. package/src/tokens/refresh-token-manager.ts +448 -0
  106. package/src/tokens/token-validator.ts +311 -0
  107. package/tsconfig.build.json +28 -0
  108. package/tsconfig.json +38 -0
  109. package/tsup.config.mjs +28 -0
  110. package/vitest.config.mjs +16 -0
  111. package/vitest.setup.d.ts +2 -0
  112. package/vitest.setup.d.ts.map +1 -0
  113. package/vitest.setup.ts +1 -0
@@ -0,0 +1,406 @@
1
+ /**
2
+ * @fileoverview Enhanced session manager for @plyaz/auth
3
+ * @module @plyaz/auth/session/enhanced-session-manager
4
+ *
5
+ * @description
6
+ * Enhanced session manager that implements all documented session management
7
+ * methods. Provides comprehensive session lifecycle management including
8
+ * CSRF token generation, concurrent session detection, and session validation.
9
+ * Uses pluggable session stores for flexible storage backends.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { EnhancedSessionManager, MemoryStore } from '@plyaz/auth';
14
+ *
15
+ * const sessionManager = new EnhancedSessionManager({
16
+ * store: new MemoryStore(),
17
+ * sessionTTL: 3600,
18
+ * maxConcurrentSessions: 5
19
+ * });
20
+ *
21
+ * const session = await sessionManager.createSession(userContext, sessionData);
22
+ * ```
23
+ */
24
+
25
+
26
+ import type { Session, SessionData, SessionStore } from '@plyaz/types';
27
+ import { randomBytes } from 'crypto';
28
+ import { CSRFProtection } from '../security';
29
+ import { NUMERIX } from '@plyaz/config';
30
+
31
+ /**
32
+ * User context for session creation
33
+ */
34
+ export interface UserContext {
35
+ /** User ID */
36
+ userId: string;
37
+ /** User email */
38
+ email?: string;
39
+ /** User roles */
40
+ roles?: string[];
41
+ /** Additional user metadata */
42
+ metadata?: Record<string, unknown>;
43
+ }
44
+
45
+ /**
46
+ * Session creation data
47
+ */
48
+ export interface SessionCreationData {
49
+ /** IP address */
50
+ ipAddress?: string;
51
+ /** User agent string */
52
+ userAgent?: string;
53
+ /** Device information */
54
+ deviceInfo?: Record<string, unknown>;
55
+ /** Additional session metadata */
56
+ metadata?: Record<string, unknown>;
57
+ }
58
+
59
+ /**
60
+ * Enhanced session manager configuration
61
+ */
62
+ export interface EnhancedSessionManagerConfig {
63
+ /** Session store implementation */
64
+ store: SessionStore;
65
+ /** Default session TTL in seconds */
66
+ sessionTTL: number;
67
+ /** Maximum concurrent sessions per user */
68
+ maxConcurrentSessions: number;
69
+ /** Enable CSRF protection */
70
+ enableCSRF: boolean;
71
+ /** CSRF token TTL in seconds */
72
+ csrfTTL: number;
73
+ /** Enable session refresh */
74
+ enableRefresh: boolean;
75
+ /** Session refresh threshold (seconds before expiry) */
76
+ refreshThreshold: number;
77
+ }
78
+
79
+ /**
80
+ * Enhanced session manager implementation
81
+ * Provides comprehensive session management with all documented methods
82
+ */
83
+ export class EnhancedSessionManager {
84
+ private readonly config: EnhancedSessionManagerConfig;
85
+ private readonly store: SessionStore;
86
+ private readonly csrfProtection: CSRFProtection;
87
+
88
+ constructor(config: Partial<EnhancedSessionManagerConfig> & { store: SessionStore }) {
89
+ this.config = {
90
+ sessionTTL: 3600,
91
+ maxConcurrentSessions: 5,
92
+ enableCSRF: true,
93
+ csrfTTL: 3600,
94
+ enableRefresh: true,
95
+ refreshThreshold: 300, // 5 minutes
96
+ ...config
97
+ };
98
+
99
+ this.store = config.store;
100
+ this.csrfProtection = new CSRFProtection();
101
+ }
102
+
103
+ /**
104
+ * Create a new session
105
+ * Generate session ID, store data, set expiry, generate CSRF token
106
+ * @param userContext - User context information
107
+ * @param sessionData - Session creation data
108
+ * @returns Created session
109
+ */
110
+ async createSession(userContext: UserContext, sessionData: SessionCreationData = {}): Promise<Session> {
111
+ // Generate unique session ID
112
+ const sessionId = this.generateSessionId();
113
+
114
+ // Enforce session limits
115
+ await this.enforceSessionLimits(userContext.userId);
116
+
117
+ // Create session data
118
+ const now = new Date();
119
+ const expiresAt = new Date(now.getTime() + this.config.sessionTTL * NUMERIX.THOUSAND);
120
+
121
+ const session: SessionData = {
122
+ id: sessionId,
123
+ userId: userContext.userId,
124
+ expiresAt,
125
+ createdAt: now,
126
+ lastActivityAt: now,
127
+ ipAddress: sessionData.ipAddress,
128
+ userAgent: sessionData.userAgent,
129
+ metadata: {
130
+ ...sessionData.metadata,
131
+ deviceInfo: sessionData.deviceInfo,
132
+ userRoles: userContext.roles,
133
+ userEmail: userContext.email
134
+ }
135
+ };
136
+
137
+ // Store session
138
+ await this.store.set(sessionId, session, this.config.sessionTTL);
139
+
140
+ // Generate CSRF token if enabled
141
+ let csrfToken: string | undefined;
142
+ if (this.config.enableCSRF) {
143
+ csrfToken = this.csrfProtection.generateToken(sessionId);
144
+ }
145
+
146
+ return {
147
+ id: sessionId,
148
+ userId: userContext.userId,
149
+ provider: 'enhanced-session-manager',
150
+ providerSessionId: sessionId,
151
+ expiresAt,
152
+ createdAt: now,
153
+ lastActivityAt: now,
154
+ ipAddress: sessionData.ipAddress,
155
+ userAgent: sessionData.userAgent,
156
+ metadata: {
157
+ ...session.metadata,
158
+ csrfToken
159
+ }
160
+ };
161
+ }
162
+
163
+ /**
164
+ * Retrieve session by ID
165
+ * @param sessionId - Session identifier
166
+ * @returns Session or null if not found
167
+ */
168
+ async getSession(sessionId: string): Promise<Session | null> {
169
+ const sessionData = await this.store.get(sessionId);
170
+
171
+ if (!sessionData) {
172
+ return null;
173
+ }
174
+
175
+ return this.mapSessionDataToSession(sessionData);
176
+ }
177
+
178
+ /**
179
+ * Update session data
180
+ * @param sessionId - Session identifier
181
+ * @param updates - Partial session updates
182
+ * @returns Updated session
183
+ */
184
+ async updateSession(sessionId: string, updates: Partial<SessionData>): Promise<Session> {
185
+ const existingSession = await this.store.get(sessionId);
186
+
187
+ if (!existingSession) {
188
+ throw new Error('Session not found');
189
+ }
190
+
191
+ // Merge updates
192
+ const updatedSession: SessionData = {
193
+ ...existingSession,
194
+ ...updates,
195
+ lastActivityAt: new Date()
196
+ };
197
+
198
+ // Calculate remaining TTL
199
+ const remainingTTL = Math.max(0, Math.floor((updatedSession.expiresAt.getTime() - Date.now()) / NUMERIX.THOUSAND));
200
+
201
+ if (remainingTTL <= 0) {
202
+ throw new Error('Session has expired');
203
+ }
204
+
205
+ // Store updated session
206
+ await this.store.set(sessionId, updatedSession, remainingTTL);
207
+
208
+ return this.mapSessionDataToSession(updatedSession);
209
+ }
210
+
211
+ /**
212
+ * Delete session
213
+ * @param sessionId - Session identifier
214
+ */
215
+ async deleteSession(sessionId: string): Promise<void> {
216
+ await this.store.delete(sessionId);
217
+
218
+ // Remove CSRF token if enabled
219
+ if (this.config.enableCSRF) {
220
+ this.csrfProtection.removeToken(sessionId);
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Refresh session (extend expiry)
226
+ * @param sessionId - Session identifier
227
+ * @returns Refreshed session
228
+ */
229
+ async refreshSession(sessionId: string): Promise<Session> {
230
+ const sessionData = await this.store.get(sessionId);
231
+
232
+ if (!sessionData) {
233
+ throw new Error('Session not found');
234
+ }
235
+
236
+ // Extend expiry
237
+ const now = new Date();
238
+ const newExpiresAt = new Date(now.getTime() + this.config.sessionTTL * NUMERIX.THOUSAND);
239
+
240
+ const refreshedSession: SessionData = {
241
+ ...sessionData,
242
+ expiresAt: newExpiresAt,
243
+ lastActivityAt: now
244
+ };
245
+
246
+ // Store refreshed session
247
+ await this.store.set(sessionId, refreshedSession, this.config.sessionTTL);
248
+
249
+ return this.mapSessionDataToSession(refreshedSession);
250
+ }
251
+
252
+ /**
253
+ * Validate session
254
+ * @param sessionId - Session identifier
255
+ * @returns True if session is valid
256
+ */
257
+ async validateSession(sessionId: string): Promise<boolean> {
258
+ const sessionData = await this.store.get(sessionId);
259
+
260
+ if (!sessionData) {
261
+ return false;
262
+ }
263
+
264
+ // Check expiration
265
+ if (sessionData.expiresAt < new Date()) {
266
+ await this.deleteSession(sessionId);
267
+ return false;
268
+ }
269
+
270
+ // Update activity
271
+ await this.store.updateActivity(sessionId);
272
+
273
+ return true;
274
+ }
275
+
276
+ /**
277
+ * Invalidate all sessions for a user (logout all devices)
278
+ * @param userId - User identifier
279
+ */
280
+ async invalidateAllUserSessions(userId: string): Promise<void> {
281
+ await this.store.deleteByUserId(userId);
282
+ }
283
+
284
+ /**
285
+ * Detect concurrent sessions for a user
286
+ * @param userId - User identifier
287
+ * @returns Array of active sessions
288
+ */
289
+ async detectConcurrentSessions(userId: string): Promise<Session[]> {
290
+ const sessionDataArray = await this.store.getByUserId(userId);
291
+
292
+ return sessionDataArray.map(sessionData => this.mapSessionDataToSession(sessionData));
293
+ }
294
+
295
+ /**
296
+ * Apply maximum session policy for a user
297
+ * @param userId - User identifier
298
+ */
299
+ async enforceSessionLimits(userId: string): Promise<void> {
300
+ const userSessions = await this.store.getByUserId(userId);
301
+
302
+ if (userSessions.length >= this.config.maxConcurrentSessions) {
303
+ // Sort by last activity (oldest first)
304
+ userSessions.sort((a, b) => a.lastActivityAt.getTime() - b.lastActivityAt.getTime());
305
+
306
+ // Remove oldest sessions to make room
307
+ const sessionsToRemove = userSessions.length - this.config.maxConcurrentSessions + 1;
308
+ for (let i = 0; i < sessionsToRemove; i++) {
309
+ await this.deleteSession(userSessions[i].id);
310
+ }
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Generate CSRF token for session
316
+ * @param sessionId - Session identifier
317
+ * @returns CSRF token
318
+ */
319
+ async generateCsrfToken(sessionId: string): Promise<string> {
320
+ if (!this.config.enableCSRF) {
321
+ throw new Error('CSRF protection is disabled');
322
+ }
323
+
324
+ return this.csrfProtection.generateToken(sessionId);
325
+ }
326
+
327
+ /**
328
+ * Validate CSRF token for session
329
+ * @param sessionId - Session identifier
330
+ * @param token - CSRF token to validate
331
+ * @returns True if token is valid
332
+ */
333
+ async validateCsrfToken(sessionId: string, token: string): Promise<boolean> {
334
+ if (!this.config.enableCSRF) {
335
+ return true; // CSRF disabled, always valid
336
+ }
337
+
338
+ return this.csrfProtection.validateToken(sessionId, token);
339
+ }
340
+
341
+ /**
342
+ * Check if session needs refresh
343
+ * @param sessionId - Session identifier
344
+ * @returns True if session should be refreshed
345
+ */
346
+ async shouldRefreshSession(sessionId: string): Promise<boolean> {
347
+ if (!this.config.enableRefresh) {
348
+ return false;
349
+ }
350
+
351
+ const sessionData = await this.store.get(sessionId);
352
+
353
+ if (!sessionData) {
354
+ return false;
355
+ }
356
+
357
+ const timeUntilExpiry = sessionData.expiresAt.getTime() - Date.now();
358
+ return timeUntilExpiry <= this.config.refreshThreshold * NUMERIX.THOUSAND;
359
+ }
360
+
361
+ /**
362
+ * Get session statistics
363
+ * @returns Session statistics
364
+ */
365
+ async getSessionStats(): Promise<{
366
+ totalSessions: number;
367
+ activeUsers: number;
368
+ }> {
369
+ // This would require additional tracking in a real implementation
370
+ return {
371
+ totalSessions: 0, // Would need to scan all sessions
372
+ activeUsers: 0 // Would need to count unique user IDs
373
+ };
374
+ }
375
+
376
+ /**
377
+ * Generate cryptographically secure session ID
378
+ * @returns Session ID
379
+ * @private
380
+ */
381
+ private generateSessionId(): string {
382
+ const randomBytesNumber = 32;
383
+ return randomBytes(randomBytesNumber).toString('hex');
384
+ }
385
+
386
+ /**
387
+ * Map session data to session interface
388
+ * @param sessionData - Session data from store
389
+ * @returns Session interface
390
+ * @private
391
+ */
392
+ private mapSessionDataToSession(sessionData: SessionData): Session {
393
+ return {
394
+ id: sessionData.id,
395
+ userId: sessionData.userId,
396
+ provider: 'enhanced-session-manager',
397
+ providerSessionId: sessionData.id,
398
+ expiresAt: sessionData.expiresAt,
399
+ createdAt: sessionData.createdAt,
400
+ lastActivityAt: sessionData.lastActivityAt,
401
+ ipAddress: sessionData.ipAddress,
402
+ userAgent: sessionData.userAgent,
403
+ metadata: sessionData.metadata
404
+ };
405
+ }
406
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @fileoverview Session management barrel export for @plyaz/auth
3
+ * @module @plyaz/auth/session
4
+ *
5
+ * @description
6
+ * Centralized export of all session management components including
7
+ * session stores, interfaces, and the enhanced session manager.
8
+ * Provides a single import point for all session-related functionality.
9
+ */
10
+
11
+ export * from './cookie-store';
12
+ export * from './memory-store';
13
+ export * from './redis-store';
14
+ export * from './enhanced-session-manager';