Оволодійте автентифікацією в Next.js: Легкий посібник 🛡️✨

Багато розробників на початку кар'єри, коли чують слово "автентифікація", відчувають холодок по спині, адже все, що стосується безпеки, здається складним і важким у реалізації, але це не зовсім так.

Тому я вирішив створити дуже простий туторіал, щоб показати, що автентифікація — це не та страшна сила, якою ви її вважаєте.

Я використовую Next.js, але логіка схожа для інших фреймворків, оскільки я не буду використовувати жодних бібліотек — створимо все з нуля.

Чому використовувати автентифікацію

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

Діаграма нижче є частиною офіційної документації Next і наочно показує, як працює процес автентифікації:

pic

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

Створення форми в Next

Перш за все, створимо форму для отримання деталей користувача. Коли форма буде надіслана, вона створить дію на сервері.

import { useServer } from 'next/server';  
import { useActionState } from 'next/action';  

function SignUpForm() {  
 const action = useServer('signupAction');  
 const { pending, error } = useActionState(action);  

 return (  





 {pending ? 'Submitting...' : 'Sign Up'}  

 {error && 
{error.message}
}        
); }  

Налаштування сервера для отримання форми

Тепер створимо новий файл, де напишемо функцію сервера, яка оброблятиме надсилання форми.
По-перше, перевіримо поля вводу перед тим, як виконувати будь-яку логіку автентифікації.

import { z } from 'zod';  
import { hash } from 'bcryptjs';  
import { prisma } from '../lib/prisma';  
import { createSession } from '../lib/session';  

export const signupAction = async (formData) => {  
 const schema = z.object({  
 name: z.string().min(1),  
 email: z.string().email(),  
 password: z.string().min(6),  
 });  

 const { success, error } = schema.safeParse(formData);  

 if (!success) {  
 return { error: 'Invalid input' };  
 }  

 const { name, email, password } = formData;  

 const hashedPassword = await hash(password, 10);  
 const user = await prisma.user.create({  
 data: { name, email, password: hashedPassword },  
 });  

 const session = await createSession(user.id);  
 return { session };  
};

Керування сесіями

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

import { sign, verify } from 'jsonwebtoken';  
import { serialize, parse } from 'cookie';  

const secretKey = process.env.JWT_SECRET;  

export const createSession = (userId) => {  
 const token = sign({ userId }, secretKey, { expiresIn: '1h' });  
 const cookie = serialize('session', token, { httpOnly: true, maxAge: 3600 });  
 return { cookie, userId };  
};  

export const verifySession = (req) => {  
 const { session } = parse(req.headers.cookie || '');  
 if (!session) return null;  

 try {  
 const payload = verify(session, secretKey);  
 return payload.userId;  
 } catch {  
 return null;  
 }  
};  

export const deleteSession = () => {  
 const cookie = serialize('session', '', { httpOnly: true, maxAge: -1 });  
 return { cookie };  
};

Інтеграція створення сесії

Коли користувач зареєструється через нашу форму, ми викликаємо функцію createSession, щоб створити сесію для користувача після успішного створення акаунта.

const { name, email, password } = formData;  

const hashedPassword = await hash(password, 10);  
const user = await prisma.user.create({  
 data: { name, email, password: hashedPassword },  
});  

const { cookie, userId } = await createSession(user.id);  
return {  
 headers: { 'Set-Cookie': cookie },  
 userId,  
};

Далі нам потрібно вирішити, до яких маршрутів і даних користувач може отримати доступ залежно від його ролей чи дозволів.
Це відомо як авторизація.

Middleware для перевірки авторизації

Ми можемо обробляти логіку авторизації в middleware, і таким чином перевіряти, чи поточний маршрут захищений або доступний для доступу. Якщо маршрут захищений, користувач буде перенаправлений на екран входу.

import { NextResponse } from 'next/server';  
import { verifySession } from '../lib/session';  

export function middleware(req) {  
 const userId = verifySession(req);  

 if (!userId && req.url.pathname.startsWith('/dashboard')) {  
 return NextResponse.redirect('/login');  
 }  

 return NextResponse.next();  
}

Захист даних за допомогою шару доступу до даних

Для забезпечення консистентності коду корисно зберігати логіку авторизації поруч із місцем, де дані запитуються, використовуючи шар доступу до даних.

import { prisma } from '../lib/prisma';  
import { verifySession } from '../lib/session';  

export const getUser = async (req) => {  
 const userId = verifySession(req);  
 if (!userId) throw new Error('Unauthorized');  

 const user = await prisma.user.findUnique({ where: { id: userId } });  
 return user;  
};

Порада з безпеки: щоб зменшити ризик витоку даних, варто отримувати лише ті дані, які є строго необхідними від API.

export const getUser = async (req) => {  
 const userId = verifySession(req);  
 if (!userId) throw new Error('Unauthorized');  

 const user = await prisma.user.findUnique({  
 where: { id: userId },  
 select: { name: true, email: true },  
 });  
 return user;  
};

Якщо хочете дізнатися більше про автентифікацію з Next, обов'язково ознайомтеся з їхньою документацією, вона дуже детальна та повна. Сподіваюся, цей посібник допоможе вам зрозуміти принципи автентифікації в додатках Next.js.

Перекладено з: Domine a autenticação no Next.js: Guia descomplicado 🛡️✨

Leave a Reply

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