11. Аутентификация
Remix не навязывает конкретное решение для аутентификации, но предоставляет все инструменты для её реализации через сессии, cookies и middleware-паттерны в loaders.
Паттерн requireUser
Заголовок раздела «Паттерн requireUser»import { redirect } from "@remix-run/node";import { getSession } from "./session";
export async function requireUser(request: Request) { const session = await getSession(request.headers.get("Cookie")); const userId = session.get("userId");
if (!userId) { throw redirect("/login"); }
const user = await db.user.findUnique({ where: { id: userId } }); if (!user) { throw redirect("/login"); }
return user;}Защита маршрутов
Заголовок раздела «Защита маршрутов»import { requireUser } from "~/session.server";
export async function loader({ request }) { const user = await requireUser(request); // throws redirect если нет авторизации return json({ user });}Login Action
Заголовок раздела «Login Action»export async function action({ request }) { const formData = await request.formData(); const email = formData.get("email") as string; const password = formData.get("password") as string;
// Валидация if (!email || !password) { return json( { errors: { email: "Обязательное поле", password: "Обязательное поле" } }, { status: 400 } ); }
// Проверка пользователя const user = await verifyLogin(email, password); if (!user) { return json( { errors: { general: "Неверный email или пароль" } }, { status: 400 } ); }
// Создание сессии return createUserSession({ request, userId: user.id, remember: formData.get("remember") === "on", redirectTo: formData.get("redirectTo") as string || "/dashboard", });}export async function action({ request }) { const session = await getSession(request.headers.get("Cookie")); return redirect("/login", { headers: { "Set-Cookie": await destroySession(session), }, });}
export function loader() { return redirect("/");}Перенаправление после логина
Заголовок раздела «Перенаправление после логина»// Сохраняем URL куда хотел попасть пользовательexport async function loader({ request }) { const user = await getUser(request); if (!user) { const url = new URL(request.url); throw redirect(\`/login?redirectTo=\${url.pathname}\`); } return json({ user });}
// В login actionconst redirectTo = formData.get("redirectTo") as string || "/dashboard";return createUserSession({ userId: user.id, redirectTo });Роли и разрешения
Заголовок раздела «Роли и разрешения»export async function requireRole(request: Request, role: "admin" | "editor") { const user = await requireUser(request);
if (user.role !== role) { throw new Response("Доступ запрещён", { status: 403 }); }
return user;}
// Использованиеexport async function loader({ request }) { const admin = await requireRole(request, "admin"); return json({ stats: await getAdminStats() });}