Refactor authentication flow and ticket handling in add-in
All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 4s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 1m23s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 5s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 0s

- Removed client-side HMAC verification; server now handles all HMAC checks.
- Updated ticket processing logic to improve UX and security.
- Adjusted API request handling to return discriminated results based on HTTP status.
- Enhanced error messages for better clarity on authentication failures.
- Added unit tests for the new token endpoint to ensure compliance with updated contract.
- Cleaned up unused code and comments related to previous HMAC handling.
This commit is contained in:
Suphonchai Phoonsawat 2026-11-04 21:18:19 +07:00
parent 2e951f99f4
commit c0833c00f7
24 changed files with 858 additions and 460 deletions

View file

@ -694,16 +694,18 @@ export const openApiSpec = {
description:
"ออก `access_token` (JWT HS256) ไว้เรียก endpoint อื่นของกลุ่ม documents — " +
"token ฝัง claims `{userid, username, fid, template}` อายุตาม env " +
"`DOCVER_TOKEN_TTL_SECONDS` (default 3600 วินาที)\n\n" +
"`DOCVER_TOKEN_TTL_SECONDS` (default 604800 วินาที = 7 วัน — token อายุยาวแทน" +
"ระบบ refresh/renew ของ add-in)\n\n" +
"**ทางเข้า 2 แบบ** — เลือกตามว่าใครเป็นคนเรียก (ดูตัวอย่างใน Request body ได้ทั้งคู่):\n\n" +
"1. **ระบบภายนอก (server-to-server)** — แนบ header `x-api-key` (ค่าจาก env " +
"`DOCVER_API_KEY`) แล้วส่ง claims ตรง ๆ ใน body ตาม schema `TokenRequest`\n" +
"2. **add-in ใน Word** — ไม่ต้องมี `x-api-key` ส่ง body `{ticket_data, HmacSha256}` " +
"ตาม schema `TicketTokenRequest` (คู่ค่าที่ e-Court stamp ไว้ใน custom properties " +
"ของไฟล์ .docx) — server ตรวจลายเซ็น HMAC-SHA256 ด้วย env `ADDIN_TICKET_SECRET` " +
'(ไม่ตั้ง → env `SECRET` → `"super_secret"`) แล้วออก token จาก claims ที่อยู่ในตัว ticket เอง · ' +
"**ticket หมดอายุ** ยังออกให้ต่อเมื่อเอกสารนี้ (`fid`) เคยมี version ที่สร้างโดย `uid` เดียวกัน " +
"(ผู้ที่เคยทำงานกับเอกสารเดิมกลับมาใช้ต่อได้แม้ ticket หมดอายุ — ไม่เคยทำงาน = 401)\n\n" +
'(ไม่ตั้ง → env `SECRET` → `"super_secret"`) แล้วออก token จาก claims ที่อยู่ในตัว ticket เอง ' +
"(contract 2026-11-04: ฟอร์แม็ตพัง/ไม่ครบ = 400 · ลายเซ็นไม่ถูก = 401 · " +
"**ลายเซ็นถูกแต่ ticket หมดอายุ = 403** — add-in ที่มี access_token เดิมของ `fid` " +
"นี้ใน session ยังใช้ต่อได้ ไม่มี = ขอไฟล์ใหม่จาก e-Court)\n\n" +
"ได้ token แล้วแนบเป็น `Authorization: Bearer <token>` กับทุก request ถัดไป",
security: [{ ApiKeyAuth: [] }],
requestBody: {
@ -753,7 +755,17 @@ export const openApiSpec = {
401: {
description:
"ทางเข้า x-api-key: ไม่ได้ส่ง header หรือค่าไม่ถูกต้อง (env DOCVER_API_KEY) · " +
"ทางเข้า ticket: ticket ไม่ถูกต้อง/หมดอายุ หรือไม่ได้ส่งมา (ข้อความเดียวกันทุกสาเหตุ)",
"ทางเข้า ticket: ลายเซ็น HMAC ไม่ถูก (ข้อความเดียวกันทุกสาเหตุ)",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/Error" },
},
},
},
403: {
description:
"เฉพาะทางเข้า ticket: ลายเซ็นถูกแต่ ticket หมดอายุ — ใช้ access_token เดิมของ " +
"fid นี้ใน session ต่อ หรือขอไฟล์ใหม่จาก e-Court (contract 2026-11-04)",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/Error" },
@ -945,7 +957,7 @@ export const openApiSpec = {
"- `type=pdf` → คืนไบต์ pdf ตรง ๆ (version ล่าสุดไม่มีสำเนา PDF = 404)\n" +
"- `type=docx` → server เติม `exp` ใหม่ (now + `TICKET_SEC` default 300 วิ) + " +
"คำนวณ `HmacSha256` (secret ตาม chain `ADDIN_TICKET_SECRET` → `SECRET` → " +
"`\"super_secret\"` ให้ตรงฝั่ง verify ของ add-in) และ `Ed25519` (เฉพาะเมื่อ " +
"`\"super_secret\"` ให้ตรงฝั่ง verify ของ server — ทาง ticket ของ /token) และ `Ed25519` (เฉพาะเมื่อ " +
"`PRIVATE_KEY_HEX` ตั้ง) แล้วเขียน custom properties ลง**สำเนาในหน่วยความจำ** " +
"ก่อนคืนไฟล์ — object ใน MinIO ไม่ถูกแต่ (non-destructive) และเขียนทับ stamp " +
"เดิมของ template ได้\n" +
@ -1127,13 +1139,13 @@ export const openApiSpec = {
},
TokenResponse: {
type: "object",
description: "access_token ของกลุ่ม documents (JWT HS256 อายุสั้น)",
description: "access_token ของกลุ่ม documents (JWT HS256)",
properties: {
access_token: { type: "string", description: "JWT พร้อมใช้แนบเป็น Bearer" },
token_type: { type: "string", example: "Bearer" },
expires_in: {
type: "integer",
description: "อายุวินาที (env DOCVER_TOKEN_TTL_SECONDS, default 3600)",
description: "อายุวินาที (env DOCVER_TOKEN_TTL_SECONDS, default 604800 = 7 วัน)",
},
},
},