use crate::Result;
use base64::engine::general_purpose::STANDARD as BASE64;
use base64::Engine;
use chrono::{DateTime, Duration, Utc};
use rand::RngCore;
pub const MIN_CHALLENGE_BYTES: usize = 32;
pub fn generate_challenge(expires_in: Duration) -> Result<(String, DateTime<Utc>)> {
let now = Utc::now();
let expires_at = now + expires_in;
let timestamp = now.to_rfc3339();
let mut random_bytes = vec![0u8; MIN_CHALLENGE_BYTES];
rand::thread_rng().fill_bytes(&mut random_bytes);
let mut challenge_data = timestamp.as_bytes().to_vec();
challenge_data.push(b'|');
challenge_data.extend_from_slice(&random_bytes);
let challenge = BASE64.encode(&challenge_data);
Ok((challenge, expires_at))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_challenges_are_unique() {
let (challenge1, _) = generate_challenge(Duration::minutes(30)).unwrap();
let (challenge2, _) = generate_challenge(Duration::minutes(30)).unwrap();
assert_ne!(challenge1, challenge2);
}
}