first
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user