brylix 0.2.2

GraphQL framework for AWS Lambda with SeaORM and multi-tenant support
Documentation
//! Password hashing and verification using Argon2.

use argon2::{
    password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
    Argon2,
};

/// Hash a password using Argon2id.
///
/// # Arguments
///
/// * `password` - The plaintext password to hash
///
/// # Returns
///
/// The hashed password string (PHC format)
///
/// # Errors
///
/// Returns an error if hashing fails
pub fn hash_password(password: &str) -> Result<String, String> {
    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| format!("Failed to hash password: {}", e))
}

/// Verify a password against a hash.
///
/// # Arguments
///
/// * `password` - The plaintext password to verify
/// * `hash` - The password hash to verify against
///
/// # Returns
///
/// `true` if the password matches the hash
///
/// # Errors
///
/// Returns an error if the hash is invalid
pub fn verify_password(password: &str, hash: &str) -> Result<bool, String> {
    let parsed_hash =
        PasswordHash::new(hash).map_err(|e| format!("Invalid password hash: {}", e))?;

    Ok(Argon2::default()
        .verify_password(password.as_bytes(), &parsed_hash)
        .is_ok())
}

/// Generate a temporary password for user invitations.
///
/// # Returns
///
/// A random 16-character alphanumeric password
pub fn generate_temp_password() -> String {
    use rand::Rng;
    const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%";
    let mut rng = rand::rng();

    (0..16)
        .map(|_| {
            let idx = rng.random_range(0..CHARSET.len());
            CHARSET[idx] as char
        })
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_hash_and_verify() {
        let password = "SecurePassword123!";
        let hash = hash_password(password).unwrap();

        assert!(verify_password(password, &hash).unwrap());
        assert!(!verify_password("wrong_password", &hash).unwrap());
    }

    #[test]
    fn test_generate_temp_password() {
        let password = generate_temp_password();
        assert_eq!(password.len(), 16);
    }
}