feat(addin): implement e-Court ticket auth (ticket_data + HmacSha256)
Replace the token/owner pass-through with the ticket flow from
docs/requirement/addin-workflow.md (full plan: docs/PLAN-ADDIN-AUTH.md):
- utils/docver-ticket.ts: shared pure ticket parse + hex digest helpers,
client/server inject their own crypto (Web Crypto / node:crypto)
- lib/addin-ticket.ts + POST /api/documents/token second entry: body
{ticket_data, HmacSha256} verified server-side (HMAC timing-safe + exp,
secret ADDIN_TICKET_SECRET -> SECRET -> "super_secret"); x-api-key path
unchanged
- wordaddin: ticketService (Web Crypto HMAC, localStorage session
wordaddin-auth keyed by fid, same-origin token request), authService
rewrite (7-step flow, silent renew), apiClient 401 renew-retry once,
metadataService reads ticket_data/HmacSha256, no-access messages per
cause
- scripts/make-ticket-docx.mjs: dev tool to stamp tickets onto any .docx
for testing in real Word
- app/wordaddin/error.tsx: error boundary showing the real error + stack
in the pane (webviews have no devtools)
- tests: docver-ticket + addin-ticket unit suites (21 cases) + temporary
crash repro suite for the ongoing client-crash debug session
- docs sync: PLAN-DOCVER, WORDADDIN-DEV, addin-ui-flow, openapi (two
request bodies for /token), CLAUDE.md, .env.example
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
8ebe5f8644
commit
cffc571da5
21 changed files with 1725 additions and 124 deletions
|
|
@ -39,8 +39,9 @@ export const openApiSpec = {
|
|||
description:
|
||||
"จัด version เอกสาร (docver): เอกสารเดียวต่อ docId แต่ละ " +
|
||||
"version เก็บ .docx + .pdf ใน MinIO พร้อมผู้อัปโหลด — auth ด้วย access_token " +
|
||||
"ของกลุ่มนี้: ขอที่ POST /api/documents/token (ยืนยัน x-api-key กับ env " +
|
||||
"DOCVER_API_KEY) แล้วแนบ Authorization: Bearer ทุก request — identity/template " +
|
||||
"ของกลุ่มนี้: ขอที่ POST /api/documents/token ได้ 2 ทาง (x-api-key + body claims " +
|
||||
"สำหรับระบบภายนอก หรือ body ticket_data+HmacSha256 สำหรับ add-in — PLAN-ADDIN-AUTH.md) " +
|
||||
"แล้วแนบ Authorization: Bearer ทุก request — identity/template " +
|
||||
"และ docId (= fid — fileId ของ e-Court, string) แกะจาก claims ของ token ทั้งหมด " +
|
||||
"ไม่รับจาก URL/multipart form อีก",
|
||||
},
|
||||
|
|
@ -613,18 +614,28 @@ export const openApiSpec = {
|
|||
summary: "ขอ access_token ของกลุ่ม documents (docver)",
|
||||
description:
|
||||
"ออก access_token ใช้กับ endpoint อื่นของกลุ่ม documents เท่านั้น — token เป็น " +
|
||||
"JWT HS256 ฝัง claims {userid, username, fid, template} ตามที่ส่งมา อายุตาม env " +
|
||||
"JWT HS256 ฝัง claims {userid, username, fid, template} อายุตาม env " +
|
||||
"DOCVER_TOKEN_TTL_SECONDS (default 3600 วินาที) · identity (userId/userName), " +
|
||||
"templateId และ docId (= fid) ตอนทำงานแกะจาก claims ของ token นี้ — " +
|
||||
"fid จึงบังคับ",
|
||||
"fid จึงบังคับ · ทางเข้า 2 ทาง (header x-api-key เป็นตัวตั้ง): " +
|
||||
"(1) ระบบภายนอก server-to-server — header x-api-key (env DOCVER_API_KEY) + body " +
|
||||
"claims ตรง ๆ (2) add-in ใน Word — ไม่มี x-api-key + body {ticket_data, " +
|
||||
"HmacSha256} ตามที่ e-Court stamp มากับไฟล์ — server ตรวจ HMAC-SHA256 ด้วย env " +
|
||||
"ADDIN_TICKET_SECRET (ไม่ตั้ง = env SECRET ไม่ตั้ง = \"super_secret\") และ exp " +
|
||||
"ของ ticket ก่อนออก token จาก claims ในตัว ticket เอง",
|
||||
security: [{ ApiKeyAuth: [] }],
|
||||
requestBody: {
|
||||
required: true,
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: { $ref: "#/components/schemas/TokenRequest" },
|
||||
schema: {
|
||||
oneOf: [
|
||||
{ $ref: "#/components/schemas/TokenRequest" },
|
||||
{ $ref: "#/components/schemas/TicketTokenRequest" },
|
||||
],
|
||||
},
|
||||
examples: {
|
||||
"ครบทุก claim": {
|
||||
"server-to-server ด้วย x-api-key (ครบทุก claim)": {
|
||||
value: {
|
||||
userid: "123",
|
||||
username: "Sorawit Bholsithi",
|
||||
|
|
@ -632,9 +643,17 @@ export const openApiSpec = {
|
|||
template: "T12",
|
||||
},
|
||||
},
|
||||
"ขั้นต่ำ (template เป็นทางเลือก)": {
|
||||
"server-to-server ขั้นต่ำ (template เป็นทางเลือก)": {
|
||||
value: { userid: "123", username: "Sorawit Bholsithi", fid: "3" },
|
||||
},
|
||||
"add-in ด้วย ticket จากไฟล์ (ไม่ต้องมี x-api-key)": {
|
||||
value: {
|
||||
ticket_data:
|
||||
'{"exp":1793067433,"fid":"T12_FD155_3s-long_ZZZ","tid":"T12","uid":"oom","uname":"สรวิชญ์"}',
|
||||
HmacSha256:
|
||||
"1dc53fbd48410845c895ddb841a8fe4ad93da413126fae9c688202e57f2baaa9",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -650,7 +669,9 @@ export const openApiSpec = {
|
|||
},
|
||||
400: { $ref: "#/components/responses/BadRequest" },
|
||||
401: {
|
||||
description: "ไม่ได้ส่ง header x-api-key หรือค่าไม่ถูกต้อง (env DOCVER_API_KEY)",
|
||||
description:
|
||||
"ทางเข้า x-api-key: ไม่ได้ส่ง header หรือค่าไม่ถูกต้อง (env DOCVER_API_KEY) · " +
|
||||
"ทางเข้า ticket: ticket ไม่ถูกต้อง/หมดอายุ หรือไม่ได้ส่งมา (ข้อความเดียวกันทุกสาเหตุ)",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: { $ref: "#/components/schemas/Error" },
|
||||
|
|
@ -880,7 +901,8 @@ export const openApiSpec = {
|
|||
},
|
||||
TokenRequest: {
|
||||
type: "object",
|
||||
description: "claims ที่จะฝังใน access_token ของกลุ่ม documents — key อื่น = 400",
|
||||
description:
|
||||
"claims ที่จะฝังใน access_token (ทางเข้า x-api-key) — key อื่น = 400",
|
||||
required: ["userid", "username", "fid"],
|
||||
properties: {
|
||||
userid: { type: "string", description: "รหัสผู้ใช้ (บังคับ, ≤ 100 ตัวอักษร)" },
|
||||
|
|
@ -898,6 +920,27 @@ export const openApiSpec = {
|
|||
},
|
||||
},
|
||||
},
|
||||
TicketTokenRequest: {
|
||||
type: "object",
|
||||
description:
|
||||
"ทางเข้าของ add-in (ไม่ต้องมี x-api-key) — ส่ง ticket ที่ e-Court stamp มากับ " +
|
||||
"ไฟล์ .docx ใน custom properties ตามเป๊ะ · key อื่น = 400 · server ตรวจ " +
|
||||
"HMAC-SHA256(ลับ, ticket_data ดิบ) เทียบ HmacSha256 + exp ของ ticket — " +
|
||||
"ไม่ผ่าน = 401 ข้อความเดียวกันทุกสาเหตุ",
|
||||
required: ["ticket_data", "HmacSha256"],
|
||||
properties: {
|
||||
ticket_data: {
|
||||
type: "string",
|
||||
description:
|
||||
'JSON string ของ ticket เช่น {"exp":1793067433,"fid":"…","tid":"…","uid":"…","uname":"…"} ' +
|
||||
"(exp = unix วินาที · tid optional · ส่งตรง ๆ ไม่แก้ string — HMAC ผูกกับค่าดิบ)",
|
||||
},
|
||||
HmacSha256: {
|
||||
type: "string",
|
||||
description: "hex 64 ตัว ของ HMAC-SHA256(secret, ticket_data ดิบ)",
|
||||
},
|
||||
},
|
||||
},
|
||||
TokenResponse: {
|
||||
type: "object",
|
||||
description: "access_token ของกลุ่ม documents (JWT HS256 อายุสั้น)",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue