AuthMethod: add weight to choose when no method was specified

This commit is contained in:
Alice Gaudon 2020-11-16 11:43:14 +01:00
parent a5ee9922ec
commit 70d80d1f0a
4 changed files with 39 additions and 2 deletions

View File

@ -7,6 +7,8 @@ import User from "./models/User";
import UserPasswordComponent from "./password/UserPasswordComponent"; import UserPasswordComponent from "./password/UserPasswordComponent";
import UserNameComponent from "./models/UserNameComponent"; import UserNameComponent from "./models/UserNameComponent";
import {UnknownRelationValidationError} from "../db/Validator"; import {UnknownRelationValidationError} from "../db/Validator";
import AuthMethod from "./AuthMethod";
import AuthProof from "./AuthProof";
export default class AuthController extends Controller { export default class AuthController extends Controller {
public getRoutesPrefix(): string { public getRoutesPrefix(): string {
@ -83,8 +85,23 @@ export default class AuthController extends Controller {
// Redirect to registration if user not found // Redirect to registration if user not found
if (methods.length === 0) return await this.redirectToRegistration(req, res, identifier); if (methods.length === 0) return await this.redirectToRegistration(req, res, identifier);
// Choose best matching method
let user: User | null = null;
let method: AuthMethod<AuthProof<User>> | null = null;
let weight = -1;
for (const entry of methods) {
const methodWeight = entry.method.getWeightForRequest(req);
if (methodWeight > weight) {
user = entry.user;
method = entry.method;
weight = methodWeight;
}
}
if (!method || !user) ({method, user} = methods[0]); // Default to first method
// Login // Login
const {user, method} = methods[0];
return await method.attemptLogin(req, res, user); return await method.attemptLogin(req, res, user);
} }

View File

@ -9,6 +9,14 @@ export default interface AuthMethod<P extends AuthProof<User>> {
*/ */
getName(): string; getName(): string;
/**
* Used for automatic auth method detection. Won't affect forced auth method.
*
* @return {@code 0} if the request is not conform to this auth method, otherwise the exact count of matching
* fields.
*/
getWeightForRequest(req: Request): number;
findUserByIdentifier(identifier: string): Promise<User | null>; findUserByIdentifier(identifier: string): Promise<User | null>;
getProofsForSession?(session: Express.Session): Promise<P[]>; getProofsForSession?(session: Express.Session): Promise<P[]>;

View File

@ -11,7 +11,7 @@ import RedirectBackComponent from "../../components/RedirectBackComponent";
import Application from "../../Application"; import Application from "../../Application";
import {MailTemplate} from "../../mail/Mail"; import {MailTemplate} from "../../mail/Mail";
import AuthMagicLinkActionType from "./AuthMagicLinkActionType"; import AuthMagicLinkActionType from "./AuthMagicLinkActionType";
import Validator from "../../db/Validator"; import Validator, {EMAIL_REGEX} from "../../db/Validator";
import ModelFactory from "../../db/ModelFactory"; import ModelFactory from "../../db/ModelFactory";
import UserNameComponent from "../models/UserNameComponent"; import UserNameComponent from "../models/UserNameComponent";
@ -26,6 +26,12 @@ export default class MagicLinkAuthMethod implements AuthMethod<MagicLink> {
return 'magic_link'; return 'magic_link';
} }
public getWeightForRequest(req: Request): number {
return !req.body.identifier || !EMAIL_REGEX.test(req.body.identifier) ?
0 :
1;
}
public async findUserByIdentifier(identifier: string): Promise<User | null> { public async findUserByIdentifier(identifier: string): Promise<User | null> {
return (await UserEmail.select() return (await UserEmail.select()
.with('user.mainEmail') .with('user.mainEmail')

View File

@ -24,6 +24,12 @@ export default class PasswordAuthMethod implements AuthMethod<PasswordAuthProof>
return 'password'; return 'password';
} }
public getWeightForRequest(req: Request): number {
return !req.body.identifier || !req.body.password || req.body.password.length === 0 ?
0 :
2;
}
public async findUserByIdentifier(identifier: string): Promise<User | null> { public async findUserByIdentifier(identifier: string): Promise<User | null> {
const query = UserEmail.select() const query = UserEmail.select()
.with('user') .with('user')