import * as express from "express"; import { createDecoder, createVerifier } from "fast-jwt"; import HttpError from "../interfaces/http-error"; import HttpStatus from "../interfaces/http-status"; import { addLogSequence } from "../interfaces/utils"; import { RequestWithUser } from "./user"; if (!process.env.AUTH_PUBLIC_KEY && !process.env.AUTH_REALM_URL) { throw new Error("Require keycloak AUTH_PUBLIC_KEY or AUTH_REALM_URL."); } if (process.env.AUTH_PUBLIC_KEY && process.env.AUTH_REALM_URL && !process.env.AUTH_PREFERRED_MODE) { throw new Error( "AUTH_PREFFERRED must be specified if AUTH_PUBLIC_KEY and AUTH_REALM_URL is provided.", ); } const jwtVerify = createVerifier({ key: async () => { return `-----BEGIN PUBLIC KEY-----\n${process.env.AUTH_PUBLIC_KEY}\n-----END PUBLIC KEY-----`; }, }); const jwtDecode = createDecoder(); export async function expressAuthentication( request: RequestWithUser, securityName: string, _scopes?: string[], ) { if (process.env.NODE_ENV !== "production" && process.env.AUTH_BYPASS) { return { preferred_username: "bypassed" }; } if (securityName !== "bearerAuth") throw new Error("ไม่ทราบวิธีการยืนยันตัวตน"); const token = request.headers["authorization"]?.includes("Bearer ") ? request.headers["authorization"].split(" ")[1] : request.headers["authorization"]; if (!token) throw new HttpError(HttpStatus.UNAUTHORIZED, "ไม่พบข้อมูลสำหรับยืนยันตัวตน"); let payload: Record = {}; switch (process.env.AUTH_PREFERRED_MODE) { case "online": payload = await verifyOnline(token); break; case "offline": payload = await verifyOffline(token); break; default: if (process.env.AUTH_REALM_URL) payload = await verifyOnline(token); if (process.env.AUTH_PUBLIC_KEY) payload = await verifyOffline(token); break; } if (!request.app.locals.logData) { request.app.locals.logData = {}; } // addLogSequence(request, { // action: "database", // status: "success", // description: "Query Data.", // }); request.app.locals.logData.userId = payload.sub; request.app.locals.logData.userName = payload.name; request.app.locals.logData.user = payload.preferred_username; // เก็บค่า profileId และ orgRootDnaId จาก token (ใช้ค่าว่างถ้าไม่มี) request.app.locals.logData.profileId = payload.profileId ?? ""; request.app.locals.logData.orgRootDnaId = payload.orgRootDnaId ?? ""; request.app.locals.logData.orgChild1DnaId = payload.orgChild1DnaId ?? ""; request.app.locals.logData.orgChild2DnaId = payload.orgChild2DnaId ?? ""; request.app.locals.logData.orgChild3DnaId = payload.orgChild3DnaId ?? ""; request.app.locals.logData.orgChild4DnaId = payload.orgChild4DnaId ?? ""; request.app.locals.logData.empType = payload.empType ?? ""; request.app.locals.logData.prefix = payload.prefix ?? ""; return payload; } async function verifyOffline(token: string) { const payload = await jwtVerify(token).catch((_) => null); if (!payload) throw new HttpError(HttpStatus.UNAUTHORIZED, "ไม่สามารถยืนยันตัวตนได้"); return payload; } async function verifyOnline(token: string) { const res = await fetch(`${process.env.AUTH_REALM_URL}/protocol/openid-connect/userinfo`, { headers: { authorization: `Bearer ${token}` }, }).catch((e) => console.error(e)); if (!res) throw new Error("ไม่สามารถเข้าถึงระบบยืนยันตัวตน"); if (!res.ok) throw new HttpError(HttpStatus.UNAUTHORIZED, "ไม่สามารถยืนยันตัวตนได้"); return await jwtDecode(token); }