oauth2/
client.rs

1use crate::{
2    AccessToken, AuthType, AuthUrl, AuthorizationCode, AuthorizationRequest,
3    ClientCredentialsTokenRequest, ClientId, ClientSecret, CodeTokenRequest, ConfigurationError,
4    CsrfToken, DeviceAccessTokenRequest, DeviceAuthorizationRequest, DeviceAuthorizationResponse,
5    DeviceAuthorizationUrl, ErrorResponse, ExtraDeviceAuthorizationFields, IntrospectionRequest,
6    IntrospectionUrl, PasswordTokenRequest, RedirectUrl, RefreshToken, RefreshTokenRequest,
7    ResourceOwnerPassword, ResourceOwnerUsername, RevocableToken, RevocationRequest, RevocationUrl,
8    TokenIntrospectionResponse, TokenResponse, TokenUrl,
9};
10
11use std::marker::PhantomData;
12
13mod private {
14    /// Private trait to make `EndpointState` a sealed trait.
15    pub trait EndpointStateSealed {}
16}
17
18/// [Typestate](https://cliffle.com/blog/rust-typestate/) base trait indicating whether an endpoint
19/// has been configured via its corresponding setter.
20pub trait EndpointState: private::EndpointStateSealed {}
21
22/// [Typestate](https://cliffle.com/blog/rust-typestate/) indicating that an endpoint has not been
23/// set and cannot be used.
24#[derive(Clone, Debug)]
25pub struct EndpointNotSet;
26impl EndpointState for EndpointNotSet {}
27impl private::EndpointStateSealed for EndpointNotSet {}
28
29/// [Typestate](https://cliffle.com/blog/rust-typestate/) indicating that an endpoint has been set
30/// and is ready to be used.
31#[derive(Clone, Debug)]
32pub struct EndpointSet;
33impl EndpointState for EndpointSet {}
34impl private::EndpointStateSealed for EndpointSet {}
35
36/// [Typestate](https://cliffle.com/blog/rust-typestate/) indicating that an endpoint may have been
37/// set and can be used via fallible methods.
38#[derive(Clone, Debug)]
39pub struct EndpointMaybeSet;
40impl EndpointState for EndpointMaybeSet {}
41impl private::EndpointStateSealed for EndpointMaybeSet {}
42
43/// Stores the configuration for an OAuth2 client.
44///
45/// This type implements the
46/// [Builder Pattern](https://doc.rust-lang.org/1.0.0/style/ownership/builders.html) together with
47/// [typestates](https://cliffle.com/blog/rust-typestate/#what-are-typestates) to encode whether
48/// certain fields have been set that are prerequisites to certain authentication flows. For
49/// example, the authorization endpoint must be set via [`set_auth_uri()`](Client::set_auth_uri)
50/// before [`authorize_url()`](Client::authorize_url) can be called. Each endpoint has a
51/// corresponding generic type
52/// parameter (e.g., `HasAuthUrl`) used to statically enforce these dependencies. These generics
53/// are set automatically by the corresponding setter functions, and in most cases user code should
54/// not need to deal with them directly.
55///
56/// In addition to unconditional setters (e.g., [`set_auth_uri()`](Client::set_auth_uri)), each
57/// endpoint has a corresponding conditional setter (e.g.,
58/// [`set_auth_uri_option()`](Client::set_auth_uri_option)) that sets a
59/// conditional typestate ([`EndpointMaybeSet`]). When the conditional typestate is set, endpoints
60/// can be used via fallible methods that return [`ConfigurationError::MissingUrl`] if an
61/// endpoint has not been set. This is useful in dynamic scenarios such as
62/// [OpenID Connect Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html), in which
63/// it cannot be determined until runtime whether an endpoint is configured.
64///
65/// # Error Types
66///
67/// To enable compile time verification that only the correct and complete set of errors for the `Client` function being
68/// invoked are exposed to the caller, the `Client` type is specialized on multiple implementations of the
69/// [`ErrorResponse`] trait. The exact [`ErrorResponse`] implementation returned varies by the RFC that the invoked
70/// `Client` function implements:
71///
72///   - Generic type `TE` (aka Token Error) for errors defined by [RFC 6749 OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749).
73///   - Generic type `TRE` (aka Token Revocation Error) for errors defined by [RFC 7009 OAuth 2.0 Token Revocation](https://tools.ietf.org/html/rfc7009).
74///
75/// For example when revoking a token, error code `unsupported_token_type` (from RFC 7009) may be returned:
76/// ```rust
77/// # use thiserror::Error;
78/// # use http::status::StatusCode;
79/// # use http::header::{HeaderValue, CONTENT_TYPE};
80/// # use http::Response;
81/// # use oauth2::{*, basic::*};
82/// #
83/// # let client = BasicClient::new(ClientId::new("aaa".to_string()))
84/// #     .set_client_secret(ClientSecret::new("bbb".to_string()))
85/// #     .set_auth_uri(AuthUrl::new("https://example.com/auth".to_string()).unwrap())
86/// #     .set_token_uri(TokenUrl::new("https://example.com/token".to_string()).unwrap())
87/// #     .set_revocation_url(RevocationUrl::new("https://revocation/url".to_string()).unwrap());
88/// #
89/// # #[derive(Debug, Error)]
90/// # enum FakeError {
91/// #     #[error("error")]
92/// #     Err,
93/// # }
94/// #
95/// # let http_client = |_| -> Result<HttpResponse, FakeError> {
96/// #     Ok(Response::builder()
97/// #         .status(StatusCode::BAD_REQUEST)
98/// #         .header(CONTENT_TYPE, HeaderValue::from_str("application/json").unwrap())
99/// #         .body(
100/// #             r#"{"error": "unsupported_token_type",
101/// #                 "error_description": "stuff happened",
102/// #                 "error_uri": "https://errors"}"#
103/// #             .to_string()
104/// #             .into_bytes(),
105/// #         )
106/// #         .unwrap())
107/// # };
108/// #
109/// let res = client
110///     .revoke_token(AccessToken::new("some token".to_string()).into())
111///     .unwrap()
112///     .request(&http_client);
113///
114/// assert!(matches!(res, Err(
115///     RequestTokenError::ServerResponse(err)) if matches!(err.error(),
116///         RevocationErrorResponseType::UnsupportedTokenType)));
117/// ```
118///
119/// # Examples
120///
121/// See the [crate] root documentation for usage examples.
122#[derive(Clone, Debug)]
123pub struct Client<
124    TE,
125    TR,
126    TIR,
127    RT,
128    TRE,
129    HasAuthUrl = EndpointNotSet,
130    HasDeviceAuthUrl = EndpointNotSet,
131    HasIntrospectionUrl = EndpointNotSet,
132    HasRevocationUrl = EndpointNotSet,
133    HasTokenUrl = EndpointNotSet,
134> where
135    TE: ErrorResponse,
136    TR: TokenResponse,
137    TIR: TokenIntrospectionResponse,
138    RT: RevocableToken,
139    TRE: ErrorResponse,
140    HasAuthUrl: EndpointState,
141    HasDeviceAuthUrl: EndpointState,
142    HasIntrospectionUrl: EndpointState,
143    HasRevocationUrl: EndpointState,
144    HasTokenUrl: EndpointState,
145{
146    pub(crate) client_id: ClientId,
147    pub(crate) client_secret: Option<ClientSecret>,
148    pub(crate) auth_url: Option<AuthUrl>,
149    pub(crate) auth_type: AuthType,
150    pub(crate) token_url: Option<TokenUrl>,
151    pub(crate) redirect_url: Option<RedirectUrl>,
152    pub(crate) introspection_url: Option<IntrospectionUrl>,
153    pub(crate) revocation_url: Option<RevocationUrl>,
154    pub(crate) device_authorization_url: Option<DeviceAuthorizationUrl>,
155    #[allow(clippy::type_complexity)]
156    pub(crate) phantom: PhantomData<(
157        TE,
158        TR,
159        TIR,
160        RT,
161        TRE,
162        HasAuthUrl,
163        HasDeviceAuthUrl,
164        HasIntrospectionUrl,
165        HasRevocationUrl,
166        HasTokenUrl,
167    )>,
168}
169impl<TE, TR, TIR, RT, TRE>
170    Client<
171        TE,
172        TR,
173        TIR,
174        RT,
175        TRE,
176        EndpointNotSet,
177        EndpointNotSet,
178        EndpointNotSet,
179        EndpointNotSet,
180        EndpointNotSet,
181    >
182where
183    TE: ErrorResponse + 'static,
184    TR: TokenResponse,
185    TIR: TokenIntrospectionResponse,
186    RT: RevocableToken,
187    TRE: ErrorResponse + 'static,
188{
189    /// Initializes an OAuth2 client with the specified client ID.
190    pub fn new(client_id: ClientId) -> Self {
191        Self {
192            client_id,
193            client_secret: None,
194            auth_url: None,
195            auth_type: AuthType::BasicAuth,
196            token_url: None,
197            redirect_url: None,
198            introspection_url: None,
199            revocation_url: None,
200            device_authorization_url: None,
201            phantom: PhantomData,
202        }
203    }
204}
205impl<
206        TE,
207        TR,
208        TIR,
209        RT,
210        TRE,
211        HasAuthUrl,
212        HasDeviceAuthUrl,
213        HasIntrospectionUrl,
214        HasRevocationUrl,
215        HasTokenUrl,
216    >
217    Client<
218        TE,
219        TR,
220        TIR,
221        RT,
222        TRE,
223        HasAuthUrl,
224        HasDeviceAuthUrl,
225        HasIntrospectionUrl,
226        HasRevocationUrl,
227        HasTokenUrl,
228    >
229where
230    TE: ErrorResponse + 'static,
231    TR: TokenResponse,
232    TIR: TokenIntrospectionResponse,
233    RT: RevocableToken,
234    TRE: ErrorResponse + 'static,
235    HasAuthUrl: EndpointState,
236    HasDeviceAuthUrl: EndpointState,
237    HasIntrospectionUrl: EndpointState,
238    HasRevocationUrl: EndpointState,
239    HasTokenUrl: EndpointState,
240{
241    /// Set the type of client authentication used for communicating with the authorization
242    /// server.
243    ///
244    /// The default is to use HTTP Basic authentication, as recommended in
245    /// [Section 2.3.1 of RFC 6749](https://tools.ietf.org/html/rfc6749#section-2.3.1). Note that
246    /// if a client secret is omitted (i.e., [`set_client_secret()`](Self::set_client_secret) is not
247    /// called), [`AuthType::RequestBody`] is used regardless of the `auth_type` passed to
248    /// this function.
249    pub fn set_auth_type(mut self, auth_type: AuthType) -> Self {
250        self.auth_type = auth_type;
251
252        self
253    }
254
255    /// Set the authorization endpoint.
256    ///
257    /// The client uses the authorization endpoint to obtain authorization from the resource owner
258    /// via user-agent redirection. This URL is used in all standard OAuth2 flows except the
259    /// [Resource Owner Password Credentials Grant](https://tools.ietf.org/html/rfc6749#section-4.3)
260    /// and the [Client Credentials Grant](https://tools.ietf.org/html/rfc6749#section-4.4).
261    pub fn set_auth_uri(
262        self,
263        auth_url: AuthUrl,
264    ) -> Client<
265        TE,
266        TR,
267        TIR,
268        RT,
269        TRE,
270        EndpointSet,
271        HasDeviceAuthUrl,
272        HasIntrospectionUrl,
273        HasRevocationUrl,
274        HasTokenUrl,
275    > {
276        Client {
277            client_id: self.client_id,
278            client_secret: self.client_secret,
279            auth_url: Some(auth_url),
280            auth_type: self.auth_type,
281            token_url: self.token_url,
282            redirect_url: self.redirect_url,
283            introspection_url: self.introspection_url,
284            revocation_url: self.revocation_url,
285            device_authorization_url: self.device_authorization_url,
286            phantom: PhantomData,
287        }
288    }
289
290    /// Conditionally set the authorization endpoint.
291    ///
292    /// The client uses the authorization endpoint to obtain authorization from the resource owner
293    /// via user-agent redirection. This URL is used in all standard OAuth2 flows except the
294    /// [Resource Owner Password Credentials Grant](https://tools.ietf.org/html/rfc6749#section-4.3)
295    /// and the [Client Credentials Grant](https://tools.ietf.org/html/rfc6749#section-4.4).
296    pub fn set_auth_uri_option(
297        self,
298        auth_url: Option<AuthUrl>,
299    ) -> Client<
300        TE,
301        TR,
302        TIR,
303        RT,
304        TRE,
305        EndpointMaybeSet,
306        HasDeviceAuthUrl,
307        HasIntrospectionUrl,
308        HasRevocationUrl,
309        HasTokenUrl,
310    > {
311        Client {
312            client_id: self.client_id,
313            client_secret: self.client_secret,
314            auth_url,
315            auth_type: self.auth_type,
316            token_url: self.token_url,
317            redirect_url: self.redirect_url,
318            introspection_url: self.introspection_url,
319            revocation_url: self.revocation_url,
320            device_authorization_url: self.device_authorization_url,
321            phantom: PhantomData,
322        }
323    }
324
325    /// Set the client secret.
326    ///
327    /// A client secret is generally used for confidential (i.e., server-side) OAuth2 clients and
328    /// omitted from public (browser or native app) OAuth2 clients (see
329    /// [RFC 8252](https://tools.ietf.org/html/rfc8252)).
330    pub fn set_client_secret(mut self, client_secret: ClientSecret) -> Self {
331        self.client_secret = Some(client_secret);
332
333        self
334    }
335
336    /// Set the [RFC 8628](https://tools.ietf.org/html/rfc8628) device authorization endpoint used
337    /// for the Device Authorization Flow.
338    ///
339    /// See [`exchange_device_code()`](Self::exchange_device_code).
340    pub fn set_device_authorization_url(
341        self,
342        device_authorization_url: DeviceAuthorizationUrl,
343    ) -> Client<
344        TE,
345        TR,
346        TIR,
347        RT,
348        TRE,
349        HasAuthUrl,
350        EndpointSet,
351        HasIntrospectionUrl,
352        HasRevocationUrl,
353        HasTokenUrl,
354    > {
355        Client {
356            client_id: self.client_id,
357            client_secret: self.client_secret,
358            auth_url: self.auth_url,
359            auth_type: self.auth_type,
360            token_url: self.token_url,
361            redirect_url: self.redirect_url,
362            introspection_url: self.introspection_url,
363            revocation_url: self.revocation_url,
364            device_authorization_url: Some(device_authorization_url),
365            phantom: PhantomData,
366        }
367    }
368
369    /// Conditionally set the [RFC 8628](https://tools.ietf.org/html/rfc8628) device authorization
370    /// endpoint used for the Device Authorization Flow.
371    ///
372    /// See [`exchange_device_code()`](Self::exchange_device_code).
373    pub fn set_device_authorization_url_option(
374        self,
375        device_authorization_url: Option<DeviceAuthorizationUrl>,
376    ) -> Client<
377        TE,
378        TR,
379        TIR,
380        RT,
381        TRE,
382        HasAuthUrl,
383        EndpointMaybeSet,
384        HasIntrospectionUrl,
385        HasRevocationUrl,
386        HasTokenUrl,
387    > {
388        Client {
389            client_id: self.client_id,
390            client_secret: self.client_secret,
391            auth_url: self.auth_url,
392            auth_type: self.auth_type,
393            token_url: self.token_url,
394            redirect_url: self.redirect_url,
395            introspection_url: self.introspection_url,
396            revocation_url: self.revocation_url,
397            device_authorization_url,
398            phantom: PhantomData,
399        }
400    }
401
402    /// Set the [RFC 7662](https://tools.ietf.org/html/rfc7662) introspection endpoint.
403    ///
404    /// See [`introspect()`](Self::introspect).
405    pub fn set_introspection_url(
406        self,
407        introspection_url: IntrospectionUrl,
408    ) -> Client<
409        TE,
410        TR,
411        TIR,
412        RT,
413        TRE,
414        HasAuthUrl,
415        HasDeviceAuthUrl,
416        EndpointSet,
417        HasRevocationUrl,
418        HasTokenUrl,
419    > {
420        Client {
421            client_id: self.client_id,
422            client_secret: self.client_secret,
423            auth_url: self.auth_url,
424            auth_type: self.auth_type,
425            token_url: self.token_url,
426            redirect_url: self.redirect_url,
427            introspection_url: Some(introspection_url),
428            revocation_url: self.revocation_url,
429            device_authorization_url: self.device_authorization_url,
430            phantom: PhantomData,
431        }
432    }
433
434    /// Conditionally set the [RFC 7662](https://tools.ietf.org/html/rfc7662) introspection
435    /// endpoint.
436    ///
437    /// See [`introspect()`](Self::introspect).
438    pub fn set_introspection_url_option(
439        self,
440        introspection_url: Option<IntrospectionUrl>,
441    ) -> Client<
442        TE,
443        TR,
444        TIR,
445        RT,
446        TRE,
447        HasAuthUrl,
448        HasDeviceAuthUrl,
449        EndpointMaybeSet,
450        HasRevocationUrl,
451        HasTokenUrl,
452    > {
453        Client {
454            client_id: self.client_id,
455            client_secret: self.client_secret,
456            auth_url: self.auth_url,
457            auth_type: self.auth_type,
458            token_url: self.token_url,
459            redirect_url: self.redirect_url,
460            introspection_url,
461            revocation_url: self.revocation_url,
462            device_authorization_url: self.device_authorization_url,
463            phantom: PhantomData,
464        }
465    }
466
467    /// Set the redirect URL used by the authorization endpoint.
468    pub fn set_redirect_uri(mut self, redirect_url: RedirectUrl) -> Self {
469        self.redirect_url = Some(redirect_url);
470
471        self
472    }
473
474    /// Set the [RFC 7009](https://tools.ietf.org/html/rfc7009) revocation endpoint.
475    ///
476    /// See [`revoke_token()`](Self::revoke_token()).
477    pub fn set_revocation_url(
478        self,
479        revocation_url: RevocationUrl,
480    ) -> Client<
481        TE,
482        TR,
483        TIR,
484        RT,
485        TRE,
486        HasAuthUrl,
487        HasDeviceAuthUrl,
488        HasIntrospectionUrl,
489        EndpointSet,
490        HasTokenUrl,
491    > {
492        Client {
493            client_id: self.client_id,
494            client_secret: self.client_secret,
495            auth_url: self.auth_url,
496            auth_type: self.auth_type,
497            token_url: self.token_url,
498            redirect_url: self.redirect_url,
499            introspection_url: self.introspection_url,
500            revocation_url: Some(revocation_url),
501            device_authorization_url: self.device_authorization_url,
502            phantom: PhantomData,
503        }
504    }
505
506    /// Conditionally set the [RFC 7009](https://tools.ietf.org/html/rfc7009) revocation
507    /// endpoint.
508    ///
509    /// See [`revoke_token()`](Self::revoke_token()).
510    pub fn set_revocation_url_option(
511        self,
512        revocation_url: Option<RevocationUrl>,
513    ) -> Client<
514        TE,
515        TR,
516        TIR,
517        RT,
518        TRE,
519        HasAuthUrl,
520        HasDeviceAuthUrl,
521        HasIntrospectionUrl,
522        EndpointMaybeSet,
523        HasTokenUrl,
524    > {
525        Client {
526            client_id: self.client_id,
527            client_secret: self.client_secret,
528            auth_url: self.auth_url,
529            auth_type: self.auth_type,
530            token_url: self.token_url,
531            redirect_url: self.redirect_url,
532            introspection_url: self.introspection_url,
533            revocation_url,
534            device_authorization_url: self.device_authorization_url,
535            phantom: PhantomData,
536        }
537    }
538
539    /// Set the token endpoint.
540    ///
541    /// The client uses the token endpoint to exchange an authorization code for an access token,
542    /// typically with client authentication. This URL is used in
543    /// all standard OAuth2 flows except the
544    /// [Implicit Grant](https://tools.ietf.org/html/rfc6749#section-4.2).
545    pub fn set_token_uri(
546        self,
547        token_url: TokenUrl,
548    ) -> Client<
549        TE,
550        TR,
551        TIR,
552        RT,
553        TRE,
554        HasAuthUrl,
555        HasDeviceAuthUrl,
556        HasIntrospectionUrl,
557        HasRevocationUrl,
558        EndpointSet,
559    > {
560        Client {
561            client_id: self.client_id,
562            client_secret: self.client_secret,
563            auth_url: self.auth_url,
564            auth_type: self.auth_type,
565            token_url: Some(token_url),
566            redirect_url: self.redirect_url,
567            introspection_url: self.introspection_url,
568            revocation_url: self.revocation_url,
569            device_authorization_url: self.device_authorization_url,
570            phantom: PhantomData,
571        }
572    }
573
574    /// Conditionally set the token endpoint.
575    ///
576    /// The client uses the token endpoint to exchange an authorization code for an access token,
577    /// typically with client authentication. This URL is used in
578    /// all standard OAuth2 flows except the
579    /// [Implicit Grant](https://tools.ietf.org/html/rfc6749#section-4.2).
580    pub fn set_token_uri_option(
581        self,
582        token_url: Option<TokenUrl>,
583    ) -> Client<
584        TE,
585        TR,
586        TIR,
587        RT,
588        TRE,
589        HasAuthUrl,
590        HasDeviceAuthUrl,
591        HasIntrospectionUrl,
592        HasRevocationUrl,
593        EndpointMaybeSet,
594    > {
595        Client {
596            client_id: self.client_id,
597            client_secret: self.client_secret,
598            auth_url: self.auth_url,
599            auth_type: self.auth_type,
600            token_url,
601            redirect_url: self.redirect_url,
602            introspection_url: self.introspection_url,
603            revocation_url: self.revocation_url,
604            device_authorization_url: self.device_authorization_url,
605            phantom: PhantomData,
606        }
607    }
608
609    /// Return the Client ID.
610    pub fn client_id(&self) -> &ClientId {
611        &self.client_id
612    }
613
614    /// Return the type of client authentication used for communicating with the authorization
615    /// server.
616    pub fn auth_type(&self) -> &AuthType {
617        &self.auth_type
618    }
619
620    /// Return the redirect URL used by the authorization endpoint.
621    pub fn redirect_uri(&self) -> Option<&RedirectUrl> {
622        self.redirect_url.as_ref()
623    }
624}
625
626/// Methods requiring an authorization endpoint.
627impl<
628        TE,
629        TR,
630        TIR,
631        RT,
632        TRE,
633        HasDeviceAuthUrl,
634        HasIntrospectionUrl,
635        HasRevocationUrl,
636        HasTokenUrl,
637    >
638    Client<
639        TE,
640        TR,
641        TIR,
642        RT,
643        TRE,
644        EndpointSet,
645        HasDeviceAuthUrl,
646        HasIntrospectionUrl,
647        HasRevocationUrl,
648        HasTokenUrl,
649    >
650where
651    TE: ErrorResponse + 'static,
652    TR: TokenResponse,
653    TIR: TokenIntrospectionResponse,
654    RT: RevocableToken,
655    TRE: ErrorResponse + 'static,
656    HasDeviceAuthUrl: EndpointState,
657    HasIntrospectionUrl: EndpointState,
658    HasRevocationUrl: EndpointState,
659    HasTokenUrl: EndpointState,
660{
661    /// Return the authorization endpoint.
662    pub fn auth_uri(&self) -> &AuthUrl {
663        // This is enforced statically via the HasAuthUrl generic type.
664        self.auth_url.as_ref().expect("should have auth_url")
665    }
666
667    /// Generate an authorization URL for a new authorization request.
668    ///
669    /// Requires [`set_auth_uri()`](Self::set_auth_uri) to have been previously
670    /// called to set the authorization endpoint.
671    ///
672    /// # Arguments
673    ///
674    /// * `state_fn` - A function that returns an opaque value used by the client to maintain state
675    ///   between the request and callback. The authorization server includes this value when
676    ///   redirecting the user-agent back to the client.
677    ///
678    /// # Security Warning
679    ///
680    /// Callers should use a fresh, unpredictable `state` for each authorization request and verify
681    /// that this value matches the `state` parameter passed by the authorization server to the
682    /// redirect URI. Doing so mitigates
683    /// [Cross-Site Request Forgery](https://tools.ietf.org/html/rfc6749#section-10.12)
684    ///  attacks. To disable CSRF protections (NOT recommended), use `insecure::authorize_url`
685    ///  instead.
686    pub fn authorize_url<S>(&self, state_fn: S) -> AuthorizationRequest
687    where
688        S: FnOnce() -> CsrfToken,
689    {
690        self.authorize_url_impl(self.auth_uri(), state_fn)
691    }
692}
693
694/// Methods with a possibly-set authorization endpoint.
695impl<
696        TE,
697        TR,
698        TIR,
699        RT,
700        TRE,
701        HasDeviceAuthUrl,
702        HasIntrospectionUrl,
703        HasRevocationUrl,
704        HasTokenUrl,
705    >
706    Client<
707        TE,
708        TR,
709        TIR,
710        RT,
711        TRE,
712        EndpointMaybeSet,
713        HasDeviceAuthUrl,
714        HasIntrospectionUrl,
715        HasRevocationUrl,
716        HasTokenUrl,
717    >
718where
719    TE: ErrorResponse + 'static,
720    TR: TokenResponse,
721    TIR: TokenIntrospectionResponse,
722    RT: RevocableToken,
723    TRE: ErrorResponse + 'static,
724    HasDeviceAuthUrl: EndpointState,
725    HasIntrospectionUrl: EndpointState,
726    HasRevocationUrl: EndpointState,
727    HasTokenUrl: EndpointState,
728{
729    /// Return the authorization endpoint.
730    pub fn auth_uri(&self) -> Option<&AuthUrl> {
731        self.auth_url.as_ref()
732    }
733
734    /// Generate an authorization URL for a new authorization request.
735    ///
736    /// Requires [`set_auth_uri_option()`](Self::set_auth_uri_option) to have been previously
737    /// called to set the authorization endpoint.
738    ///
739    /// # Arguments
740    ///
741    /// * `state_fn` - A function that returns an opaque value used by the client to maintain state
742    ///   between the request and callback. The authorization server includes this value when
743    ///   redirecting the user-agent back to the client.
744    ///
745    /// # Security Warning
746    ///
747    /// Callers should use a fresh, unpredictable `state` for each authorization request and verify
748    /// that this value matches the `state` parameter passed by the authorization server to the
749    /// redirect URI. Doing so mitigates
750    /// [Cross-Site Request Forgery](https://tools.ietf.org/html/rfc6749#section-10.12)
751    ///  attacks. To disable CSRF protections (NOT recommended), use `insecure::authorize_url`
752    ///  instead.
753    pub fn authorize_url<S>(&self, state_fn: S) -> Result<AuthorizationRequest, ConfigurationError>
754    where
755        S: FnOnce() -> CsrfToken,
756    {
757        Ok(self.authorize_url_impl(
758            self.auth_uri()
759                .ok_or(ConfigurationError::MissingUrl("authorization"))?,
760            state_fn,
761        ))
762    }
763}
764
765/// Methods requiring a token endpoint.
766impl<TE, TR, TIR, RT, TRE, HasAuthUrl, HasDeviceAuthUrl, HasIntrospectionUrl, HasRevocationUrl>
767    Client<
768        TE,
769        TR,
770        TIR,
771        RT,
772        TRE,
773        HasAuthUrl,
774        HasDeviceAuthUrl,
775        HasIntrospectionUrl,
776        HasRevocationUrl,
777        EndpointSet,
778    >
779where
780    TE: ErrorResponse + 'static,
781    TR: TokenResponse,
782    TIR: TokenIntrospectionResponse,
783    RT: RevocableToken,
784    TRE: ErrorResponse + 'static,
785    HasAuthUrl: EndpointState,
786    HasDeviceAuthUrl: EndpointState,
787    HasIntrospectionUrl: EndpointState,
788    HasRevocationUrl: EndpointState,
789{
790    /// Request an access token using the
791    /// [Client Credentials Flow](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4).
792    ///
793    /// Requires [`set_token_uri()`](Self::set_token_uri) to have been previously
794    /// called to set the token endpoint.
795    pub fn exchange_client_credentials(&self) -> ClientCredentialsTokenRequest<TE, TR> {
796        self.exchange_client_credentials_impl(self.token_uri())
797    }
798
799    /// Exchange a code returned during the
800    /// [Authorization Code Flow](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1)
801    /// for an access token.
802    ///
803    /// Acquires ownership of the `code` because authorization codes may only be used once to
804    /// retrieve an access token from the authorization server.
805    ///
806    /// Requires [`set_token_uri()`](Self::set_token_uri) to have been previously
807    /// called to set the token endpoint.
808    pub fn exchange_code(&self, code: AuthorizationCode) -> CodeTokenRequest<TE, TR> {
809        self.exchange_code_impl(self.token_uri(), code)
810    }
811
812    /// Exchange an [RFC 8628](https://tools.ietf.org/html/rfc8628#section-3.2) Device Authorization
813    /// Response returned by [`exchange_device_code()`](Self::exchange_device_code) for an access
814    /// token.
815    ///
816    /// Requires [`set_token_uri()`](Self::set_token_uri) to have been previously
817    /// called to set the token endpoint.
818    pub fn exchange_device_access_token<'a, EF>(
819        &'a self,
820        auth_response: &'a DeviceAuthorizationResponse<EF>,
821    ) -> DeviceAccessTokenRequest<'a, 'static, TR, EF>
822    where
823        EF: ExtraDeviceAuthorizationFields,
824    {
825        self.exchange_device_access_token_impl(self.token_uri(), auth_response)
826    }
827
828    /// Request an access token using the
829    /// [Resource Owner Password Credentials Flow](https://datatracker.ietf.org/doc/html/rfc6749#section-4.3).
830    ///
831    /// Requires
832    /// [`set_token_uri()`](Self::set_token_uri) to have
833    /// been previously called to set the token endpoint.
834    pub fn exchange_password<'a>(
835        &'a self,
836        username: &'a ResourceOwnerUsername,
837        password: &'a ResourceOwnerPassword,
838    ) -> PasswordTokenRequest<'a, TE, TR> {
839        self.exchange_password_impl(self.token_uri(), username, password)
840    }
841
842    /// Exchange a refresh token for an access token.
843    ///
844    /// See <https://tools.ietf.org/html/rfc6749#section-6>.
845    ///
846    /// Requires
847    /// [`set_token_uri()`](Self::set_token_uri) to have
848    /// been previously called to set the token endpoint.
849    pub fn exchange_refresh_token<'a>(
850        &'a self,
851        refresh_token: &'a RefreshToken,
852    ) -> RefreshTokenRequest<'a, TE, TR> {
853        self.exchange_refresh_token_impl(self.token_uri(), refresh_token)
854    }
855
856    /// Return the token endpoint.
857    pub fn token_uri(&self) -> &TokenUrl {
858        // This is enforced statically via the HasTokenUrl generic type.
859        self.token_url.as_ref().expect("should have token_url")
860    }
861}
862
863/// Methods with a possibly-set token endpoint.
864impl<TE, TR, TIR, RT, TRE, HasAuthUrl, HasDeviceAuthUrl, HasIntrospectionUrl, HasRevocationUrl>
865    Client<
866        TE,
867        TR,
868        TIR,
869        RT,
870        TRE,
871        HasAuthUrl,
872        HasDeviceAuthUrl,
873        HasIntrospectionUrl,
874        HasRevocationUrl,
875        EndpointMaybeSet,
876    >
877where
878    TE: ErrorResponse + 'static,
879    TR: TokenResponse,
880    TIR: TokenIntrospectionResponse,
881    RT: RevocableToken,
882    TRE: ErrorResponse + 'static,
883    HasAuthUrl: EndpointState,
884    HasDeviceAuthUrl: EndpointState,
885    HasIntrospectionUrl: EndpointState,
886    HasRevocationUrl: EndpointState,
887{
888    /// Request an access token using the
889    /// [Client Credentials Flow](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4).
890    ///
891    /// Requires [`set_token_uri_option()`](Self::set_token_uri_option) to have been previously
892    /// called to set the token endpoint.
893    pub fn exchange_client_credentials(
894        &self,
895    ) -> Result<ClientCredentialsTokenRequest<TE, TR>, ConfigurationError> {
896        Ok(self.exchange_client_credentials_impl(
897            self.token_url
898                .as_ref()
899                .ok_or(ConfigurationError::MissingUrl("token"))?,
900        ))
901    }
902
903    /// Exchange a code returned during the
904    /// [Authorization Code Flow](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1)
905    /// for an access token.
906    ///
907    /// Acquires ownership of the `code` because authorization codes may only be used once to
908    /// retrieve an access token from the authorization server.
909    ///
910    /// Requires [`set_token_uri_option()`](Self::set_token_uri_option) to have been previously
911    /// called to set the token endpoint.
912    pub fn exchange_code(
913        &self,
914        code: AuthorizationCode,
915    ) -> Result<CodeTokenRequest<TE, TR>, ConfigurationError> {
916        Ok(self.exchange_code_impl(
917            self.token_url
918                .as_ref()
919                .ok_or(ConfigurationError::MissingUrl("token"))?,
920            code,
921        ))
922    }
923
924    /// Exchange an [RFC 8628](https://tools.ietf.org/html/rfc8628#section-3.2) Device Authorization
925    /// Response returned by [`exchange_device_code()`](Self::exchange_device_code) for an access
926    /// token.
927    ///
928    /// Requires [`set_token_uri_option()`](Self::set_token_uri_option) to have been previously
929    /// called to set the token endpoint.
930    pub fn exchange_device_access_token<'a, EF>(
931        &'a self,
932        auth_response: &'a DeviceAuthorizationResponse<EF>,
933    ) -> Result<DeviceAccessTokenRequest<'a, 'static, TR, EF>, ConfigurationError>
934    where
935        EF: ExtraDeviceAuthorizationFields,
936    {
937        Ok(self.exchange_device_access_token_impl(
938            self.token_url
939                .as_ref()
940                .ok_or(ConfigurationError::MissingUrl("token"))?,
941            auth_response,
942        ))
943    }
944
945    /// Request an access token using the
946    /// [Resource Owner Password Credentials Flow](https://datatracker.ietf.org/doc/html/rfc6749#section-4.3).
947    ///
948    /// Requires
949    /// [`set_token_uri_option()`](Self::set_token_uri_option) to have
950    /// been previously called to set the token endpoint.
951    pub fn exchange_password<'a>(
952        &'a self,
953        username: &'a ResourceOwnerUsername,
954        password: &'a ResourceOwnerPassword,
955    ) -> Result<PasswordTokenRequest<'a, TE, TR>, ConfigurationError> {
956        Ok(self.exchange_password_impl(
957            self.token_url
958                .as_ref()
959                .ok_or(ConfigurationError::MissingUrl("token"))?,
960            username,
961            password,
962        ))
963    }
964
965    /// Exchange a refresh token for an access token.
966    ///
967    /// See <https://tools.ietf.org/html/rfc6749#section-6>.
968    ///
969    /// Requires
970    /// [`set_token_uri_option()`](Self::set_token_uri_option) to have
971    /// been previously called to set the token endpoint.
972    pub fn exchange_refresh_token<'a>(
973        &'a self,
974        refresh_token: &'a RefreshToken,
975    ) -> Result<RefreshTokenRequest<'a, TE, TR>, ConfigurationError> {
976        Ok(self.exchange_refresh_token_impl(
977            self.token_url
978                .as_ref()
979                .ok_or(ConfigurationError::MissingUrl("token"))?,
980            refresh_token,
981        ))
982    }
983
984    /// Return the token endpoint.
985    pub fn token_uri(&self) -> Option<&TokenUrl> {
986        self.token_url.as_ref()
987    }
988}
989
990/// Methods requiring a device authorization endpoint.
991impl<TE, TR, TIR, RT, TRE, HasAuthUrl, HasIntrospectionUrl, HasRevocationUrl, HasTokenUrl>
992    Client<
993        TE,
994        TR,
995        TIR,
996        RT,
997        TRE,
998        HasAuthUrl,
999        EndpointSet,
1000        HasIntrospectionUrl,
1001        HasRevocationUrl,
1002        HasTokenUrl,
1003    >
1004where
1005    TE: ErrorResponse + 'static,
1006    TR: TokenResponse,
1007    TIR: TokenIntrospectionResponse,
1008    RT: RevocableToken,
1009    TRE: ErrorResponse + 'static,
1010    HasAuthUrl: EndpointState,
1011    HasIntrospectionUrl: EndpointState,
1012    HasRevocationUrl: EndpointState,
1013    HasTokenUrl: EndpointState,
1014{
1015    /// Begin the [RFC 8628](https://tools.ietf.org/html/rfc8628) Device Authorization Flow and
1016    /// retrieve a Device Authorization Response.
1017    ///
1018    /// Requires
1019    /// [`set_device_authorization_url()`](Self::set_device_authorization_url) to have
1020    /// been previously called to set the device authorization endpoint.
1021    ///
1022    /// See [`exchange_device_access_token()`](Self::exchange_device_access_token).
1023    pub fn exchange_device_code(&self) -> DeviceAuthorizationRequest<TE> {
1024        self.exchange_device_code_impl(self.device_authorization_url())
1025    }
1026
1027    /// Return the [RFC 8628](https://tools.ietf.org/html/rfc8628) device authorization endpoint
1028    /// used for the Device Authorization Flow.
1029    ///
1030    /// See [`exchange_device_code()`](Self::exchange_device_code).
1031    pub fn device_authorization_url(&self) -> &DeviceAuthorizationUrl {
1032        // This is enforced statically via the HasDeviceAuthUrl generic type.
1033        self.device_authorization_url
1034            .as_ref()
1035            .expect("should have device_authorization_url")
1036    }
1037}
1038
1039/// Methods with a possibly-set device authorization endpoint.
1040impl<TE, TR, TIR, RT, TRE, HasAuthUrl, HasIntrospectionUrl, HasRevocationUrl, HasTokenUrl>
1041    Client<
1042        TE,
1043        TR,
1044        TIR,
1045        RT,
1046        TRE,
1047        HasAuthUrl,
1048        EndpointMaybeSet,
1049        HasIntrospectionUrl,
1050        HasRevocationUrl,
1051        HasTokenUrl,
1052    >
1053where
1054    TE: ErrorResponse + 'static,
1055    TR: TokenResponse,
1056    TIR: TokenIntrospectionResponse,
1057    RT: RevocableToken,
1058    TRE: ErrorResponse + 'static,
1059    HasAuthUrl: EndpointState,
1060    HasIntrospectionUrl: EndpointState,
1061    HasRevocationUrl: EndpointState,
1062    HasTokenUrl: EndpointState,
1063{
1064    /// Begin the [RFC 8628](https://tools.ietf.org/html/rfc8628) Device Authorization Flow.
1065    ///
1066    /// Requires
1067    /// [`set_device_authorization_url_option()`](Self::set_device_authorization_url_option) to have
1068    /// been previously called to set the device authorization endpoint.
1069    ///
1070    /// See [`exchange_device_access_token()`](Self::exchange_device_access_token).
1071    pub fn exchange_device_code(
1072        &self,
1073    ) -> Result<DeviceAuthorizationRequest<TE>, ConfigurationError> {
1074        Ok(self.exchange_device_code_impl(
1075            self.device_authorization_url
1076                .as_ref()
1077                .ok_or(ConfigurationError::MissingUrl("device authorization"))?,
1078        ))
1079    }
1080
1081    /// Return the [RFC 8628](https://tools.ietf.org/html/rfc8628) device authorization endpoint
1082    /// used for the Device Authorization Flow.
1083    ///
1084    /// See [`exchange_device_code()`](Self::exchange_device_code).
1085    pub fn device_authorization_url(&self) -> Option<&DeviceAuthorizationUrl> {
1086        self.device_authorization_url.as_ref()
1087    }
1088}
1089
1090/// Methods requiring an introspection endpoint.
1091impl<TE, TR, TIR, RT, TRE, HasAuthUrl, HasDeviceAuthUrl, HasRevocationUrl, HasTokenUrl>
1092    Client<
1093        TE,
1094        TR,
1095        TIR,
1096        RT,
1097        TRE,
1098        HasAuthUrl,
1099        HasDeviceAuthUrl,
1100        EndpointSet,
1101        HasRevocationUrl,
1102        HasTokenUrl,
1103    >
1104where
1105    TE: ErrorResponse + 'static,
1106    TR: TokenResponse,
1107    TIR: TokenIntrospectionResponse,
1108    RT: RevocableToken,
1109    TRE: ErrorResponse + 'static,
1110    HasAuthUrl: EndpointState,
1111    HasDeviceAuthUrl: EndpointState,
1112    HasRevocationUrl: EndpointState,
1113    HasTokenUrl: EndpointState,
1114{
1115    /// Retrieve metadata for an access token using the
1116    /// [`RFC 7662`](https://tools.ietf.org/html/rfc7662) introspection endpoint.
1117    ///
1118    /// Requires [`set_introspection_url()`](Self::set_introspection_url) to have been previously
1119    /// called to set the introspection endpoint.
1120    pub fn introspect<'a>(&'a self, token: &'a AccessToken) -> IntrospectionRequest<'a, TE, TIR> {
1121        self.introspect_impl(self.introspection_url(), token)
1122    }
1123
1124    /// Return the [RFC 7662](https://tools.ietf.org/html/rfc7662) introspection endpoint.
1125    pub fn introspection_url(&self) -> &IntrospectionUrl {
1126        // This is enforced statically via the HasIntrospectionUrl generic type.
1127        self.introspection_url
1128            .as_ref()
1129            .expect("should have introspection_url")
1130    }
1131}
1132
1133/// Methods with a possibly-set introspection endpoint.
1134impl<TE, TR, TIR, RT, TRE, HasAuthUrl, HasDeviceAuthUrl, HasRevocationUrl, HasTokenUrl>
1135    Client<
1136        TE,
1137        TR,
1138        TIR,
1139        RT,
1140        TRE,
1141        HasAuthUrl,
1142        HasDeviceAuthUrl,
1143        EndpointMaybeSet,
1144        HasRevocationUrl,
1145        HasTokenUrl,
1146    >
1147where
1148    TE: ErrorResponse + 'static,
1149    TR: TokenResponse,
1150    TIR: TokenIntrospectionResponse,
1151    RT: RevocableToken,
1152    TRE: ErrorResponse + 'static,
1153    HasAuthUrl: EndpointState,
1154    HasDeviceAuthUrl: EndpointState,
1155    HasRevocationUrl: EndpointState,
1156    HasTokenUrl: EndpointState,
1157{
1158    /// Retrieve metadata for an access token using the
1159    /// [`RFC 7662`](https://tools.ietf.org/html/rfc7662) introspection endpoint.
1160    ///
1161    /// Requires [`set_introspection_url_option()`](Self::set_introspection_url_option) to have been
1162    /// previously called to set the introspection endpoint.
1163    pub fn introspect<'a>(
1164        &'a self,
1165        token: &'a AccessToken,
1166    ) -> Result<IntrospectionRequest<'a, TE, TIR>, ConfigurationError> {
1167        Ok(self.introspect_impl(
1168            self.introspection_url
1169                .as_ref()
1170                .ok_or(ConfigurationError::MissingUrl("introspection"))?,
1171            token,
1172        ))
1173    }
1174
1175    /// Return the [RFC 7662](https://tools.ietf.org/html/rfc7662) introspection endpoint.
1176    pub fn introspection_url(&self) -> Option<&IntrospectionUrl> {
1177        self.introspection_url.as_ref()
1178    }
1179}
1180
1181/// Methods requiring a revocation endpoint.
1182impl<TE, TR, TIR, RT, TRE, HasAuthUrl, HasDeviceAuthUrl, HasIntrospectionUrl, HasTokenUrl>
1183    Client<
1184        TE,
1185        TR,
1186        TIR,
1187        RT,
1188        TRE,
1189        HasAuthUrl,
1190        HasDeviceAuthUrl,
1191        HasIntrospectionUrl,
1192        EndpointSet,
1193        HasTokenUrl,
1194    >
1195where
1196    TE: ErrorResponse + 'static,
1197    TR: TokenResponse,
1198    TIR: TokenIntrospectionResponse,
1199    RT: RevocableToken,
1200    TRE: ErrorResponse + 'static,
1201    HasAuthUrl: EndpointState,
1202    HasDeviceAuthUrl: EndpointState,
1203    HasIntrospectionUrl: EndpointState,
1204    HasTokenUrl: EndpointState,
1205{
1206    /// Revoke an access or refresh token using the [RFC 7009](https://tools.ietf.org/html/rfc7009)
1207    /// revocation endpoint.
1208    ///
1209    /// Requires [`set_revocation_url()`](Self::set_revocation_url) to have been previously
1210    /// called to set the revocation endpoint.
1211    pub fn revoke_token(
1212        &self,
1213        token: RT,
1214    ) -> Result<RevocationRequest<RT, TRE>, ConfigurationError> {
1215        self.revoke_token_impl(self.revocation_url(), token)
1216    }
1217
1218    /// Return the [RFC 7009](https://tools.ietf.org/html/rfc7009) revocation endpoint.
1219    ///
1220    /// See [`revoke_token()`](Self::revoke_token()).
1221    pub fn revocation_url(&self) -> &RevocationUrl {
1222        // This is enforced statically via the HasRevocationUrl generic type.
1223        self.revocation_url
1224            .as_ref()
1225            .expect("should have revocation_url")
1226    }
1227}
1228
1229/// Methods with a possible-set revocation endpoint.
1230impl<TE, TR, TIR, RT, TRE, HasAuthUrl, HasDeviceAuthUrl, HasIntrospectionUrl, HasTokenUrl>
1231    Client<
1232        TE,
1233        TR,
1234        TIR,
1235        RT,
1236        TRE,
1237        HasAuthUrl,
1238        HasDeviceAuthUrl,
1239        HasIntrospectionUrl,
1240        EndpointMaybeSet,
1241        HasTokenUrl,
1242    >
1243where
1244    TE: ErrorResponse + 'static,
1245    TR: TokenResponse,
1246    TIR: TokenIntrospectionResponse,
1247    RT: RevocableToken,
1248    TRE: ErrorResponse + 'static,
1249    HasAuthUrl: EndpointState,
1250    HasDeviceAuthUrl: EndpointState,
1251    HasIntrospectionUrl: EndpointState,
1252    HasTokenUrl: EndpointState,
1253{
1254    /// Revoke an access or refresh token using the [RFC 7009](https://tools.ietf.org/html/rfc7009)
1255    /// revocation endpoint.
1256    ///
1257    /// Requires [`set_revocation_url_option()`](Self::set_revocation_url_option) to have been
1258    /// previously called to set the revocation endpoint.
1259    pub fn revoke_token(
1260        &self,
1261        token: RT,
1262    ) -> Result<RevocationRequest<RT, TRE>, ConfigurationError> {
1263        self.revoke_token_impl(
1264            self.revocation_url
1265                .as_ref()
1266                .ok_or(ConfigurationError::MissingUrl("revocation"))?,
1267            token,
1268        )
1269    }
1270
1271    /// Return the [RFC 7009](https://tools.ietf.org/html/rfc7009) revocation endpoint.
1272    ///
1273    /// See [`revoke_token()`](Self::revoke_token()).
1274    pub fn revocation_url(&self) -> Option<&RevocationUrl> {
1275        self.revocation_url.as_ref()
1276    }
1277}