@opengis/fastify-table
Version:
core-plugins
137 lines (136 loc) • 5.81 kB
JavaScript
import { existsSync } from "node:fs";
import cookie from "@fastify/cookie";
import session from "@fastify/session";
import { Authenticator } from "@fastify/passport";
import RedisStore from "fastify-session-redis-store";
import config from "../../../config.js";
import getRedis from "../redis/funcs/getRedis.js";
const fastifyPassport = new Authenticator();
const { prefix = "/api" } = config;
export async function onRequest(req, reply) {
const { hostname, headers, routeOptions } = req;
const { config: routeConfig, method, handler, url } = routeOptions || {};
const { policy } = routeConfig || {};
const isApi = method && url && typeof handler === "function" && url !== "*";
// handle non-api at vite/vike
if (!isApi) {
return null;
}
// proxy from old apps to editor, bi etc.
const validToken = (req.ip === "193.239.152.181" ||
req.ip === "127.0.0.1" ||
req.ip?.startsWith?.("192.168.") ||
config.debug) &&
req.headers?.token &&
config.auth?.tokens?.includes?.(headers.token);
if (validToken && !req?.user?.uid) {
req.user = {
uid: req.headers?.uid?.toString?.(),
user_type: req.headers?.user_type?.toString?.() || "regular",
};
}
const isAdmin = process.env.NODE_ENV === "admin" ||
hostname?.split?.(":")?.shift?.() === config.adminDomain ||
config.admin ||
hostname?.startsWith?.("admin");
const isPublic = Array.isArray(policy)
? policy.includes("public")
: policy === "L0";
if (req.cookies?.["session_auth"] &&
!req.session?.passport?.user?.uid &&
(config.auth?.disable || config.auth?.user)) {
req.session = req.session || {};
req.session.passport = req.session.passport || {}; // ensure passport session exists
req.session.passport.user = {
...(config.auth?.user || {}),
uid: config.auth?.user?.uid?.toString?.() || "1",
user_rnokpp: config.auth?.user?.rnokpp,
user_type: config.auth?.user?.type || "regular",
};
req.user = req.session.passport.user;
}
// ! intentional: null || undefined > undefined
req.user = req.user || req.session?.passport?.user || undefined; // fix for user.uid errors, by default user is null, while with express passport it was {}, unauthorized user does not trigger serializer
// currently 2factor + auth with passwd file not supported
const ispasswd = (existsSync("passwd") && !config.auth?.["2factor"]) || config.auth?.passwd;
const loginPageUrl = config.auth?.link?.core?.login || config?.auth?.redirect || "/login";
if (!req.user?.uid &&
!config.auth?.disable &&
isAdmin &&
!isPublic &&
!config.auth?.disableRedirect &&
!req.url.startsWith(prefix) &&
!req.url.startsWith("/api") &&
!req.url.includes(loginPageUrl) &&
!req.url.includes(".") &&
!req.url.includes("@")) {
if (isApi) {
return reply.status(401).send({ error: "unauthorized", code: 401 });
}
return reply.redirect(`${loginPageUrl}` + `?redirect=${req.url}`);
}
// by default, disable 2factor for id.gov.ua auth
const check = req.user?.auth_type === "govid" ? config.auth?.["2factor"]?.govid : true;
const login2faPage = config.auth?.link?.["2fa"]?.login || "/2factor";
// example: 2factor for admin env only, while public env does not require it
const checkEnv = () => {
if (!config.auth?.["2factorEnv"])
return true;
if ((config.auth?.["2factorEnv"] &&
process.env.NODE_ENV === config.auth?.["2factorEnv"]) ||
(config.auth?.["2factorEnv"] === "admin" && isAdmin)) {
return true;
}
return false;
};
// if 2factor is enabled globally + for user and secondFactorPassed not true => redirect to 2factor login page
if (req.user?.uid &&
req.user?.twofa &&
// config.auth?.["2factor"] &&
// !isPublic &&
(routeOptions?.method || "GET") === "GET" &&
!req.session?.secondFactorPassed &&
!ispasswd &&
!config.auth?.disableRedirect &&
!config.auth?.disable &&
check &&
checkEnv() &&
!req.url.startsWith(login2faPage) &&
!routeOptions.url?.includes?.("/logout") &&
!routeOptions.url?.includes?.("/2fa") &&
!routeOptions.url?.includes?.("/assets")) {
if (isApi) {
return reply
.status(403)
.send({ error: "access restricted: twofa", code: 403 });
}
return reply.redirect(login2faPage);
}
return null;
}
function plugin(fastify) {
if (!config.redis) {
return;
}
fastify.register(cookie, {
parseOptions: config?.auth?.cookieOptions || { secure: false },
});
fastify.register(session, {
secret: config?.auth?.secret || "61b820e12858570a4b0633020d4394a17903d9a9",
cookieName: "session_auth",
cookie: config?.auth?.cookieOptions || { secure: false },
store: new RedisStore({
prefix: `session_auth:${config.pg?.database || "db"}:`,
client: getRedis({ db: 2 }),
}),
});
// register passport AFTER session is ready
fastify.register(fastifyPassport.initialize());
fastify.register(fastifyPassport.secureSession());
// serialize user used to store user info in session store
fastifyPassport.registerUserSerializer(async (user) => ({ user }));
// deserialize user used to add user info from session store to req
fastifyPassport.registerUserDeserializer(async (passport) => passport?.user || passport);
fastify.addHook("onRequest", onRequest);
}
export default plugin;