import ApplicationComponent from "../ApplicationComponent"; import {NextFunction, Request, Response} from "express"; import AuthGuard from "./AuthGuard"; import Controller from "../Controller"; import {ForbiddenHttpError} from "../HttpError"; import Middleware from "../Middleware"; import User from "./models/User"; import Application from "../Application"; import AuthMethod from "./AuthMethod"; import AuthProof from "./AuthProof"; 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 init(): 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(Controller.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(Controller.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(Controller.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(); } }