1use crate::endpoint::{endpoint_request, endpoint_response};
2use crate::{
3 AccessToken, AsyncHttpClient, AuthType, AuthorizationCode, Client, ClientId, ClientSecret,
4 EndpointState, ErrorResponse, HttpRequest, PkceCodeVerifier, RedirectUrl, RefreshToken,
5 RequestTokenError, ResourceOwnerPassword, ResourceOwnerUsername, RevocableToken, Scope,
6 SyncHttpClient, TokenIntrospectionResponse, TokenUrl,
7};
8
9use serde::de::DeserializeOwned;
10use serde::{Deserialize, Serialize};
11
12use std::borrow::Cow;
13use std::error::Error;
14use std::fmt::Debug;
15use std::future::Future;
16use std::marker::PhantomData;
17use std::time::Duration;
18
19#[cfg(test)]
20mod tests;
21
22impl<
23 TE,
24 TR,
25 TIR,
26 RT,
27 TRE,
28 HasAuthUrl,
29 HasDeviceAuthUrl,
30 HasIntrospectionUrl,
31 HasRevocationUrl,
32 HasTokenUrl,
33 >
34 Client<
35 TE,
36 TR,
37 TIR,
38 RT,
39 TRE,
40 HasAuthUrl,
41 HasDeviceAuthUrl,
42 HasIntrospectionUrl,
43 HasRevocationUrl,
44 HasTokenUrl,
45 >
46where
47 TE: ErrorResponse + 'static,
48 TR: TokenResponse,
49 TIR: TokenIntrospectionResponse,
50 RT: RevocableToken,
51 TRE: ErrorResponse + 'static,
52 HasAuthUrl: EndpointState,
53 HasDeviceAuthUrl: EndpointState,
54 HasIntrospectionUrl: EndpointState,
55 HasRevocationUrl: EndpointState,
56 HasTokenUrl: EndpointState,
57{
58 pub(crate) fn exchange_client_credentials_impl<'a>(
59 &'a self,
60 token_url: &'a TokenUrl,
61 ) -> ClientCredentialsTokenRequest<'a, TE, TR> {
62 ClientCredentialsTokenRequest {
63 auth_type: &self.auth_type,
64 client_id: &self.client_id,
65 client_secret: self.client_secret.as_ref(),
66 extra_params: Vec::new(),
67 scopes: Vec::new(),
68 token_url,
69 _phantom: PhantomData,
70 }
71 }
72
73 pub(crate) fn exchange_code_impl<'a>(
74 &'a self,
75 token_url: &'a TokenUrl,
76 code: AuthorizationCode,
77 ) -> CodeTokenRequest<'a, TE, TR> {
78 CodeTokenRequest {
79 auth_type: &self.auth_type,
80 client_id: &self.client_id,
81 client_secret: self.client_secret.as_ref(),
82 code,
83 extra_params: Vec::new(),
84 pkce_verifier: None,
85 token_url,
86 redirect_url: self.redirect_url.as_ref().map(Cow::Borrowed),
87 _phantom: PhantomData,
88 }
89 }
90
91 pub(crate) fn exchange_password_impl<'a>(
92 &'a self,
93 token_url: &'a TokenUrl,
94 username: &'a ResourceOwnerUsername,
95 password: &'a ResourceOwnerPassword,
96 ) -> PasswordTokenRequest<'a, TE, TR> {
97 PasswordTokenRequest {
98 auth_type: &self.auth_type,
99 client_id: &self.client_id,
100 client_secret: self.client_secret.as_ref(),
101 username,
102 password,
103 extra_params: Vec::new(),
104 scopes: Vec::new(),
105 token_url,
106 _phantom: PhantomData,
107 }
108 }
109
110 pub(crate) fn exchange_refresh_token_impl<'a>(
111 &'a self,
112 token_url: &'a TokenUrl,
113 refresh_token: &'a RefreshToken,
114 ) -> RefreshTokenRequest<'a, TE, TR> {
115 RefreshTokenRequest {
116 auth_type: &self.auth_type,
117 client_id: &self.client_id,
118 client_secret: self.client_secret.as_ref(),
119 extra_params: Vec::new(),
120 refresh_token,
121 scopes: Vec::new(),
122 token_url,
123 _phantom: PhantomData,
124 }
125 }
126}
127
128#[derive(Debug)]
132pub struct CodeTokenRequest<'a, TE, TR>
133where
134 TE: ErrorResponse,
135 TR: TokenResponse,
136{
137 pub(crate) auth_type: &'a AuthType,
138 pub(crate) client_id: &'a ClientId,
139 pub(crate) client_secret: Option<&'a ClientSecret>,
140 pub(crate) code: AuthorizationCode,
141 pub(crate) extra_params: Vec<(Cow<'a, str>, Cow<'a, str>)>,
142 pub(crate) pkce_verifier: Option<PkceCodeVerifier>,
143 pub(crate) token_url: &'a TokenUrl,
144 pub(crate) redirect_url: Option<Cow<'a, RedirectUrl>>,
145 pub(crate) _phantom: PhantomData<(TE, TR)>,
146}
147impl<'a, TE, TR> CodeTokenRequest<'a, TE, TR>
148where
149 TE: ErrorResponse + 'static,
150 TR: TokenResponse,
151{
152 pub fn add_extra_param<N, V>(mut self, name: N, value: V) -> Self
166 where
167 N: Into<Cow<'a, str>>,
168 V: Into<Cow<'a, str>>,
169 {
170 self.extra_params.push((name.into(), value.into()));
171 self
172 }
173
174 pub fn set_pkce_verifier(mut self, pkce_verifier: PkceCodeVerifier) -> Self {
180 self.pkce_verifier = Some(pkce_verifier);
181 self
182 }
183
184 pub fn set_redirect_uri(mut self, redirect_url: Cow<'a, RedirectUrl>) -> Self {
186 self.redirect_url = Some(redirect_url);
187 self
188 }
189
190 fn prepare_request<RE>(self) -> Result<HttpRequest, RequestTokenError<RE, TE>>
191 where
192 RE: Error + 'static,
193 {
194 let mut params = vec![
195 ("grant_type", "authorization_code"),
196 ("code", self.code.secret()),
197 ];
198 if let Some(ref pkce_verifier) = self.pkce_verifier {
199 params.push(("code_verifier", pkce_verifier.secret()));
200 }
201
202 endpoint_request(
203 self.auth_type,
204 self.client_id,
205 self.client_secret,
206 &self.extra_params,
207 self.redirect_url,
208 None,
209 self.token_url.url(),
210 params,
211 )
212 .map_err(|err| RequestTokenError::Other(format!("failed to prepare request: {err}")))
213 }
214
215 pub fn request<C>(
217 self,
218 http_client: &C,
219 ) -> Result<TR, RequestTokenError<<C as SyncHttpClient>::Error, TE>>
220 where
221 C: SyncHttpClient,
222 {
223 endpoint_response(http_client.call(self.prepare_request()?)?)
224 }
225
226 pub fn request_async<'c, C>(
228 self,
229 http_client: &'c C,
230 ) -> impl Future<Output = Result<TR, RequestTokenError<<C as AsyncHttpClient<'c>>::Error, TE>>> + 'c
231 where
232 Self: 'c,
233 C: AsyncHttpClient<'c>,
234 {
235 Box::pin(async move { endpoint_response(http_client.call(self.prepare_request()?).await?) })
236 }
237}
238
239#[derive(Debug)]
243pub struct RefreshTokenRequest<'a, TE, TR>
244where
245 TE: ErrorResponse,
246 TR: TokenResponse,
247{
248 pub(crate) auth_type: &'a AuthType,
249 pub(crate) client_id: &'a ClientId,
250 pub(crate) client_secret: Option<&'a ClientSecret>,
251 pub(crate) extra_params: Vec<(Cow<'a, str>, Cow<'a, str>)>,
252 pub(crate) refresh_token: &'a RefreshToken,
253 pub(crate) scopes: Vec<Cow<'a, Scope>>,
254 pub(crate) token_url: &'a TokenUrl,
255 pub(crate) _phantom: PhantomData<(TE, TR)>,
256}
257impl<'a, TE, TR> RefreshTokenRequest<'a, TE, TR>
258where
259 TE: ErrorResponse + 'static,
260 TR: TokenResponse,
261{
262 pub fn add_extra_param<N, V>(mut self, name: N, value: V) -> Self
276 where
277 N: Into<Cow<'a, str>>,
278 V: Into<Cow<'a, str>>,
279 {
280 self.extra_params.push((name.into(), value.into()));
281 self
282 }
283
284 pub fn add_scope(mut self, scope: Scope) -> Self {
286 self.scopes.push(Cow::Owned(scope));
287 self
288 }
289
290 pub fn add_scopes<I>(mut self, scopes: I) -> Self
292 where
293 I: IntoIterator<Item = Scope>,
294 {
295 self.scopes.extend(scopes.into_iter().map(Cow::Owned));
296 self
297 }
298
299 pub fn request<C>(
301 self,
302 http_client: &C,
303 ) -> Result<TR, RequestTokenError<<C as SyncHttpClient>::Error, TE>>
304 where
305 C: SyncHttpClient,
306 {
307 endpoint_response(http_client.call(self.prepare_request()?)?)
308 }
309 pub fn request_async<'c, C>(
311 self,
312 http_client: &'c C,
313 ) -> impl Future<Output = Result<TR, RequestTokenError<<C as AsyncHttpClient<'c>>::Error, TE>>> + 'c
314 where
315 Self: 'c,
316 C: AsyncHttpClient<'c>,
317 {
318 Box::pin(async move { endpoint_response(http_client.call(self.prepare_request()?).await?) })
319 }
320
321 fn prepare_request<RE>(&self) -> Result<HttpRequest, RequestTokenError<RE, TE>>
322 where
323 RE: Error + 'static,
324 {
325 endpoint_request(
326 self.auth_type,
327 self.client_id,
328 self.client_secret,
329 &self.extra_params,
330 None,
331 Some(&self.scopes),
332 self.token_url.url(),
333 vec![
334 ("grant_type", "refresh_token"),
335 ("refresh_token", self.refresh_token.secret()),
336 ],
337 )
338 .map_err(|err| RequestTokenError::Other(format!("failed to prepare request: {err}")))
339 }
340}
341
342#[derive(Debug)]
346pub struct PasswordTokenRequest<'a, TE, TR>
347where
348 TE: ErrorResponse,
349 TR: TokenResponse,
350{
351 pub(crate) auth_type: &'a AuthType,
352 pub(crate) client_id: &'a ClientId,
353 pub(crate) client_secret: Option<&'a ClientSecret>,
354 pub(crate) extra_params: Vec<(Cow<'a, str>, Cow<'a, str>)>,
355 pub(crate) username: &'a ResourceOwnerUsername,
356 pub(crate) password: &'a ResourceOwnerPassword,
357 pub(crate) scopes: Vec<Cow<'a, Scope>>,
358 pub(crate) token_url: &'a TokenUrl,
359 pub(crate) _phantom: PhantomData<(TE, TR)>,
360}
361impl<'a, TE, TR> PasswordTokenRequest<'a, TE, TR>
362where
363 TE: ErrorResponse + 'static,
364 TR: TokenResponse,
365{
366 pub fn add_extra_param<N, V>(mut self, name: N, value: V) -> Self
380 where
381 N: Into<Cow<'a, str>>,
382 V: Into<Cow<'a, str>>,
383 {
384 self.extra_params.push((name.into(), value.into()));
385 self
386 }
387
388 pub fn add_scope(mut self, scope: Scope) -> Self {
390 self.scopes.push(Cow::Owned(scope));
391 self
392 }
393
394 pub fn add_scopes<I>(mut self, scopes: I) -> Self
396 where
397 I: IntoIterator<Item = Scope>,
398 {
399 self.scopes.extend(scopes.into_iter().map(Cow::Owned));
400 self
401 }
402
403 pub fn request<C>(
405 self,
406 http_client: &C,
407 ) -> Result<TR, RequestTokenError<<C as SyncHttpClient>::Error, TE>>
408 where
409 C: SyncHttpClient,
410 {
411 endpoint_response(http_client.call(self.prepare_request()?)?)
412 }
413
414 pub fn request_async<'c, C>(
416 self,
417 http_client: &'c C,
418 ) -> impl Future<Output = Result<TR, RequestTokenError<<C as AsyncHttpClient<'c>>::Error, TE>>> + 'c
419 where
420 Self: 'c,
421 C: AsyncHttpClient<'c>,
422 {
423 Box::pin(async move { endpoint_response(http_client.call(self.prepare_request()?).await?) })
424 }
425
426 fn prepare_request<RE>(&self) -> Result<HttpRequest, RequestTokenError<RE, TE>>
427 where
428 RE: Error + 'static,
429 {
430 endpoint_request(
431 self.auth_type,
432 self.client_id,
433 self.client_secret,
434 &self.extra_params,
435 None,
436 Some(&self.scopes),
437 self.token_url.url(),
438 vec![
439 ("grant_type", "password"),
440 ("username", self.username),
441 ("password", self.password.secret()),
442 ],
443 )
444 .map_err(|err| RequestTokenError::Other(format!("failed to prepare request: {err}")))
445 }
446}
447
448#[derive(Debug)]
452pub struct ClientCredentialsTokenRequest<'a, TE, TR>
453where
454 TE: ErrorResponse,
455 TR: TokenResponse,
456{
457 pub(crate) auth_type: &'a AuthType,
458 pub(crate) client_id: &'a ClientId,
459 pub(crate) client_secret: Option<&'a ClientSecret>,
460 pub(crate) extra_params: Vec<(Cow<'a, str>, Cow<'a, str>)>,
461 pub(crate) scopes: Vec<Cow<'a, Scope>>,
462 pub(crate) token_url: &'a TokenUrl,
463 pub(crate) _phantom: PhantomData<(TE, TR)>,
464}
465impl<'a, TE, TR> ClientCredentialsTokenRequest<'a, TE, TR>
466where
467 TE: ErrorResponse + 'static,
468 TR: TokenResponse,
469{
470 pub fn add_extra_param<N, V>(mut self, name: N, value: V) -> Self
484 where
485 N: Into<Cow<'a, str>>,
486 V: Into<Cow<'a, str>>,
487 {
488 self.extra_params.push((name.into(), value.into()));
489 self
490 }
491
492 pub fn add_scope(mut self, scope: Scope) -> Self {
494 self.scopes.push(Cow::Owned(scope));
495 self
496 }
497
498 pub fn add_scopes<I>(mut self, scopes: I) -> Self
500 where
501 I: IntoIterator<Item = Scope>,
502 {
503 self.scopes.extend(scopes.into_iter().map(Cow::Owned));
504 self
505 }
506
507 pub fn request<C>(
509 self,
510 http_client: &C,
511 ) -> Result<TR, RequestTokenError<<C as SyncHttpClient>::Error, TE>>
512 where
513 C: SyncHttpClient,
514 {
515 endpoint_response(http_client.call(self.prepare_request()?)?)
516 }
517
518 pub fn request_async<'c, C>(
520 self,
521 http_client: &'c C,
522 ) -> impl Future<Output = Result<TR, RequestTokenError<<C as AsyncHttpClient<'c>>::Error, TE>>> + 'c
523 where
524 Self: 'c,
525 C: AsyncHttpClient<'c>,
526 {
527 Box::pin(async move { endpoint_response(http_client.call(self.prepare_request()?).await?) })
528 }
529
530 fn prepare_request<RE>(&self) -> Result<HttpRequest, RequestTokenError<RE, TE>>
531 where
532 RE: Error + 'static,
533 {
534 endpoint_request(
535 self.auth_type,
536 self.client_id,
537 self.client_secret,
538 &self.extra_params,
539 None,
540 Some(&self.scopes),
541 self.token_url.url(),
542 vec![("grant_type", "client_credentials")],
543 )
544 .map_err(|err| RequestTokenError::Other(format!("failed to prepare request: {err}")))
545 }
546}
547
548pub trait TokenType: Clone + DeserializeOwned + Debug + PartialEq + Serialize {}
550
551pub trait ExtraTokenFields: DeserializeOwned + Debug + Serialize {}
553
554#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
556pub struct EmptyExtraTokenFields {}
557impl ExtraTokenFields for EmptyExtraTokenFields {}
558
559pub trait TokenResponse: Debug + DeserializeOwned + Serialize {
566 type TokenType: TokenType;
568
569 fn access_token(&self) -> &AccessToken;
571 fn token_type(&self) -> &Self::TokenType;
575 fn expires_in(&self) -> Option<Duration>;
580 fn refresh_token(&self) -> Option<&RefreshToken>;
584 fn scopes(&self) -> Option<&Vec<Scope>>;
590}
591
592#[derive(Clone, Debug, Deserialize, Serialize)]
598pub struct StandardTokenResponse<EF, TT>
599where
600 EF: ExtraTokenFields,
601 TT: TokenType,
602{
603 access_token: AccessToken,
604 #[serde(bound = "TT: TokenType")]
605 #[serde(deserialize_with = "crate::helpers::deserialize_untagged_enum_case_insensitive")]
606 token_type: TT,
607 #[serde(skip_serializing_if = "Option::is_none")]
608 expires_in: Option<u64>,
609 #[serde(skip_serializing_if = "Option::is_none")]
610 refresh_token: Option<RefreshToken>,
611 #[serde(rename = "scope")]
612 #[serde(deserialize_with = "crate::helpers::deserialize_space_delimited_vec")]
613 #[serde(serialize_with = "crate::helpers::serialize_space_delimited_vec")]
614 #[serde(skip_serializing_if = "Option::is_none")]
615 #[serde(default)]
616 scopes: Option<Vec<Scope>>,
617
618 #[serde(bound = "EF: ExtraTokenFields")]
619 #[serde(flatten)]
620 extra_fields: EF,
621}
622impl<EF, TT> StandardTokenResponse<EF, TT>
623where
624 EF: ExtraTokenFields,
625 TT: TokenType,
626{
627 pub fn new(access_token: AccessToken, token_type: TT, extra_fields: EF) -> Self {
629 Self {
630 access_token,
631 token_type,
632 expires_in: None,
633 refresh_token: None,
634 scopes: None,
635 extra_fields,
636 }
637 }
638
639 pub fn set_access_token(&mut self, access_token: AccessToken) {
641 self.access_token = access_token;
642 }
643
644 pub fn set_token_type(&mut self, token_type: TT) {
646 self.token_type = token_type;
647 }
648
649 pub fn set_expires_in(&mut self, expires_in: Option<&Duration>) {
651 self.expires_in = expires_in.map(Duration::as_secs);
652 }
653
654 pub fn set_refresh_token(&mut self, refresh_token: Option<RefreshToken>) {
656 self.refresh_token = refresh_token;
657 }
658
659 pub fn set_scopes(&mut self, scopes: Option<Vec<Scope>>) {
661 self.scopes = scopes;
662 }
663
664 pub fn extra_fields(&self) -> &EF {
666 &self.extra_fields
667 }
668
669 pub fn set_extra_fields(&mut self, extra_fields: EF) {
671 self.extra_fields = extra_fields;
672 }
673}
674impl<EF, TT> TokenResponse for StandardTokenResponse<EF, TT>
675where
676 EF: ExtraTokenFields,
677 TT: TokenType,
678{
679 type TokenType = TT;
680
681 fn access_token(&self) -> &AccessToken {
683 &self.access_token
684 }
685 fn token_type(&self) -> &TT {
689 &self.token_type
690 }
691 fn expires_in(&self) -> Option<Duration> {
696 self.expires_in.map(Duration::from_secs)
697 }
698 fn refresh_token(&self) -> Option<&RefreshToken> {
702 self.refresh_token.as_ref()
703 }
704 fn scopes(&self) -> Option<&Vec<Scope>> {
710 self.scopes.as_ref()
711 }
712}