rustauth_oauth/oauth2/
utils.rs1use base64::engine::general_purpose::URL_SAFE_NO_PAD;
2use base64::Engine;
3use sha2::{Digest, Sha256};
4
5use super::error::OAuthError;
6
7pub use super::tokens::{get_oauth2_tokens, get_primary_client_id};
8
9const CODE_VERIFIER_MIN_LEN: usize = 43;
11const CODE_VERIFIER_MAX_LEN: usize = 128;
12
13pub fn validate_code_verifier(code_verifier: &str) -> Result<(), OAuthError> {
19 let len = code_verifier.len();
20 if !(CODE_VERIFIER_MIN_LEN..=CODE_VERIFIER_MAX_LEN).contains(&len) {
21 return Err(OAuthError::InvalidCodeVerifier(format!(
22 "length {len} is outside the RFC 7636 range of \
23 {CODE_VERIFIER_MIN_LEN}-{CODE_VERIFIER_MAX_LEN} characters"
24 )));
25 }
26 if !code_verifier
27 .bytes()
28 .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'.' | b'_' | b'~'))
29 {
30 return Err(OAuthError::InvalidCodeVerifier(
31 "only RFC 7636 unreserved characters (A-Z, a-z, 0-9, -, ., _, ~) are allowed"
32 .to_owned(),
33 ));
34 }
35 Ok(())
36}
37
38pub fn generate_code_challenge(code_verifier: &str) -> Result<String, OAuthError> {
39 validate_code_verifier(code_verifier)?;
40 let hash = Sha256::digest(code_verifier.as_bytes());
41 Ok(URL_SAFE_NO_PAD.encode(hash))
42}