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:
@@ -4,6 +4,13 @@ import { Strategy } from 'passport-local';
|
||||
import { DomainException, normalizeVietnamPhone, UnauthorizedException } from '@modules/shared';
|
||||
import { USER_REPOSITORY, type IUserRepository } from '../../domain/repositories/user.repository';
|
||||
|
||||
export interface LocalStrategyResult {
|
||||
id: string;
|
||||
phone: string;
|
||||
role: string;
|
||||
isMfaRequired: boolean;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class LocalStrategy extends PassportStrategy(Strategy) {
|
||||
private readonly logger = new Logger(LocalStrategy.name);
|
||||
@@ -15,7 +22,7 @@ export class LocalStrategy extends PassportStrategy(Strategy) {
|
||||
super({ usernameField: 'phone', passwordField: 'password' });
|
||||
}
|
||||
|
||||
async validate(phone: string, password: string): Promise<{ id: string; phone: string; role: string }> {
|
||||
async validate(phone: string, password: string): Promise<LocalStrategyResult> {
|
||||
try {
|
||||
if (!phone || !password) {
|
||||
throw new UnauthorizedException('Số điện thoại hoặc mật khẩu không đúng');
|
||||
@@ -40,7 +47,12 @@ export class LocalStrategy extends PassportStrategy(Strategy) {
|
||||
throw new UnauthorizedException('Số điện thoại hoặc mật khẩu không đúng');
|
||||
}
|
||||
|
||||
return { id: user.id, phone: user.phone.value, role: user.role };
|
||||
return {
|
||||
id: user.id,
|
||||
phone: user.phone.value,
|
||||
role: user.role,
|
||||
isMfaRequired: user.totpEnabled,
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof DomainException) throw error;
|
||||
this.logger.error(
|
||||
|
||||
Reference in New Issue
Block a user