Menu
Public repository / 228b5716ba0b7bcc87cce261ea6df891133a35bb
AkurAI-CRM / crates/crm-api/src/auth/pkce.rs
Latest change 201b04d27b0773782f4a1a0558cee777ec8e3462 - Recover deployed web login and MCP group gate by Ólafur Búi Ólafsson
//! Random tokens and RFC 7636 PKCE (S256) for the login flow.
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use base64::Engine as _;
use ring::digest::{digest, SHA256};
use std::io::Read;
/// 32 random bytes from the OS CSPRNG, base64url-encoded (43 chars).
pub fn random_token() -> Result<String, String> {
let mut bytes = [0_u8; 32];
std::fs::File::open("/dev/urandom")
.map_err(|error| format!("csprng unavailable: {error}"))?
.read_exact(&mut bytes)
.map_err(|error| format!("csprng read failed: {error}"))?;
Ok(URL_SAFE_NO_PAD.encode(bytes))
}
/// `BASE64URL(SHA256(verifier))`.
pub fn s256_challenge(verifier: &str) -> String {
URL_SAFE_NO_PAD.encode(digest(&SHA256, verifier.as_bytes()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn challenge_matches_rfc7636_appendix_b() {
assert_eq!(
s256_challenge("dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"),
"E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
);
}
#[test]
fn random_tokens_are_long_and_distinct() {
let first = random_token().unwrap();
assert_eq!(first.len(), 43);
assert_ne!(first, random_token().unwrap());
}
}