Files
hms/crates/erp-auth/src/service/password.rs
iven 9568dd7875 chore: apply cargo fmt across workspace and update docs
- Run cargo fmt on all Rust crates for consistent formatting
- Update CLAUDE.md with WASM plugin commands and dev.ps1 instructions
- Update wiki: add WASM plugin architecture, rewrite dev environment docs
- Minor frontend cleanup (unused imports)
2026-04-15 00:49:20 +08:00

57 lines
1.7 KiB
Rust

use argon2::{
Argon2,
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString, rand_core::OsRng},
};
use crate::error::{AuthError, AuthResult};
/// Hash a plaintext password using Argon2 with a random salt.
///
/// Returns a PHC-format string suitable for database storage.
pub fn hash_password(plain: &str) -> AuthResult<String> {
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let hash = argon2
.hash_password(plain.as_bytes(), &salt)
.map_err(|e| AuthError::HashError(e.to_string()))?;
Ok(hash.to_string())
}
/// Verify a plaintext password against a stored PHC-format hash.
///
/// Returns `Ok(true)` if the password matches, `Ok(false)` if not.
pub fn verify_password(plain: &str, hash: &str) -> AuthResult<bool> {
let parsed = PasswordHash::new(hash).map_err(|e| AuthError::HashError(e.to_string()))?;
Ok(Argon2::default()
.verify_password(plain.as_bytes(), &parsed)
.is_ok())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hash_and_verify() {
let hash = hash_password("test123").unwrap();
assert!(
verify_password("test123", &hash).unwrap(),
"Correct password should verify"
);
assert!(
!verify_password("wrong", &hash).unwrap(),
"Wrong password should not verify"
);
}
#[test]
fn test_hash_is_unique() {
let hash1 = hash_password("same_password").unwrap();
let hash2 = hash_password("same_password").unwrap();
assert_ne!(
hash1, hash2,
"Two hashes of the same password should differ (different salts)"
);
}
}