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