clone

a takuzu puzzle field for agents to explore tile by tile, with a referee and a replayable spectator view

main
mosaic / site / routes / api / session.ts
session.ts 1.8K
1// POST /api/session {id_token}: verify a shoo.dev id_token for this2// origin and set the session cookie.3import { define } from "../../utils.ts";4import {5 IdentityError,6 SESSION_TTL_S,7 sessionCookie,8 sessionSecret,9 signSession,10 verifyIdToken,11} from "../../lib/identity.ts";12import { machineRegistry } from "../../lib/mcp.ts";13import { publicOrigin } from "../../lib/prompt.ts";1415export const handler = define.handlers({16 async POST(ctx) {17 let body: { id_token?: unknown };18 try {19 body = await ctx.req.json();20 } catch {21 return Response.json({ error: "JSON body expected" }, { status: 400 });22 }23 if (typeof body.id_token !== "string") {24 return Response.json({ error: "id_token is required" }, { status: 400 });25 }26 try {27 // the audience is the public origin (behind the proxy the request28 // itself arrives over http)29 const claims = await verifyIdToken(body.id_token, publicOrigin());30 const store = await machineRegistry();31 const account = store.upsertAccount(claims.sub, claims.name);32 const exp = Math.floor(Date.now() / 1000) + SESSION_TTL_S;33 const value = await signSession(sessionSecret(), account.id, exp);34 return Response.json(35 { account: { id: account.id, name: account.name } },36 {37 headers: {38 "Set-Cookie": sessionCookie(39 value,40 ctx.url.protocol === "https:",41 SESSION_TTL_S,42 ),43 "Cache-Control": "no-store",44 },45 },46 );47 } catch (e) {48 const message = e instanceof Error ? e.message : String(e);49 return Response.json({ error: message }, {50 status: e instanceof IdentityError && e.code === "session" ? 500 : 401,51 });52 }53 },54});