Аутентифікація — це одна з основ будь-якого безпечного додатку. Сьогодні ми розглянемо, як реалізувати аутентифікацію за допомогою JSON Web Token (JWT) у додатку, створеному з використанням NestJS. Я поділюсь кроками, яких я дотримувався для створення цієї функціональності, пояснюючи концепції та реалізацію на шляху.
Вступ
JWT — популярне та ефективне рішення для аутентифікації в сучасних додатках. Воно надає безпечний метод для обміну інформацією між надійними сторонами. Ідея проста: генерується токен для ідентифікації аутентифікованого користувача, який може бути перевірений сервером.
Але чому це важливо? Без належної аутентифікації будь-хто може отримати доступ до конфіденційної інформації або виконати обмежені дії у вашому додатку. JWT ефективно вирішує це питання.
Проблема
Під час розробки додатку ви можете зіткнутися з такими викликами:
- Як захистити чутливі маршрути від несанкціонованого доступу?
- Як реалізувати просту, але ефективну аутентифікацію для REST API?
- Як безпечно обробляти паролі та токени?
Відповідь полягає в поєднанні JWT з найкращими практиками шифрування та чітко спланованим потоком.
Чому це важливо
JWT працює на основі трьох основних компонентів:
- Заголовок (Header): містить тип токену та алгоритм підпису.
- Вантаж (Payload): містить інформацію про користувача (зазвичай не чутливі дані).
- Підпис (Signature): гарантує, що токен не був змінений під час транспортування.
Ці компоненти створюють систему, яка дозволяє:
- Перевіряти особу користувача.
- Гарантувати цілісність токену.
Якщо реалізувати це неправильно, можуть виникнути проблеми, такі як термін дії токену, витік паролів або атаки на маршрути, що загрожують безпеці додатку.
Рішення
Давайте заглибимося в реалізацію! Ось покрокова інструкція зі створення аутентифікації за допомогою JWT у NestJS з використанням Sequelize для управління базою даних.
1. Встановлення залежностей
Спочатку переконайтеся, що у вас є необхідні залежності:
npm install @nestjs/jwt bcrypt sequelize sequelize-typescript
2. Створення AuthService
AuthService
керує процесом аутентифікації.
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcrypt';
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/sequelize';
import { User } from './schemas/user.schema';
import { SignUpDto } from './dtos/signup.dto';
import { SignInDto } from './dtos/signin.dto';
@Injectable()
export class AuthService {
constructor(
@InjectModel(User)
private readonly userModel: typeof User,
private readonly jwtService: JwtService,
) {}
async signIn(signInData: SignInDto) {
const { email, password } = signInData;
const user = await this.userModel.findOne({ where: { email } });
if (!user) {
throw new NotFoundException('User not found');
}
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
throw new NotFoundException('Invalid password');
}
const token = await this.jwtService.signAsync({ id: user.userId });
return { token };
}
async signUp(signUpData: SignUpDto) {
const { name, email, password, role } = signUpData;
const hashedPassword = await bcrypt.hash(password, 10);
const user = await this.userModel.create({
name,
email,
password: hashedPassword,
role,
});
const token = await this.jwtService.signAsync({ id: user.userId });
return { token };
}
}
3.
Налаштування AuthModule
AuthModule
— це місце, де все з'єднується.
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { JwtModule } from '@nestjs/jwt';
import { AuthController } from './auth.controller';
import { SequelizeModule } from '@nestjs/sequelize';
import { User } from './schemas/user.schema';
@Module({
imports: [
SequelizeModule.forFeature([User]),
JwtModule.register({
global: true,
secret: 'yourSecretKey',
signOptions: { expiresIn: '1h' },
}),
],
providers: [AuthService],
controllers: [AuthController],
exports: [AuthService],
})
export class AuthModule {}
4. Створення AuthGuard
AuthGuard
захищає маршрути від несанкціонованого доступу.
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Request } from 'express';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private readonly jwtService: JwtService) {}
async canActivate(context: ExecutionContext): Promise {
const request = context.switchToHttp().getRequest();
const token = this.extractTokenFromHeader(request);
if (!token) {
throw new UnauthorizedException('Token not found');
}
try {
const payload = await this.jwtService.verifyAsync(token, {
secret: 'yourSecretKey',
});
request['user'] = payload;
} catch {
throw new UnauthorizedException('Invalid token');
}
return true;
}
private extractTokenFromHeader(request: Request): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
}
5. Створення AuthController
AuthController
обробляє маршрути аутентифікації.
import { Body, Controller, HttpCode, HttpStatus, Post } from '@nestjs/common';
import { AuthService } from './auth.service';
import { SignUpDto } from './dtos/signup.dto';
import { SignInDto } from './dtos/signin.dto';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@HttpCode(HttpStatus.OK)
@Post('login')
signIn(@Body() signInDto: SignInDto) {
return this.authService.signIn(signInDto);
}
@HttpCode(HttpStatus.CREATED)
@Post('signup')
signUp(@Body() signUpDto: SignUpDto) {
return this.authService.signUp(signUpDto);
}
}
6. Налаштування AppModule
AppModule
реєструє всі модулі додатку. Переконайтеся, що створені модулі включені, щоб NestJS міг їх розпізнати.
import { Module } from '@nestjs/common';
import { SequelizeModule } from '@nestjs/sequelize';
import { AuthModule } from './auth/auth.module';
import { ProfileModule } from './profiles/profiles.module';
import { User } from './auth/schemas/user.schema';
import { Profile } from './profiles/schema/profile.schema';
@Module({
imports: [
ProfileModule,
AuthModule,
SequelizeModule.forRoot({
dialect: 'mysql',
host: 'localhost',
port: 3306,
username: 'your_username',
password: 'your_password',
database: 'your_database_name',
models: [User, Profile] // ваші схеми моделей,
autoLoadModels: true,
synchronize: true,
}),
],
})
export class AppModule {}
7. Захист маршруту
Додайте AuthGuard
до маршруту для його захисту:
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';
@Controller('profile')
export class ProfileController {
@UseGuards(AuthGuard)
@Get()
getProfile() {
return { message: 'Access granted' };
}
}
Ключові висновки
- Ніколи не зберігайте паролі у відкритому вигляді: Використовуйте
bcrypt
або аналогічні для хешування. - Термін дії токенів: Налаштуйте час життя токенів.
- JWT секрет: Використовуйте змінні середовища для зберігання чутливих ключів.
4.
## Глобальний захист
Розгляньте можливість застосування AuthGuard
глобально, якщо це необхідно.
Висновок
Аутентифікація через JWT у NestJS є простою та ефективною, але потребує уваги до безпеки та найкращих практик. Сподіваюся, цей посібник був корисним! Якщо у вас є питання або пропозиції, не соромтеся залишати коментарі нижче. Давайте обмінюватися ідеями!
Перекладено з: How to Create JWT Authentication Using NestJS