Files
guru-connect/server/src/auth/password.rs
Mike Swanson 3fc4e1f96a Add user management system with JWT authentication
- Database schema: users, permissions, client_access tables
- Auth: JWT tokens with Argon2 password hashing
- API: login, user CRUD, permission management
- Dashboard: login required, admin Users tab
- Auto-creates initial admin user on first run
2025-12-29 21:00:20 -07:00

58 lines
1.7 KiB
Rust

//! Password hashing using Argon2id
use anyhow::{anyhow, Result};
use argon2::{
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Argon2,
};
/// Hash a password using Argon2id
pub fn hash_password(password: &str) -> Result<String> {
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let hash = argon2
.hash_password(password.as_bytes(), &salt)
.map_err(|e| anyhow!("Failed to hash password: {}", e))?;
Ok(hash.to_string())
}
/// Verify a password against a stored hash
pub fn verify_password(password: &str, hash: &str) -> Result<bool> {
let parsed_hash = PasswordHash::new(hash)
.map_err(|e| anyhow!("Invalid password hash format: {}", e))?;
let argon2 = Argon2::default();
Ok(argon2.verify_password(password.as_bytes(), &parsed_hash).is_ok())
}
/// Generate a random password (for initial admin)
pub fn generate_random_password(length: usize) -> String {
use rand::Rng;
const CHARSET: &[u8] = b"ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789!@#$%";
let mut rng = rand::thread_rng();
(0..length)
.map(|_| {
let idx = rng.gen_range(0..CHARSET.len());
CHARSET[idx] as char
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hash_and_verify() {
let password = "test_password_123";
let hash = hash_password(password).unwrap();
assert!(verify_password(password, &hash).unwrap());
assert!(!verify_password("wrong_password", &hash).unwrap());
}
#[test]
fn test_random_password() {
let password = generate_random_password(16);
assert_eq!(password.len(), 16);
}
}