// PLAN-SSO.md §3.1 — POST /api/note/auth/token: เส้น mint login-token ของแอป note // (ทางเข้า x-api-key ตัวเดียวกับ docver แต่แยกเส้นจาก /api/documents/token — D1 // contract docver ไม่ถูกแต้) — สำหรับ backend e-Court ประกอบลิงก์ // `/note?…&t=` ให้ผู้ใช้คลิก (ฉบับ token 2026-10-30: หน้าแรกเป็น // client bootstrap แลก credential เป็น session token ที่ /api/note/auth/session) // token ที่ได้เป็น purpose="login" ใช้ได้กับการเข้าแอป note เท่านั้น (D4) // ไม่ import @/db · ห้าม log ค่า key/body ดิบ import { docverError } from "@/lib/document-versioning"; import { issueDocverToken, requireDocverApiKey } from "@/lib/docver-token"; import { parseLoginTokenBody } from "@/lib/note-auth"; export const runtime = "nodejs"; export const dynamic = "force-dynamic"; export async function POST(request: Request): Promise { const apiKeyFail = requireDocverApiKey(request); if (apiKeyFail !== null) return apiKeyFail; let raw: unknown; try { raw = await request.json(); } catch { return docverError("body ต้องเป็น JSON", 400); } const parsed = parseLoginTokenBody(raw); if (!parsed.ok) return docverError(parsed.error, 400); // ความผิด config ฝั่ง server ไม่ใช่ความผิดของผู้เรียก — ไม่ตั้ง = 500 (ห้าม log ค่า secret) const secret = process.env.DOCVER_TOKEN_SECRET?.trim(); if (secret === undefined || secret === "") { return docverError("DOCVER_TOKEN_SECRET ยังไม่ถูกตั้งค่า", 500); } const accessToken = issueDocverToken( { userid: parsed.claims.userid, username: parsed.claims.username, fid: parsed.claims.fid, template: null }, secret, parsed.ttlSeconds, "login", ); return new Response( JSON.stringify({ access_token: accessToken, token_type: "Bearer", expires_in: parsed.ttlSeconds, }), { headers: { "content-type": "application/json; charset=utf-8" } }, ); }