feat: add MFA/TOTP auth, PII encryption, agents/leads/inquiries modules, and comprehensive tests
- Add TOTP-based MFA with setup, verify, disable, backup codes, and challenge flow - Add PII field encryption middleware with AES-256-GCM and deterministic search hashes - Add agents, inquiries, and leads domain modules with entities, events, value objects - Add web dashboard pages for inquiries and leads with detail dialogs - Add 30+ component tests (valuation, charts, listings, search, providers, UI) - Add Prisma migrations for encryption hash columns and MFA TOTP support - Fix all ESLint errors (unused imports, duplicate imports, lint auto-fixes) - Update dependencies and lock file - Clean up obsolete exploration/QA docs, add audit documentation Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -14,6 +14,7 @@ import { Throttle } from '@nestjs/throttler';
|
||||
import { type Request, type Response } from 'express';
|
||||
import { EndpointRateLimit, EndpointRateLimitGuard, UnauthorizedException } from '@modules/shared';
|
||||
import { LoginUserCommand } from '../../application/commands/login-user/login-user.command';
|
||||
import { type LoginResult } from '../../application/commands/login-user/login-user.handler';
|
||||
import { RefreshTokenCommand } from '../../application/commands/refresh-token/refresh-token.command';
|
||||
import { RegisterUserCommand } from '../../application/commands/register-user/register-user.command';
|
||||
import { VerifyKycCommand } from '../../application/commands/verify-kyc/verify-kyc.command';
|
||||
@@ -22,6 +23,7 @@ import { GetAgentByUserIdQuery } from '../../application/queries/get-agent-by-us
|
||||
import { type UserProfileDto } from '../../application/queries/get-profile/get-profile.handler';
|
||||
import { GetProfileQuery } from '../../application/queries/get-profile/get-profile.query';
|
||||
import { type TokenService, type JwtPayload, type TokenPair } from '../../infrastructure/services/token.service';
|
||||
import { type LocalStrategyResult } from '../../infrastructure/strategies/local.strategy';
|
||||
import { CurrentUser } from '../decorators/current-user.decorator';
|
||||
import { Roles } from '../decorators/roles.decorator';
|
||||
import { LoginDto } from '../dto/login.dto';
|
||||
@@ -107,20 +109,29 @@ export class AuthController {
|
||||
@Post('login')
|
||||
@ApiOperation({ summary: 'Login with phone and password' })
|
||||
@ApiBody({ type: LoginDto })
|
||||
@ApiResponse({ status: 201, description: 'Login successful, auth cookies set' })
|
||||
@ApiResponse({ status: 201, description: 'Login successful, auth cookies set (or MFA challenge returned)' })
|
||||
@ApiResponse({ status: 401, description: 'Invalid credentials' })
|
||||
async login(
|
||||
@CurrentUser() user: { id: string; phone: string; role: string },
|
||||
@CurrentUser() user: LocalStrategyResult,
|
||||
@Res({ passthrough: true }) res: Response,
|
||||
): Promise<{ message: string; accessToken: string; refreshToken: string }> {
|
||||
const tokens: TokenPair = await this.commandBus.execute(
|
||||
new LoginUserCommand(user.id, user.phone, user.role),
|
||||
): Promise<{ message: string; accessToken?: string; refreshToken?: string; requiresMfa?: boolean; challengeId?: string }> {
|
||||
const result: LoginResult = await this.commandBus.execute(
|
||||
new LoginUserCommand(user.id, user.phone, user.role, user.isMfaRequired),
|
||||
);
|
||||
setAuthCookies(res, tokens);
|
||||
|
||||
if (result.requiresMfa) {
|
||||
return {
|
||||
message: 'Yêu cầu xác thực MFA',
|
||||
requiresMfa: true,
|
||||
challengeId: result.challengeId,
|
||||
};
|
||||
}
|
||||
|
||||
setAuthCookies(res, result.tokens!);
|
||||
return {
|
||||
message: 'Đăng nhập thành công',
|
||||
accessToken: tokens.accessToken,
|
||||
refreshToken: tokens.refreshToken,
|
||||
accessToken: result.tokens!.accessToken,
|
||||
refreshToken: result.tokens!.refreshToken,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user