summaryrefslogtreecommitdiff
path: root/app/routes/api.auth.$step.ts
diff options
context:
space:
mode:
Diffstat (limited to 'app/routes/api.auth.$step.ts')
-rw-r--r--app/routes/api.auth.$step.ts63
1 files changed, 63 insertions, 0 deletions
diff --git a/app/routes/api.auth.$step.ts b/app/routes/api.auth.$step.ts
new file mode 100644
index 0000000..64940f2
--- /dev/null
+++ b/app/routes/api.auth.$step.ts
@@ -0,0 +1,63 @@
+/**
+ * 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<string>): Promise<Response> {
+ 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 });
+}