oauth/v2_0/
client.rs

1//! Client builder, used by other flows to send requests and build
2//! URLs.
3
4use std::ops::Deref;
5
6use oauth2::{
7    http::{Method, Response},
8    AuthUrl, ClientId, ClientSecret, EndpointNotSet, EndpointSet, HttpRequest, HttpResponse,
9    RedirectUrl, TokenUrl,
10};
11
12use super::{Error, Result};
13
14type BasicClient = oauth2::basic::BasicClient<
15    EndpointSet,
16    EndpointNotSet,
17    EndpointNotSet,
18    EndpointNotSet,
19    EndpointSet,
20>;
21
22/// Client builder, used by other flows to send requests and build
23/// URLs.
24#[derive(Clone, Debug)]
25pub struct Client {
26    inner: BasicClient,
27
28    /// Hostname of the client's redirection endpoint.
29    pub redirect_host: String,
30
31    /// Port of the client's redirection endpoint.
32    pub redirect_port: u16,
33}
34
35impl Client {
36    pub fn new(
37        client_id: impl ToString,
38        client_secret: Option<impl ToString>,
39        auth_url: impl ToString,
40        token_url: impl ToString,
41        redirect_scheme: impl ToString,
42        redirect_host: impl ToString,
43        redirect_port: impl Into<u16>,
44    ) -> Result<Self> {
45        let redirect_host = redirect_host.to_string();
46        let redirect_port = redirect_port.into();
47
48        let mut client = oauth2::basic::BasicClient::new(ClientId::new(client_id.to_string()))
49            .set_auth_uri(AuthUrl::new(auth_url.to_string()).map_err(Error::BuildAuthUrlError)?)
50            .set_token_uri(TokenUrl::new(token_url.to_string()).map_err(Error::BuildTokenUrlError)?)
51            .set_redirect_uri({
52                let scheme = redirect_scheme.to_string();
53                RedirectUrl::new(format!("{scheme}://{redirect_host}:{redirect_port}"))
54                    .map_err(Error::BuildRedirectUrlError)
55            }?);
56
57        if let Some(secret) = client_secret {
58            client = client.set_client_secret(ClientSecret::new(secret.to_string()));
59        }
60
61        Ok(Self {
62            inner: client,
63            redirect_host,
64            redirect_port,
65        })
66    }
67
68    pub(crate) async fn send_oauth2_request(oauth2_request: HttpRequest) -> Result<HttpResponse> {
69        let client = http::Client::new();
70
71        let response = client
72            .send(move |agent| match *oauth2_request.method() {
73                Method::GET => {
74                    let mut request = agent.get(&oauth2_request.uri().to_string());
75
76                    for (key, val) in oauth2_request.headers() {
77                        let Ok(val) = val.to_str() else {
78                            continue;
79                        };
80
81                        request = request.header(key, val);
82                    }
83
84                    Ok(request.call()?)
85                }
86                Method::POST => {
87                    let mut request = agent.post(&oauth2_request.uri().to_string());
88
89                    for (key, val) in oauth2_request.headers() {
90                        let Ok(val) = val.to_str() else {
91                            continue;
92                        };
93
94                        request = request.header(key, val);
95                    }
96
97                    Ok(request.send(oauth2_request.body())?)
98                }
99                _ => unreachable!(),
100            })
101            .await?;
102
103        let mut oauth2_response = Response::builder();
104
105        for (key, val) in response.headers() {
106            oauth2_response = oauth2_response.header(key, val);
107        }
108
109        let body = response
110            .into_body()
111            .read_to_vec()
112            .map_err(http::Error::from)?;
113
114        let oauth2_response = oauth2_response
115            .body(body)
116            .map_err(http::Error::from)
117            .map_err(Error::ReadResponseBodyError)?;
118
119        Ok(oauth2_response)
120    }
121}
122
123impl Deref for Client {
124    type Target = BasicClient;
125
126    fn deref(&self) -> &Self::Target {
127        &self.inner
128    }
129}