@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.
- package/.github/pull_request_template.md +71 -0
- package/.github/workflows/deploy.yml +9 -0
- package/.github/workflows/publish.yml +14 -0
- package/.github/workflows/security.yml +20 -0
- package/README.md +89 -0
- package/commits.txt +5 -0
- package/dist/common/index.cjs +48 -0
- package/dist/common/index.cjs.map +1 -0
- package/dist/common/index.mjs +43 -0
- package/dist/common/index.mjs.map +1 -0
- package/dist/index.cjs +20411 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.mjs +5139 -0
- package/dist/index.mjs.map +1 -0
- package/eslint.config.mjs +13 -0
- package/index.html +13 -0
- package/package.json +141 -0
- package/src/adapters/auth-adapter-factory.ts +26 -0
- package/src/adapters/auth-adapter.mapper.ts +53 -0
- package/src/adapters/base-auth.adapter.ts +119 -0
- package/src/adapters/clerk/clerk.adapter.ts +204 -0
- package/src/adapters/custom/custom.adapter.ts +119 -0
- package/src/adapters/index.ts +4 -0
- package/src/adapters/next-auth/authOptions.ts +81 -0
- package/src/adapters/next-auth/next-auth.adapter.ts +211 -0
- package/src/api/client.ts +37 -0
- package/src/audit/audit.logger.ts +52 -0
- package/src/client/components/ProtectedRoute.tsx +37 -0
- package/src/client/hooks/useAuth.ts +128 -0
- package/src/client/hooks/useConnectedAccounts.ts +108 -0
- package/src/client/hooks/usePermissions.ts +36 -0
- package/src/client/hooks/useRBAC.ts +36 -0
- package/src/client/hooks/useSession.ts +18 -0
- package/src/client/providers/AuthProvider.tsx +104 -0
- package/src/client/store/auth.store.ts +306 -0
- package/src/client/utils/storage.ts +70 -0
- package/src/common/constants/oauth-providers.ts +49 -0
- package/src/common/errors/auth.errors.ts +64 -0
- package/src/common/errors/specific-auth-errors.ts +201 -0
- package/src/common/index.ts +19 -0
- package/src/common/regex/index.ts +27 -0
- package/src/common/types/auth.types.ts +641 -0
- package/src/common/types/index.ts +297 -0
- package/src/common/utils/index.ts +84 -0
- package/src/core/blacklist/token.blacklist.ts +60 -0
- package/src/core/index.ts +2 -0
- package/src/core/jwt/jwt.manager.ts +131 -0
- package/src/core/session/session.manager.ts +56 -0
- package/src/db/repositories/connected-account.repository.ts +415 -0
- package/src/db/repositories/role.repository.ts +519 -0
- package/src/db/repositories/session.repository.ts +308 -0
- package/src/db/repositories/user.repository.ts +320 -0
- package/src/flows/index.ts +2 -0
- package/src/flows/sign-in.flow.ts +106 -0
- package/src/flows/sign-up.flow.ts +121 -0
- package/src/index.ts +54 -0
- package/src/libs/clerk.helper.ts +36 -0
- package/src/libs/supabase.helper.ts +255 -0
- package/src/libs/supabaseClient.ts +6 -0
- package/src/providers/base/auth-provider.interface.ts +42 -0
- package/src/providers/base/index.ts +1 -0
- package/src/providers/index.ts +2 -0
- package/src/providers/oauth/facebook.provider.ts +97 -0
- package/src/providers/oauth/github.provider.ts +148 -0
- package/src/providers/oauth/google.provider.ts +126 -0
- package/src/providers/oauth/index.ts +3 -0
- package/src/rbac/dynamic-roles.ts +552 -0
- package/src/rbac/index.ts +4 -0
- package/src/rbac/permission-checker.ts +464 -0
- package/src/rbac/role-hierarchy.ts +545 -0
- package/src/rbac/role.manager.ts +75 -0
- package/src/security/csrf/csrf.protection.ts +37 -0
- package/src/security/index.ts +3 -0
- package/src/security/rate-limiting/auth/auth.controller.ts +12 -0
- package/src/security/rate-limiting/auth/rate-limiting.interface.ts +67 -0
- package/src/security/rate-limiting/auth.module.ts +32 -0
- package/src/server/auth.module.ts +158 -0
- package/src/server/decorators/auth.decorator.ts +43 -0
- package/src/server/decorators/auth.decorators.ts +31 -0
- package/src/server/decorators/current-user.decorator.ts +49 -0
- package/src/server/decorators/permission.decorator.ts +49 -0
- package/src/server/guards/auth.guard.ts +56 -0
- package/src/server/guards/custom-throttler.guard.ts +46 -0
- package/src/server/guards/permissions.guard.ts +115 -0
- package/src/server/guards/roles.guard.ts +31 -0
- package/src/server/middleware/auth.middleware.ts +46 -0
- package/src/server/middleware/index.ts +2 -0
- package/src/server/middleware/middleware.ts +11 -0
- package/src/server/middleware/session.middleware.ts +255 -0
- package/src/server/services/account.service.ts +269 -0
- package/src/server/services/auth.service.ts +79 -0
- package/src/server/services/brute-force.service.ts +98 -0
- package/src/server/services/index.ts +15 -0
- package/src/server/services/rate-limiter.service.ts +60 -0
- package/src/server/services/session.service.ts +287 -0
- package/src/server/services/token.service.ts +262 -0
- package/src/session/cookie-store.ts +255 -0
- package/src/session/enhanced-session-manager.ts +406 -0
- package/src/session/index.ts +14 -0
- package/src/session/memory-store.ts +320 -0
- package/src/session/redis-store.ts +443 -0
- package/src/strategies/oauth.strategy.ts +128 -0
- package/src/strategies/traditional-auth.strategy.ts +116 -0
- package/src/tokens/index.ts +4 -0
- package/src/tokens/refresh-token-manager.ts +448 -0
- package/src/tokens/token-validator.ts +311 -0
- package/tsconfig.build.json +28 -0
- package/tsconfig.json +38 -0
- package/tsup.config.mjs +28 -0
- package/vitest.config.mjs +16 -0
- package/vitest.setup.d.ts +2 -0
- package/vitest.setup.d.ts.map +1 -0
- package/vitest.setup.ts +1 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// src/security/rate-limiting/auth/brute-force.service.ts
|
|
2
|
+
import { Injectable, BadRequestException } from "@nestjs/common";
|
|
3
|
+
import { NUMERIX } from "@plyaz/config";
|
|
4
|
+
import type { Redis } from "ioredis";
|
|
5
|
+
|
|
6
|
+
const thirty = 30;
|
|
7
|
+
@Injectable()
|
|
8
|
+
export class BruteForceService {
|
|
9
|
+
private static readonly USER_HARD_LIMIT = 6;
|
|
10
|
+
|
|
11
|
+
private static readonly USER_HARD_BLOCK_MS = thirty * NUMERIX.SIXTY * NUMERIX.THOUSAND; // 30 min
|
|
12
|
+
private static readonly USER_PROGRESSIVE_DELAYS: Record<number, number> = { 4: 5000, 5: 5000 };
|
|
13
|
+
|
|
14
|
+
private static readonly IP_HARD_LIMIT = 10;
|
|
15
|
+
private static readonly IP_HARD_BLOCK_MS = NUMERIX.SIXTY * NUMERIX.SIXTY * NUMERIX.THOUSAND; // 1 hour
|
|
16
|
+
|
|
17
|
+
private static readonly PROVIDER_HARD_LIMIT = 5;
|
|
18
|
+
private static readonly PROVIDER_HARD_BLOCK_MS = thirty * NUMERIX.SIXTY * NUMERIX.THOUSAND; // 30 min
|
|
19
|
+
|
|
20
|
+
constructor(private readonly redis: Redis) {}
|
|
21
|
+
|
|
22
|
+
async trackFailedLogin(
|
|
23
|
+
userId: string,
|
|
24
|
+
ip: string,
|
|
25
|
+
providerId?: string
|
|
26
|
+
): Promise<void> {
|
|
27
|
+
// User-level progressive delay + hard block
|
|
28
|
+
await this.trackEntity(
|
|
29
|
+
`bf:user:${userId}`,
|
|
30
|
+
`bf:user:block:${userId}`,
|
|
31
|
+
BruteForceService.USER_HARD_LIMIT,
|
|
32
|
+
BruteForceService.USER_HARD_BLOCK_MS,
|
|
33
|
+
BruteForceService.USER_PROGRESSIVE_DELAYS
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// IP-level block
|
|
37
|
+
await this.trackEntity(
|
|
38
|
+
`bf:ip:${ip}`,
|
|
39
|
+
`bf:ip:block:${ip}`,
|
|
40
|
+
BruteForceService.IP_HARD_LIMIT,
|
|
41
|
+
BruteForceService.IP_HARD_BLOCK_MS
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// Provider-level block (optional)
|
|
45
|
+
if (providerId) {
|
|
46
|
+
await this.trackEntity(
|
|
47
|
+
`bf:provider:${providerId}`,
|
|
48
|
+
`bf:provider:block:${providerId}`,
|
|
49
|
+
BruteForceService.PROVIDER_HARD_LIMIT,
|
|
50
|
+
BruteForceService.PROVIDER_HARD_BLOCK_MS
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// eslint-disable-next-line max-params
|
|
56
|
+
private async trackEntity(
|
|
57
|
+
entityKey: string,
|
|
58
|
+
blockKey: string,
|
|
59
|
+
limit: number,
|
|
60
|
+
blockMs: number,
|
|
61
|
+
progressiveDelays?: Record<number, number>
|
|
62
|
+
): Promise<void> {
|
|
63
|
+
const blockedUntil = await this.redis.get(blockKey);
|
|
64
|
+
if (blockedUntil && Date.now() < Number(blockedUntil)) {
|
|
65
|
+
throw new BadRequestException(
|
|
66
|
+
"Account temporarily blocked due to repeated failed attempts."
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const failures = await this.redis.incr(entityKey);
|
|
71
|
+
if (failures === 1) await this.redis.pexpire(entityKey, blockMs);
|
|
72
|
+
|
|
73
|
+
if (progressiveDelays?.[failures]) {
|
|
74
|
+
await new Promise<void>((resolve) => {
|
|
75
|
+
globalThis.setTimeout(resolve, progressiveDelays[failures]);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if (failures >= limit) {
|
|
81
|
+
await this.redis.set(blockKey, Date.now() + blockMs, "PX", blockMs);
|
|
82
|
+
await this.redis.del(entityKey); // reset failures
|
|
83
|
+
throw new BadRequestException(
|
|
84
|
+
"Account locked due to too many failed login attempts."
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async reset(userId: string, ip: string, providerId?: string): Promise<void> {
|
|
90
|
+
await this.redis.del(`bf:user:${userId}`, `bf:user:block:${userId}`);
|
|
91
|
+
await this.redis.del(`bf:ip:${ip}`, `bf:ip:block:${ip}`);
|
|
92
|
+
if (providerId)
|
|
93
|
+
await this.redis.del(
|
|
94
|
+
`bf:provider:${providerId}`,
|
|
95
|
+
`bf:provider:block:${providerId}`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Server services barrel export for @plyaz/auth
|
|
3
|
+
* @module @plyaz/auth/server/services
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* Centralized export of all NestJS services for authentication and authorization.
|
|
7
|
+
* Provides a single import point for all service functionality.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export * from "./auth.service";
|
|
11
|
+
export * from "./session.service";
|
|
12
|
+
export * from "./token.service";
|
|
13
|
+
export * from "./account.service";
|
|
14
|
+
export * from "./brute-force.service";
|
|
15
|
+
export * from "./rate-limiter.service"
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common";
|
|
2
|
+
import type { Redis } from "ioredis";
|
|
3
|
+
|
|
4
|
+
export interface RateLimiterOptions {
|
|
5
|
+
windowMs: number; // time window in milliseconds
|
|
6
|
+
limit: number; // max requests allowed in the window
|
|
7
|
+
blockMs?: number; // optional block duration in milliseconds
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
@Injectable()
|
|
11
|
+
export class RateLimiterService {
|
|
12
|
+
constructor(private readonly redis: Redis) {}
|
|
13
|
+
|
|
14
|
+
getOption(routeKey: string): RateLimiterOptions {
|
|
15
|
+
globalThis.console.log(routeKey)
|
|
16
|
+
// Example placeholder, replace with your actual logic
|
|
17
|
+
throw new Error("Method not implemented.");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async isRateLimited(
|
|
21
|
+
identifier: string,
|
|
22
|
+
options: RateLimiterOptions
|
|
23
|
+
): Promise<boolean> {
|
|
24
|
+
const rateKey = `rate:${identifier}`;
|
|
25
|
+
const blockKey = `block:${identifier}`;
|
|
26
|
+
|
|
27
|
+
// Check if user is already blocked
|
|
28
|
+
const blockedUntil = await this.redis.get(blockKey);
|
|
29
|
+
if (blockedUntil && Date.now() < Number(blockedUntil)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Increase request count
|
|
34
|
+
const requests = await this.redis.incr(rateKey);
|
|
35
|
+
|
|
36
|
+
// Set window expiry on first request
|
|
37
|
+
if (requests === 1) {
|
|
38
|
+
await this.redis.pexpire(rateKey, options.windowMs);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Block user if limit exceeded
|
|
42
|
+
if (requests > options.limit && options.blockMs) {
|
|
43
|
+
const blockUntil = Date.now() + options.blockMs;
|
|
44
|
+
|
|
45
|
+
await this.redis.set(blockKey, blockUntil.toString(), "PX", options.blockMs);
|
|
46
|
+
await this.redis.del(rateKey); // reset counter
|
|
47
|
+
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async reset(identifier: string): Promise<void> {
|
|
55
|
+
const rateKey = `rate:${identifier}`;
|
|
56
|
+
const blockKey = `block:${identifier}`;
|
|
57
|
+
|
|
58
|
+
await this.redis.del(rateKey, blockKey);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Session service for @plyaz/auth
|
|
3
|
+
* @module @plyaz/auth/server/services/session-service
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* NestJS service for session management operations. Provides high-level
|
|
7
|
+
* session operations for controllers and other services. Wraps the
|
|
8
|
+
* EnhancedSessionManager with NestJS dependency injection and logging.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { SessionService } from '@plyaz/auth';
|
|
13
|
+
*
|
|
14
|
+
* @Controller('auth')
|
|
15
|
+
* export class AuthController {
|
|
16
|
+
* constructor(private sessionService: SessionService) {}
|
|
17
|
+
*
|
|
18
|
+
* @Post('logout-all')
|
|
19
|
+
* async logoutAll(@CurrentUser() user) {
|
|
20
|
+
* await this.sessionService.invalidateAllUserSessions(user.id);
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { Injectable, Logger } from '@nestjs/common';
|
|
27
|
+
import type { EnhancedSessionManager, UserContext, SessionCreationData } from '../../session/enhanced-session-manager';
|
|
28
|
+
|
|
29
|
+
import type { Session, SessionData } from '@plyaz/types';
|
|
30
|
+
import { AUTH_EVENTS } from '@plyaz/types';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Session service implementation
|
|
34
|
+
* Provides session management operations for NestJS applications
|
|
35
|
+
*/
|
|
36
|
+
@Injectable()
|
|
37
|
+
export class SessionService {
|
|
38
|
+
private readonly logger = new Logger(SessionService.name);
|
|
39
|
+
|
|
40
|
+
constructor(private sessionManager: EnhancedSessionManager) {}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Create a new session
|
|
44
|
+
* @param userContext - User context information
|
|
45
|
+
* @param sessionData - Session creation data
|
|
46
|
+
* @returns Created session
|
|
47
|
+
*/
|
|
48
|
+
async createSession(userContext: UserContext, sessionData: SessionCreationData = {}): Promise<Session> {
|
|
49
|
+
this.logger.debug(`Creating session for user: ${userContext.userId}`);
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const session = await this.sessionManager.createSession(userContext, sessionData);
|
|
53
|
+
|
|
54
|
+
this.logger.log(`Session created: ${session.id} for user: ${userContext.userId}`);
|
|
55
|
+
this.emitEvent(AUTH_EVENTS.SESSION_CREATED, {
|
|
56
|
+
userId: userContext.userId,
|
|
57
|
+
sessionId: session.id,
|
|
58
|
+
timestamp: new Date()
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return session;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
this.logger.error(`Failed to create session for user: ${userContext.userId}`, error);
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get session by ID
|
|
70
|
+
* @param sessionId - Session identifier
|
|
71
|
+
* @returns Session or null if not found
|
|
72
|
+
*/
|
|
73
|
+
async getSession(sessionId: string): Promise<Session | null> {
|
|
74
|
+
this.logger.debug(`Getting session: ${sessionId}`);
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
return await this.sessionManager.getSession(sessionId);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
this.logger.error(`Failed to get session: ${sessionId}`, error);
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Validate session
|
|
86
|
+
* @param sessionId - Session identifier
|
|
87
|
+
* @returns True if session is valid
|
|
88
|
+
*/
|
|
89
|
+
async validateSession(sessionId: string): Promise<boolean> {
|
|
90
|
+
this.logger.debug(`Validating session: ${sessionId}`);
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const isValid = await this.sessionManager.validateSession(sessionId);
|
|
94
|
+
|
|
95
|
+
if (!isValid) {
|
|
96
|
+
this.logger.warn(`Invalid session: ${sessionId}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return isValid;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
this.logger.error(`Failed to validate session: ${sessionId}`, error);
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Refresh session
|
|
108
|
+
* @param sessionId - Session identifier
|
|
109
|
+
* @returns Refreshed session
|
|
110
|
+
*/
|
|
111
|
+
async refreshSession(sessionId: string): Promise<Session> {
|
|
112
|
+
this.logger.debug(`Refreshing session: ${sessionId}`);
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
const session = await this.sessionManager.refreshSession(sessionId);
|
|
116
|
+
|
|
117
|
+
this.logger.log(`Session refreshed: ${sessionId}`);
|
|
118
|
+
this.emitEvent(AUTH_EVENTS.SESSION_REFRESHED, {
|
|
119
|
+
userId: session.userId,
|
|
120
|
+
sessionId: session.id,
|
|
121
|
+
timestamp: new Date()
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return session;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
this.logger.error(`Failed to refresh session: ${sessionId}`, error);
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Update session data
|
|
133
|
+
* @param sessionId - Session identifier
|
|
134
|
+
* @param updates - Partial session updates
|
|
135
|
+
* @returns Updated session
|
|
136
|
+
*/
|
|
137
|
+
async updateSession(sessionId: string, updates:Partial<SessionData>): Promise<Session> {
|
|
138
|
+
this.logger.debug(`Updating session: ${sessionId}`);
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
return await this.sessionManager.updateSession(sessionId, updates);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
this.logger.error(`Failed to update session: ${sessionId}`, error);
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Delete session
|
|
150
|
+
* @param sessionId - Session identifier
|
|
151
|
+
*/
|
|
152
|
+
async deleteSession(sessionId: string): Promise<void> {
|
|
153
|
+
this.logger.debug(`Deleting session: ${sessionId}`);
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
await this.sessionManager.deleteSession(sessionId);
|
|
157
|
+
|
|
158
|
+
this.logger.log(`Session deleted: ${sessionId}`);
|
|
159
|
+
this.emitEvent(AUTH_EVENTS.SESSION_INVALIDATED, {
|
|
160
|
+
sessionId,
|
|
161
|
+
timestamp: new Date()
|
|
162
|
+
});
|
|
163
|
+
} catch (error) {
|
|
164
|
+
this.logger.error(`Failed to delete session: ${sessionId}`, error);
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Invalidate all sessions for a user
|
|
171
|
+
* @param userId - User identifier
|
|
172
|
+
*/
|
|
173
|
+
async invalidateAllUserSessions(userId: string): Promise<void> {
|
|
174
|
+
this.logger.debug(`Invalidating all sessions for user: ${userId}`);
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
await this.sessionManager.invalidateAllUserSessions(userId);
|
|
178
|
+
|
|
179
|
+
this.logger.log(`All sessions invalidated for user: ${userId}`);
|
|
180
|
+
this.emitEvent(AUTH_EVENTS.SESSION_INVALIDATED, {
|
|
181
|
+
userId,
|
|
182
|
+
timestamp: new Date()
|
|
183
|
+
});
|
|
184
|
+
} catch (error) {
|
|
185
|
+
this.logger.error(`Failed to invalidate all sessions for user: ${userId}`, error);
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Detect concurrent sessions for a user
|
|
192
|
+
* @param userId - User identifier
|
|
193
|
+
* @returns Array of active sessions
|
|
194
|
+
*/
|
|
195
|
+
async detectConcurrentSessions(userId: string): Promise<Session[]> {
|
|
196
|
+
this.logger.debug(`Detecting concurrent sessions for user: ${userId}`);
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
return await this.sessionManager.detectConcurrentSessions(userId);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
this.logger.error(`Failed to detect concurrent sessions for user: ${userId}`, error);
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Generate CSRF token for session
|
|
208
|
+
* @param sessionId - Session identifier
|
|
209
|
+
* @returns CSRF token
|
|
210
|
+
*/
|
|
211
|
+
async generateCsrfToken(sessionId: string): Promise<string> {
|
|
212
|
+
this.logger.debug(`Generating CSRF token for session: ${sessionId}`);
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
return await this.sessionManager.generateCsrfToken(sessionId);
|
|
216
|
+
} catch (error) {
|
|
217
|
+
this.logger.error(`Failed to generate CSRF token for session: ${sessionId}`, error);
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Validate CSRF token for session
|
|
224
|
+
* @param sessionId - Session identifier
|
|
225
|
+
* @param token - CSRF token to validate
|
|
226
|
+
* @returns True if token is valid
|
|
227
|
+
*/
|
|
228
|
+
async validateCsrfToken(sessionId: string, token: string): Promise<boolean> {
|
|
229
|
+
this.logger.debug(`Validating CSRF token for session: ${sessionId}`);
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
const isValid = await this.sessionManager.validateCsrfToken(sessionId, token);
|
|
233
|
+
|
|
234
|
+
if (!isValid) {
|
|
235
|
+
this.logger.warn(`Invalid CSRF token for session: ${sessionId}`);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return isValid;
|
|
239
|
+
} catch (error) {
|
|
240
|
+
this.logger.error(`Failed to validate CSRF token for session: ${sessionId}`, error);
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Check if session needs refresh
|
|
247
|
+
* @param sessionId - Session identifier
|
|
248
|
+
* @returns True if session should be refreshed
|
|
249
|
+
*/
|
|
250
|
+
async shouldRefreshSession(sessionId: string): Promise<boolean> {
|
|
251
|
+
try {
|
|
252
|
+
return await this.sessionManager.shouldRefreshSession(sessionId);
|
|
253
|
+
} catch (error) {
|
|
254
|
+
this.logger.error(`Failed to check if session needs refresh: ${sessionId}`, error);
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Get session statistics
|
|
261
|
+
* @returns Session statistics
|
|
262
|
+
*/
|
|
263
|
+
async getSessionStats(): Promise<{
|
|
264
|
+
totalSessions: number;
|
|
265
|
+
activeUsers: number;
|
|
266
|
+
}> {
|
|
267
|
+
this.logger.debug('Getting session statistics');
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
return await this.sessionManager.getSessionStats();
|
|
271
|
+
} catch (error) {
|
|
272
|
+
this.logger.error('Failed to get session statistics', error);
|
|
273
|
+
throw error;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Emit event (mock implementation)
|
|
279
|
+
* @param eventType - Event type
|
|
280
|
+
* @param payload - Event payload
|
|
281
|
+
* @private
|
|
282
|
+
*/
|
|
283
|
+
private emitEvent(eventType: string, payload:unknown): void {
|
|
284
|
+
// Mock event emission - in real implementation would use event system
|
|
285
|
+
this.logger.debug(`Event: ${eventType}`, payload);
|
|
286
|
+
}
|
|
287
|
+
}
|