feat(auth): implement Auth module with register, login, JWT, guards, and CQRS
- Add RefreshToken and OAuthAccount models to Prisma schema - Implement clean architecture: domain (entities, VOs, events, repo interfaces), infrastructure (Prisma repos, Passport strategies, token service), application (CQRS command/query handlers), presentation (controller, guards, DTOs) - Endpoints: POST /auth/register, /auth/login, /auth/refresh, GET /auth/profile, GET /auth/profile/agent, PATCH /auth/kyc - JWT access + refresh token rotation with family-based revocation - Role-based guards (BUYER, SELLER, AGENT, ADMIN) - 16 unit tests (value objects, entity) + integration test suite - All 80 tests passing, clean TypeScript build Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
export class RefreshTokenCommand {
|
||||
constructor(public readonly refreshToken: string) {}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { CommandHandler, type ICommandHandler } from '@nestjs/cqrs';
|
||||
import { UnauthorizedException, Inject } from '@nestjs/common';
|
||||
import { RefreshTokenCommand } from './refresh-token.command';
|
||||
import { TokenService, type TokenPair } from '../../../infrastructure/services/token.service';
|
||||
import { USER_REPOSITORY, type IUserRepository } from '../../../domain/repositories/user.repository';
|
||||
|
||||
@CommandHandler(RefreshTokenCommand)
|
||||
export class RefreshTokenHandler implements ICommandHandler<RefreshTokenCommand> {
|
||||
constructor(
|
||||
private readonly tokenService: TokenService,
|
||||
@Inject(USER_REPOSITORY) private readonly userRepo: IUserRepository,
|
||||
) {}
|
||||
|
||||
async execute(command: RefreshTokenCommand): Promise<TokenPair> {
|
||||
const rotated = await this.tokenService.rotateRefreshToken(command.refreshToken);
|
||||
if (!rotated) {
|
||||
throw new UnauthorizedException('Refresh token không hợp lệ hoặc đã hết hạn');
|
||||
}
|
||||
|
||||
const user = await this.userRepo.findById(rotated.userId);
|
||||
if (!user || !user.isActive) {
|
||||
throw new UnauthorizedException('Tài khoản không tồn tại hoặc đã bị vô hiệu hóa');
|
||||
}
|
||||
|
||||
const accessToken = this.tokenService.generateAccessToken({
|
||||
sub: user.id,
|
||||
phone: user.phone.value,
|
||||
role: user.role,
|
||||
});
|
||||
|
||||
return {
|
||||
accessToken,
|
||||
refreshToken: rotated.refreshToken,
|
||||
expiresIn: 900,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user