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 { private password?: string = undefined; public constructor(props: any) { super(props); } public init(): void { this.setValidation('password').acceptUndefined().maxLength(128); } public async setPassword(rawPassword: string): Promise { await new Validator().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 { if (!this.password) return false; return await argon2.verify(this.password, passwordGuess); } } export class PasswordAuthProof implements AuthProof { 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 { return await User.getById(this.userID); } public setResource(user: User) { this.userID = user.id!; this.save(); } public async isAuthorized(): Promise { return this.authorized; } public async isValid(): Promise { if (typeof this.userID === 'number') { return Boolean(await this.getResource()); } else { return await this.isAuthorized(); } } public async revoke(): Promise { this.session.auth_password_proof = undefined; } private async getUserPassword(): Promise { if (!this.userPassword) { this.userPassword = (await User.getById(this.userID))?.as(UserPasswordComponent) || null; } return this.userPassword; } public async authorize(passwordGuess: string): Promise { 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, }; } }