/** * MOCK — แทน Keycloak OIDC/PKCE ของต้นฉบับ (ผู้ใช้งานเลือกไม่ใช้ Keycloak ช่วงพอร์ต) * ต้นฉบับ: D:\word-addin-poc\addin\src\services\authService.ts (login ผ่าน Office Dialog API, * token เก็บใน memory เท่านั้น, auto-refresh) * * วันสลับเป็น auth จริง: เขียน implementation ใหม่โดยคง export surface ด้านล่างครบทุกตัว * (เพิ่ม refreshAccessToken ที่ต้นฉบับมีอยู่แล้วสำหรับ apiClient) — โค้ดอื่นไม่ต้องแตะ */ import { config } from "../config"; /** discriminated union เหมือนต้นฉบับ — ok:false ต้องมี error เสมอ */ export type LoginResult = { ok: true } | { ok: false; error: string }; /** state เดียวของ mock — null = signed out */ let username: string | null = null; /** token จาก custom properties ของไฟล์ (e-Court pass-through) — เก็บใน memory เท่านั้น */ let docToken: string | null = null; function delay(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } /** จำลองเปิดหน้าต่าง login — สำเร็จทันที ตั้งชื่อจาก NEXT_PUBLIC_ADDIN_MOCK_USERNAME */ export async function login(): Promise { await delay(300); username = config.mockUsername; return { ok: true }; } /** mock-only — โหมด ?addindemo ใช้ตั้ง session ทันทีไม่ผ่านปุ่ม Sign in */ 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 (ระยะแรก) — ไฟล์ที่ e-Court stamp `token`/`owner` ไว้ใน custom properties * ใช้งานได้เลยโดยไม่ต้องกด login: username จาก owner → uid ใน token → 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; } export async function getAccessToken(): Promise { if (docToken) return docToken; return username ? `mock-token-${username}` : null; } /** mock: token ไม่มีวันหมดอายุจริง — คืนตัวเดิมหรือ null ตามสถานะ login */ export async function refreshAccessToken(): Promise { return getAccessToken(); } export function getUsername(): string | undefined { return username ?? undefined; } export function isTokenValid(): boolean { return username !== null; } export function hasRefreshToken(): boolean { return username !== null; } export function startAutoRefresh(): void { // mock: ไม่มีอะไรต้อง refresh } export function signOut(): void { username = null; docToken = null; }