Skip to main content

openauth_plugins/two_factor/
backup_codes.rs

1use openauth_core::crypto::{symmetric_decrypt, symmetric_encrypt};
2use openauth_core::error::OpenAuthError;
3
4use super::options::{BackupCodeOptions, BackupCodeStorage};
5
6pub fn generate_backup_codes(options: &BackupCodeOptions) -> Vec<String> {
7    (0..options.amount)
8        .map(|_| {
9            let raw = openauth_core::crypto::random::generate_random_string(options.length);
10            let split = options.length / 2;
11            format!("{}-{}", &raw[..split], &raw[split..])
12        })
13        .collect()
14}
15
16pub fn encode_backup_codes(
17    codes: &[String],
18    secret: &str,
19    options: &BackupCodeOptions,
20) -> Result<String, OpenAuthError> {
21    let json =
22        serde_json::to_string(codes).map_err(|error| OpenAuthError::Api(error.to_string()))?;
23    match options.storage {
24        BackupCodeStorage::Plain => Ok(json),
25        BackupCodeStorage::Encrypted => symmetric_encrypt(secret, &json),
26    }
27}
28
29pub fn decode_backup_codes(
30    encoded: &str,
31    secret: &str,
32    options: &BackupCodeOptions,
33) -> Result<Vec<String>, OpenAuthError> {
34    let json = match options.storage {
35        BackupCodeStorage::Plain => encoded.to_owned(),
36        BackupCodeStorage::Encrypted => symmetric_decrypt(secret, encoded)?,
37    };
38    serde_json::from_str(&json).map_err(|error| OpenAuthError::Api(error.to_string()))
39}
40
41pub fn consume_backup_code(codes: &[String], code: &str) -> Option<Vec<String>> {
42    codes.iter().any(|candidate| candidate == code).then(|| {
43        codes
44            .iter()
45            .filter(|candidate| *candidate != code)
46            .cloned()
47            .collect()
48    })
49}