Перейти к содержимому

11. Аутентификация

Remix не навязывает конкретное решение для аутентификации, но предоставляет все инструменты для её реализации через сессии, cookies и middleware-паттерны в loaders.

app/session.server.ts
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;
}
app/routes/dashboard.tsx
import { requireUser } from "~/session.server";
export async function loader({ request }) {
const user = await requireUser(request); // throws redirect если нет авторизации
return json({ user });
}
app/routes/login.tsx
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",
});
}
app/routes/logout.tsx
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 action
const 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() });
}