Security legate! Consul Caesar.js entrusts you with the greatest challenge - creating a complete authentication system for the entire legion of Roman war cohorts. This will be a real Fort Knox of the provinces, combining all the skills you learned in the previous modules!
Your system must support:
1// auth.controller.ts
2@Controller('auth')
3@ApiTags('Authentication')
4export class AuthController {
5 constructor(
6 private authService: AuthService,
7 private passwordService: PasswordService,
8 private refreshTokenService: RefreshTokenService,
9 private twoFactorService: TwoFactorService,
10 private securityService: SecurityService,
11 ) {}
12
13 @Post('register')
14 @Throttle(3, 300) // 3 attempts per 5 minutes
15 @ApiOperation({ summary: 'Register a new legionary' })
16 async register(
17 @Body() registerDto: RegisterDto,
18 @Headers('user-agent') userAgent: string,
19 @Ip() ipAddress: string,
20 ) {
21 // Check if the IP is blacklisted
22 await this.securityService.checkIpBlacklist(ipAddress);
23
24 // Registration
25 const user = await this.authService.register(registerDto);
26
27 // Security logging
28 await this.securityService.logSecurityEvent({
29 type: 'user_registered',
30 userId: user.id,
31 ipAddress,
32 userAgent,
33 details: { email: user.email },
34 });
35
36 return {
37 message: 'Legionary has been registered! Check your email to activate your account.',
38 user: {
39 id: user.id,
40 email: user.email,
41 emailVerified: user.emailVerified,
42 },
43 };
44 }
45
46 @Post('login')
47 @Throttle(5, 300) // 5 attempts per 5 minutes
48 @UseGuards(LocalAuthGuard)
49 @ApiOperation({ summary: 'Legionary login' })
50 async login(
51 @Request() req,
52 @Body() loginDto: LoginDto,
53 @Headers('user-agent') userAgent: string,
54 @Ip() ipAddress: string,
55 ) {
56 const user = req.user;
57
58 // Check if the account is locked
59 await this.authService.checkAccountLockout(user.id);
60
61 // Check if 2FA is required
62 if (user.twoFactorEnabled) {
63 if (!loginDto.twoFactorCode) {
64 return {
65 requiresTwoFactor: true,
66 tempToken: await this.twoFactorService.generateTempToken(user.id),
67 };
68 }
69
70 const isValid2FA = await this.twoFactorService.verifyCode(
71 user.id,
72 loginDto.twoFactorCode,
73 );
74
75 if (!isValid2FA) {
76 await this.securityService.logSecurityEvent({
77 type: 'invalid_2fa_attempt',
78 userId: user.id,
79 ipAddress,
80 userAgent,
81 });
82
83 throw new UnauthorizedException('Invalid 2FA code!');
84 }
85 }
86
87 // Generate tokens
88 const tokens = await this.authService.generateAuthTokens(
89 user,
90 userAgent,
91 ipAddress,
92 );
93
94 // Update login data
95 await this.authService.updateLoginInfo(user.id, ipAddress);
96
97 return {
98 message: 'Welcome to the system, legionary!',
99 user: {
100 id: user.id,
101 email: user.email,
102 firstName: user.firstName,
103 lastName: user.lastName,
104 role: user.role,
105 emailVerified: user.emailVerified,
106 twoFactorEnabled: user.twoFactorEnabled,
107 },
108 tokens,
109 };
110 }
111
112 @Post('logout')
113 @UseGuards(JwtAuthGuard)
114 async logout(
115 @Request() req,
116 @Body() logoutDto: LogoutDto,
117 @Ip() ipAddress: string,
118 ) {
119 if (logoutDto.allDevices) {
120 await this.refreshTokenService.revokeAllUserTokens(
121 req.user.id,
122 'user_logout_all',
123 );
124 } else if (logoutDto.refreshToken) {
125 await this.refreshTokenService.revokeTokenByValue(
126 logoutDto.refreshToken,
127 'user_logout',
128 );
129 }
130
131 return {
132 message: logoutDto.allDevices
133 ? 'Logged out from all devices'
134 : 'Logged out successfully',
135 };
136 }
137
138 @Post('forgot-password')
139 @Throttle(3, 3600) // 3 attempts per hour
140 async forgotPassword(
141 @Body() forgotDto: ForgotPasswordDto,
142 @Ip() ipAddress: string,
143 ) {
144 await this.passwordService.sendPasswordResetEmail(
145 forgotDto.email,
146 ipAddress,
147 );
148
149 return {
150 message: 'If the account exists, instructions have been sent to the email.',
151 };
152 }
153}1// auth.service.ts
2@Injectable()
3export class AuthService {
4 constructor(
5 @InjectRepository(User) private usersRepository: Repository<User>,
6 private passwordService: PasswordService,
7 private refreshTokenService: RefreshTokenService,
8 private emailService: EmailService,
9 private jwtService: JwtService,
10 ) {}
11
12 async register(registerDto: RegisterDto): Promise<User> {
13 // Check if the user already exists
14 const existingUser = await this.usersRepository.findOne({
15 where: { email: registerDto.email },
16 });
17
18 if (existingUser) {
19 throw new ConflictException('A legionary with this email already exists in the legion!');
20 }
21
22 // Hash the password
23 const hashedPassword = await this.passwordService.hashPassword(
24 registerDto.password,
25 );
26
27 // Create the user
28 const user = this.usersRepository.create({
29 email: registerDto.email,
30 firstName: registerDto.firstName,
31 lastName: registerDto.lastName,
32 password: hashedPassword,
33 role: 'legionary', // Default role
34 emailVerified: false,
35 createdAt: new Date(),
36 });
37
38 const savedUser = await this.usersRepository.save(user);
39
40 // Send verification email
41 await this.sendEmailVerification(savedUser);
42
43 return savedUser;
44 }
45
46 async validateUser(email: string, password: string): Promise<User | null> {
47 // Find the user
48 const user = await this.usersRepository.findOne({
49 where: { email },
50 select: ['id', 'email', 'password', 'firstName', 'lastName', 'role',
51 'emailVerified', 'twoFactorEnabled', 'accountLocked', 'lockoutUntil'],
52 });
53
54 if (!user) {
55 return null;
56 }
57
58 // Check account lockout
59 if (user.accountLocked && user.lockoutUntil && user.lockoutUntil > new Date()) {
60 throw new UnauthorizedException(
61 `Account locked until ${user.lockoutUntil.toLocaleString()}`
62 );
63 }
64
65 // Check the password
66 const isPasswordValid = await this.passwordService.verifyPassword(
67 password,
68 user.password,
69 );
70
71 if (!isPasswordValid) {
72 await this.handleFailedLogin(user.id);
73 return null;
74 }
75
76 // Reset the failed attempts counter
77 await this.resetFailedLoginAttempts(user.id);
78
79 // Remove the password from the object
80 delete user.password;
81 return user;
82 }
83
84 async generateAuthTokens(
85 user: User,
86 userAgent: string,
87 ipAddress: string,
88 ): Promise<{ accessToken: string; refreshToken: string }> {
89 // Access Token
90 const accessTokenPayload = {
91 sub: user.id,
92 email: user.email,
93 role: user.role,
94 emailVerified: user.emailVerified,
95 };
96
97 const accessToken = this.jwtService.sign(accessTokenPayload, {
98 expiresIn: '15m',
99 });
100
101 // Refresh Token
102 const refreshToken = await this.refreshTokenService.generateRefreshToken(
103 user.id,
104 userAgent,
105 ipAddress,
106 );
107
108 return { accessToken, refreshToken };
109 }
110
111 private async handleFailedLogin(userId: number): Promise<void> {
112 const maxAttempts = 5;
113 const lockoutDuration = 30; // minutes
114
115 // Get the number of failed attempts from the last 15 minutes
116 const since = new Date(Date.now() - 15 * 60 * 1000);
117 const failedAttempts = await this.getFailedAttemptsCount(userId, since);
118
119 if (failedAttempts >= maxAttempts) {
120 const lockoutUntil = new Date(Date.now() + lockoutDuration * 60 * 1000);
121
122 await this.usersRepository.update(userId, {
123 accountLocked: true,
124 lockoutUntil,
125 failedLoginAttempts: failedAttempts,
126 });
127
128 console.warn(`Account ${userId} has been locked after ${failedAttempts} failed attempts`);
129 }
130 }
131
132 private async resetFailedLoginAttempts(userId: number): Promise<void> {
133 await this.usersRepository.update(userId, {
134 accountLocked: false,
135 lockoutUntil: null,
136 failedLoginAttempts: 0,
137 });
138 }
139
140 async updateLoginInfo(userId: number, ipAddress: string): Promise<void> {
141 await this.usersRepository.update(userId, {
142 lastLoginAt: new Date(),
143 lastLoginIp: ipAddress,
144 });
145 }
146}✓ Functionality - All requirements implemented correctly ✓ Security - Best security practices applied ✓ Code - Clean, well-organized code following best practices ✓ Tests - High test coverage, different test types ✓ Documentation - Complete API documentation and installation instructions ✓ Performance - Query and performance optimization ✓ Monitoring - Security logging and monitoring
This is the greatest challenge in the entire NestJS course! Good luck, security legate. Your legion counts on an impenetrable protection system!