feat(docver): add access token endpoint and Bearer auth on all endpoints
- POST /api/documents/token issues JWT HS256 (claims userid/username/fid/template) after x-api-key check against new env DOCVER_API_KEY (fail-closed, timing-safe, uniform 401) - all docver endpoints now require Authorization: Bearer, verified with env DOCVER_TOKEN_SECRET (TTL via DOCVER_TOKEN_TTL_SECONDS, default 3600); secret unset = 401 on verify / 500 on issue - identity (userId/userName) + templateId extracted from token claims instead of request payload; removed from multipart form (legacy fields now 400) - lib/docver-token.ts: hand-rolled JWT via node:crypto only — alg pinned to HS256 (anti alg-confusion), timing-safe compare with both sides hashed, exp enforced, identical 401 for all bearer failure modes - openapi spec: /api/documents/token path + DocverBearer scheme; PLAN-DOCVER.md / CLAUDE.md / .env.example updated - tests: new tests/lib/docver-token.test.ts + adjusted parseVersionUploadForm tests (258 pass, no DB env needed) - package-lock.json: sync minio (^8.0.7) that was missing from the earlier docver commit Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
280cc2da40
commit
98b370c925
13 changed files with 932 additions and 131 deletions
|
|
@ -112,11 +112,11 @@ describe("parseFieldsJson", () => {
|
|||
});
|
||||
|
||||
describe("parseVersionUploadForm", () => {
|
||||
// identity (userId/userName) กับ templateId ถอดออกจาก form แล้ว — route แกะจาก
|
||||
// Bearer token ผ่าน lib/docver-token.ts (เคสของ token อยู่ docver-token.test.ts)
|
||||
function baseForm(): FormData {
|
||||
const form = new FormData();
|
||||
form.append("file", new File([Buffer.from("PK\x03\x04")], "draft.docx"));
|
||||
form.append("userId", "u0001");
|
||||
form.append("userName", "สมชาย ใจดี");
|
||||
return form;
|
||||
}
|
||||
|
||||
|
|
@ -124,17 +124,13 @@ describe("parseVersionUploadForm", () => {
|
|||
const form = baseForm();
|
||||
form.append("clientVersionId", " 11111111-1111-4111-8111-111111111111 ");
|
||||
form.append("message", " ");
|
||||
form.append("templateId", "tmpl-main");
|
||||
form.append("fields", '{"blackno":"1"}');
|
||||
form.append("fileName", "C:\\Users\\judge\\คำสั่งเรียก.docx");
|
||||
const result = parseVersionUploadForm(form);
|
||||
ok(result);
|
||||
expect(result.form).toEqual({
|
||||
userId: "u0001",
|
||||
userName: "สมชาย ใจดี",
|
||||
clientVersionId: "11111111-1111-4111-8111-111111111111",
|
||||
message: null,
|
||||
templateId: "tmpl-main",
|
||||
fileName: "คำสั่งเรียก.docx",
|
||||
fields: { blackno: "1" },
|
||||
});
|
||||
|
|
@ -148,22 +144,22 @@ describe("parseVersionUploadForm", () => {
|
|||
if (!result.ok) expect(result.error).toContain("oops");
|
||||
});
|
||||
|
||||
it("ตกเมื่อขาด userId หรือ userName (stand-alone บอกผู้อัปโหลดเอง)", () => {
|
||||
const noUser = baseForm();
|
||||
noUser.delete("userId");
|
||||
expect(parseVersionUploadForm(noUser).ok).toBe(false);
|
||||
const noName = baseForm();
|
||||
noName.delete("userName");
|
||||
expect(parseVersionUploadForm(noName).ok).toBe(false);
|
||||
it("ส่ง userId/userName/templateId ใน form = 400 ไม่รู้จัก field (identity มาจาก token)", () => {
|
||||
for (const legacy of ["userId", "userName", "templateId"]) {
|
||||
const form = baseForm();
|
||||
form.append(legacy, "x");
|
||||
const result = parseVersionUploadForm(form);
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) expect(result.error).toContain(legacy);
|
||||
}
|
||||
});
|
||||
|
||||
it("ตกเมื่อ text field ถูกส่งมาเป็นไฟล์", () => {
|
||||
const form = baseForm();
|
||||
form.delete("userId");
|
||||
form.append("userId", new File(["x"], "u.txt"));
|
||||
form.append("message", new File(["x"], "m.txt"));
|
||||
const result = parseVersionUploadForm(form);
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) expect(result.error).toContain("userId");
|
||||
if (!result.ok) expect(result.error).toContain("message");
|
||||
});
|
||||
|
||||
it(`message ยาว ${DOCVER_MESSAGE_MAX} ผ่าน แต่เกิน = ตก`, () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue