@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.
- package/commits.txt +3 -3
- package/dist/common/index.cjs +3 -1
- package/dist/common/index.cjs.map +1 -1
- package/dist/common/index.mjs +3 -1
- package/dist/common/index.mjs.map +1 -1
- package/dist/index.cjs +424 -154
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +421 -152
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
- package/release_message.txt +28 -0
- package/src/adapters/auth-adapter-factory.ts +4 -3
- package/src/adapters/auth-adapter.mapper.ts +2 -2
- package/src/adapters/base-auth.adapter.ts +17 -9
- package/src/adapters/clerk/clerk.adapter.ts +9 -12
- package/src/adapters/custom/custom.adapter.ts +19 -10
- package/src/adapters/index.ts +0 -1
- package/src/adapters/next-auth/authOptions.ts +20 -16
- package/src/adapters/next-auth/next-auth.adapter.ts +13 -15
- package/src/api/client.ts +4 -6
- package/src/audit/audit.logger.ts +19 -10
- package/src/client/components/ProtectedRoute.tsx +15 -11
- package/src/client/hooks/useAuth.ts +23 -21
- package/src/client/hooks/useConnectedAccounts.ts +57 -45
- package/src/client/hooks/usePermissions.ts +1 -1
- package/src/client/hooks/useRBAC.ts +6 -6
- package/src/client/hooks/useSession.ts +5 -5
- package/src/client/providers/AuthProvider.tsx +23 -17
- package/src/client/store/auth.store.ts +71 -62
- package/src/client/utils/storage.ts +45 -18
- package/src/common/constants/oauth-providers.ts +10 -7
- package/src/common/errors/auth.errors.ts +4 -4
- package/src/common/errors/specific-auth-errors.ts +5 -9
- package/src/common/regex/index.ts +6 -4
- package/src/common/types/auth.types.ts +47 -38
- package/src/common/types/index.ts +12 -6
- package/src/common/utils/index.ts +15 -11
- package/src/core/blacklist/token.blacklist.ts +13 -7
- package/src/core/index.ts +2 -2
- package/src/core/jwt/jwt.manager.ts +47 -22
- package/src/core/session/session.manager.ts +17 -14
- package/src/db/repositories/connected-account.repository.ts +120 -78
- package/src/db/repositories/role.repository.ts +41 -26
- package/src/db/repositories/session.repository.ts +9 -10
- package/src/db/repositories/user.repository.ts +105 -91
- package/src/flows/index.ts +2 -2
- package/src/flows/sign-in.flow.ts +28 -14
- package/src/flows/sign-up.flow.ts +31 -20
- package/src/index.ts +36 -37
- package/src/libs/clerk.helper.ts +6 -7
- package/src/libs/supabase.helper.ts +79 -61
- package/src/libs/supabaseClient.ts +3 -3
- package/src/providers/base/auth-provider.interface.ts +13 -11
- package/src/providers/base/index.ts +1 -1
- package/src/providers/index.ts +1 -1
- package/src/providers/oauth/facebook.provider.ts +63 -39
- package/src/providers/oauth/github.provider.ts +14 -10
- package/src/providers/oauth/google.provider.ts +39 -28
- package/src/providers/oauth/index.ts +1 -1
- package/src/rbac/dynamic-roles.ts +88 -54
- package/src/rbac/index.ts +4 -4
- package/src/rbac/permission-checker.ts +147 -75
- package/src/rbac/role-hierarchy.ts +8 -8
- package/src/rbac/role.manager.ts +11 -8
- package/src/security/csrf/csrf.protection.ts +9 -7
- package/src/security/index.ts +2 -2
- package/src/security/rate-limiting/auth/auth.controller.ts +2 -4
- package/src/security/rate-limiting/auth/rate-limiting.interface.ts +26 -6
- package/src/security/rate-limiting/auth.module.ts +1 -2
- package/src/server/auth.module.ts +55 -52
- package/src/server/decorators/auth.decorator.ts +9 -11
- package/src/server/decorators/auth.decorators.ts +8 -9
- package/src/server/decorators/current-user.decorator.ts +6 -6
- package/src/server/decorators/permission.decorator.ts +17 -9
- package/src/server/guards/auth.guard.ts +21 -16
- package/src/server/guards/custom-throttler.guard.ts +4 -9
- package/src/server/guards/permissions.guard.ts +32 -23
- package/src/server/guards/roles.guard.ts +14 -12
- package/src/server/middleware/auth.middleware.ts +4 -4
- package/src/server/middleware/session.middleware.ts +4 -4
- package/src/server/services/account.service.ts +96 -48
- package/src/server/services/auth.service.ts +57 -28
- package/src/server/services/brute-force.service.ts +24 -19
- package/src/server/services/index.ts +1 -1
- package/src/server/services/rate-limiter.service.ts +9 -4
- package/src/server/services/session.service.ts +84 -48
- package/src/server/services/token.service.ts +71 -51
- package/src/session/cookie-store.ts +47 -34
- package/src/session/enhanced-session-manager.ts +69 -48
- package/src/session/index.ts +5 -5
- package/src/session/memory-store.ts +37 -30
- package/src/session/redis-store.ts +105 -72
- package/src/strategies/oauth.strategy.ts +10 -9
- package/src/strategies/traditional-auth.strategy.ts +41 -29
- package/src/tokens/index.ts +4 -4
- package/src/tokens/refresh-token-manager.ts +70 -55
- package/src/tokens/token-validator.ts +109 -53
- package/vitest.setup.d.ts +2 -2
- package/vitest.setup.ts +1 -1
|
@@ -1,30 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Cookie-based session store for @plyaz/auth
|
|
3
3
|
* @module @plyaz/auth/session/cookie-store
|
|
4
|
-
*
|
|
4
|
+
*
|
|
5
5
|
* @description
|
|
6
6
|
* Implements session storage using HTTP cookies. Provides stateless session
|
|
7
7
|
* management by storing session data directly in encrypted cookies.
|
|
8
8
|
* Suitable for applications that don't require server-side session storage
|
|
9
9
|
* or need to minimize server memory usage.
|
|
10
|
-
*
|
|
10
|
+
*
|
|
11
11
|
* @example
|
|
12
12
|
* ```typescript
|
|
13
13
|
* import { CookieStore } from '@plyaz/auth';
|
|
14
|
-
*
|
|
14
|
+
*
|
|
15
15
|
* const store = new CookieStore({
|
|
16
16
|
* secretKey: 'your-secret-key',
|
|
17
17
|
* cookieName: 'session',
|
|
18
18
|
* secure: true
|
|
19
19
|
* });
|
|
20
|
-
*
|
|
20
|
+
*
|
|
21
21
|
* await store.set('session_123', sessionData, 3600);
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
import { NUMERIX } from
|
|
26
|
-
import type {
|
|
27
|
-
|
|
25
|
+
import { NUMERIX } from "@plyaz/config";
|
|
26
|
+
import type {
|
|
27
|
+
SessionData,
|
|
28
|
+
SessionStore,
|
|
29
|
+
SessionStoreConfig,
|
|
30
|
+
} from "@plyaz/types";
|
|
31
|
+
import { createCipher, createDecipher } from "crypto";
|
|
28
32
|
|
|
29
33
|
/**
|
|
30
34
|
* Cookie store configuration
|
|
@@ -43,7 +47,7 @@ export interface CookieStoreConfig extends Partial<SessionStoreConfig> {
|
|
|
43
47
|
/** HttpOnly flag */
|
|
44
48
|
httpOnly?: boolean;
|
|
45
49
|
/** SameSite policy */
|
|
46
|
-
sameSite?:
|
|
50
|
+
sameSite?: "strict" | "lax" | "none";
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
/**
|
|
@@ -59,13 +63,13 @@ export class CookieStore implements SessionStore {
|
|
|
59
63
|
defaultTTL: 3600,
|
|
60
64
|
maxSessionsPerUser: 5,
|
|
61
65
|
cleanupInterval: 300,
|
|
62
|
-
keyPrefix:
|
|
63
|
-
domain:
|
|
64
|
-
path:
|
|
66
|
+
keyPrefix: "session:",
|
|
67
|
+
domain: "",
|
|
68
|
+
path: "/",
|
|
65
69
|
secure: true,
|
|
66
70
|
httpOnly: true,
|
|
67
|
-
sameSite:
|
|
68
|
-
...config
|
|
71
|
+
sameSite: "lax",
|
|
72
|
+
...config,
|
|
69
73
|
};
|
|
70
74
|
}
|
|
71
75
|
|
|
@@ -75,12 +79,16 @@ export class CookieStore implements SessionStore {
|
|
|
75
79
|
* @param data - Session data to store
|
|
76
80
|
* @param ttlSeconds - Time to live in seconds
|
|
77
81
|
*/
|
|
78
|
-
async set(
|
|
82
|
+
async set(
|
|
83
|
+
sessionId: string,
|
|
84
|
+
data: SessionData,
|
|
85
|
+
ttlSeconds: number,
|
|
86
|
+
): Promise<void> {
|
|
79
87
|
// Store in memory for this implementation
|
|
80
88
|
// In real implementation, this would set HTTP cookie
|
|
81
89
|
this.sessions.set(sessionId, {
|
|
82
90
|
...data,
|
|
83
|
-
expiresAt: new Date(Date.now() + ttlSeconds * NUMERIX.THOUSAND)
|
|
91
|
+
expiresAt: new Date(Date.now() + ttlSeconds * NUMERIX.THOUSAND),
|
|
84
92
|
});
|
|
85
93
|
|
|
86
94
|
// Simulate cookie setting
|
|
@@ -94,7 +102,7 @@ export class CookieStore implements SessionStore {
|
|
|
94
102
|
*/
|
|
95
103
|
async get(sessionId: string): Promise<SessionData | null> {
|
|
96
104
|
const data = this.sessions.get(sessionId);
|
|
97
|
-
|
|
105
|
+
|
|
98
106
|
if (!data) {
|
|
99
107
|
return null;
|
|
100
108
|
}
|
|
@@ -125,14 +133,14 @@ export class CookieStore implements SessionStore {
|
|
|
125
133
|
*/
|
|
126
134
|
async deleteByUserId(userId: string): Promise<number> {
|
|
127
135
|
let deletedCount = 0;
|
|
128
|
-
|
|
136
|
+
|
|
129
137
|
for (const [sessionId, data] of this.sessions.entries()) {
|
|
130
138
|
if (data.userId === userId) {
|
|
131
139
|
await this.delete(sessionId);
|
|
132
140
|
deletedCount++;
|
|
133
141
|
}
|
|
134
142
|
}
|
|
135
|
-
|
|
143
|
+
|
|
136
144
|
return deletedCount;
|
|
137
145
|
}
|
|
138
146
|
|
|
@@ -165,13 +173,13 @@ export class CookieStore implements SessionStore {
|
|
|
165
173
|
*/
|
|
166
174
|
async getByUserId(userId: string): Promise<SessionData[]> {
|
|
167
175
|
const userSessions: SessionData[] = [];
|
|
168
|
-
|
|
176
|
+
|
|
169
177
|
for (const data of this.sessions.values()) {
|
|
170
178
|
if (data.userId === userId && data.expiresAt > new Date()) {
|
|
171
179
|
userSessions.push(data);
|
|
172
180
|
}
|
|
173
181
|
}
|
|
174
|
-
|
|
182
|
+
|
|
175
183
|
return userSessions;
|
|
176
184
|
}
|
|
177
185
|
|
|
@@ -182,14 +190,14 @@ export class CookieStore implements SessionStore {
|
|
|
182
190
|
async cleanup(): Promise<number> {
|
|
183
191
|
let cleanedCount = 0;
|
|
184
192
|
const now = new Date();
|
|
185
|
-
|
|
193
|
+
|
|
186
194
|
for (const [sessionId, data] of this.sessions.entries()) {
|
|
187
195
|
if (data.expiresAt < now) {
|
|
188
196
|
await this.delete(sessionId);
|
|
189
197
|
cleanedCount++;
|
|
190
198
|
}
|
|
191
199
|
}
|
|
192
|
-
|
|
200
|
+
|
|
193
201
|
return cleanedCount;
|
|
194
202
|
}
|
|
195
203
|
|
|
@@ -210,9 +218,9 @@ export class CookieStore implements SessionStore {
|
|
|
210
218
|
* @private
|
|
211
219
|
*/
|
|
212
220
|
private encrypt(data: string): string {
|
|
213
|
-
const cipher = createCipher(
|
|
214
|
-
let encrypted = cipher.update(data,
|
|
215
|
-
encrypted += cipher.final(
|
|
221
|
+
const cipher = createCipher("aes-256-cbc", this.config.secretKey);
|
|
222
|
+
let encrypted = cipher.update(data, "utf8", "hex");
|
|
223
|
+
encrypted += cipher.final("hex");
|
|
216
224
|
return encrypted;
|
|
217
225
|
}
|
|
218
226
|
|
|
@@ -223,9 +231,9 @@ export class CookieStore implements SessionStore {
|
|
|
223
231
|
* @private
|
|
224
232
|
*/
|
|
225
233
|
private decrypt(encryptedData: string): string {
|
|
226
|
-
const decipher = createDecipher(
|
|
227
|
-
let decrypted = decipher.update(encryptedData,
|
|
228
|
-
decrypted += decipher.final(
|
|
234
|
+
const decipher = createDecipher("aes-256-cbc", this.config.secretKey);
|
|
235
|
+
let decrypted = decipher.update(encryptedData, "hex", "utf8");
|
|
236
|
+
decrypted += decipher.final("utf8");
|
|
229
237
|
return decrypted;
|
|
230
238
|
}
|
|
231
239
|
|
|
@@ -236,10 +244,16 @@ export class CookieStore implements SessionStore {
|
|
|
236
244
|
* @param ttlSeconds - Time to live in seconds
|
|
237
245
|
* @private
|
|
238
246
|
*/
|
|
239
|
-
private setCookie(
|
|
247
|
+
private setCookie(
|
|
248
|
+
sessionId: string,
|
|
249
|
+
data: SessionData,
|
|
250
|
+
ttlSeconds: number,
|
|
251
|
+
): void {
|
|
240
252
|
// Mock implementation - in real app this would use response.cookie()
|
|
241
253
|
const encryptedData = this.encrypt(JSON.stringify(data));
|
|
242
|
-
globalThis.console.log(
|
|
254
|
+
globalThis.console.log(
|
|
255
|
+
`Setting cookie: ${this.config.cookieName}=${encryptedData}; Max-Age=${ttlSeconds}`,
|
|
256
|
+
);
|
|
243
257
|
}
|
|
244
258
|
|
|
245
259
|
/**
|
|
@@ -247,9 +261,8 @@ export class CookieStore implements SessionStore {
|
|
|
247
261
|
* @param sessionId - Session identifier
|
|
248
262
|
* @private
|
|
249
263
|
*/
|
|
250
|
-
private clearCookie(sessionId:string): void {
|
|
251
|
-
globalThis.console.log(
|
|
264
|
+
private clearCookie(sessionId: string): void {
|
|
265
|
+
globalThis.console.log("session_id", sessionId);
|
|
252
266
|
// Mock implementation - in real app this would use response.clearCookie()
|
|
253
|
-
|
|
254
267
|
}
|
|
255
|
-
}
|
|
268
|
+
}
|
|
@@ -1,32 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Enhanced session manager for @plyaz/auth
|
|
3
3
|
* @module @plyaz/auth/session/enhanced-session-manager
|
|
4
|
-
*
|
|
4
|
+
*
|
|
5
5
|
* @description
|
|
6
6
|
* Enhanced session manager that implements all documented session management
|
|
7
7
|
* methods. Provides comprehensive session lifecycle management including
|
|
8
8
|
* CSRF token generation, concurrent session detection, and session validation.
|
|
9
9
|
* Uses pluggable session stores for flexible storage backends.
|
|
10
|
-
*
|
|
10
|
+
*
|
|
11
11
|
* @example
|
|
12
12
|
* ```typescript
|
|
13
13
|
* import { EnhancedSessionManager, MemoryStore } from '@plyaz/auth';
|
|
14
|
-
*
|
|
14
|
+
*
|
|
15
15
|
* const sessionManager = new EnhancedSessionManager({
|
|
16
16
|
* store: new MemoryStore(),
|
|
17
17
|
* sessionTTL: 3600,
|
|
18
18
|
* maxConcurrentSessions: 5
|
|
19
19
|
* });
|
|
20
|
-
*
|
|
20
|
+
*
|
|
21
21
|
* const session = await sessionManager.createSession(userContext, sessionData);
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
import
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import { NUMERIX } from '@plyaz/config';
|
|
25
|
+
import type { Session, SessionData, SessionStore } from "@plyaz/types";
|
|
26
|
+
import { randomBytes } from "crypto";
|
|
27
|
+
import { CSRFProtection } from "../security";
|
|
28
|
+
import { NUMERIX } from "@plyaz/config";
|
|
30
29
|
|
|
31
30
|
/**
|
|
32
31
|
* User context for session creation
|
|
@@ -85,7 +84,9 @@ export class EnhancedSessionManager {
|
|
|
85
84
|
private readonly store: SessionStore;
|
|
86
85
|
private readonly csrfProtection: CSRFProtection;
|
|
87
86
|
|
|
88
|
-
constructor(
|
|
87
|
+
constructor(
|
|
88
|
+
config: Partial<EnhancedSessionManagerConfig> & { store: SessionStore },
|
|
89
|
+
) {
|
|
89
90
|
this.config = {
|
|
90
91
|
sessionTTL: 3600,
|
|
91
92
|
maxConcurrentSessions: 5,
|
|
@@ -93,7 +94,7 @@ export class EnhancedSessionManager {
|
|
|
93
94
|
csrfTTL: 3600,
|
|
94
95
|
enableRefresh: true,
|
|
95
96
|
refreshThreshold: 300, // 5 minutes
|
|
96
|
-
...config
|
|
97
|
+
...config,
|
|
97
98
|
};
|
|
98
99
|
|
|
99
100
|
this.store = config.store;
|
|
@@ -107,17 +108,22 @@ export class EnhancedSessionManager {
|
|
|
107
108
|
* @param sessionData - Session creation data
|
|
108
109
|
* @returns Created session
|
|
109
110
|
*/
|
|
110
|
-
async createSession(
|
|
111
|
+
async createSession(
|
|
112
|
+
userContext: UserContext,
|
|
113
|
+
sessionData: SessionCreationData = {},
|
|
114
|
+
): Promise<Session> {
|
|
111
115
|
// Generate unique session ID
|
|
112
116
|
const sessionId = this.generateSessionId();
|
|
113
|
-
|
|
117
|
+
|
|
114
118
|
// Enforce session limits
|
|
115
119
|
await this.enforceSessionLimits(userContext.userId);
|
|
116
120
|
|
|
117
121
|
// Create session data
|
|
118
122
|
const now = new Date();
|
|
119
|
-
const expiresAt = new Date(
|
|
120
|
-
|
|
123
|
+
const expiresAt = new Date(
|
|
124
|
+
now.getTime() + this.config.sessionTTL * NUMERIX.THOUSAND,
|
|
125
|
+
);
|
|
126
|
+
|
|
121
127
|
const session: SessionData = {
|
|
122
128
|
id: sessionId,
|
|
123
129
|
userId: userContext.userId,
|
|
@@ -130,8 +136,8 @@ export class EnhancedSessionManager {
|
|
|
130
136
|
...sessionData.metadata,
|
|
131
137
|
deviceInfo: sessionData.deviceInfo,
|
|
132
138
|
userRoles: userContext.roles,
|
|
133
|
-
userEmail: userContext.email
|
|
134
|
-
}
|
|
139
|
+
userEmail: userContext.email,
|
|
140
|
+
},
|
|
135
141
|
};
|
|
136
142
|
|
|
137
143
|
// Store session
|
|
@@ -146,7 +152,7 @@ export class EnhancedSessionManager {
|
|
|
146
152
|
return {
|
|
147
153
|
id: sessionId,
|
|
148
154
|
userId: userContext.userId,
|
|
149
|
-
provider:
|
|
155
|
+
provider: "enhanced-session-manager",
|
|
150
156
|
providerSessionId: sessionId,
|
|
151
157
|
expiresAt,
|
|
152
158
|
createdAt: now,
|
|
@@ -155,8 +161,8 @@ export class EnhancedSessionManager {
|
|
|
155
161
|
userAgent: sessionData.userAgent,
|
|
156
162
|
metadata: {
|
|
157
163
|
...session.metadata,
|
|
158
|
-
csrfToken
|
|
159
|
-
}
|
|
164
|
+
csrfToken,
|
|
165
|
+
},
|
|
160
166
|
};
|
|
161
167
|
}
|
|
162
168
|
|
|
@@ -167,7 +173,7 @@ export class EnhancedSessionManager {
|
|
|
167
173
|
*/
|
|
168
174
|
async getSession(sessionId: string): Promise<Session | null> {
|
|
169
175
|
const sessionData = await this.store.get(sessionId);
|
|
170
|
-
|
|
176
|
+
|
|
171
177
|
if (!sessionData) {
|
|
172
178
|
return null;
|
|
173
179
|
}
|
|
@@ -181,25 +187,33 @@ export class EnhancedSessionManager {
|
|
|
181
187
|
* @param updates - Partial session updates
|
|
182
188
|
* @returns Updated session
|
|
183
189
|
*/
|
|
184
|
-
async updateSession(
|
|
190
|
+
async updateSession(
|
|
191
|
+
sessionId: string,
|
|
192
|
+
updates: Partial<SessionData>,
|
|
193
|
+
): Promise<Session> {
|
|
185
194
|
const existingSession = await this.store.get(sessionId);
|
|
186
|
-
|
|
195
|
+
|
|
187
196
|
if (!existingSession) {
|
|
188
|
-
throw new Error(
|
|
197
|
+
throw new Error("Session not found");
|
|
189
198
|
}
|
|
190
199
|
|
|
191
200
|
// Merge updates
|
|
192
201
|
const updatedSession: SessionData = {
|
|
193
202
|
...existingSession,
|
|
194
203
|
...updates,
|
|
195
|
-
lastActivityAt: new Date()
|
|
204
|
+
lastActivityAt: new Date(),
|
|
196
205
|
};
|
|
197
206
|
|
|
198
207
|
// Calculate remaining TTL
|
|
199
|
-
const remainingTTL = Math.max(
|
|
200
|
-
|
|
208
|
+
const remainingTTL = Math.max(
|
|
209
|
+
0,
|
|
210
|
+
Math.floor(
|
|
211
|
+
(updatedSession.expiresAt.getTime() - Date.now()) / NUMERIX.THOUSAND,
|
|
212
|
+
),
|
|
213
|
+
);
|
|
214
|
+
|
|
201
215
|
if (remainingTTL <= 0) {
|
|
202
|
-
throw new Error(
|
|
216
|
+
throw new Error("Session has expired");
|
|
203
217
|
}
|
|
204
218
|
|
|
205
219
|
// Store updated session
|
|
@@ -214,7 +228,7 @@ export class EnhancedSessionManager {
|
|
|
214
228
|
*/
|
|
215
229
|
async deleteSession(sessionId: string): Promise<void> {
|
|
216
230
|
await this.store.delete(sessionId);
|
|
217
|
-
|
|
231
|
+
|
|
218
232
|
// Remove CSRF token if enabled
|
|
219
233
|
if (this.config.enableCSRF) {
|
|
220
234
|
this.csrfProtection.removeToken(sessionId);
|
|
@@ -228,19 +242,21 @@ export class EnhancedSessionManager {
|
|
|
228
242
|
*/
|
|
229
243
|
async refreshSession(sessionId: string): Promise<Session> {
|
|
230
244
|
const sessionData = await this.store.get(sessionId);
|
|
231
|
-
|
|
245
|
+
|
|
232
246
|
if (!sessionData) {
|
|
233
|
-
throw new Error(
|
|
247
|
+
throw new Error("Session not found");
|
|
234
248
|
}
|
|
235
249
|
|
|
236
250
|
// Extend expiry
|
|
237
251
|
const now = new Date();
|
|
238
|
-
const newExpiresAt = new Date(
|
|
239
|
-
|
|
252
|
+
const newExpiresAt = new Date(
|
|
253
|
+
now.getTime() + this.config.sessionTTL * NUMERIX.THOUSAND,
|
|
254
|
+
);
|
|
255
|
+
|
|
240
256
|
const refreshedSession: SessionData = {
|
|
241
257
|
...sessionData,
|
|
242
258
|
expiresAt: newExpiresAt,
|
|
243
|
-
lastActivityAt: now
|
|
259
|
+
lastActivityAt: now,
|
|
244
260
|
};
|
|
245
261
|
|
|
246
262
|
// Store refreshed session
|
|
@@ -256,7 +272,7 @@ export class EnhancedSessionManager {
|
|
|
256
272
|
*/
|
|
257
273
|
async validateSession(sessionId: string): Promise<boolean> {
|
|
258
274
|
const sessionData = await this.store.get(sessionId);
|
|
259
|
-
|
|
275
|
+
|
|
260
276
|
if (!sessionData) {
|
|
261
277
|
return false;
|
|
262
278
|
}
|
|
@@ -288,8 +304,10 @@ export class EnhancedSessionManager {
|
|
|
288
304
|
*/
|
|
289
305
|
async detectConcurrentSessions(userId: string): Promise<Session[]> {
|
|
290
306
|
const sessionDataArray = await this.store.getByUserId(userId);
|
|
291
|
-
|
|
292
|
-
return sessionDataArray.map(sessionData =>
|
|
307
|
+
|
|
308
|
+
return sessionDataArray.map((sessionData) =>
|
|
309
|
+
this.mapSessionDataToSession(sessionData),
|
|
310
|
+
);
|
|
293
311
|
}
|
|
294
312
|
|
|
295
313
|
/**
|
|
@@ -298,13 +316,16 @@ export class EnhancedSessionManager {
|
|
|
298
316
|
*/
|
|
299
317
|
async enforceSessionLimits(userId: string): Promise<void> {
|
|
300
318
|
const userSessions = await this.store.getByUserId(userId);
|
|
301
|
-
|
|
319
|
+
|
|
302
320
|
if (userSessions.length >= this.config.maxConcurrentSessions) {
|
|
303
321
|
// Sort by last activity (oldest first)
|
|
304
|
-
userSessions.sort(
|
|
305
|
-
|
|
322
|
+
userSessions.sort(
|
|
323
|
+
(a, b) => a.lastActivityAt.getTime() - b.lastActivityAt.getTime(),
|
|
324
|
+
);
|
|
325
|
+
|
|
306
326
|
// Remove oldest sessions to make room
|
|
307
|
-
const sessionsToRemove =
|
|
327
|
+
const sessionsToRemove =
|
|
328
|
+
userSessions.length - this.config.maxConcurrentSessions + 1;
|
|
308
329
|
for (let i = 0; i < sessionsToRemove; i++) {
|
|
309
330
|
await this.deleteSession(userSessions[i].id);
|
|
310
331
|
}
|
|
@@ -318,7 +339,7 @@ export class EnhancedSessionManager {
|
|
|
318
339
|
*/
|
|
319
340
|
async generateCsrfToken(sessionId: string): Promise<string> {
|
|
320
341
|
if (!this.config.enableCSRF) {
|
|
321
|
-
throw new Error(
|
|
342
|
+
throw new Error("CSRF protection is disabled");
|
|
322
343
|
}
|
|
323
344
|
|
|
324
345
|
return this.csrfProtection.generateToken(sessionId);
|
|
@@ -349,7 +370,7 @@ export class EnhancedSessionManager {
|
|
|
349
370
|
}
|
|
350
371
|
|
|
351
372
|
const sessionData = await this.store.get(sessionId);
|
|
352
|
-
|
|
373
|
+
|
|
353
374
|
if (!sessionData) {
|
|
354
375
|
return false;
|
|
355
376
|
}
|
|
@@ -369,7 +390,7 @@ export class EnhancedSessionManager {
|
|
|
369
390
|
// This would require additional tracking in a real implementation
|
|
370
391
|
return {
|
|
371
392
|
totalSessions: 0, // Would need to scan all sessions
|
|
372
|
-
activeUsers: 0
|
|
393
|
+
activeUsers: 0, // Would need to count unique user IDs
|
|
373
394
|
};
|
|
374
395
|
}
|
|
375
396
|
|
|
@@ -380,7 +401,7 @@ export class EnhancedSessionManager {
|
|
|
380
401
|
*/
|
|
381
402
|
private generateSessionId(): string {
|
|
382
403
|
const randomBytesNumber = 32;
|
|
383
|
-
return randomBytes(randomBytesNumber).toString(
|
|
404
|
+
return randomBytes(randomBytesNumber).toString("hex");
|
|
384
405
|
}
|
|
385
406
|
|
|
386
407
|
/**
|
|
@@ -393,14 +414,14 @@ export class EnhancedSessionManager {
|
|
|
393
414
|
return {
|
|
394
415
|
id: sessionData.id,
|
|
395
416
|
userId: sessionData.userId,
|
|
396
|
-
provider:
|
|
417
|
+
provider: "enhanced-session-manager",
|
|
397
418
|
providerSessionId: sessionData.id,
|
|
398
419
|
expiresAt: sessionData.expiresAt,
|
|
399
420
|
createdAt: sessionData.createdAt,
|
|
400
421
|
lastActivityAt: sessionData.lastActivityAt,
|
|
401
422
|
ipAddress: sessionData.ipAddress,
|
|
402
423
|
userAgent: sessionData.userAgent,
|
|
403
|
-
metadata: sessionData.metadata
|
|
424
|
+
metadata: sessionData.metadata,
|
|
404
425
|
};
|
|
405
426
|
}
|
|
406
|
-
}
|
|
427
|
+
}
|
package/src/session/index.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Session management barrel export for @plyaz/auth
|
|
3
3
|
* @module @plyaz/auth/session
|
|
4
|
-
*
|
|
4
|
+
*
|
|
5
5
|
* @description
|
|
6
6
|
* Centralized export of all session management components including
|
|
7
7
|
* session stores, interfaces, and the enhanced session manager.
|
|
8
8
|
* Provides a single import point for all session-related functionality.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
export * from
|
|
12
|
-
export * from
|
|
13
|
-
export * from
|
|
14
|
-
export * from
|
|
11
|
+
export * from "./cookie-store";
|
|
12
|
+
export * from "./memory-store";
|
|
13
|
+
export * from "./redis-store";
|
|
14
|
+
export * from "./enhanced-session-manager";
|