auth_framework/auth_modular/mfa/
backup_codes.rs1use crate::errors::Result;
4use crate::storage::AuthStorage;
5use std::sync::Arc;
6use tracing::{debug, info};
7
8pub struct BackupCodesManager {
10 storage: Arc<dyn AuthStorage>,
11}
12
13impl BackupCodesManager {
14 pub fn new(storage: Arc<dyn AuthStorage>) -> Self {
16 Self { storage }
17 }
18
19 pub async fn generate_codes(&self, user_id: &str, count: usize) -> Result<Vec<String>> {
21 debug!("Generating {} backup codes for user '{}'", count, user_id);
22
23 let codes: Vec<String> = (0..count)
24 .map(|_| format!("{:08}", rand::random::<u32>() % 100000000))
25 .collect();
26
27 let backup_key = format!("user:{}:backup_codes", user_id);
29 let codes_json = serde_json::to_string(&codes).unwrap_or("[]".to_string());
30 self.storage
31 .store_kv(&backup_key, codes_json.as_bytes(), None)
32 .await?;
33
34 info!("Generated {} backup codes for user '{}'", count, user_id);
35 Ok(codes)
36 }
37
38 pub async fn verify_code(&self, user_id: &str, code: &str) -> Result<bool> {
40 debug!("Verifying backup code for user '{}'", user_id);
41
42 if code.len() != 8 || !code.chars().all(|c| c.is_ascii_digit()) {
44 return Ok(false);
45 }
46
47 let backup_key = format!("user:{}:backup_codes", user_id);
49 if let Some(codes_data) = self.storage.get_kv(&backup_key).await? {
50 let codes_str = std::str::from_utf8(&codes_data).unwrap_or("[]");
51 let mut backup_codes: Vec<String> = serde_json::from_str(codes_str).unwrap_or_default();
52
53 if let Some(index) = backup_codes.iter().position(|c| c == code) {
54 backup_codes.remove(index);
56 let updated_codes =
57 serde_json::to_string(&backup_codes).unwrap_or("[]".to_string());
58 self.storage
59 .store_kv(&backup_key, updated_codes.as_bytes(), None)
60 .await?;
61
62 info!("Backup code verified and consumed for user '{}'", user_id);
63 Ok(true)
64 } else {
65 Ok(false)
66 }
67 } else {
68 Ok(false)
69 }
70 }
71
72 pub async fn get_remaining_count(&self, user_id: &str) -> Result<usize> {
74 debug!("Getting remaining backup codes for user '{}'", user_id);
75
76 let backup_key = format!("user:{}:backup_codes", user_id);
77 if let Some(codes_data) = self.storage.get_kv(&backup_key).await? {
78 let codes_str = std::str::from_utf8(&codes_data).unwrap_or("[]");
79 let backup_codes: Vec<String> = serde_json::from_str(codes_str).unwrap_or_default();
80 Ok(backup_codes.len())
81 } else {
82 Ok(0)
83 }
84 }
85
86 pub async fn has_backup_codes(&self, user_id: &str) -> Result<bool> {
88 let count = self.get_remaining_count(user_id).await?;
89 Ok(count > 0)
90 }
91
92 pub async fn regenerate_codes(&self, user_id: &str, count: usize) -> Result<Vec<String>> {
94 info!("Regenerating backup codes for user '{}'", user_id);
95
96 self.generate_codes(user_id, count).await
98 }
99}
100
101