openauth-plugins 0.0.3

Official OpenAuth plugin modules.
Documentation
use openauth_core::crypto::{symmetric_decrypt, symmetric_encrypt};
use openauth_core::error::OpenAuthError;

use super::options::{BackupCodeOptions, BackupCodeStorage};

pub fn generate_backup_codes(options: &BackupCodeOptions) -> Vec<String> {
    (0..options.amount)
        .map(|_| {
            let raw = openauth_core::crypto::random::generate_random_string(options.length);
            let split = options.length / 2;
            format!("{}-{}", &raw[..split], &raw[split..])
        })
        .collect()
}

pub fn encode_backup_codes(
    codes: &[String],
    secret: &str,
    options: &BackupCodeOptions,
) -> Result<String, OpenAuthError> {
    let json =
        serde_json::to_string(codes).map_err(|error| OpenAuthError::Api(error.to_string()))?;
    match options.storage {
        BackupCodeStorage::Plain => Ok(json),
        BackupCodeStorage::Encrypted => symmetric_encrypt(secret, &json),
    }
}

pub fn decode_backup_codes(
    encoded: &str,
    secret: &str,
    options: &BackupCodeOptions,
) -> Result<Vec<String>, OpenAuthError> {
    let json = match options.storage {
        BackupCodeStorage::Plain => encoded.to_owned(),
        BackupCodeStorage::Encrypted => symmetric_decrypt(secret, encoded)?,
    };
    serde_json::from_str(&json).map_err(|error| OpenAuthError::Api(error.to_string()))
}

pub fn consume_backup_code(codes: &[String], code: &str) -> Option<Vec<String>> {
    codes.iter().any(|candidate| candidate == code).then(|| {
        codes
            .iter()
            .filter(|candidate| *candidate != code)
            .cloned()
            .collect()
    })
}