use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD}; use sha2::{Sha256, Digest}; const VERIFIER_LENGTH: usize = 64; pub struct CodeVerifier(String); impl CodeVerifier { pub fn new() -> Self { let bytes: Vec = (0..VERIFIER_LENGTH).map(|_| rand_byte()).collect(); CodeVerifier(URL_SAFE_NO_PAD.encode(&bytes)) } pub fn as_str(&self) -> &str { &self.0 } pub fn challenge(&self) -> CodeChallenge { let mut hasher = Sha256::new(); hasher.update(self.0.as_bytes()); let digest = hasher.finalize(); CodeChallenge(URL_SAFE_NO_PAD.encode(digest)) } } fn rand_byte() -> u8 { use std::time::{SystemTime, UNIX_EPOCH}; let nanos = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_else(|_| { eprintln!("[pkce] system time before UNIX_EPOCH, using 0 for random byte"); std::time::Duration::default() }) .subsec_nanos(); (nanos & 0xFF) as u8 } pub struct CodeChallenge(String); impl CodeChallenge { pub fn as_str(&self) -> &str { &self.0 } }