legacie bezpieczeństwa! Konsul Caesar.js powierza ci największe wyzwanie - stworzenie kompletnego systemu autentyfikacji dla całego legionu rzymskich kohort. To będzie prawdziwy Fort Knox Imperium, łączący wszystkie umiejętności, które poznałeś w poprzednich modułach!
Twój system musi obsługiwać:
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 próby na 5 minut
15 @ApiOperation({ summary: 'Rejestracja nowego legionariusza' })
16 async register(
17 @Body() registerDto: RegisterDto,
18 @Headers('user-agent') userAgent: string,
19 @Ip() ipAddress: string,
20 ) {
21 // Sprawdź czy IP nie jest zablokowany
22 await this.securityService.checkIpBlacklist(ipAddress);
23
24 // Rejestracja
25 const user = await this.authService.register(registerDto);
26
27 // Logowanie bezpieczeństwa
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: 'Legionariusz został zarejestrowany! Sprawdź email aby aktywować konto.',
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 prób na 5 minut
48 @UseGuards(LocalAuthGuard)
49 @ApiOperation({ summary: 'Logowanie legionariusza' })
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 // Sprawdź czy konto nie jest zablokowane
59 await this.authService.checkAccountLockout(user.id);
60
61 // Sprawdź czy wymagana jest 2FA
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('Nieprawidłowy kod 2FA!');
84 }
85 }
86
87 // Generuj tokeny
88 const tokens = await this.authService.generateAuthTokens(
89 user,
90 userAgent,
91 ipAddress,
92 );
93
94 // Aktualizuj dane logowania
95 await this.authService.updateLoginInfo(user.id, ipAddress);
96
97 return {
98 message: 'Witaj w legionie, legionisto!',
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 ? 'Wylogowano ze wszystkich urządzeń'
134 : 'Wylogowano pomyślnie',
135 };
136 }
137
138 @Post('forgot-password')
139 @Throttle(3, 3600) // 3 próby na godzinę
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: 'Jeśli konto istnieje, instrukcje zostały wysłane na 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 // Sprawdź czy user już istnieje
14 const existingUser = await this.usersRepository.findOne({
15 where: { email: registerDto.email },
16 });
17
18 if (existingUser) {
19 throw new ConflictException('Legionariusz z tym emailem już istnieje w legionie!');
20 }
21
22 // Hashuj hasło
23 const hashedPassword = await this.passwordService.hashPassword(
24 registerDto.password,
25 );
26
27 // Utwórz użytkownika
28 const user = this.usersRepository.create({
29 email: registerDto.email,
30 firstName: registerDto.firstName,
31 lastName: registerDto.lastName,
32 password: hashedPassword,
33 role: 'legionary', // Domyślna rola
34 emailVerified: false,
35 createdAt: new Date(),
36 });
37
38 const savedUser = await this.usersRepository.save(user);
39
40 // Wyślij email weryfikacyjny
41 await this.sendEmailVerification(savedUser);
42
43 return savedUser;
44 }
45
46 async validateUser(email: string, password: string): Promise<User | null> {
47 // Znajdź użytkownika
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 // Sprawdź blokadę konta
59 if (user.accountLocked && user.lockoutUntil && user.lockoutUntil > new Date()) {
60 throw new UnauthorizedException(
61 `Konto zablokowane do ${user.lockoutUntil.toLocaleString()}`
62 );
63 }
64
65 // Sprawdź hasło
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 licznika nieudanych prób
77 await this.resetFailedLoginAttempts(user.id);
78
79 // Usuń hasło z obiektu
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; // minut
114
115 // Pobierz liczbę nieudanych prób z ostatnich 15 minut
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(`Konto ${userId} zostało zablokowane po ${failedAttempts} nieudanych próbach`);
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}✓ Funkcjonalność - Wszystkie wymagania zaimplementowane poprawnie ✓ Bezpieczeństwo - Zastosowane najlepsze praktyki bezpieczeństwa ✓ Kod - Czytelny, dobrze zorganizowany kod zgodny z best practices ✓ Testy - Wysokie pokrycie testami, różne typy testów ✓ Dokumentacja - Kompletna dokumentacja API i instrukcje instalacji ✓ Performance - Optymalizacja zapytań i wydajności ✓ Monitoring - Logowanie i monitorowanie bezpieczeństwa
To największe wyzwanie w całym kursie NestJS! Powodzenia, legacie bezpieczeństwa. Twój legion liczy na nieprzenikalny system ochrony!