openauth_plugins/two_factor/
backup_codes.rs1use 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}