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 });
}
|