Refactor ticket verification and installation scripts
All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 3s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 1m17s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 4s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 3s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 1m17s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 4s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
- Split `verifyAddinTicket` into two functions: `verifyAddinTicketHmac` (ignores expiration) and `verifyAddinTicket` (checks expiration). - Add `hasVersionByUser` function to check if a document has been previously worked on by a user, allowing expired tickets to be accepted if the user has a history with the document. - Update OpenAPI specification to reflect changes in ticket expiration handling. - Introduce new scripts for installing and uninstalling the Word add-in on Windows and macOS, providing a streamlined process for sideloading the add-in. - Enhance tests for ticket verification to cover new functionality regarding expired tickets and user history.
This commit is contained in:
parent
3726cb742f
commit
b1449ab668
14 changed files with 727 additions and 75 deletions
|
|
@ -4,8 +4,10 @@
|
|||
*
|
||||
* ไม่มี login ใน add-in: ไฟล์ที่ e-Court stamp custom properties `ticket_data`
|
||||
* + `HmacSha256` มา (ผ่านกลุ่ม template) = ผ่านเข้าได้ — signInWithTicket ตรวจ
|
||||
* HMAC → exp → session ใน localStorage ตาม uid/fid แล้วขอ access_token จาก
|
||||
* /api/documents/token เก็บตาม fid ("คีย์คือ fid")
|
||||
* HMAC → session ใน localStorage ตาม uid/fid → ขอ access_token จาก
|
||||
* /api/documents/token เก็บตาม fid ("คีย์คือ fid") · exp ของ ticket ไม่ใช่
|
||||
* เกณฑ์ตัดสิทธิ์ฝั่ง client — server ออก token ให้แม้ ticket หมดอายุเมื่อ
|
||||
* เอกสารนี้เคยมี version โดย uid เดียวกัน (requirement ขั้น 7)
|
||||
*
|
||||
* token เก็บใน localStorage (ข้าม session ได้ — กลับมาเปิดเอกสารเดิมไม่ต้องขอใหม่
|
||||
* ตาม requirement ขั้น 7) ส่วน ticket ดิบของเอกสารปัจจุบันเก็บใน memory ไว้ renew
|
||||
|
|
@ -47,9 +49,10 @@ export function isDemoMode(): boolean {
|
|||
* e-Court ticket — จุดเข้าเดียวของ auth ตอนเปิดเอกสาร (requirement ขั้น 1–7):
|
||||
* 1. ตรวจมี ticket_data + HmacSha256 ครบ
|
||||
* 2. parse + ตรวจ HMAC ฝั่ง client (UX gate)
|
||||
* 3. session ใน localStorage: ยังไม่มี/user ใหม่ → ตรวจ exp → เคลียร์ session เก่า
|
||||
* แล้วขอ token ใหม่ · user เดิม + fid เดิม → ผ่านเลย (renew เงียบ ๆ ถ้า token
|
||||
* หมดอายุแต่ ticket ยังไม่หมด) · user เดิม + fid ใหม่ → ตรวจ exp → ขอ token เก็บเพิ่ม
|
||||
* 3. session ใน localStorage ตาม uid/fid — token ยังใช้ได้ = ผ่านเลย · ต้องขอ
|
||||
* token ใหม่ = ยิงขอเสมอแล้วให้ **server เป็นคนตัดสิน** (ticket หมดอายุแต่
|
||||
* เอกสารนี้เคยมี version โดย uid เดียวกัน = ออกให้ต่อ — requirement ขั้น 7
|
||||
* กลับมาทำเอกสารเดิมได้แม้ ticket หมดอายุ แม้ session หายไปจากเครื่อง)
|
||||
*/
|
||||
export async function signInWithTicket(input: {
|
||||
ticketData?: string;
|
||||
|
|
@ -82,63 +85,57 @@ export async function signInWithTicket(input: {
|
|||
};
|
||||
}
|
||||
const ticket = parsed.ticket;
|
||||
const ticketAlive = !isTicketExpired(ticket.exp);
|
||||
const expiredMessage = {
|
||||
ok: false as const,
|
||||
reason: "expired" as const,
|
||||
message: "ticket ของ e-Court หมดอายุ — โปรดขอไฟล์ใหม่จาก e-Court แล้วเปิดอีกครั้ง",
|
||||
};
|
||||
|
||||
const session = ticketService.loadSession();
|
||||
if (session === null || session.userId !== ticket.uid) {
|
||||
// ไม่มี session / user ใหม่ — ตรวจ exp ก่อนแล้วเริ่ม session ใหม่ทั้งก้อน
|
||||
if (!ticketAlive) {
|
||||
return {
|
||||
ok: false,
|
||||
reason: "expired",
|
||||
message: "ticket ของ e-Court หมดอายุ — โปรดขอไฟล์ใหม่จาก e-Court แล้วเปิดอีกครั้ง",
|
||||
};
|
||||
}
|
||||
ticketService.clearSession();
|
||||
// ไม่มี session / user ใหม่ — ขอ token ใหม่เสมอ (แม้ ticket หมดอายุ: server
|
||||
// ออกให้ต่อเมื่อเอกสารนี้เคยทำงานโดย uid เดียวกัน ซึ่งช่วยเคส localStorage หาย
|
||||
// ของคนที่กลับมาทำเอกสารเดิมด้วย) แล้วเริ่ม session ใหม่ทั้งก้อน
|
||||
let entry: ticketService.SessionTokenEntry;
|
||||
try {
|
||||
entry = await ticketService.requestDocverToken(ticketData, ticketHmac);
|
||||
} catch {
|
||||
return {
|
||||
ok: false,
|
||||
reason: "token-request-failed",
|
||||
message: "ขอ token จากระบบไม่สำเร็จ — ตรวจการเชื่อมต่อแล้วเปิดเอกสารอีกครั้ง",
|
||||
};
|
||||
return isTicketExpired(ticket.exp)
|
||||
? expiredMessage
|
||||
: {
|
||||
ok: false,
|
||||
reason: "token-request-failed",
|
||||
message: "ขอ token จากระบบไม่สำเร็จ — ตรวจการเชื่อมต่อแล้วเปิดเอกสารอีกครั้ง",
|
||||
};
|
||||
}
|
||||
ticketService.clearSession();
|
||||
ticketService.saveSession({ userId: ticket.uid, userName: ticket.uname, tokens: { [ticket.fid]: entry } });
|
||||
} else {
|
||||
// คนเดิม — userName อัปเดตตามไฟล์ล่าสุด (ชื่อเปลี่ยนได้)
|
||||
session.userName = ticket.uname;
|
||||
let entry = session.tokens[ticket.fid] ?? null;
|
||||
if (entry !== null && ticketService.isTokenExpired(entry)) {
|
||||
// token หมดอายุ — renew ได้เฉพาะตอน ticket ยังไม่หมด; ticket หมดด้วยก็เก็บของ
|
||||
// เก่าไว้ (requirement ขั้น 7: ผ่านเลยไม่ตรวจ exp) ให้ server ตัดสินตอนใช้งาน
|
||||
if (ticketAlive) {
|
||||
try {
|
||||
entry = await ticketService.requestDocverToken(ticketData, ticketHmac);
|
||||
session.tokens[ticket.fid] = entry;
|
||||
} catch {
|
||||
// ใช้ token เก่าต่อ — ถ้าเน่าจริง apiClient จะ 401 → renew-retry → no-access
|
||||
}
|
||||
// token หมดอายุ — ยิง renew เสมอแล้วให้ server ตัดสิน (ticket ยังไม่หมด =
|
||||
// ออกให้เสมอ · หมดแล้ว = ออกเฉพาะเอกสารนี้เคยทำงานโดย uid เดียวกัน)
|
||||
try {
|
||||
entry = await ticketService.requestDocverToken(ticketData, ticketHmac);
|
||||
session.tokens[ticket.fid] = entry;
|
||||
} catch {
|
||||
// ใช้ token เก่าต่อ — ถ้าเน่าจริง apiClient จะ 401 → renew-retry → no-access
|
||||
}
|
||||
}
|
||||
if (entry === null) {
|
||||
// เอกสารใหม่ของคนเดิม — ตรวจ exp แล้วขอ token เก็บเพิ่ม
|
||||
if (!ticketAlive) {
|
||||
return {
|
||||
ok: false,
|
||||
reason: "expired",
|
||||
message: "ticket ของ e-Court หมดอายุ — โปรดขอไฟล์ใหม่จาก e-Court แล้วเปิดอีกครั้ง",
|
||||
};
|
||||
}
|
||||
// เอกสารใหม่ของคนเดิม — ขอ token เก็บเพิ่ม (server ตัดสินเรื่อง ticket หมดอายุ)
|
||||
try {
|
||||
session.tokens[ticket.fid] = await ticketService.requestDocverToken(ticketData, ticketHmac);
|
||||
} catch {
|
||||
return {
|
||||
ok: false,
|
||||
reason: "token-request-failed",
|
||||
message: "ขอ token จากระบบไม่สำเร็จ — ตรวจการเชื่อมต่อแล้วเปิดเอกสารอีกครั้ง",
|
||||
};
|
||||
return isTicketExpired(ticket.exp)
|
||||
? expiredMessage
|
||||
: {
|
||||
ok: false,
|
||||
reason: "token-request-failed",
|
||||
message: "ขอ token จากระบบไม่สำเร็จ — ตรวจการเชื่อมต่อแล้วเปิดเอกสารอีกครั้ง",
|
||||
};
|
||||
}
|
||||
}
|
||||
ticketService.saveSession(session);
|
||||
|
|
@ -152,14 +149,16 @@ export async function signInWithTicket(input: {
|
|||
}
|
||||
|
||||
/**
|
||||
* ขอ token ใหม่จาก ticket ของเอกสารปัจจุบัน (ticket ต้องยังไม่หมดอายุ) —
|
||||
* เรียกจาก apiClient ตอนโดน 401 ก่อน throw AuthRequiredError 1 รอบ
|
||||
* ขอ token ใหม่จาก ticket ของเอกสารปัจจุบัน — เรียกจาก apiClient ตอนโดน 401
|
||||
* ก่อน throw AuthRequiredError 1 รอบ · ไม่ตัดสิน exp ของ ticket ฝั่งนี้ (server
|
||||
* ออกให้ต่อได้แม้ ticket หมดอายุเมื่อเอกสารนี้เคยทำงานโดย uid เดียวกัน —
|
||||
* requirement ขั้น 7) 401 กลับมา = false ตามเดิม
|
||||
* คืน true = ได้ token ใหม่เรียบร้อย (getAccessToken จะเห็นค่าใหม่)
|
||||
*/
|
||||
export async function renewTokenFromTicket(): Promise<boolean> {
|
||||
if (currentTicket === null || currentFid === null) return false;
|
||||
const parsed = parseDocverTicket(currentTicket.data);
|
||||
if (!parsed.ok || isTicketExpired(parsed.ticket.exp)) return false;
|
||||
if (!parsed.ok) return false;
|
||||
try {
|
||||
const entry = await ticketService.requestDocverToken(currentTicket.data, currentTicket.hmac);
|
||||
const session =
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue