import {NextFunction, Request, Response} from "express"; import Application from "../Application.js"; import ApplicationComponent from "../ApplicationComponent.js"; import {route} from "../common/Routing.js"; import {ForbiddenHttpError} from "../HttpError.js"; import Middleware from "../Middleware.js"; import AuthGuard from "./AuthGuard.js"; import AuthMethod from "./AuthMethod.js"; import AuthProof from "./AuthProof.js"; import User from "./models/User.js"; export default class AuthComponent extends ApplicationComponent { private readonly authGuard: AuthGuard; public constructor(app: Application, ...authMethods: AuthMethod>[]) { super(); this.authGuard = new AuthGuard(app, ...authMethods); } public async initRoutes(): Promise { this.use(AuthMiddleware); } public getAuthGuard(): AuthGuard { return this.authGuard; } } export class AuthMiddleware extends Middleware { private authGuard?: AuthGuard; private user: User | null = null; protected async handle(req: Request, res: Response, next: NextFunction): Promise { this.authGuard = this.app.as(AuthComponent).getAuthGuard(); const proofs = await this.authGuard.getProofsForSession(req.getSession()); if (proofs.length > 0) { this.user = await proofs[0].getResource(); res.locals.user = this.user; } next(); } public getUser(): User | null { return this.user; } public getAuthGuard(): AuthGuard { if (!this.authGuard) throw new Error('AuthGuard was not initialized.'); return this.authGuard; } } export class RequireRequestAuthMiddleware extends Middleware { private user?: User; protected async handle(req: Request, res: Response, next: NextFunction): Promise { const proofs = await req.as(AuthMiddleware).getAuthGuard().getProofsForRequest(req); const user = await proofs[0]?.getResource(); if (user) { this.user = user; next(); return; } req.flash('error', `You must be logged in to access ${req.url}.`); res.redirect(route('auth', undefined, { redirect_uri: req.url, })); } public getUser(): User { if (!this.user) throw new Error('user not initialized.'); return this.user; } } export class RequireAuthMiddleware extends Middleware { private user?: User; protected async handle(req: Request, res: Response, next: NextFunction): Promise { const authGuard = req.as(AuthMiddleware).getAuthGuard(); // Via request let proofs = await authGuard.getProofsForRequest(req); let user = await proofs[0]?.getResource(); if (user) { this.user = user; next(); return; } // Via session proofs = await authGuard.getProofsForSession(req.getSession()); user = await proofs[0]?.getResource(); if (user) { this.user = user; next(); return; } req.flash('error', `You must be logged in to access ${req.url}.`); res.redirect(route('auth', undefined, { redirect_uri: req.url, })); } public getUser(): User { if (!this.user) throw new Error('user not initialized.'); return this.user; } } export class RequireGuestMiddleware extends Middleware { protected async handle(req: Request, res: Response, next: NextFunction): Promise { const proofs = await req.as(AuthMiddleware).getAuthGuard().getProofsForSession(req.getSession()); if (proofs.length > 0) { res.redirect(route('home')); return; } next(); } } export class RequireAdminMiddleware extends Middleware { protected async handle(req: Request, res: Response, next: NextFunction): Promise { const user = req.as(AuthMiddleware).getUser(); if (!user || !user.is_admin) { throw new ForbiddenHttpError('secret tool', req.url); } next(); } }