use argon2::password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString}; use argon2::Argon2; use crate::error::{AppError, AppResult}; /// Hash a plaintext password with Argon2id + random salt (PHC string). pub fn hash_password(plain: &str) -> AppResult { let salt = SaltString::generate(&mut OsRng); let hash = Argon2::default() .hash_password(plain.as_bytes(), &salt) .map_err(|e| AppError::Internal(anyhow::anyhow!("password hash failed: {e}")))?; Ok(hash.to_string()) } /// Verify a plaintext password against a stored PHC hash. /// Returns Ok(false) on mismatch, error only on malformed stored hash. pub fn verify_password(plain: &str, stored_hash: &str) -> AppResult { let parsed = PasswordHash::new(stored_hash) .map_err(|e| AppError::Internal(anyhow::anyhow!("stored hash malformed: {e}")))?; Ok(Argon2::default() .verify_password(plain.as_bytes(), &parsed) .is_ok()) }