43 lines
2.9 KiB
TypeScript
43 lines
2.9 KiB
TypeScript
// N10 (PLAN-API.md D1) — auth ของ /api/external/* (ผู้เรียก: backend e-Court,
|
|
// server-to-server ไม่มี browser)
|
|
// - API Key เดียวใน env EXTERNAL_API_KEY ส่งผ่าน header "x-api-key"
|
|
// - เทียบแบบ timing-safe: hash SHA-256 ทั้งสองค่าแล้ว timingSafeEqual
|
|
// (digest ยาว 32 ไบต์เท่ากันเสมอ — กัน timing + length leak)
|
|
// - env ไม่ตั้ง/ว่าง = ปฏิเสธทุก request (fail closed) — ไม่มี mode เปิดฟรี
|
|
// - ข้อความ 401 ของ "ไม่ใส่ key" กับ "key ผิด" เป็นอันเดียวกัน (กัน enumerate)
|
|
// - ห้าม log ค่า key / header ดิบ
|
|
//
|
|
// ห้าม import อะไรที่ลาก @/db มาด้วย (db/index.ts throw ตอน import เมื่อไม่มี
|
|
// DATABASE_URL) — unit test ของไฟล์นี้ต้องรันได้โดยไม่ต้องตั้งค่า DB เลย
|
|
import { createHash, timingSafeEqual } from "node:crypto";
|
|
|
|
/** Response ผิดพลาดของ API ภายนอกทุกกรณี — JSON {"error": "<ไทย>"} ตาม D2 */
|
|
export function externalApiError(message: string, status: number): Response {
|
|
return new Response(JSON.stringify({ error: message }), {
|
|
status,
|
|
headers: { "content-type": "application/json; charset=utf-8" },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* คืน null เมื่อผ่าน / คืน Response 401 เมื่อไม่ผ่าน — route เรียกเป็นอย่างแรก
|
|
* ก่อนแตะ DB ทุกครั้ง (PLAN-API.md D3)
|
|
*/
|
|
export function requireExternalApiKey(req: Request): Response | null {
|
|
// เทียบกับค่า trim แล้ว — key สุ่มไม่ควรมี whitespace อยู่แล้ว และกัน
|
|
// trailing newline จาก K8s secret / docker env_file ทำ 401 ทั้งระบบตามผู้เรียก
|
|
// ที่ส่ง key ถูก (header เองก็ strip OWS รอบค่า ผู้เรียกส่ง whitespace ครอบไม่ได้)
|
|
const envKey = process.env.EXTERNAL_API_KEY?.trim();
|
|
if (envKey === undefined || envKey === "") {
|
|
return externalApiError("API ยังไม่ได้ตั้งค่าคีย์", 401);
|
|
}
|
|
const provided = req.headers.get("x-api-key");
|
|
if (provided === null || provided === "") {
|
|
return externalApiError("API key ไม่ถูกต้อง", 401);
|
|
}
|
|
const providedHash = createHash("sha256").update(provided).digest();
|
|
const envHash = createHash("sha256").update(envKey).digest();
|
|
return timingSafeEqual(providedHash, envHash)
|
|
? null
|
|
: externalApiError("API key ไม่ถูกต้อง", 401);
|
|
}
|