oauth2_baidu/
authorization_code_grant.rs1use oauth2_client::{
2 authorization_code_grant::provider_ext::AccessTokenRequestBody,
3 oauth2_core::access_token_request::GRANT_TYPE_WITH_AUTHORIZATION_CODE_GRANT,
4 re_exports::{
5 serde_qs, thiserror, Body, ClientId, ClientSecret, Deserialize, HttpError, RedirectUri,
6 Request, SerdeQsError, Serialize, Url, UrlParseError,
7 },
8 Provider, ProviderExtAuthorizationCodeGrant,
9};
10
11use crate::{BaiduScope, AUTHORIZATION_URL, TOKEN_URL};
12
13#[derive(Debug, Clone)]
14pub struct BaiduProviderWithWebApplication {
15 client_id: ClientId,
16 client_secret: ClientSecret,
17 redirect_uri: RedirectUri,
18 token_endpoint_url: Url,
20 authorization_endpoint_url: Url,
21}
22impl BaiduProviderWithWebApplication {
23 pub fn new(
24 app_key: ClientId,
25 secret_key: ClientSecret,
26 redirect_uri: RedirectUri,
27 ) -> Result<Self, UrlParseError> {
28 Ok(Self {
29 client_id: app_key,
30 client_secret: secret_key,
31 redirect_uri,
32 token_endpoint_url: TOKEN_URL.parse()?,
33 authorization_endpoint_url: AUTHORIZATION_URL.parse()?,
34 })
35 }
36}
37impl Provider for BaiduProviderWithWebApplication {
38 type Scope = BaiduScope;
39
40 fn client_id(&self) -> Option<&ClientId> {
41 Some(&self.client_id)
42 }
43
44 fn client_secret(&self) -> Option<&ClientSecret> {
45 Some(&self.client_secret)
46 }
47
48 fn token_endpoint_url(&self) -> &Url {
49 &self.token_endpoint_url
50 }
51}
52impl ProviderExtAuthorizationCodeGrant for BaiduProviderWithWebApplication {
53 fn redirect_uri(&self) -> Option<&RedirectUri> {
54 Some(&self.redirect_uri)
55 }
56
57 fn scopes_default(&self) -> Option<Vec<<Self as Provider>::Scope>> {
58 Some(vec![BaiduScope::Basic])
59 }
60
61 fn authorization_endpoint_url(&self) -> &Url {
62 &self.authorization_endpoint_url
63 }
64
65 fn access_token_request_rendering(
66 &self,
67 body: &AccessTokenRequestBody,
68 ) -> Option<Result<Request<Body>, Box<dyn std::error::Error + Send + Sync + 'static>>> {
69 fn doing(
70 this: &BaiduProviderWithWebApplication,
71 body: &AccessTokenRequestBody,
72 ) -> Result<Request<Body>, Box<dyn std::error::Error + Send + Sync + 'static>> {
73 let query = BaiduAccessTokenRequestBody {
74 grant_type: GRANT_TYPE_WITH_AUTHORIZATION_CODE_GRANT.to_owned(),
75 code: body.code.to_owned(),
76 client_id: this.client_id.to_string(),
77 client_secret: this.client_secret.to_string(),
78 redirect_uri: this.redirect_uri.to_string(),
79 };
80
81 let query_str = serde_qs::to_string(&query)
82 .map_err(AccessTokenRequestRenderingError::SerRequestQueryFailed)?;
83
84 let mut url = this.token_endpoint_url().to_owned();
85 url.set_query(Some(query_str.as_str()));
86
87 let request = Request::builder()
88 .uri(url.as_str())
89 .body(vec![])
90 .map_err(AccessTokenRequestRenderingError::MakeRequestFailed)?;
91
92 Ok(request)
93 }
94
95 Some(doing(self, body))
96 }
97}
98
99#[derive(Serialize, Deserialize)]
101pub struct BaiduAccessTokenRequestBody {
102 pub grant_type: String,
103 pub code: String,
104 pub client_id: String,
105 pub client_secret: String,
106 pub redirect_uri: String,
107}
108
109#[derive(thiserror::Error, Debug)]
110pub enum AccessTokenRequestRenderingError {
111 #[error("SerRequestQueryFailed {0}")]
112 SerRequestQueryFailed(SerdeQsError),
113 #[error("MakeRequestFailed {0}")]
114 MakeRequestFailed(HttpError),
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 use oauth2_client::{
122 authorization_code_grant::{AccessTokenEndpoint, AuthorizationEndpoint},
123 re_exports::{Endpoint as _, Response},
124 };
125
126 #[test]
127 fn authorization_request() -> Result<(), Box<dyn std::error::Error>> {
128 let provider = BaiduProviderWithWebApplication::new(
129 "APP_KEY".to_owned(),
130 "SECRET_KEY".to_owned(),
131 RedirectUri::new("https://client.example.com/cb")?,
132 )?;
133
134 let request =
135 AuthorizationEndpoint::new(&provider, vec![BaiduScope::Basic, BaiduScope::Netdisk])
136 .configure(|x| x.state = Some("STATE".to_owned()))
137 .render_request()?;
138
139 assert_eq!(request.uri(), "http://openapi.baidu.com/oauth/2.0/authorize?response_type=code&client_id=APP_KEY&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb&scope=basic+netdisk&state=STATE");
140
141 Ok(())
142 }
143
144 #[test]
145 fn access_token_request() -> Result<(), Box<dyn std::error::Error>> {
146 let provider = BaiduProviderWithWebApplication::new(
147 "APP_KEY".to_owned(),
148 "SECRET_KEY".to_owned(),
149 RedirectUri::new("https://client.example.com/cb")?,
150 )?;
151
152 let request = AccessTokenEndpoint::new(&provider, "CODE".to_owned()).render_request()?;
153
154 assert_eq!(request.uri().query().unwrap(), "grant_type=authorization_code&code=CODE&client_id=APP_KEY&client_secret=SECRET_KEY&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb");
155
156 Ok(())
157 }
158
159 #[test]
160 fn access_token_response() -> Result<(), Box<dyn std::error::Error>> {
161 let provider = BaiduProviderWithWebApplication::new(
162 "APP_KEY".to_owned(),
163 "SECRET_KEY".to_owned(),
164 RedirectUri::new("https://client.example.com/cb")?,
165 )?;
166
167 let response_body = include_str!(
168 "../tests/response_body_json_files/access_token_with_authorization_code_grant.json"
169 );
170 let body_ret = AccessTokenEndpoint::new(&provider, "CODE".to_owned())
171 .parse_response(Response::builder().body(response_body.as_bytes().to_vec())?)?;
172
173 match body_ret {
174 Ok(_body) => {}
175 Err(body) => panic!("{body:?}"),
176 }
177
178 Ok(())
179 }
180}