1use crate::errors::EsiResult;
2use base64::engine::{general_purpose::URL_SAFE_NO_PAD as base64, Engine};
3use rand::random;
4use sha2::{Digest, Sha256};
5
6pub type PkceVerifier = String;
9pub type PkceChallenge = String;
10
11pub struct Pkce {
12 pub challenge: PkceChallenge,
13 pub verifier: PkceVerifier,
14}
15
16pub fn generate() -> EsiResult<Pkce> {
17 let verifier: Vec<u8> = (0..32).map(|_| random()).collect();
18
19 let verifier_base64 = base64url(&verifier);
20
21 let mut hasher = Sha256::new();
23 hasher.update(&verifier_base64);
24 let challenge = hasher.finalize();
25
26 let challenge_base64 = base64url(&challenge.to_vec());
28 Ok(Pkce {
29 challenge: challenge_base64,
30 verifier: verifier_base64,
31 })
32}
33
34pub fn base64url(verifier: &Vec<u8>) -> String {
35 let mut verifier_out = vec![0; verifier.len() * 4 / 3 + 4];
36 let written_size = base64.encode_slice(verifier, &mut verifier_out).unwrap();
37 verifier_out.truncate(written_size);
38 String::from_utf8(verifier_out).unwrap()
39}