use argon2::{ Argon2, password_hash::{ PasswordHash, PasswordHasher as Argon2Hasher, PasswordVerifier, SaltString, rand_core::OsRng, }, }; use async_trait::async_trait; use rand::RngCore; use crate::domain::{DomainError, DomainResult}; use crate::ports::{PasswordHasher, TokenGenerator}; pub struct Argon2PasswordHasher; #[async_trait] impl PasswordHasher for Argon2PasswordHasher { fn hash(&self, password: &str) -> DomainResult { let salt = SaltString::generate(&mut OsRng); Argon2::default() .hash_password(password.as_bytes(), &salt) .map(|hash| hash.to_string()) .map_err(|error| DomainError::Password(error.to_string())) } fn verify(&self, password: &str, encoded_hash: &str) -> DomainResult { let hash = PasswordHash::new(encoded_hash) .map_err(|error| DomainError::Password(error.to_string()))?; Ok(Argon2::default() .verify_password(password.as_bytes(), &hash) .is_ok()) } } pub struct RandomTokenGenerator; #[async_trait] impl TokenGenerator for RandomTokenGenerator { fn generate(&self) -> String { hex::encode(new_secret()) } } /// Generates 32 cryptographically random bytes. Callers that need a /// client-facing string should hex-encode the result. pub fn new_secret() -> Vec { let mut bytes = [0_u8; 32]; OsRng.fill_bytes(&mut bytes); bytes.to_vec() }