import AuthProof from "./AuthProof"; import MysqlConnectionManager from "../db/MysqlConnectionManager"; import User from "./models/User"; import UserEmail from "./models/UserEmail"; import {Connection} from "mysql"; export default abstract class AuthGuard

{ public abstract async getProofForSession(session: Express.Session): Promise

; public async getUserForSession(session: Express.Session): Promise { if (!await this.isAuthenticated(session)) return null; return await User.getById(`${session.auth_id}`); } public async authenticateOrRegister(session: Express.Session, proof: P, registerCallback?: (connection: Connection, userID: number) => Promise<(() => Promise)[]>): Promise { if (!await proof.isAuthorized()) { throw new AuthError('Invalid argument: cannot authenticate with an unauthorized proof.'); } let user = await proof.getUser(); if (!user) { // Register const callbacks: (() => Promise)[] = []; await MysqlConnectionManager.wrapTransaction(async connection => { const email = await proof.getEmail(); user = new User({ name: email.split('@')[0], }); await user.save(connection, c => callbacks.push(c)); const userEmail = new UserEmail({ user_id: user.id, email: email, main: true, }); await userEmail.save(connection, c => callbacks.push(c)); if (registerCallback) { (await registerCallback(connection, user.id!)).forEach(c => callbacks.push(c)); } }); for (const callback of callbacks) { await callback(); } if (!user) { throw new Error('Unable to register user.'); } } else if (registerCallback) { throw new UserAlreadyExistsAuthError(await proof.getEmail()); } session.auth_id = user.id; } public async isAuthenticated(session: Express.Session): Promise { return await this.checkCurrentSessionProofValidity(session); } public async logout(session: Express.Session): Promise { const proof = await this.getProofForSession(session); if (proof) { await proof.revoke(session); } session.auth_id = undefined; } private async checkCurrentSessionProofValidity(session: Express.Session): Promise { if (typeof session.auth_id !== 'number') return false; const proof = await this.getProofForSession(session); if (!proof || !await proof.isValid() || !await proof.isAuthorized() || !await proof.isOwnedBy(session.auth_id)) { await this.logout(session); return false; } return true; } } export class AuthError extends Error { constructor(message: string) { super(message); } } export class UserAlreadyExistsAuthError extends AuthError { public readonly email: string; constructor(userEmail: string) { super(`User with email ${userEmail} already exists.`); this.email = userEmail; } }