import * as express from "express"; import { createDecoder, createVerifier } from "fast-jwt"; import HttpError from "../interfaces/http-error"; import HttpStatusCode from "../interfaces/http-status"; import { JwtPayload } from "jsonwebtoken"; if (!process.env.PUBLIC_KEY && !process.env.REALM_URL) { throw new Error("Require public key or realm url."); } if (process.env.PUBLIC_KEY && process.env.REALM_URL && !process.env.PREFERRED_AUTH) { throw new Error("Preferred auth type must be specified if public key and realm url is provided."); } if (!process.env.MANAGEMENT_ROLE) { throw new Error("Management role env is required."); } const jwtVerify = createVerifier({ key: async () => { return `-----BEGIN PUBLIC KEY-----\n${process.env.PUBLIC_KEY}\n-----END PUBLIC KEY-----`; }, }); const jwtDecode = createDecoder(); export async function expressAuthentication( request: express.Request, securityName: string, scopes?: string[], ) { if (process.env.NODE_ENV !== "production" && process.env.AUTH_BYPASS) { return { preferred_username: "bypassed" }; } if (securityName !== "bearerAuth") throw new Error("Unknown authentication method."); const token = request.headers["authorization"]?.includes("Bearer ") ? request.headers["authorization"].split(" ")[1] : null; if (!token) throw new HttpError(HttpStatusCode.UNAUTHORIZED, "ไม่พบข้อมูลสำหัรบบืนบันตัวตน"); let payload: JwtPayload = {}; switch (process.env.PREFERRED_AUTH) { case "online": payload = await verifyOnline(token); break; case "offline": payload = await verifyOffline(token); break; default: if (process.env.REALM_URL) payload = await verifyOnline(token); if (process.env.PUBLIC_KEY) payload = await verifyOffline(token); break; } if ( scopes && scopes.length > 0 && scopes .map((v) => (v === "management-role" ? process.env.MANAGEMENT_ROLE : v)) .every((v) => !payload.resource_access[payload.azp].roles.includes(v)) ) { throw new HttpError(HttpStatusCode.FORBIDDEN, "คุณไม่มีสิทธิในเข้าถึงข้อมูลนี้"); } return payload; } async function verifyOffline(token: string) { const payload = await jwtVerify(token).catch((_) => null); if (!payload) throw new HttpError(HttpStatusCode.UNAUTHORIZED, "ไม่สามารถยืนยันตัวตนได้"); return payload; } async function verifyOnline(token: string) { const res = await fetch(`${process.env.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(HttpStatusCode.UNAUTHORIZED, "ไม่สามารถยืนยันตัวตนได้"); return await jwtDecode(token); }