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): 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));
});
for (const callback of callbacks) {
await callback();
}
if (!user) {
throw new Error('Unable to register user.');
}
}
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);
}
}