feat: implementar melhorias na autenticação
- Adicionar refresh tokens para renovação automática de tokens - Implementar controle de sessões simultâneas - Adicionar blacklist de tokens para logout seguro - Implementar rate limiting para proteção contra ataques - Melhorar detecção de IP e identificação de sessão atual - Adicionar endpoints para gerenciamento de sessões - Corrigir inconsistências na validação de usuário - Atualizar configuração Redis com nova conexão
This commit is contained in:
103
src/auth/services/token-blacklist.service.ts
Normal file
103
src/auth/services/token-blacklist.service.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { Injectable, Inject } from '@nestjs/common';
|
||||
import { RedisClientToken } from '../../core/configs/cache/redis-client.adapter.provider';
|
||||
import { IRedisClient } from '../../core/configs/cache/IRedisClient';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { JwtPayload } from '../models/jwt-payload.model';
|
||||
|
||||
@Injectable()
|
||||
export class TokenBlacklistService {
|
||||
constructor(
|
||||
@Inject(RedisClientToken) private readonly redis: IRedisClient,
|
||||
private readonly jwtService: JwtService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Adiciona um token à blacklist
|
||||
* @param token Token JWT a ser invalidado
|
||||
* @param expiresIn Tempo de expiração do token em segundos
|
||||
*/
|
||||
async addToBlacklist(token: string, expiresIn?: number): Promise<void> {
|
||||
try {
|
||||
const decoded = this.jwtService.decode(token) as JwtPayload;
|
||||
if (!decoded) {
|
||||
throw new Error('Token inválido');
|
||||
}
|
||||
|
||||
const blacklistKey = this.buildBlacklistKey(token);
|
||||
const ttl = expiresIn || this.calculateTokenTTL(decoded);
|
||||
|
||||
await this.redis.set(blacklistKey, 'blacklisted', ttl);
|
||||
} catch (error) {
|
||||
throw new Error(`Erro ao adicionar token à blacklist: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica se um token está na blacklist
|
||||
* @param token Token JWT a ser verificado
|
||||
* @returns true se o token estiver blacklistado
|
||||
*/
|
||||
async isBlacklisted(token: string): Promise<boolean> {
|
||||
try {
|
||||
const blacklistKey = this.buildBlacklistKey(token);
|
||||
const result = await this.redis.get(blacklistKey);
|
||||
return result === 'blacklisted';
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove um token da blacklist (útil para testes)
|
||||
* @param token Token JWT a ser removido
|
||||
*/
|
||||
async removeFromBlacklist(token: string): Promise<void> {
|
||||
const blacklistKey = this.buildBlacklistKey(token);
|
||||
await this.redis.del(blacklistKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Limpa todos os tokens blacklistados de um usuário
|
||||
* @param userId ID do usuário
|
||||
*/
|
||||
async clearUserBlacklist(userId: number): Promise<void> {
|
||||
const pattern = `auth:blacklist:${userId}:*`;
|
||||
const keys = await this.redis.keys(pattern);
|
||||
|
||||
if (keys.length > 0) {
|
||||
await this.redis.del(...keys);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constrói a chave para armazenar o token na blacklist
|
||||
* @param token Token JWT
|
||||
* @returns Chave para o Redis
|
||||
*/
|
||||
private buildBlacklistKey(token: string): string {
|
||||
const decoded = this.jwtService.decode(token) as JwtPayload;
|
||||
const tokenHash = this.hashToken(token);
|
||||
return `auth:blacklist:${decoded.id}:${tokenHash}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcula o TTL do token baseado na expiração
|
||||
* @param payload Payload do JWT
|
||||
* @returns TTL em segundos
|
||||
*/
|
||||
private calculateTokenTTL(payload: JwtPayload): number {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const exp = payload.exp || (now + 8 * 60 * 60); // 8h padrão
|
||||
return Math.max(0, exp - now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gera um hash do token para usar como identificador único
|
||||
* @param token Token JWT
|
||||
* @returns Hash do token
|
||||
*/
|
||||
private hashToken(token: string): string {
|
||||
const crypto = require('crypto');
|
||||
return crypto.createHash('sha256').update(token).digest('hex').substring(0, 16);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user