use anyhow::{Context, Result};
use argon2::Argon2;
use argon2::password_hash::rand_core::OsRng;
use argon2::password_hash::{PasswordHasher, SaltString};
pub fn hash_password(password: &str) -> Result<String> {
let salt = SaltString::generate(&mut OsRng);
Argon2::default()
.hash_password(password.as_bytes(), &salt)
.map(|hash| hash.to_string())
.map_err(|e| anyhow::anyhow!("{e}"))
.context("hashing the password")
}
#[cfg(test)]
mod tests {
use super::*;
use argon2::password_hash::{PasswordHash, PasswordVerifier};
#[test]
fn hash_is_not_plaintext_and_verifies() {
let hash = hash_password("correct horse battery staple").unwrap();
assert!(!hash.contains("correct horse"));
assert!(hash.starts_with("$argon2"));
let parsed = PasswordHash::new(&hash).unwrap();
assert!(
Argon2::default()
.verify_password("correct horse battery staple".as_bytes(), &parsed)
.is_ok()
);
}
#[test]
fn each_hash_is_salted_uniquely() {
assert_ne!(
hash_password("hunter2000").unwrap(),
hash_password("hunter2000").unwrap()
);
}
}