use argon2::password_hash::{
rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString,
};
use argon2::Argon2;
use crate::error::AuthError;
pub fn hash_password(password: &str) -> Result<String, AuthError> {
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
argon2
.hash_password(password.as_bytes(), &salt)
.map(|h| h.to_string())
.map_err(|e| AuthError::Hash(e.to_string()))
}
pub fn verify_password(phc: &str, candidate: &str) -> bool {
match PasswordHash::new(phc) {
Ok(parsed) => Argon2::default()
.verify_password(candidate.as_bytes(), &parsed)
.is_ok(),
Err(_) => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hash_then_verify_roundtrips() {
let h = hash_password("correct horse").unwrap();
assert!(verify_password(&h, "correct horse"));
}
#[test]
fn verify_rejects_wrong_password() {
let h = hash_password("s3cret").unwrap();
assert!(!verify_password(&h, "wrong"));
}
#[test]
fn two_hashes_of_same_password_differ() {
assert_ne!(
hash_password("same").unwrap(),
hash_password("same").unwrap()
);
}
#[test]
fn verify_rejects_garbage_hash() {
assert!(!verify_password("not-a-phc-string", "x"));
}
}