/** * Auth ของ add-in — e-Court pass-through ทางเดียว (ยกเลิก Keycloak 2026-10-27 ตามคำขอผู้ใช้) * ไม่มีขั้นตอน login ใน add-in อีกต่อไป: ไฟล์ที่ e-Court stamp custom properties * `token`/`owner` มา (`readTrackedProperties` อ่านรวม) = ใช้งานได้ทันที ไม่ต้องกดปุ่มใด ๆ * ไฟล์ที่ไม่มีทั้งคู่ = phase no-access ของ TaskpaneApp (ขั้นตอนสำหรับเอกสารทั่วไปจะมีในอนาคต) * * token เก็บใน memory เท่านั้น — getAccessToken คืน docToken ให้ apiClient แนบ Bearer * (ช่วงไม่ต่อ backend จริง versioningService เป็น mock ไม่เรียก apiClient อยู่แล้ว) */ import { config } from "../config"; /** state เดียว — null = ยังไม่เคย pass-through สำเร็จ (จากไฟล์ หรือ ?addindemo) */ let username: string | null = null; /** token จาก custom properties ของไฟล์ (e-Court pass-through) — เก็บใน memory เท่านั้น */ let docToken: string | null = null; /** dev-only — โหมด ?addindemo ของ WordAddinGate ใช้ตั้ง session ทันทีไม่อ่านไฟล์จริง */ export function signInForDemo(): void { username = config.mockUsername; } /** ถอด field จาก JWT payload (base64url) โดยไม่ตรวจ signature — ใช้ดู uid ประกอบชื่อผู้ใช้เท่านั้น */ function decodeJwtField(token: string, field: string): string | undefined { const payload = token.split(".")[1]; if (!payload) return undefined; try { // atob คืน binary string (1 char = 1 byte) — ต้องถอด UTF-8 ด้วย TextDecoder ก่อน JSON.parse // ไม่งั้น uid ที่ไม่ใช่ ASCII (เช่น ชื่อไทย) จะกลายเป็น mojibake const bytes = Uint8Array.from(atob(payload.replace(/-/g, "+").replace(/_/g, "/")), (char) => char.charCodeAt(0)); const json: unknown = JSON.parse(new TextDecoder().decode(bytes)); const value = typeof json === "object" && json !== null ? (json as Record)[field] : undefined; return typeof value === "string" && value !== "" ? value : undefined; } catch { return undefined; } } /** * e-Court pass-through — จุดเข้าเดียวของ auth: ไฟล์ที่ stamp `token`/`owner` ไว้ * username ได้จาก owner → uid ใน JWT → mockUsername (ตามลำดับ) * คืน true = ผ่านเข้าได้ (มี token หรือ owner อย่างน้อยหนึ่งตัว) */ export function signInFromDocument(input: { token?: string; owner?: string }): boolean { const token = input.token?.trim() || undefined; const owner = input.owner?.trim() || (token ? decodeJwtField(token, "uid") : undefined); if (!token && !owner) { return false; } username = owner ?? config.mockUsername; docToken = token ?? null; return true; } /** Bearer ของ apiClient — docToken จากไฟล์ (ส่วน demo/mock คืน token ปลอม) */ export async function getAccessToken(): Promise { if (docToken) return docToken; return username ? `mock-token-${username}` : null; } export function getUsername(): string | undefined { return username ?? undefined; }