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 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): void { this.userId = user.getOrFail('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, }; } }