import * as express from "express"; import { createDecoder, createVerifier } from "fast-jwt"; import HttpError from "../interfaces/http-error"; import HttpStatusCode from "../interfaces/http-status"; if (!process.env.PUBLIC_KEY && !process.env.REALM_URL) { throw new Error("Requires PUBLIC_KEY or REALM_URL"); } if (process.env.PUBLIC_KEY && process.env.REALM_URL && !process.env.PREFERRED_AUTH) { throw new Error( "PREFERRED_AUTH must be specified when both REALM_URL and PUBLIC_KEY are provided.", ); } 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.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] : request.headers["authorization"]; if (!token) throw new HttpError(HttpStatusCode.UNAUTHORIZED, "กรุณาเข้าสู่ระบบก่อนใช้งาน!"); let payload: Record = {}; 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.every((v) => !payload.role.includes(v))) { throw new HttpError(HttpStatusCode.FORBIDDEN, "You are not allowed to perform this action."); } return payload; } async function verifyOffline(token: string) { const payload = await jwtVerify(token).catch((_) => null); if (!payload) throw new HttpError(HttpStatusCode.UNAUTHORIZED, "Invalid token provided."); 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("Cannot connect to auth service."); if (!res.ok) throw new HttpError(HttpStatusCode.UNAUTHORIZED, "Invalid token provided."); return await jwtDecode(token); }