use base64::Engine;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use sha2::{Digest, Sha256};
#[derive(Debug, Clone)]
pub struct Pkce {
pub verifier: String,
pub challenge: String,
}
pub fn generate() -> Pkce {
let mut raw = [0u8; 32];
getrandom::fill(&mut raw).expect("OS random number generator unavailable");
let verifier = URL_SAFE_NO_PAD.encode(raw);
let challenge = challenge_for(&verifier);
Pkce {
verifier,
challenge,
}
}
pub fn challenge_for(verifier: &str) -> String {
let digest = Sha256::digest(verifier.as_bytes());
URL_SAFE_NO_PAD.encode(digest)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn challenge_matches_rfc_test_vector() {
let verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk";
let expected = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM";
assert_eq!(challenge_for(verifier), expected);
}
#[test]
fn generate_roundtrips() {
let p = generate();
assert_eq!(challenge_for(&p.verifier), p.challenge);
assert!(!p.verifier.is_empty());
}
}