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
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:
parent
2e951f99f4
commit
c0833c00f7
24 changed files with 858 additions and 460 deletions
|
|
@ -12,10 +12,10 @@ Task pane ทั้งหมดเป็น state machine เดียว **ไ
|
|||
|
||||
ตั้งแต่ 2026-10-19 taskpane มี **2 แท็บ** (toggle กลุ่มปุ่ม `aria-pressed` ใต้ header — แสดงเฉพาะ phase `untracked`/`tracking`): **Versioning** คือทุกอย่างในเอกสารนี้ตามเดิม ส่วน **เครื่องมือ Template** (ดูหัวข้อ 10) คือเครื่องมือจัดการ Content Controls ฝั่ง Office.js ล้วน ไม่ยิง backend จึงใช้ได้ทั้งสอง phase ปุ่ม toggle **ทั้งสองปิดระหว่าง flow ที่เขียนเอกสาร** — `saving || starting` (versioning), `saveBusy` (บันทึก local/ปิดเอกสาร), `templateBusy` (operation ของแท็บ template รายงานผ่าน `onBusyChange` — กันสลับกลาง operation แล้ว flow versioning มา stamp/capture ซ้อน หรือกลับกัน; code review 2026-10-20) · สถานะแท็บรีเซ็ตกลับ `versioning` ตอนเข้า `no-access` กัน surprise · banner auto-save error (FR-17) ยกไว้เหนือ toggle ให้เห็นทั้งสองแท็บ
|
||||
|
||||
**ไม่มี login ตั้งแต่ 2026-10-27** (ยกเลิก Keycloak ตามคำขอผู้ใช้) — auth ทางเข้าเดียวคือ **ticket ของ e-Court**: `detect()` อ่าน custom properties `ticket_data` + `HmacSha256` ที่ e-Court stamp มากับไฟล์ แล้วเรียก `authService.signInWithTicket()` — ตรวจ HMAC ฝั่ง client → ตรวจ exp → ดู session ใน localStorage ตาม uid/fid → ขอ access_token ที่ `/api/documents/token` เก็บตาม fid (flow เต็ม: `docs/requirement/addin-workflow.md` + แผน `docs/PLAN-ADDIN-AUTH.md`)
|
||||
**ไม่มี login ตั้งแต่ 2026-10-27** (ยกเลิก Keycloak ตามคำขอผู้ใช้) — auth ทางเข้าเดียวคือ **ticket ของ e-Court**: `detect()` อ่าน custom properties `ticket_data` + `HmacSha256` ที่ e-Court stamp มากับไฟล์ แล้วเรียก `authService.signInWithTicket()` — pre-check ฟอร์แม็ต/exp ฝั่ง client (UX gate) → ยิง `/api/documents/token` **ทุกครั้งที่เปิดเอกสาร** ให้ server ตรวจลายเซ็น (ไม่มี secret ฝั่ง client ตาม PLAN-SSO.md D13 — แก้ 2026-11-04) → แยกทางตาม status 200/400/401/403 → เก็บ access_token ใน localStorage ตาม fid (flow เต็ม: `docs/requirement/addin-workflow.md` + แผน `docs/PLAN-ADDIN-AUTH.md`)
|
||||
|
||||
```
|
||||
checking ──(ไม่มี ticket / HMAC ไม่ตรง / ticket หมดอายุ / ขอ token ไม่สำเร็จ)──→ no-access
|
||||
checking ──(ไม่มี ticket / ฟอร์แม็ตพัง / ลายเซ็นไม่ผ่าน(401) / ticket หมดอายุ / ขอ token ไม่สำเร็จ)──→ no-access
|
||||
│
|
||||
├─(ticket ผ่าน + ไม่ใช่ tracked doc)──→ untracked
|
||||
└─(ticket ผ่าน + tracked doc)────────→ tracking
|
||||
|
|
@ -25,7 +25,7 @@ checking ──(ไม่มี ticket / HMAC ไม่ตรง / ticket หม
|
|||
|
||||
- `untracked` → (Start Versioning สำเร็จ) → `tracking`
|
||||
- ทุก phase → (ไม่มี ticket ตอน detect ใหม่ / 401 หลังพยายาม renew จาก ticket แล้ว = ticket หมดอายุไม่มีทางต่ออายุ) → `no-access`
|
||||
- ปุ่ม "ลองใหม่" มีจริงแค่ 2 จุด: `bootError` ใน `checking` และ history error ใน `VersionHistoryPanel` — `untracked`/`tracking` **ไม่มี**ปุ่มลองใหม่แยกต่างหาก (เริ่ม flow ใหม่ด้วยการกดปุ่ม flow ซ้ำ)
|
||||
- ปุ่ม "ลองใหม่" มีจริงแค่ 3 จุด: `bootError` ใน `checking`, history error ใน `VersionHistoryPanel` และ (เพิ่ม 2026-11-04) no-access เคส "อ่านข้อมูลเอกสารไม่สำเร็จ" — `untracked`/`tracking` **ไม่มี**ปุ่มลองใหม่แยกต่างหาก (เริ่ม flow ใหม่ด้วยการกดปุ่ม flow ซ้ำ)
|
||||
|
||||
State ที่ UI ถือนอกเหนือจาก phase:
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ State ที่ UI ถือนอกเหนือจาก phase:
|
|||
| `documentId` | tracking id ของเอกสาร (มีเฉพาะ `tracking`) |
|
||||
| `documentTemplateId` | template ต้นทางของ**ไฟล์ร่าง** (มีเฉพาะ `untracked` ที่เป็นไฟล์ร่าง — FR-16) |
|
||||
| `versions` + `historyLoading` + `historyError` | version history ของ `documentId` |
|
||||
| `noAccessNotice` | ข้อความบนหน้า `no-access` — ต่างกันตามสาเหตุ (ไม่มี ticket ในไฟล์ / HMAC ไม่ตรง = ไฟล์ถูกแก้ / ticket หมดอายุ / ขอ token ไม่สำเร็จ) |
|
||||
| `noAccessNotice` | ข้อความบนหน้า `no-access` — ต่างกันตามสาเหตุ (ไม่มี ticket ในไฟล์ / ฟอร์แม็ตพัง / ลายเซ็นไม่ผ่าน = ไฟล์ถูกแก้ / ticket หมดอายุ / ขอ token ไม่สำเร็จ) |
|
||||
| `starting`, `saving`, `progressMessage` | busy + ข้อความ progress ของ flow Start Versioning / Save & Version |
|
||||
| `confirmOpen`, `startConfirmOpen` | เปิด dialog ยืนยันของ flow ไหน |
|
||||
| `flowError`, `flowWarning`, `flowSuccess` | MessageBar 3 แบบของ flow หลัก (แสดงเฉพาะ `tracking`) |
|
||||
|
|
@ -64,12 +64,17 @@ State ที่ UI ถือนอกเหนือจาก phase:
|
|||
### `no-access`
|
||||
|
||||
- ข้อความ: "เอกสารนี้ยังใช้งาน add-in ไม่ได้"
|
||||
- คำอธิบายจางๆ (`noAccessNotice` ตามสาเหตุ):
|
||||
- คำอธิบายจางๆ (`noAccessNotice` ตามสาเหตุ — ข้อความฉบับ 2026-11-04):
|
||||
- ไม่มี ticket ในไฟล์: "ไม่พบ ticket ของ e-Court ในไฟล์ — add-in ใช้ได้กับเอกสารที่ส่งมาจาก e-Court เท่านั้น"
|
||||
- ticket รูปผิด / HMAC ไม่ตรง: "ตรวจสอบ ticket ไม่ผ่าน — ไฟล์ถูกแก้ไขหรือเสียหาย"
|
||||
- ticket หมดอายุ (ตอนเปิด หรือโดน 401 กลาง flow หลังพยายาม renew): "ticket ของ e-Court หมดอายุ — โปรดขอไฟล์ใหม่จาก e-Court แล้วเปิดอีกครั้ง"
|
||||
- ฟอร์แม็ตพัง (parse ฝั่ง client หรือ server ตอบ 400): "รูปแบบ ticket ในไฟล์ไม่ถูกต้อง — ไฟล์ถูกแก้ไขหรือเสียหาย โปรดขอไฟล์ใหม่จาก e-Court"
|
||||
- ลายเซ็นไม่ผ่าน (server 401): "ตรวจสอบลายเซ็น (HMAC) ของ ticket กับระบบไม่ผ่าน — ไฟล์ถูกแก้ไขหรือเสียหาย โปรดขอไฟล์ใหม่จาก e-Court"
|
||||
- ticket หมดอายุ (pre-check / server 403 และไม่มี JWT ที่ยังใช้ได้ใน session / โดน 401 กลาง flow หลังพยายาม renew): "ticket ของ e-Court หมดอายุ — โปรดขอไฟล์ใหม่จาก e-Court แล้วเปิดอีกครั้ง"
|
||||
- ขอ token ไม่สำเร็จ (server ลง/network): "ขอ token จากระบบไม่สำเร็จ — ตรวจการเชื่อมต่อแล้วเปิดเอกสารอีกครั้ง"
|
||||
- **ไม่มีปุ่มใด ๆ** — ไม่มี login ให้กด (ยกเลิก Keycloak 2026-10-27; ขั้นตอนสำหรับเอกสารทั่วไปจะมีในอนาคต) ผู้ใช้ได้ไฟล์ใหม่จาก e-Court แล้วเปิดใหม่เท่านั้น
|
||||
- อ่านไฟล์ไม่ได้ (Word.run พัง เช่นเปิดใน browser ธรรมดา): "อ่านข้อมูลเอกสารไม่สำเร็จ — ลองกด \"ลองใหม่\" หรือปิดแล้วเปิด task pane อีกครั้ง"
|
||||
- **ปุ่ม "ลองใหม่" (เพิ่ม 2026-11-04) แสดงเฉพาะเคสอ่านไฟล์ไม่ได้** (`noAccessRetryable`) — กดแล้วรัน
|
||||
`detect()` ใหม่ (ช่วยเคส office.js มาช้า/Word สะดุดชั่วคราว) · เคสอื่นไม่มีปุ่ม — ไม่มี login ให้กด
|
||||
(ยกเลิก Keycloak 2026-10-27; ขั้นตอนสำหรับเอกสารทั่วไปจะมีในอนาคต) ผู้ใช้ได้ไฟล์ใหม่จาก e-Court
|
||||
แล้วเปิดใหม่ หรือรอเน็ตกลับมาเท่านั้น
|
||||
|
||||
### `untracked`
|
||||
|
||||
|
|
@ -167,7 +172,7 @@ Dialog เดียวใช้ 2 flow (ปรับข้อความด้
|
|||
### Detect (FR-1) — ตอนเปิด task pane / กดลองใหม่
|
||||
|
||||
1. อ่าน custom properties ครั้งเดียวทั้งชุด: `ticket_data`, `HmacSha256`, `SavedByAddIn`, `DocumentTrackingId`, `LastVersionId`, `LastVersionTimestamp`, `TemplateId`
|
||||
2. ticket auth (`signInWithTicket` — PLAN-ADDIN-AUTH.md §5): อ่านไฟล์ไม่ได้ / ไม่มี ticket / HMAC ไม่ตรง / ticket หมดอายุ (ตามเงื่อนไข uid/fid ของ session) / ขอ token ไม่สำเร็จ → `no-access` แยกข้อความตามสาเหตุ · ผ่าน = มี access_token ของ fid นี้เก็บใน localStorage `wordaddin-auth` แล้วไปต่อ
|
||||
2. ticket auth (`signInWithTicket` — PLAN-ADDIN-AUTH.md §5 ฉบับ 2026-11-04): อ่านไฟล์ไม่ได้ / ไม่มี ticket / ฟอร์แม็ตพัง (client หรือ server 400) / ลายเซ็นไม่ผ่าน (server 401) / ticket หมดอายุ (pre-check หรือ server 403 โดยไม่มี JWT ที่ยังใช้ได้ใน session) / ขอ token ไม่สำเร็จ → `no-access` แยกข้อความตามสาเหตุ · ผ่าน = มี access_token ของ fid นี้เก็บใน localStorage `wordaddin-auth` แล้วไปต่อ
|
||||
3. `tracked = (SavedByAddIn === GUID ของ add-in)`:
|
||||
- tracked **และมี** `DocumentTrackingId` → `tracking` + โหลด history
|
||||
- ไม่ track หรือ track แต่ id หาย → `untracked` (เก็บ `TemplateId` ไว้ใช้ตอน Start Versioning — ไฟล์ร่างอยู่กลุ่มนี้)
|
||||
|
|
@ -255,7 +260,7 @@ response: `{ url: string, expiresAt: string }` — UI เปิด `url` ใน
|
|||
|
||||
### Auth — e-Court ticket (ไม่มี login)
|
||||
|
||||
ยกเลิก Keycloak OIDC/PKCE แล้ว (2026-10-27) — ไม่มี Office Dialog ไม่มีหน้า sign-in: e-Court stamp custom properties **`ticket_data`** (JSON `exp`/`fid`/`tid`/`uid`/`uname`) + **`HmacSha256`** (HMAC-SHA256 ของ string ดิบ ด้วย secret ที่ config ได้ — ปัจจุบัน `super_secret`) ลงไฟล์ตอนสร้าง แล้ว `signInWithTicket()` ใช้ได้ทันที · flow ตาม `docs/requirement/addin-workflow.md` (แผนเต็ม `docs/PLAN-ADDIN-AUTH.md`): ตรวจ HMAC ฝั่ง client (UX gate) → ตรวจ exp → session ใน localStorage `wordaddin-auth` ตาม uid (user ใหม่ = เคลียร์ session เก่าทั้งก้อน) → **ขอ access_token ที่ `POST /api/documents/token`** แบบ body `{ticket_data, HmacSha256}` (server ตรวจ HMAC+exp อีกชั้นด้วย secret ฝั่ง server) → เก็บ token ตาม fid ("คีย์คือ fid") — กลับมาเปิดเอกสารเดิม = ใช้ token เดิมไม่ต้องขอใหม่ · token หมดอายุกลางใช้งาน (โดน 401) = ลองขอใหม่จาก ticket 1 ครั้ง (ticket ต้องยังไม่หมดอายุ) ยังไม่ผ่าน = `AuthRequiredError` → หน้า `no-access` ให้ขอไฟล์ใหม่จาก e-Court · token นี้คือสิ่งที่ `apiClient` แนบเป็น `Authorization: Bearer` ทุก request ตอนต่อ backend จริง (กลุ่ม docver บังคับ Bearer — docs/PLAN-DOCVER.md)
|
||||
ยกเลิก Keycloak OIDC/PKCE แล้ว (2026-10-27) — ไม่มี Office Dialog ไม่มีหน้า sign-in: e-Court stamp custom properties **`ticket_data`** (JSON `exp`/`fid`/`tid`/`uid`/`uname`) + **`HmacSha256`** (HMAC-SHA256 ของ string ดิบ ด้วย secret ที่ config ได้ — ปัจจุบัน `super_secret`) ลงไฟล์ตอนสร้าง แล้ว `signInWithTicket()` ใช้ได้ทันที · flow ตาม `docs/requirement/addin-workflow.md` (แผนเต็ม `docs/PLAN-ADDIN-AUTH.md` — แก้ 2026-11-04): **client ไม่ตรวจ HMAC เอง** (ถอด secret ออกจาก bundle ตาม PLAN-SSO.md D13) — pre-check ฟอร์แม็ต + exp (หมดอายุและไม่มี JWT ของ fid ใน session = จบเลย) → **ยิง `POST /api/documents/token` ทุกครั้งที่เปิดเอกสาร** แบบ body `{ticket_data, HmacSha256}` ให้ server ตรวจลายเซ็นแล้วแยกตาม contract: 200 = ได้ token ใหม่ (user ใหม่ = เคลียร์ session เก่าทั้งก้อน) · 400 = ฟอร์แม็ตพัง · 401 = ลายเซ็นไม่ถูก (ไฟล์ถูกแก้) · 403 = ลายเซ็นถูกแต่ ticket หมดอายุ (มี JWT ของ fid ใน session ที่ยังไม่หมด = ใช้ต่อ) → เก็บ token ตาม fid ("คีย์คือ fid") อายุ 7 วัน (default ใหม่ของ `DOCVER_TOKEN_TTL_SECONDS`) · token หมดอายุกลางใช้งาน (โดน 401) = ลองขอใหม่จาก ticket 1 ครั้ง (ticket หมดแล้ว server ตอบ 403) ยังไม่ผ่าน = `AuthRequiredError` → หน้า `no-access` ให้ขอไฟล์ใหม่จาก e-Court · token นี้คือสิ่งที่ `apiClient` แนบเป็น `Authorization: Bearer` ทุก request ตอนต่อ backend จริง (กลุ่ม docver บังคับ Bearer — docs/PLAN-DOCVER.md)
|
||||
|
||||
> **หมายเหตุสถานะปัจจุบัน:** UI นี้ถูก implement แล้วใน repo นี้ที่ route `/docedit/wordaddin` (ดู `components/wordaddin/` + `wordaddin/`) — auth เป็น ticket auth จริงครบ flow (`wordaddin/services/authService.ts` + `ticketService.ts` + ทางเข้า ticket ของ `/api/documents/token`) และ **API ทั้งสามข้างบนเป็นของจริงแล้ว** (`wordaddin/services/versioningService.ts` ยิงกลุ่ม `/api/documents/*` ผ่าน Bearer — ปลด mock 2026-10-27; เหลือ mock เฉพาะโหมด `?addindemo`) · รายละเอียดการรันกับ backend: `docs/WORDADDIN-DEV.md` · สร้างไฟล์ทดสอบพร้อม ticket: `node scripts/make-ticket-docx.mjs`
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue