Files
erp/crates/erp-auth/src/service/password.rs
iven edc41a1500 feat(auth): implement core service layer (password, JWT, auth, user CRUD)
- error.rs: AuthError with proper HTTP status mapping
- service/password.rs: Argon2 hash/verify with tests
- service/token_service.rs: JWT sign/validate, token DB storage with SHA-256 hash
- service/auth_service.rs: login/refresh/logout flows with event publishing
- service/user_service.rs: user CRUD with soft delete and tenant isolation
- Added sha2 dependency to workspace for token hashing
2026-04-11 03:05:17 +08:00

57 lines
1.7 KiB
Rust

use argon2::{
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Argon2,
};
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)"
);
}
}