use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
#[derive(Debug, Clone)]
pub enum Pkce {
S256(PkceSha256),
Plain(String),
}
impl Pkce {
pub fn code_verifier(&self) -> &str {
match self {
Pkce::S256(pkce) => &pkce.code_verifier,
Pkce::Plain(code_verifier) => code_verifier,
}
}
pub fn code_challenge(&self) -> &str {
match self {
Pkce::S256(pkce) => &pkce.code_challenge,
Pkce::Plain(code_challenge) => code_challenge,
}
}
pub fn code_challenge_method(&self) -> &str {
match self {
Pkce::S256(_) => "S256",
Pkce::Plain(_) => "plain",
}
}
}
#[derive(Debug, Clone)]
pub struct PkceSha256 {
pub code_verifier: String,
pub code_challenge: String,
}
impl PkceSha256 {
pub fn replicate(code_verifier: String) -> Self {
let code_challenge = generate_s256_code_challenge(&code_verifier);
PkceSha256 {
code_verifier,
code_challenge,
}
}
pub fn generate() -> Self {
let code_verifier = generate_s256_code_verifier();
Self::replicate(code_verifier)
}
}
pub fn generate_s256_pkce() -> Pkce {
Pkce::S256(PkceSha256::generate())
}
fn generate_s256_code_verifier() -> String {
let mut bytes = [0u8; 32];
getrandom::fill(&mut bytes).unwrap();
URL_SAFE_NO_PAD.encode(bytes)
}
fn generate_s256_code_challenge(code_verifier: &str) -> String {
URL_SAFE_NO_PAD.encode(hmac_sha256::Hash::hash(code_verifier.as_bytes()))
}