This commit is contained in:
Joelson
2025-09-17 18:49:23 -03:00
parent 21c3225c52
commit e081df9ced
42 changed files with 4129 additions and 411 deletions

View File

@@ -4,10 +4,12 @@ import { IRedisClient } from '../../core/configs/cache/IRedisClient';
import { JwtService } from '@nestjs/jwt';
import { JwtPayload } from '../models/jwt-payload.model';
import { randomBytes } from 'crypto';
import { DateUtil } from 'src/shared/date.util';
export interface RefreshTokenData {
userId: number;
tokenId: string;
sessionId?: string;
expiresAt: number;
createdAt: number;
}
@@ -25,26 +27,30 @@ export class RefreshTokenService {
/**
* Gera um novo refresh token para o usuário
* @param userId ID do usuário
* @param sessionId ID da sessão (opcional)
* @returns Refresh token
*/
async generateRefreshToken(userId: number): Promise<string> {
async generateRefreshToken(userId: number, sessionId?: string): Promise<string> {
const tokenId = randomBytes(32).toString('hex');
const refreshToken = this.jwtService.sign(
{ userId, tokenId, type: 'refresh' },
{ userId, tokenId, sessionId, type: 'refresh' },
{ expiresIn: '7d' }
);
const tokenData: RefreshTokenData = {
userId,
tokenId,
expiresAt: Date.now() + (this.REFRESH_TOKEN_TTL * 1000),
createdAt: Date.now(),
sessionId,
expiresAt: DateUtil.nowTimestamp() + (this.REFRESH_TOKEN_TTL * 1000),
createdAt: DateUtil.nowTimestamp(),
};
const key = this.buildRefreshTokenKey(userId, tokenId);
await this.redis.set(key, tokenData, this.REFRESH_TOKEN_TTL);
// Limita o número de refresh tokens por usuário
/**
* Limita o número de refresh tokens por usuário
*/
await this.limitRefreshTokensPerUser(userId);
return refreshToken;
@@ -63,7 +69,7 @@ export class RefreshTokenService {
throw new UnauthorizedException('Token inválido');
}
const { userId, tokenId } = decoded;
const { userId, tokenId, sessionId } = decoded;
const key = this.buildRefreshTokenKey(userId, tokenId);
const tokenData = await this.redis.get<RefreshTokenData>(key);
@@ -71,7 +77,7 @@ export class RefreshTokenService {
throw new UnauthorizedException('Refresh token expirado ou inválido');
}
if (tokenData.expiresAt < Date.now()) {
if (tokenData.expiresAt < DateUtil.nowTimestamp()) {
await this.revokeRefreshToken(userId, tokenId);
throw new UnauthorizedException('Refresh token expirado');
}
@@ -82,6 +88,7 @@ export class RefreshTokenService {
storeId: '',
username: '',
email: '',
sessionId: sessionId || tokenData.sessionId,
tokenId
} as JwtPayload;
} catch (error) {
@@ -125,7 +132,7 @@ export class RefreshTokenService {
for (const key of keys) {
const tokenData = await this.redis.get<RefreshTokenData>(key);
if (tokenData && tokenData.expiresAt > Date.now()) {
if (tokenData && tokenData.expiresAt > DateUtil.nowTimestamp()) {
tokens.push(tokenData);
}
}
@@ -141,7 +148,9 @@ export class RefreshTokenService {
const activeTokens = await this.getActiveRefreshTokens(userId);
if (activeTokens.length > this.MAX_REFRESH_TOKENS_PER_USER) {
// Remove os tokens mais antigos
/**
* Remove os tokens mais antigos
*/
const tokensToRemove = activeTokens
.slice(this.MAX_REFRESH_TOKENS_PER_USER)
.map(token => token.tokenId);