1use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
4use base64::Engine;
5use std::collections::HashMap;
6use std::error::Error;
7
8use crate::primitives::{OpenIDConfig, OpenIDTokenResponse, OpenIDUserInfo};
9
10impl OpenIDConfig {
11 pub async fn load_from_url(url: &str) -> Result<Self, Box<dyn Error>> {
13 Ok(reqwest::get(url).await?.json().await?)
14 }
15
16 pub fn gen_authorization_url(
18 &self,
19 client_id: &str,
20 state: &str,
21 redirect_uri: &str,
22 ) -> String {
23 let client_id = urlencoding::encode(client_id);
24 let state = urlencoding::encode(state);
25 let redirect_uri = urlencoding::encode(redirect_uri);
26
27 format!("{}?response_type=code&scope=openid%20profile%20email&client_id={client_id}&state={state}&redirect_uri={redirect_uri}", self.authorization_endpoint)
28 }
29
30 pub async fn request_token(
35 &self,
36 client_id: &str,
37 client_secret: &str,
38 code: &str,
39 redirect_uri: &str,
40 ) -> Result<(OpenIDTokenResponse, String), Box<dyn Error>> {
41 let authorization = BASE64_STANDARD.encode(format!("{}:{}", client_id, client_secret));
42
43 let mut params = HashMap::new();
44 params.insert("grant_type", "authorization_code");
45 params.insert("code", code);
46 params.insert("redirect_uri", redirect_uri);
47
48 let response = reqwest::Client::new()
49 .post(&self.token_endpoint)
50 .header("Authorization", format!("Basic {authorization}"))
51 .form(¶ms)
52 .send()
53 .await?
54 .text()
55 .await?;
56
57 Ok((serde_json::from_str(&response)?, response))
58 }
59
60 pub async fn request_user_info(
67 &self,
68 token: &OpenIDTokenResponse,
69 ) -> Result<(OpenIDUserInfo, String), Box<dyn Error>> {
70 let response = reqwest::Client::new()
71 .get(self.userinfo_endpoint.as_ref().expect(
72 "This client only support information retrieval through userinfo endpoint!",
73 ))
74 .header("Authorization", format!("Bearer {}", token.access_token))
75 .send()
76 .await?
77 .text()
78 .await?;
79
80 Ok((serde_json::from_str(&response)?, response))
81 }
82}