quorum_public/utility/
auth_common.rs1use crate::models::user::EmailBackupCode;
5use argon2::{
6 Algorithm, Argon2, Params, Version,
7 password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
8};
9use email_address::*;
10use rand::RngExt;
11use rand_core::OsRng;
12
13pub fn generate_backup_code(length: usize) -> String {
28 const CHARSET: &[u8] = b"ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789";
29 let mut rng = rand::rng();
30
31 (0..length)
32 .map(|_| {
33 let idx = rng.random_range(0..CHARSET.len());
34 CHARSET[idx] as char
35 })
36 .collect()
37}
38
39pub fn generate_backup_codes() -> Vec<EmailBackupCode> {
52 let mut backup_code_array = Vec::with_capacity(10);
53 for _ in 0..10 {
54 let code = generate_backup_code(24);
55 let hashed_code = hash(&code).expect("Failed to hash backup code");
56 backup_code_array.push(EmailBackupCode {
57 plain: Some(code),
58 hash: hashed_code,
59 });
60 }
61 backup_code_array
62}
63
64pub fn get_argon2() -> Argon2<'static> {
65 let params = Params::new(
66 262_144, 3, 2, Some(32),
70 )
71 .expect("valid Argon2 params");
72
73 Argon2::new(Algorithm::Argon2id, Version::V0x13, params)
74}
75
76pub fn hash(plaintext: &str) -> Result<String, String> {
77 let argon2 = get_argon2();
78 let salt = SaltString::generate(&mut OsRng);
79 argon2
80 .hash_password(plaintext.as_bytes(), &salt)
81 .map(|phc| phc.to_string())
82 .map_err(|e| format!("Failed to hash: {}", e))
83}
84
85pub fn verify(plaintext: &str, stored_hash: &str) -> Result<bool, String> {
86 let argon2 = get_argon2();
87 let parsed_hash =
88 PasswordHash::new(stored_hash).map_err(|e| format!("Failed to parse hash: {}", e))?;
89 argon2
90 .verify_password(plaintext.as_bytes(), &parsed_hash)
91 .map(|_| true)
92 .map_err(|e| format!("Verification failed: {}", e))
93}
94
95pub fn check_email_address(email: &str) -> bool {
96 EmailAddress::is_valid(email)
97}