diff --git a/src/api/middlewares/Authentication.ts b/src/api/middlewares/Authentication.ts index a71dfdf1f..fe57880a4 100644 --- a/src/api/middlewares/Authentication.ts +++ b/src/api/middlewares/Authentication.ts @@ -20,7 +20,7 @@ import { NextFunction, Request, Response } from "express"; import { HTTPError } from "lambert-server/HTTPError"; import { Session, User } from "@spacebar/database"; import { Random } from "@spacebar/extensions"; -import { checkToken, Rights, UserTokenData } from "@spacebar/util"; +import { checkToken, DiscordApiErrors, Rights, UserTokenData } from "@spacebar/util"; export const NO_AUTHORIZATION_ROUTES = [ // Authentication routes @@ -77,6 +77,7 @@ declare global { session?: Session; rights: Rights; fingerprint?: string; + isAuthenticated: boolean; } } } @@ -96,33 +97,16 @@ export async function Authentication(req: Request, res: Response, next: NextFunc `__sb_sessid=${(req.fingerprint = Random.getString("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 32))}; Secure; HttpOnly; SameSite=None; Path=/`, ); - if ( - NO_AUTHORIZATION_ROUTES.some((x) => { - if (typeof x !== "string") { - return x.test(req.method + " " + url); - } + await handleAuthentication(req); - const fullRoute = req.method + " " + url; + return next(); +} - if (req.method === "HEAD") { - const urlPart = x.split(" ").slice(1).join(" "); - if (urlPart.endsWith("/")) { - return url.startsWith(urlPart); - } else { - return url === urlPart; - } - } - - if (x.endsWith("/")) { - return fullRoute.startsWith(x); - } else { - return fullRoute === x; - } - }) - ) - return next(); - - if (!req.headers.authorization) return next(new HTTPError("Missing Authorization Header", 401)); +export async function handleAuthentication(req: Request) { + if (!req.headers.authorization) { + req.isAuthenticated = false; + return; + } try { const { decoded, user, session } = (req.tokenData = await checkToken(req.headers.authorization, { @@ -136,11 +120,9 @@ export async function Authentication(req: Request, res: Response, next: NextFunc req.user = user; req.session = session; req.rights = new Rights(Number(user.rights)); - return next(); - } catch (error) { - if (error instanceof HTTPError) { - return next(error); - } - return next(new HTTPError(error!.toString(), 400)); + req.isAuthenticated = true; + } catch (e) { + req.isAuthenticated = false; + console.error("[Authentication] Token was provided, but was invalid:", e); } } diff --git a/src/api/util/handlers/route.ts b/src/api/middlewares/Route.ts similarity index 96% rename from src/api/util/handlers/route.ts rename to src/api/middlewares/Route.ts index c00a959d3..69c2b25eb 100644 --- a/src/api/util/handlers/route.ts +++ b/src/api/middlewares/Route.ts @@ -74,6 +74,11 @@ export interface RouteOptions { // event?: EVENT | EVENT[]; // headers?: Record; // }; + + /** + * @defaultValue "required" + */ + authentication?: "never" | "optional" | "required"; } export function stripNull(obj: object) { for (const [key, value] of Object.entries(obj)) { @@ -126,7 +131,11 @@ export function route(opts: RouteOptions) { if (!validate) throw new Error(`Body schema ${opts.requestBody} not found`); } + opts.authentication ??= "required"; + return async (req: Request, res: Response, next: NextFunction) => { + if (opts.authentication === "required" && !req.isAuthenticated) throw DiscordApiErrors.UNAUTHORIZED; + if (opts.permission) { const { guild_id, channel_id } = req.params as { [key: string]: string }; req.permission = await getPermission(req.user_id, guild_id, channel_id); diff --git a/src/api/middlewares/index.ts b/src/api/middlewares/index.ts index a2bb00b37..22651b534 100644 --- a/src/api/middlewares/index.ts +++ b/src/api/middlewares/index.ts @@ -22,4 +22,5 @@ export * from "./CORS"; export * from "./ErrorHandler"; export * from "./ImageProxy"; export * from "./RateLimit"; +export * from "./Route"; export * from "./Translation"; diff --git a/src/api/util/index.ts b/src/api/util/index.ts index b57d263ac..5fb5a684e 100644 --- a/src/api/util/index.ts +++ b/src/api/util/index.ts @@ -19,7 +19,6 @@ export * from "./utility/ipAddress"; export * from "./handlers/Message"; export * from "./utility/passwordStrength"; -export * from "./handlers/route"; export * from "./handlers/Voice"; export * from "./utility/captcha"; export * from "./utility/EmbedHandlers";