import type express from "express"; import { config } from "../config"; /** * Express middleware that checks if the user is authenticated. */ export const authCheck: express.RequestHandler = async (req, res, next) => { try { if (!config.login?.enabled) { next(); return; } let { user } = req; let isUsingKeyOrSocketToken = false; let keyOrSocketTokenAuthenticated = false; if (req.query.key ?? req.cookies.socketToken) { isUsingKeyOrSocketToken = true; const apiKey = await res.locals.databaseAdapter.findApiKey( req.query.key ?? req.cookies.socketToken, ); // No record of this API Key found, reject the request. if (!apiKey) { // Ensure we delete the existing cookie so that it doesn't become poisoned // and cause an infinite login loop. req.session?.destroy(() => { res.clearCookie("socketToken", { secure: req.secure, sameSite: req.secure ? "none" : undefined, }); res.clearCookie("connect.sid", { path: "/" }); res.clearCookie("io", { path: "/" }); res.redirect("/login"); }); return; } user = (await res.locals.databaseAdapter.findUser(apiKey.user.id)) ?? undefined; } if (!user) { if (req.session) { req.session.returnTo = req.url; } res.status(403).redirect("/login"); return; } const allowed = res.locals.databaseAdapter.isSuperUser(user); keyOrSocketTokenAuthenticated = isUsingKeyOrSocketToken && allowed; const provider = user.identities[0]!.provider_type; const providerAllowed = config.login?.[provider]?.enabled; if ( (keyOrSocketTokenAuthenticated || req.isAuthenticated()) && allowed && providerAllowed ) { let apiKey = user.apiKeys[0]; // This should only happen if the database is manually edited, say, in the event of a security breach // that reavealed an API key that needed to be deleted. if (!apiKey) { // Make a new api key. apiKey = await res.locals.databaseAdapter.createApiKey(); // Assign this key to the user. user.apiKeys.push(apiKey); await res.locals.databaseAdapter.saveUser(user); } // Set the cookie so that requests to other resources on the page // can also be authenticated. // This is crucial for things like OBS browser sources, // where we don't have a session. res.cookie("socketToken", apiKey.secret_key, { secure: req.secure, sameSite: req.secure ? "none" : undefined, }); next(); return; } if (req.session) { req.session.returnTo = req.url; } res.status(403).redirect("/login"); return; } catch (error: unknown) { next(error); } };