summaryrefslogtreecommitdiff
path: root/app/routes/api.auth.$step.ts
blob: 64940f2a711b671ee3667da18527e4a6f0d169ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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 });
}