2026-07-11 18:23:01 +07:00
|
|
|
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<u8> = (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)
|
2026-07-12 10:50:34 +07:00
|
|
|
.unwrap_or_else(|_| {
|
|
|
|
|
eprintln!("[pkce] system time before UNIX_EPOCH, using 0 for random byte");
|
|
|
|
|
std::time::Duration::default()
|
|
|
|
|
})
|
2026-07-11 18:23:01 +07:00
|
|
|
.subsec_nanos();
|
|
|
|
|
(nanos & 0xFF) as u8
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct CodeChallenge(String);
|
|
|
|
|
|
|
|
|
|
impl CodeChallenge {
|
|
|
|
|
pub fn as_str(&self) -> &str {
|
|
|
|
|
&self.0
|
|
|
|
|
}
|
|
|
|
|
}
|