Reduce the amount of SQL requests made for authentication

This commit is contained in:
Alice Gaudon 2020-07-28 11:47:20 +02:00
parent 95632f5880
commit 272688da26
3 changed files with 59 additions and 39 deletions

View File

@ -15,14 +15,15 @@ export default class AuthComponent extends ApplicationComponent<void> {
public async init(router: Router): Promise<void> {
router.use(async (req, res, next) => {
req.authGuard = this.authGuard;
res.locals.user = await (await req.authGuard.getProofForSession(req.session!))?.getResource();
res.locals.user = await (await req.authGuard.getProof(req))?.getResource();
next();
});
}
}
export const REQUIRE_REQUEST_AUTH_MIDDLEWARE = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
if (!await req.authGuard.isAuthenticatedViaRequest(req)) {
let proof = await req.authGuard.isAuthenticatedViaRequest(req);
if (!proof) {
req.flash('error', `You must be logged in to access ${req.url}.`);
res.redirect(Controller.route('auth', undefined, {
redirect_uri: req.url,
@ -30,26 +31,31 @@ export const REQUIRE_REQUEST_AUTH_MIDDLEWARE = async (req: Request, res: Respons
return;
}
req.models.user = await (await req.authGuard.getProofForRequest(req))?.getResource();
req.models.user = await proof.getResource();
next();
};
export const REQUIRE_AUTH_MIDDLEWARE = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
if (await req.authGuard.isAuthenticatedViaRequest(req)) {
req.models.user = await (await req.authGuard.getProofForRequest(req))?.getResource();
next();
} else {
if (!await req.authGuard.isAuthenticated(req.session!)) {
req.flash('error', `You must be logged in to access ${req.url}.`);
res.redirect(Controller.route('auth', undefined, {
redirect_uri: req.url,
}));
return;
}
req.models.user = await (await req.authGuard.getProofForSession(req.session!))?.getResource();
// Via request
let proof = await req.authGuard.isAuthenticatedViaRequest(req);
if (proof) {
req.models.user = await proof.getResource();
next();
return;
}
// Via session
proof = await req.authGuard.isAuthenticated(req.session!);
if (!proof) {
req.flash('error', `You must be logged in to access ${req.url}.`);
res.redirect(Controller.route('auth', undefined, {
redirect_uri: req.url,
}));
return;
}
req.models.user = await proof.getResource();
next();
};
export const REQUIRE_GUEST_MIDDLEWARE = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
if (await req.authGuard.isAuthenticated(req.session!)) {

View File

@ -26,7 +26,7 @@ export default abstract class AuthController extends Controller {
protected abstract async getCheckAuth(req: Request, res: Response, next: NextFunction): Promise<void>;
protected async postLogout(req: Request, res: Response, next: NextFunction): Promise<void> {
const proof = await req.authGuard.getProofForSession(req.session!);
const proof = await req.authGuard.getProof(req);
await proof?.revoke();
req.flash('success', 'Successfully logged out.');
res.redirect(req.query.redirect_uri?.toString() || '/');

View File

@ -7,15 +7,47 @@ import {PENDING_ACCOUNT_REVIEW_MAIL_TEMPLATE} from "../Mails";
import Mail from "../Mail";
import Controller from "../Controller";
import config from "config";
import ModelFactory from "../db/ModelFactory";
export default abstract class AuthGuard<P extends AuthProof<User>> {
public abstract async getProofForSession(session: Express.Session): Promise<P | null>;
protected abstract async getProofForSession(session: Express.Session): Promise<P | null>;
public async getProofForRequest(req: Request): Promise<P | null> {
protected async getProofForRequest(req: Request): Promise<P | null> {
return null;
}
public async getProof(req: Request): Promise<P | null> {
let proof = await this.isAuthenticatedViaRequest(req);
if (!proof && req.session) {
proof = await this.isAuthenticated(req.session);
}
return proof;
}
public async isAuthenticated(session: Express.Session): Promise<P | null> {
if (!session.is_authenticated) return null;
const proof = await this.getProofForSession(session);
if (!proof || !await proof.isValid() || !await proof.isAuthorized()) {
await proof?.revoke();
session.is_authenticated = false;
return null;
}
return proof;
}
public async isAuthenticatedViaRequest(req: Request): Promise<P | null> {
const proof = await this.getProofForRequest(req);
if (!proof || !await proof.isValid() || !await proof.isAuthorized()) {
await proof?.revoke();
return null;
}
return proof;
}
public async authenticateOrRegister(
session: Express.Session,
proof: P,
@ -61,28 +93,10 @@ export default abstract class AuthGuard<P extends AuthProof<User>> {
}
// Login
session.auth_id = user.id;
session.is_authenticated = true;
if (onLogin) await onLogin(user);
}
public async isAuthenticated(session: Express.Session): Promise<boolean> {
if (typeof session.auth_id !== 'number') return false;
const proof = await this.getProofForSession(session);
if (!proof || !await proof.isValid() || !await proof.isAuthorized()) {
await proof?.revoke();
return false;
}
return true;
}
public async isAuthenticatedViaRequest(req: Request): Promise<boolean> {
const proof = await this.getProofForRequest(req);
return Boolean(proof && await proof.isValid() && await proof.isAuthorized());
}
}
export class AuthError extends Error {