108 lines
3.4 KiB
TypeScript
108 lines
3.4 KiB
TypeScript
import Validator from "wms-core/db/Validator";
|
|
import User from "wms-core/auth/models/User";
|
|
import argon2, {argon2id} from "argon2";
|
|
import AuthProof from "wms-core/auth/AuthProof";
|
|
import ModelComponent from "wms-core/db/ModelComponent";
|
|
|
|
export default class UserPasswordComponent extends ModelComponent<User> {
|
|
private password?: string = undefined;
|
|
|
|
public constructor(props: any) {
|
|
super(props);
|
|
}
|
|
|
|
public init(): void {
|
|
super.init();
|
|
this.setValidation('password').acceptUndefined().maxLength(128);
|
|
}
|
|
|
|
public async setPassword(rawPassword: string): Promise<void> {
|
|
await new Validator<string>().defined().minLength(8).maxLength(512)
|
|
.execute('password', rawPassword, true);
|
|
this.password = await argon2.hash(rawPassword, {
|
|
timeCost: 10,
|
|
memoryCost: 65536,
|
|
parallelism: 4,
|
|
type: argon2id,
|
|
hashLength: 32,
|
|
});
|
|
}
|
|
|
|
public async verifyPassword(passwordGuess: string): Promise<boolean> {
|
|
if (!this.password) return false;
|
|
|
|
return await argon2.verify(this.password, passwordGuess);
|
|
}
|
|
}
|
|
|
|
export class PasswordAuthProof implements AuthProof<User> {
|
|
public static getProofForSession(session: Express.Session): PasswordAuthProof | null {
|
|
return session.auth_password_proof ? new PasswordAuthProof(session) : null;
|
|
}
|
|
|
|
public static createAuthorizedProofForRegistration(session: Express.Session): PasswordAuthProof {
|
|
const proofForSession = new PasswordAuthProof(session);
|
|
proofForSession.authorized = true;
|
|
proofForSession.save();
|
|
return proofForSession;
|
|
}
|
|
|
|
public static createProofForLogin(session: Express.Session): PasswordAuthProof {
|
|
return new PasswordAuthProof(session);
|
|
}
|
|
|
|
private readonly session: Express.Session;
|
|
private userID: number | null;
|
|
private authorized: boolean;
|
|
private userPassword: UserPasswordComponent | null = null;
|
|
|
|
private constructor(session: Express.Session) {
|
|
this.session = session;
|
|
this.authorized = session.auth_password_proof?.authorized || false;
|
|
this.userID = session.auth_password_proof?.userID || null;
|
|
}
|
|
|
|
public async getResource(): Promise<User | null> {
|
|
return await User.getById(this.userID);
|
|
}
|
|
|
|
public setResource(user: User) {
|
|
this.userID = user.id!;
|
|
this.save();
|
|
}
|
|
|
|
public async isAuthorized(): Promise<boolean> {
|
|
return this.authorized;
|
|
}
|
|
|
|
public async isValid(): Promise<boolean> {
|
|
return await this.isAuthorized() || typeof this.userID === 'number';
|
|
}
|
|
|
|
public async revoke(): Promise<void> {
|
|
this.session.auth_password_proof = undefined;
|
|
}
|
|
|
|
private async getUserPassword(): Promise<UserPasswordComponent | null> {
|
|
if (!this.userPassword) {
|
|
this.userPassword = (await User.getById(this.userID))?.as(UserPasswordComponent) || null;
|
|
}
|
|
return this.userPassword;
|
|
}
|
|
|
|
public async authorize(passwordGuess: string): Promise<boolean> {
|
|
const password = await this.getUserPassword();
|
|
if (!password || !await password.verifyPassword(passwordGuess)) return false;
|
|
|
|
this.authorized = true;
|
|
this.save();
|
|
return true;
|
|
}
|
|
|
|
private save() {
|
|
this.session.auth_password_proof = {
|
|
authorized: this.authorized,
|
|
userID: this.userID,
|
|
};
|
|
}
|
|
} |