Як реалізувати автентифікацію з Passport у NestJS?
Аутентифікація в NestJS з Passport
NestJS безшовно інтегрується з Passport.js — найпопулярнішою бібліотекою аутентифікації для Node.js — через пакет @nestjs/passport.
Налаштування
bash
npm install @nestjs/passport passport passport-local passport-jwt
npm install @nestjs/jwt
npm install --save-dev @types/passport-local @types/passport-jwtJWT Стратегія (Найпоширеніша для API)
1. Модуль Аутентифікації
typescript
@Module({
imports: [
PassportModule,
JwtModule.register({
secret: process.env.JWT_SECRET,
signOptions: { expiresIn: '1h' },
}),
UsersModule,
],
providers: [AuthService, JwtStrategy, LocalStrategy],
controllers: [AuthController],
})
export class AuthModule {}2. Локальна Стратегія (Увійти)
typescript
import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super({ usernameField: 'email' });
}
async validate(email: string, password: string): Promise<User> {
const user = await this.authService.validateUser(email, password);
if (!user) {
throw new UnauthorizedException('Невірні облікові дані');
}
return user;
}
}3. JWT Стратегія (Захистити маршрути)
typescript
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: process.env.JWT_SECRET,
});
}
async validate(payload: { sub: string; email: string }) {
return { id: payload.sub, email: payload.email };
}
}4. Сервіс Аутентифікації
typescript
@Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
private jwtService: JwtService,
) {}
async validateUser(email: string, password: string) {
const user = await this.usersService.findByEmail(email);
if (user && await bcrypt.compare(password, user.password)) {
const { password, ...result } = user;
return result;
}
return null;
}
async login(user: User) {
const payload = { sub: user.id, email: user.email };
return {
access_token: this.jwtService.sign(payload),
refresh_token: this.jwtService.sign(payload, { expiresIn: '7d' }),
};
}
async refreshToken(token: string) {
const payload = this.jwtService.verify(token);
const newPayload = { sub: payload.sub, email: payload.email };
return {
access_token: this.jwtService.sign(newPayload),
};
}
}5. Контролер Аутентифікації
typescript
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}
@UseGuards(AuthGuard('local'))
@Post('login')
async login(@Request() req) {
return this.authService.login(req.user);
}
@UseGuards(AuthGuard('jwt'))
@Get('profile')
getProfile(@Request() req) {
return req.user;
}
@Post('refresh')
async refresh(@Body('refresh_token') token: string) {
return this.authService.refreshToken(token);
}
}Глобальний JWT Гард
typescript
// jwt-auth.guard.ts
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
constructor(private reflector: Reflector) {
super();
}
canActivate(context: ExecutionContext) {
const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [
context.getHandler(),
context.getClass(),
]);
if (isPublic) return true;
return super.canActivate(context);
}
}typescript
// app.module.ts — застосувати глобально
@Module({
providers: [
{ provide: APP_GUARD, useClass: JwtAuthGuard },
],
})
export class AppModule {}Тепер використовуйте декоратор @Public(), щоб пропустити аутентифікацію:
typescript
@Controller('health')
export class HealthController {
@Public()
@Get()
check() {
return { status: 'ok' };
}
}Підсумок Потоку
Увійти: POST /auth/login
→ LocalStrategy.validate() → AuthService.login() → JWT токен
Запит: GET /api/protected
Header: Authorization: Bearer <token>
→ JwtStrategy.validate() → req.user заповнений → КонтролерНайкраща практика: Використовуйте JWT для безстанної аутентифікації API. Реалізуйте токени оновлення з ротацією. Зберігайте токени в httpOnly куках для веб-додатків. Застосовуйте
JwtAuthGuardглобально та використовуйте@Public()для відкритих маршрутів.
Коротка відповідь
Для співбесідиPremium
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.