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}