use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use sha2::{Digest, Sha256};
pub fn generate_code_verifier() -> String {
use rand::Rng;
const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
let mut rng = rand::thread_rng();
(0..43)
.map(|_| {
let idx = rng.gen_range(0..CHARSET.len());
CHARSET[idx] as char
})
.collect()
}
pub(crate) fn generate_pkce_challenge(verifier: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(verifier);
URL_SAFE_NO_PAD.encode(hasher.finalize())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn verifier_is_43_chars_and_unreserved() {
let v = generate_code_verifier();
assert_eq!(v.len(), 43);
assert!(v.chars().all(|c| c.is_ascii_alphanumeric() || "-._~".contains(c)));
}
#[test]
fn challenge_matches_known_vector() {
let verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk";
assert_eq!(
generate_pkce_challenge(verifier),
"E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
);
}
}