80 lines
2.6 KiB
TypeScript
80 lines
2.6 KiB
TypeScript
|
import AuthProof from "./AuthProof";
|
||
|
import MysqlConnectionManager from "../db/MysqlConnectionManager";
|
||
|
import User from "./models/User";
|
||
|
import UserEmail from "./models/UserEmail";
|
||
|
|
||
|
export default abstract class AuthGuard<P extends AuthProof> {
|
||
|
public abstract async getProofForSession(sessionID: string): Promise<P | null>;
|
||
|
|
||
|
public async getUserForSession(session: Express.Session): Promise<User | null> {
|
||
|
if (!await this.isAuthenticated(session)) return null;
|
||
|
return await User.getById<User>(session.auth_id);
|
||
|
}
|
||
|
|
||
|
public async authenticateOrRegister(session: Express.Session, proof: P): Promise<void> {
|
||
|
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<void>)[] = [];
|
||
|
|
||
|
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<boolean> {
|
||
|
return await this.checkCurrentSessionProofValidity(session);
|
||
|
}
|
||
|
|
||
|
public async logout(session: Express.Session): Promise<void> {
|
||
|
const proof = await this.getProofForSession(session.id);
|
||
|
if (proof) {
|
||
|
await proof.revoke();
|
||
|
}
|
||
|
session.auth_id = undefined;
|
||
|
}
|
||
|
|
||
|
private async checkCurrentSessionProofValidity(session: Express.Session): Promise<boolean> {
|
||
|
if (typeof session.auth_id !== 'number') return false;
|
||
|
|
||
|
const proof = await this.getProofForSession(session.id);
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
}
|