lonewolf_auth_toolkit/mfa/
mod.rs

1use std::time::{SystemTime, UNIX_EPOCH};
2
3use anyhow::Error;
4use rand::{thread_rng, Rng};
5use totp_rs::{Rfc6238, TOTP};
6
7/// Generate a random string
8///
9/// ### Example
10/// ```rust
11/// use lonewolf_auth_toolkit::mfa::generate_random_string;
12///
13/// let random_string = generate_random_string();
14/// ```
15pub fn generate_random_string() -> String {
16    let mut rng = thread_rng();
17    let random_bytes: [u8; 32] = rng.gen();
18    let hex_string: String = random_bytes.iter().map(|b| format!("{:02x}", b)).collect();
19
20    hex_string
21}
22
23/// Generate a TOTP 6 Digit QR Code
24///
25/// ### Example
26/// ```rust
27/// use lonewolf_auth_toolkit::mfa::generate;
28///
29/// #[tokio::main]
30/// pub async fn main() -> Result<(), anyhow::Error> {
31///     let result = generate("SomeIssuer".to_string(), "SomeAccountName".to_string()).await?;
32/// 
33///     println!("{:?}", result.0);
34///     println!("{:?}", result.1);
35/// 
36///     Ok(())
37/// }
38/// ```
39pub async fn generate(issuer: String, account_name: String) -> Result<(String, String), Error> {
40    let secret_string = generate_random_string();
41    let mut rfc = Rfc6238::with_defaults(secret_string.clone().into_bytes().to_vec())?;
42
43    rfc.digits(6)?;
44    rfc.issuer(issuer);
45    rfc.account_name(account_name);
46
47    let totp = TOTP::from_rfc6238(rfc)?;
48    let qr_code = totp.get_qr_base64();
49
50    match qr_code {
51        Ok(qr_code) => Ok((qr_code, secret_string)),
52        Err(error) => Err(Error::msg(error)),
53    }
54}
55
56/// Verify a TOTP 6 Digit Code
57/// 
58/// ### Example
59/// ```rust
60/// use lonewolf_auth_toolkit::mfa::verify;
61/// 
62/// #[tokio::main]
63/// pub async fn main() -> Result<(), anyhow::Error> {
64///     let verified = verify("123456".to_string(), "5BAD23B477D625825019A4C895E8C5B8D22A88D3193E6928B7FC7AEFF1CC578F2A9551A1919ADE27EC50E48DFD4A2F95D9B52636C141E5B5FADE5C24A0EC71E7".to_string()).await?;
65/// 
66///     Ok(())
67/// }
68/// ```
69pub async fn verify(code: String, secret: String) -> Result<bool, Error> {
70    let mut rfc = Rfc6238::with_defaults(secret.clone().into_bytes().to_vec())?;
71
72    rfc.digits(6)?;
73
74    let totp = TOTP::from_rfc6238(rfc)?;
75    let time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
76    let token = totp.generate(time);
77
78    Ok(code == token)
79}