Як створити аутентифікацію через JWT за допомогою NestJS

pic

Аутентифікація — це одна з основ будь-якого безпечного додатку. Сьогодні ми розглянемо, як реалізувати аутентифікацію за допомогою JSON Web Token (JWT) у додатку, створеному з використанням NestJS. Я поділюсь кроками, яких я дотримувався для створення цієї функціональності, пояснюючи концепції та реалізацію на шляху.

Вступ

JWT — популярне та ефективне рішення для аутентифікації в сучасних додатках. Воно надає безпечний метод для обміну інформацією між надійними сторонами. Ідея проста: генерується токен для ідентифікації аутентифікованого користувача, який може бути перевірений сервером.

Але чому це важливо? Без належної аутентифікації будь-хто може отримати доступ до конфіденційної інформації або виконати обмежені дії у вашому додатку. JWT ефективно вирішує це питання.

Проблема

Під час розробки додатку ви можете зіткнутися з такими викликами:

  • Як захистити чутливі маршрути від несанкціонованого доступу?
  • Як реалізувати просту, але ефективну аутентифікацію для REST API?
  • Як безпечно обробляти паролі та токени?

Відповідь полягає в поєднанні JWT з найкращими практиками шифрування та чітко спланованим потоком.

Чому це важливо

JWT працює на основі трьох основних компонентів:

  1. Заголовок (Header): містить тип токену та алгоритм підпису.
  2. Вантаж (Payload): містить інформацію про користувача (зазвичай не чутливі дані).
  3. Підпис (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' };  
 }  
}

Ключові висновки

  1. Ніколи не зберігайте паролі у відкритому вигляді: Використовуйте bcrypt або аналогічні для хешування.
  2. Термін дії токенів: Налаштуйте час життя токенів.
  3. JWT секрет: Використовуйте змінні середовища для зберігання чутливих ключів.
    4.
    ## Глобальний захист

Розгляньте можливість застосування AuthGuard глобально, якщо це необхідно.

Висновок

Аутентифікація через JWT у NestJS є простою та ефективною, але потребує уваги до безпеки та найкращих практик. Сподіваюся, цей посібник був корисним! Якщо у вас є питання або пропозиції, не соромтеся залишати коментарі нижче. Давайте обмінюватися ідеями!

Перекладено з: How to Create JWT Authentication Using NestJS

Leave a Reply

Your email address will not be published. Required fields are marked *