feat: add SessionsModule with service for session management
Implemented SessionsModule and SessionsService to manage user sessions. Includes methods for session creation, refresh token rotation, and session revocation. Integrated with database and CryptoService for secure token handling.
This commit is contained in:
11
backend/src/sessions/sessions.module.ts
Normal file
11
backend/src/sessions/sessions.module.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Module } from "@nestjs/common";
|
||||||
|
import { CryptoModule } from "../crypto/crypto.module";
|
||||||
|
import { DatabaseModule } from "../database/database.module";
|
||||||
|
import { SessionsService } from "./sessions.service";
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [DatabaseModule, CryptoModule],
|
||||||
|
providers: [SessionsService],
|
||||||
|
exports: [SessionsService],
|
||||||
|
})
|
||||||
|
export class SessionsModule {}
|
||||||
88
backend/src/sessions/sessions.service.ts
Normal file
88
backend/src/sessions/sessions.service.ts
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import { Injectable, UnauthorizedException } from "@nestjs/common";
|
||||||
|
import { and, eq } from "drizzle-orm";
|
||||||
|
import { CryptoService } from "../crypto/crypto.service";
|
||||||
|
import { DatabaseService } from "../database/database.service";
|
||||||
|
import { sessions } from "../database/schemas";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class SessionsService {
|
||||||
|
constructor(
|
||||||
|
private readonly databaseService: DatabaseService,
|
||||||
|
private readonly cryptoService: CryptoService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async createSession(userId: string, userAgent?: string, ip?: string) {
|
||||||
|
const refreshToken = await this.cryptoService.generateJwt(
|
||||||
|
{ sub: userId, type: "refresh" },
|
||||||
|
"7d",
|
||||||
|
);
|
||||||
|
const ipHash = ip ? await this.cryptoService.hashIp(ip) : null;
|
||||||
|
const expiresAt = new Date();
|
||||||
|
expiresAt.setDate(expiresAt.getDate() + 7);
|
||||||
|
|
||||||
|
const [session] = await this.databaseService.db
|
||||||
|
.insert(sessions)
|
||||||
|
.values({
|
||||||
|
userId,
|
||||||
|
refreshToken,
|
||||||
|
userAgent,
|
||||||
|
ipHash,
|
||||||
|
expiresAt,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
async refreshSession(oldRefreshToken: string) {
|
||||||
|
const session = await this.databaseService.db
|
||||||
|
.select()
|
||||||
|
.from(sessions)
|
||||||
|
.where(
|
||||||
|
and(eq(sessions.refreshToken, oldRefreshToken), eq(sessions.isValid, true)),
|
||||||
|
)
|
||||||
|
.limit(1)
|
||||||
|
.then((res) => res[0]);
|
||||||
|
|
||||||
|
if (!session || session.expiresAt < new Date()) {
|
||||||
|
if (session) {
|
||||||
|
await this.revokeSession(session.id);
|
||||||
|
}
|
||||||
|
throw new UnauthorizedException("Invalid refresh token");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotation du refresh token
|
||||||
|
const newRefreshToken = await this.cryptoService.generateJwt(
|
||||||
|
{ sub: session.userId, type: "refresh" },
|
||||||
|
"7d",
|
||||||
|
);
|
||||||
|
const expiresAt = new Date();
|
||||||
|
expiresAt.setDate(expiresAt.getDate() + 7);
|
||||||
|
|
||||||
|
const [updatedSession] = await this.databaseService.db
|
||||||
|
.update(sessions)
|
||||||
|
.set({
|
||||||
|
refreshToken: newRefreshToken,
|
||||||
|
expiresAt,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
})
|
||||||
|
.where(eq(sessions.id, session.id))
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return updatedSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
async revokeSession(sessionId: string) {
|
||||||
|
await this.databaseService.db
|
||||||
|
.update(sessions)
|
||||||
|
.set({ isValid: false, updatedAt: new Date() })
|
||||||
|
.where(eq(sessions.id, sessionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
async revokeAllUserSessions(userId: string) {
|
||||||
|
await this.databaseService.db
|
||||||
|
.update(sessions)
|
||||||
|
.set({ isValid: false, updatedAt: new Date() })
|
||||||
|
.where(eq(sessions.userId, userId));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user