Безперебійна аутентифікація користувачів з NestJS та Prisma: Посібник для початківців

текст перекладу
pic

Фото від Timothy Cuenat на Unsplash

Аутентифікація є критично важливою частиною будь-якого застосунку, але вона не обов'язково має бути складною. У цій статті я проведу вас через процес створення простого, але надійного механізму аутентифікації користувачів за допомогою NestJS та Prisma. Prisma спрощує управління базами даних, виступаючи як інструмент ORM (Object-Relational Mapping), дозволяючи нам визначати та взаємодіяти з нашою схемою бази даних без зайвих зусиль. Ми розглянемо все, починаючи від налаштування схеми бази даних до впровадження JWT для захисту маршрутів — все в ефективному та зручному для початківців форматі.

Передумови

  1. Встановлений Node.js
  2. Базові знання Typescript та NestJS

Встановлення NestJS CLI глобально

Запустіть цей код у вашому терміналі для встановлення NestJS CLI на вашу систему

npm install -g @nestjs/cli

Створення нового проекту NestJS

Ця команда створює новий проект NestJS під назвою ‘auth-demo’.

nest new auth-demo

Встановлення залежностей

Перейдіть у директорію проекту, а потім встановіть залежності, які ми будемо використовувати. Остання частина коду встановлює prisma як залежність для розробки, оскільки вона не потрібна у виробництві.

  • prisma: інструмент для взаємодії з базами даних у типізованому вигляді.
  • @prisma/client: бібліотека-клієнт, створена Prisma для взаємодії з вашою базою даних.
  • bcryptjs: бібліотека для хешування та перевірки паролів.
  • jsonwebtoken: бібліотека для створення та перевірки JSON Web Tokens (JWT), які використовуються для аутентифікації.
  • @nestjs/config: бібліотека nestjs, яку ми будемо використовувати для налаштування та роботи з нашими змінними середовища.
cd auth-demo  
npm install prisma @prisma/client bcryptjs jsonwebtoken @nestjs/config  
npm install -D prisma

Ініціалізація Prisma

Це ініціалізує prisma у вашому проекті та створить папку prisma, яка містить ваш файл схеми.

npx prisma init

Оновлення змінних середовища

Файл prisma/schema.prisma вже очікує змінну середовища з назвою ‘DATABASE_URL’ з вашого файлу .env. Ваш файл .env має виглядати приблизно так. Замініть рядок на ваш фактичний рядок підключення до бази даних.

pic

Створення та міграція схеми User

Додайте наступний код в кінець файлу prisma/schema.prisma для створення схеми для користувача

model User {  
 id Int @id @default(autoincrement())  
 email String @unique  
 password String  
 createdAt DateTime @default(now())  
}

Після збереження цього файлу, запустіть наступний код, щоб виконати міграцію схеми користувача в вашу базу даних. Це створить таблицю під назвою ‘User’ у вашій базі даних з відповідними атрибутами та типами даних.

npx prisma migrate dev --name init

Генерація Prisma модуля та сервісу

CLI NestJS дозволяє нам запускати деякі команди для генерації необхідних файлів безпосередньо з терміналу. Ми будемо використовувати це для створення модуля та сервісу для prisma. Це створить папку з назвою ‘prisma’ з необхідними файлами.

nest generate module prisma  
nest generate service prisma

Ваш файл src/prisma/prisma.module.ts має виглядати ось так:

import { Module } from '@nestjs/common';  
import { PrismaService } from './prisma.service';  

@Module({  
 providers: [PrismaService],  
 exports: [PrismaService],  
})  
export class PrismaModule {}

Ваш файл src/prisma/prisma.service.ts має виглядати ось так.
текст перекладу
Це налаштує базу даних на URL, вказаний у вашому файлі .env.

import { Injectable } from '@nestjs/common';  
import { ConfigService } from '@nestjs/config';  
import { PrismaClient } from '@prisma/client';  

@Injectable()  
export class PrismaService extends PrismaClient {  
 constructor(private configService: ConfigService) {  
 super({  
 datasources: {  
 db: {  
 url: configService.get('DATABASE_URL'),  
 },  
 },  
 });  
 }  
}

Генерація модуля, контролера та сервісу для аутентифікації

Використовуючи CLI NestJS, ми створимо модуль аутентифікації, контролер та сервіс за допомогою наступних команд.

nest generate module auth  
nest generate service auth  
nest generate controller auth

Ваш файл src/auth/auth.module.ts має виглядати так:

import { Module } from '@nestjs/common';  
import { AuthService } from './auth.service';  
import { AuthController } from './auth.controller';  
import { PrismaService } from 'src/prisma/prisma.service';  
import { ConfigModule } from '@nestjs/config';  

@Module({  
 imports: [ConfigModule],  
 providers: [AuthService, PrismaService],  
 exports: [AuthService],  
})  
export class AuthModule {}

Ми будемо створювати сервіс аутентифікації з функціями для хешування та соління паролів за допомогою bcrypt, генерації та перевірки токенів за допомогою Json Web Tokens та, зрештою, для входу та реєстрації. Ваш файл src/auth/auth.service.ts має виглядати так:

import { Injectable, UnauthorizedException } from '@nestjs/common';  
import { PrismaService } from '../prisma/prisma.service';  
import * as bcrypt from 'bcrypt';  
import * as jwt from 'jsonwebtoken';  

@Injectable()  
export class AuthService {  
 private readonly jwtSecret = 'supersecretkey'; // Замініть на .env для безпеки  

 constructor(private prisma: PrismaService) {}  

 async hashPassword(password: string): Promise {  
 const salt = await bcrypt.genSalt();  
 return bcrypt.hash(password, salt);  
 }  

 async generateToken(userId: number): Promise {  
 return jwt.sign({ userId }, this.jwtSecret, { expiresIn: '1h' });  
 }  

 async register(email: string, password: string): Promise<{ token: string }> {  
 const hashedPassword = await this.hashPassword(password);  
 const user = await this.prisma.user.create({  
 data: { email, password: hashedPassword },  
 });  
 const token = await this.generateToken(user.id);  
 return { token };  
 }  

 async login(email: string, password: string): Promise<{ token: string }> {  
 const user = await this.prisma.user.findUnique({ where: { email } });  
 if (!user || !(await bcrypt.compare(password, user.password))) {  
 throw new UnauthorizedException('Невірні дані для входу');  
 }  
 const token = await this.generateToken(user.id);  
 return { token };  
 }  

 async validateToken(token: string): Promise {  
 try {  
 return jwt.verify(token, this.jwtSecret);  
 } catch (error) {  
 throw new UnauthorizedException('Невірний або прострочений токен');  
 }  
 }  
}

Наступним кроком ми налаштуємо контролер для обробки запитів на вхід та вихід. Ваш файл src/auth/auth.controller.ts має виглядати так.

import { Body, Controller, Post } from '@nestjs/common';  
import { AuthService } from './auth.service';  

@Controller('auth')  
export class AuthController {  
 constructor(private authService: AuthService) {}  

 // Це буде отримувати запити на '/auth/register'  
 @Post('register')  
 async register(@Body() body: { email: string; password: string }) {  
 return this.authService.register(body.email, body.password);  
 }  

 // Це буде отримувати запити на '/auth/login'  
 @Post('login')  
 async login(@Body() body: { email: string; password: string }) {  
 return this.authService.login(body.email, body.password);  
 }  
}

Налаштування проміжного програмного забезпечення для перевірки токенів

Для покращення безпеки ми впроваджуємо Json Web Tokens для перевірки користувачів. Для цього ми налаштуємо проміжне програмне забезпечення.
текст перекладу
У контексті NestJS, проміжне програмне забезпечення (middleware) можна розглядати як функцію, яка виконується до обробників маршруту.

Спочатку ми створимо проміжне програмне забезпечення, виконавши:

nest generate middleware auth

Це створить файл src/auth/auth.middleware.ts. У цьому файлі ми налаштуємо проміжне програмне забезпечення для перевірки вхідних запитів, перевіряючи наявність дійсного заголовка Authorization, що містить JSON Web Token (JWT). Проміжне програмне забезпечення перехоплює кожен запит, перевіряє токен за допомогою AuthService і додає декодовану інформацію про користувача до об'єкта запиту для подальшого використання в контролерах. Якщо токен відсутній, недійсний або прострочений, проміжне програмне забезпечення зупиняє запит і повертає помилку UnauthorizedException. Ваш файл src/auth/auth.middleware.ts має виглядати так:

import { Injectable, NestMiddleware, UnauthorizedException } from '@nestjs/common';  
import { AuthService } from './auth.service';  
import { Request, Response, NextFunction } from 'express';  

@Injectable()  
export class AuthMiddleware implements NestMiddleware {  
 constructor(private authService: AuthService) {}  

 async use(req: Request, res: Response, next: NextFunction) {  
 const authHeader = req.headers['authorization'];  
 if (!authHeader) {  
 throw new UnauthorizedException('Authorization header is missing');  
 }  

 const token = authHeader.split(' ')[1];  
 if (!token) {  
 throw new UnauthorizedException('Token is missing');  
 }  

 try {  
 const payload = await this.authService.validateToken(token);  
 req['user'] = payload;  
 next();  
 } catch (error) {  
 throw new UnauthorizedException('Invalid or expired token');  
 }  
 }  
}

Застосування проміжного програмного забезпечення

Щоб застосувати проміжне програмне забезпечення для аутентифікації в нашому застосунку, нам потрібно оновити файл src/app.module.ts, щоб він виглядав так:

import { MiddlewareConsumer, Module } from '@nestjs/common';  
import { AuthMiddleware } from './auth/auth.middleware';  
import { AuthModule } from './auth/auth.module';  
import { PrismaModule } from './prisma/prisma.module';  
import { ConfigModule } from '@nestjs/config';  

@Module({  
 imports: [  
 ConfigModule.forRoot({  
 isGlobal: true,  
 }),  
 AuthModule,  
 PrismaModule,  
 ],  
})  
export class AppModule {  
 configure(consumer: MiddlewareConsumer) {  
 consumer.apply(AuthMiddleware).forRoutes('*');  
 }  
}

Отже, це в принципі все! Я розумію, що це досить детально, але я хотів переконатися, що кожен крок зрозумілий, щоб ви могли легко йти слідом. Ви можете взяти цей код, налаштувати його за потребою і використовувати у своїх проектах. Дякую за вашу увагу, не забувайте поставити аплауз і поділитися. Сподіваюся, що це було корисно. Бажаю чудового дня і до зустрічі в наступному матеріалі! Па-па!

Перекладено з: Effortless User Authentication with NestJS and Prisma: A Beginner-Friendly Guide

Leave a Reply

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