oauth2_client/authorization_code_grant/
flow.rs1use http_api_client::{Client, ClientRespondEndpointError};
2use http_api_client_endpoint::Endpoint as _;
3use oauth2_core::{
4 authorization_code_grant::{
5 access_token_response::{
6 ErrorBody as AT_RES_ErrorBody, SuccessfulBody as AT_RES_SuccessfulBody,
7 },
8 authorization_response::ErrorQuery as A_RES_ErrorQuery,
9 },
10 serde::{de::DeserializeOwned, Serialize},
11 types::{Code, CodeChallenge, CodeChallengeMethod, CodeVerifier, Nonce, Scope, State},
12 url::{ParseError as UrlParseError, Url},
13};
14
15use crate::ProviderExtAuthorizationCodeGrant;
16
17use super::{
18 parse_redirect_uri_query, AccessTokenEndpoint, AccessTokenEndpointError, AuthorizationEndpoint,
19 AuthorizationEndpointError, ParseRedirectUriQueryError,
20};
21
22#[derive(Debug, Clone)]
26pub struct Flow<C>
27where
28 C: Client,
29{
30 pub client_with_token: C,
31}
32impl<C> Flow<C>
33where
34 C: Client,
35{
36 pub fn new(client_with_token: C) -> Self {
37 Self { client_with_token }
38 }
39}
40
41impl<'a, C> Flow<C>
42where
43 C: Client,
44{
45 pub fn build_authorization_url<SCOPE>(
47 &self,
48 provider: &'a dyn ProviderExtAuthorizationCodeGrant<Scope = SCOPE>,
49 scopes: impl Into<Option<Vec<SCOPE>>>,
50 config: impl Into<Option<FlowBuildAuthorizationUrlConfiguration>>,
51 ) -> Result<Url, FlowBuildAuthorizationUrlError>
52 where
53 SCOPE: Scope + Serialize,
54 {
55 build_authorization_url(provider, scopes, config)
57 }
58}
59
60impl<C> Flow<C>
61where
62 C: Client + Send + Sync,
63{
64 pub async fn handle_callback_by_query<SCOPE>(
65 &self,
66 provider: &(dyn ProviderExtAuthorizationCodeGrant<Scope = SCOPE> + Send + Sync),
67 query: impl AsRef<str>,
68 config: impl Into<Option<FlowHandleCallbackByQueryConfiguration>>,
69 ) -> Result<AT_RES_SuccessfulBody<SCOPE>, FlowHandleCallbackError>
70 where
71 SCOPE: Scope + Serialize + DeserializeOwned + Send + Sync,
72 {
73 let query = parse_redirect_uri_query(query.as_ref())
75 .map_err(FlowHandleCallbackError::ParseRedirectUriQueryError)?;
76
77 let query = query.map_err(FlowHandleCallbackError::AuthorizationFailed)?;
78
79 let config: FlowHandleCallbackByQueryConfiguration = config.into().unwrap_or_default();
80
81 if let Some(ref state) = &config.state {
82 if let Some(query_state) = &query.state {
83 if state != query_state {
84 return Err(FlowHandleCallbackError::StateMismatch);
85 }
86 } else {
87 return Err(FlowHandleCallbackError::StateMissing);
88 }
89 }
90
91 self.handle_callback(provider, query.code, Some(config.into()))
92 .await
93 }
94
95 pub async fn handle_callback<SCOPE>(
96 &self,
97 provider: &(dyn ProviderExtAuthorizationCodeGrant<Scope = SCOPE> + Send + Sync),
98 code: Code,
99 config: impl Into<Option<FlowHandleCallbackConfiguration>>,
100 ) -> Result<AT_RES_SuccessfulBody<SCOPE>, FlowHandleCallbackError>
101 where
102 SCOPE: Scope + Serialize + DeserializeOwned + Send + Sync,
103 {
104 let config: FlowHandleCallbackConfiguration = config.into().unwrap_or_default();
105
106 let mut access_token_endpoint = AccessTokenEndpoint::new(provider, code);
107
108 if let Some(code_verifier) = &config.code_verifier {
109 access_token_endpoint.set_code_verifier(code_verifier.to_owned());
110 }
111
112 let access_token_ret = self
113 .client_with_token
114 .respond_endpoint(&access_token_endpoint)
115 .await
116 .map_err(|err| match err {
117 ClientRespondEndpointError::RespondFailed(err) => {
118 FlowHandleCallbackError::AccessTokenEndpointRespondFailed(Box::new(err))
119 }
120 ClientRespondEndpointError::EndpointRenderRequestFailed(err) => {
121 FlowHandleCallbackError::AccessTokenEndpointError(err)
122 }
123 ClientRespondEndpointError::EndpointParseResponseFailed(err) => {
124 FlowHandleCallbackError::AccessTokenEndpointError(err)
125 }
126 })?;
127
128 let access_token_successful_body =
129 access_token_ret.map_err(FlowHandleCallbackError::AccessTokenFailed)?;
130
131 Ok(access_token_successful_body)
132 }
133}
134
135#[derive(thiserror::Error, Debug)]
136pub enum FlowHandleCallbackError {
137 #[error("ParseRedirectUriQueryError {0}")]
138 ParseRedirectUriQueryError(ParseRedirectUriQueryError),
139 #[error("AuthorizationFailed {0:?}")]
141 AuthorizationFailed(A_RES_ErrorQuery),
142 #[error("StateMismatch")]
143 StateMismatch,
144 #[error("StateMissing")]
145 StateMissing,
146 #[error("AccessTokenEndpointRespondFailed {0}")]
148 AccessTokenEndpointRespondFailed(Box<dyn std::error::Error + Send + Sync>),
149 #[error("AccessTokenEndpointError {0}")]
150 AccessTokenEndpointError(AccessTokenEndpointError),
151 #[error("AccessTokenFailed {0:?}")]
152 AccessTokenFailed(AT_RES_ErrorBody),
153}
154
155#[derive(Debug, Clone, Default)]
159pub struct FlowBuildAuthorizationUrlConfiguration {
160 pub state: Option<State>,
161 pub code_challenge: Option<(CodeChallenge, CodeChallengeMethod)>,
162 pub nonce: Option<Nonce>,
163}
164impl FlowBuildAuthorizationUrlConfiguration {
165 pub fn new() -> Self {
166 Self::default()
167 }
168
169 pub fn configure<F>(mut self, mut f: F) -> Self
170 where
171 F: FnMut(&mut Self),
172 {
173 f(&mut self);
174 self
175 }
176
177 pub fn set_state(&mut self, state: State) {
178 self.state = Some(state);
179 }
180
181 pub fn set_code_challenge(
182 &mut self,
183 code_challenge: CodeChallenge,
184 code_challenge_method: CodeChallengeMethod,
185 ) {
186 self.code_challenge = Some((code_challenge, code_challenge_method));
187 }
188
189 pub fn set_nonce(&mut self, nonce: Nonce) {
190 self.nonce = Some(nonce);
191 }
192}
193
194#[derive(Debug, Clone, Default)]
196pub struct FlowHandleCallbackByQueryConfiguration {
197 pub state: Option<State>,
198 pub code_verifier: Option<CodeVerifier>,
199}
200impl FlowHandleCallbackByQueryConfiguration {
201 pub fn new() -> Self {
202 Self::default()
203 }
204
205 pub fn configure<F>(mut self, mut f: F) -> Self
206 where
207 F: FnMut(&mut Self),
208 {
209 f(&mut self);
210 self
211 }
212
213 pub fn set_state(&mut self, state: State) {
214 self.state = Some(state);
215 }
216
217 pub fn set_code_verifier(&mut self, code_verifier: CodeVerifier) {
218 self.code_verifier = Some(code_verifier);
219 }
220}
221
222#[derive(Debug, Clone, Default)]
224pub struct FlowHandleCallbackConfiguration {
225 pub code_verifier: Option<CodeVerifier>,
226}
227impl FlowHandleCallbackConfiguration {
228 pub fn new() -> Self {
229 Self::default()
230 }
231
232 pub fn configure<F>(mut self, mut f: F) -> Self
233 where
234 F: FnMut(&mut Self),
235 {
236 f(&mut self);
237 self
238 }
239
240 pub fn set_code_verifier(&mut self, code_verifier: CodeVerifier) {
241 self.code_verifier = Some(code_verifier);
242 }
243}
244
245impl From<FlowHandleCallbackByQueryConfiguration> for FlowHandleCallbackConfiguration {
246 fn from(c: FlowHandleCallbackByQueryConfiguration) -> Self {
247 Self {
248 code_verifier: c.code_verifier,
249 }
250 }
251}
252
253pub fn build_authorization_url<SCOPE>(
257 provider: &dyn ProviderExtAuthorizationCodeGrant<Scope = SCOPE>,
258 scopes: impl Into<Option<Vec<SCOPE>>>,
259 config: impl Into<Option<FlowBuildAuthorizationUrlConfiguration>>,
260) -> Result<Url, FlowBuildAuthorizationUrlError>
261where
262 SCOPE: Scope + Serialize,
263{
264 let scopes = scopes.into().or_else(|| provider.scopes_default());
265
266 let config: FlowBuildAuthorizationUrlConfiguration = config.into().unwrap_or_default();
267
268 let mut authorization_endpoint = AuthorizationEndpoint::new(provider, scopes);
269
270 if let Some(state) = &config.state {
271 authorization_endpoint.set_state(state.to_owned());
272 }
273
274 if let Some((code_challenge, code_challenge_method)) = &config.code_challenge {
275 authorization_endpoint
276 .set_code_challenge(code_challenge.to_owned(), code_challenge_method.to_owned());
277 }
278
279 if let Some(nonce) = &config.nonce {
280 authorization_endpoint.set_nonce(nonce.to_owned());
281 }
282
283 let authorization_endpoint_request = authorization_endpoint
284 .render_request()
285 .map_err(FlowBuildAuthorizationUrlError::AuthorizationEndpointError)?;
286
287 let url = authorization_endpoint_request.uri();
288
289 let url = Url::parse(url.to_string().as_str())
290 .map_err(FlowBuildAuthorizationUrlError::ToUrlFailed)?;
291
292 Ok(url)
293}
294
295#[derive(thiserror::Error, Debug)]
296pub enum FlowBuildAuthorizationUrlError {
297 #[error("AuthorizationEndpointError {0}")]
298 AuthorizationEndpointError(AuthorizationEndpointError),
299 #[error("ToUrlFailed {0}")]
300 ToUrlFailed(UrlParseError),
301}