Работа с сессиями в серверных API-роутах строится вокруг идеи безопасного хранения состояния пользователя между запросами без использования внешнего хранилища. Библиотека iron-session реализует этот подход через зашифрованные и подписанные cookie, которые содержат минимально необходимую информацию о сессии.
В основе механизма лежит принцип: сервер не хранит состояние, а доверяет только зашифрованному содержимому cookie, которое невозможно подделать без секретного ключа.
Настройка начинается с определения параметров шифрования и структуры сессии. Обычно это отдельный модуль, который используется во всех API routes.
// lib/session.js
import { withIronSessionApiRoute } from "iron-session/next";
export const sessionOptions = {
password: process.env.SESSION_SECRET,
cookieName: "app_session",
cookieOptions: {
secure: process.env.NODE_ENV === "production",
},
};
Ключевой параметр password должен быть достаточно
длинным (не менее 32 символов), поскольку он используется для шифрования
данных сессии.
API route в Next.js оборачивается функцией-обёрткой, которая
добавляет объект req.session.
// pages/api/login.js
import { withIronSessionApiRoute } from "iron-session/next";
import { sessionOptions } from "../. ./lib/session";
export default withIronSessionApiRoute(loginRoute, sessionOptions);
async function loginRoute(req, res) {
const { username, password } = req.body;
if (username === "admin" && password === "1234") {
req.session.user = {
id: 1,
username: "admin",
role: "admin",
};
await req.session.save();
return res.status(200).json({ ok: true });
}
res.status(401).json({ error: "Invalid credentials" });
}
После успешной аутентификации объект пользователя записывается в
req.session.user, а затем сохраняется в зашифрованной
cookie.
Каждый защищённый маршрут может получать доступ к данным сессии
напрямую через req.session.
// pages/api/profile.js
import { withIronSessionApiRoute } from "iron-session/next";
import { sessionOptions } from "../. ./lib/session";
export default withIronSessionApiRoute(profileRoute, sessionOptions);
async function profileRoute(req, res) {
const user = req.session.user;
if (!user) {
return res.status(401).json({ error: "Unauthorized" });
}
res.json({
id: user.id,
username: user.username,
role: user.role,
});
}
Проверка авторизации сводится к проверке наличия объекта
req.session.user.
Удаление сессии выполняется через явное уничтожение данных.
// pages/api/logout.js
import { withIronSessionApiRoute } from "iron-session/next";
import { sessionOptions } from "../. ./lib/session";
export default withIronSessionApiRoute(logoutRoute, sessionOptions);
async function logoutRoute(req, res) {
req.session.destroy();
res.json({ ok: true });
}
После вызова destroy() cookie удаляется на стороне
клиента, а данные становятся недействительными.
В API routes часто требуется единый механизм защиты. Это можно реализовать через функцию-обёртку.
export function requireAuth(handler) {
return withIronSessionApiRoute(async (req, res) => {
if (!req.session.user) {
return res.status(401).json({ error: "Unauthorized" });
}
return handler(req, res);
}, sessionOptions);
}
Использование:
// pages/api/admin.js
async function adminRoute(req, res) {
if (req.session.user.role !== "admin") {
return res.status(403).json({ error: "Forbidden" });
}
res.json({ secret: "admin data" });
}
export default requireAuth(adminRoute);
Такой подход позволяет централизовать проверку авторизации и уменьшить дублирование кода.
Сессия может изменяться в процессе работы приложения, например при обновлении профиля пользователя.
req.session.user.username = "newName";
await req.session.save();
Важно явно вызывать save(), иначе изменения не будут
записаны в cookie.
Сессия представляет собой обычный JavaScript-объект, но хранить в ней следует только минимально необходимую информацию:
Не рекомендуется хранить:
Причина заключается в ограничении размера cookie и необходимости минимизации поверхности атаки.
Клиентская часть взаимодействует с API routes стандартными запросами:
const res = await fetch("/api/profile");
const data = await res.json();
Если сессия отсутствует, сервер возвращает 401, и клиент может перенаправить пользователя на страницу входа.
Так как cookie отправляется автоматически, каждый запрос к API может использоваться как источник проверки авторизации. Это делает архитектуру stateless с точки зрения сервера, при этом сохраняется возможность хранения состояния пользователя.
iron-session использует:
Даже если пользователь получит cookie, её содержимое невозможно изменить без обнаружения подделки.
/lib
session.js
/pages
/api
login.js
logout.js
profile.js
admin.js
Централизация конфигурации сессии позволяет использовать единые правила безопасности во всех маршрутах.
По мере роста приложения структура сессии может расширяться:
req.session.user = {
id: 1,
username: "admin",
role: "admin",
permissions: ["read", "write"],
lastLogin: Date.now(),
};
При этом важно сохранять баланс между удобством и размером cookie, так как чрезмерное увеличение данных приводит к проблемам с заголовками HTTP.
Часто API разделяются на:
req.session.user)Это разделение обычно реализуется через отдельные директории или обёртки.
if (!req.session.user) {
return res.status(401).end();
}
Такой подход формирует базовый слой защиты, поверх которого строится бизнес-логика маршрутов.