synaps_cli/core/auth/
pkce.rs1use rand::Rng;
2use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
3use sha2::{Sha256, Digest};
4
5use super::{AUTHORIZE_URL, CLIENT_ID, SCOPES};
6
7pub fn generate_code_verifier() -> String {
9 let mut bytes = [0u8; 32];
10 rand::rng().fill(&mut bytes);
11 URL_SAFE_NO_PAD.encode(bytes)
12}
13
14pub fn generate_code_challenge(verifier: &str) -> String {
16 let mut hasher = Sha256::new();
17 hasher.update(verifier.as_bytes());
18 let hash = hasher.finalize();
19 URL_SAFE_NO_PAD.encode(hash)
20}
21
22pub fn generate_state() -> String {
24 let mut bytes = [0u8; 32];
25 rand::rng().fill(&mut bytes);
26 URL_SAFE_NO_PAD.encode(bytes)
27}
28
29pub fn build_auth_url(challenge: &str, state: &str, port: u16) -> String {
31 let redirect_uri = format!("http://localhost:{}/callback", port);
32 let params = [
33 ("code", "true"),
34 ("client_id", CLIENT_ID),
35 ("response_type", "code"),
36 ("redirect_uri", &redirect_uri),
37 ("scope", SCOPES),
38 ("code_challenge", challenge),
39 ("code_challenge_method", "S256"),
40 ("state", state),
41 ];
42
43 let query: String = params
44 .iter()
45 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
46 .collect::<Vec<_>>()
47 .join("&");
48
49 format!("{}?{}", AUTHORIZE_URL, query)
50}