/** * Resource route: POST /api/auth/:step * * register-options / register-verify — パスキー登録(ログイン済み or 登録トークン必須) * login-options / login-verify — パスキーでログイン * logout — セッション破棄して /events へ */ import { redirect } from "react-router"; import type { Route } from "./+types/api.auth.$step"; import { authenticationOptions, canRegister, isLoggedIn, logout, registrationOptions, verifyAuthentication, verifyRegistration, } from "~/lib/auth.server"; export async function action({ request, params }: Route.ActionArgs) { switch (params.step) { case "register-options": { const { token } = await request.json(); if (!canRegister(request, token ?? null)) return forbidden(); const { options, setCookie } = await registrationOptions(request); return Response.json(options, { headers: { "Set-Cookie": setCookie } }); } case "register-verify": { const { token, response, label } = await request.json(); const loggedIn = isLoggedIn(request); if (!canRegister(request, token ?? null)) return forbidden(); return withSession(() => verifyRegistration(request, response, label ?? null, !loggedIn) ); } case "login-options": { const { options, setCookie } = await authenticationOptions(request); return Response.json(options, { headers: { "Set-Cookie": setCookie } }); } case "login-verify": { const { response } = await request.json(); return withSession(() => verifyAuthentication(request, response)); } case "logout": return redirect("/events", { headers: { "Set-Cookie": logout(request) } }); default: throw new Response("Not Found", { status: 404 }); } } async function withSession(verify: () => Promise): Promise { try { const setCookie = await verify(); return Response.json({ ok: true }, { headers: { "Set-Cookie": setCookie } }); } catch (e) { console.warn("[auth]", e); return Response.json({ ok: false, error: (e as Error).message }, { status: 400 }); } } function forbidden(): Response { return Response.json({ ok: false, error: "登録トークンが無効です" }, { status: 403 }); }