Skip to main content

oauth2/
lib.rs

1//! [<img alt="github" src="https://img.shields.io/badge/github-udoprog/async--oauth2-8da0cb?style=for-the-badge&logo=github" height="20">](https://github.com/udoprog/async-oauth2)
2//! [<img alt="crates.io" src="https://img.shields.io/crates/v/async-oauth2.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/async-oauth2)
3//! [<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-async--oauth2-66c2a5?style=for-the-badge&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/async-oauth2)
4//!
5//! An asynchronous OAuth2 flow implementation, trying to adhere as much as
6//! possible to [RFC 6749].
7//!
8//! <br>
9//!
10//! ## Examples
11//!
12//! To see the library in action, you can go to one of our examples:
13//!
14//! - [Google]
15//! - [Spotify]
16//! - [Twitch]
17//!
18//! If you've checked out the project they can be run like this:
19//!
20//! ```sh
21//! cargo run --manifest-path=examples/Cargo.toml --bin spotify --
22//!     --client-id <client-id> --client-secret <client-secret>
23//! cargo run --manifest-path=examples/Cargo.toml --bin google --
24//!     --client-id <client-id> --client-secret <client-secret>
25//! cargo run --manifest-path=examples/Cargo.toml --bin twitch --
26//!     --client-id <client-id> --client-secret <client-secret>
27//! ```
28//!
29//! > Note: You need to configure your client integration to permit redirects to
30//! > `http://localhost:8080/api/auth/redirect` for these to work. How this is
31//! > done depends on the integration used.
32//!
33//! <br>
34//!
35//! ## Authorization Code Grant
36//!
37//! This is the most common OAuth2 flow.
38//!
39//! ```no_run
40//! use oauth2::*;
41//! use url::Url;
42//!
43//! pub struct ReceivedCode {
44//!     pub code: AuthorizationCode,
45//!     pub state: State,
46//! }
47//!
48//! # async fn listen_for_code(port: u32) -> Result<ReceivedCode, Box<dyn std::error::Error>> { todo!() }
49//! # #[tokio::main]
50//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
51//! let reqwest_client = reqwest::Client::new();
52//!
53//! // Create an OAuth2 client by specifying the client ID, client secret,
54//! // authorization URL and token URL.
55//! let mut client = Client::new(
56//!     "client_id",
57//!     Url::parse("http://authorize")?,
58//!     Url::parse("http://token")?
59//! );
60//!
61//! client.set_client_secret("client_secret");
62//! // Set the URL the user will be redirected to after the authorization
63//! // process.
64//! client.set_redirect_url(Url::parse("http://redirect")?);
65//! // Set the desired scopes.
66//! client.add_scope("read");
67//! client.add_scope("write");
68//!
69//! // Generate the full authorization URL.
70//! let state = State::new_random();
71//! let auth_url = client.authorize_url(&state);
72//!
73//! // This is the URL you should redirect the user to, in order to trigger the
74//! // authorization process.
75//! println!("Browse to: {}", auth_url);
76//!
77//! // Once the user has been redirected to the redirect URL, you'll have the
78//! // access code. For security reasons, your code should verify that the
79//! // `state` parameter returned by the server matches `state`.
80//! let received: ReceivedCode = listen_for_code(8080).await?;
81//!
82//! if received.state != state {
83//!    panic!("CSRF token mismatch :(");
84//! }
85//!
86//! // Now you can trade it for an access token.
87//! let token = client.exchange_code(received.code)
88//!     .with_reqwest_client(&reqwest_client)
89//!     .execute::<StandardToken>()
90//!     .await?;
91//!
92//! # Ok(())
93//! # }
94//! ```
95//!
96//! <br>
97//!
98//! ## Implicit Grant
99//!
100//! This flow fetches an access token directly from the authorization endpoint.
101//!
102//! Be sure to understand the security implications of this flow before using
103//! it. In most cases the Authorization Code Grant flow above is preferred to
104//! the Implicit Grant flow.
105//!
106//! ```no_run
107//! use oauth2::*;
108//! use url::Url;
109//!
110//! pub struct ReceivedCode {
111//!     pub code: AuthorizationCode,
112//!     pub state: State,
113//! }
114//!
115//! # async fn get_code() -> Result<ReceivedCode, Box<dyn std::error::Error>> { todo!() }
116//! # #[tokio::main]
117//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
118//! let mut client = Client::new(
119//!     "client_id",
120//!     Url::parse("http://authorize")?,
121//!     Url::parse("http://token")?
122//! );
123//!
124//! client.set_client_secret("client_secret");
125//!
126//! // Generate the full authorization URL.
127//! let state = State::new_random();
128//! let auth_url = client.authorize_url_implicit(&state);
129//!
130//! // This is the URL you should redirect the user to, in order to trigger the
131//! // authorization process.
132//! println!("Browse to: {}", auth_url);
133//!
134//! // Once the user has been redirected to the redirect URL, you'll have the
135//! // access code. For security reasons, your code should verify that the
136//! // `state` parameter returned by the server matches `state`.
137//! let received: ReceivedCode = get_code().await?;
138//!
139//! if received.state != state {
140//!     panic!("CSRF token mismatch :(");
141//! }
142//!
143//! # Ok(()) }
144//! ```
145//!
146//! <br>
147//!
148//! ## Resource Owner Password Credentials Grant
149//!
150//! You can ask for a *password* access token by calling the
151//! `Client::exchange_password` method, while including the username and
152//! password.
153//!
154//! ```no_run
155//! use oauth2::*;
156//! use url::Url;
157//!
158//! # #[tokio::main]
159//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
160//! let reqwest_client = reqwest::Client::new();
161//!
162//! let mut client = Client::new(
163//!     "client_id",
164//!     Url::parse("http://authorize")?,
165//!     Url::parse("http://token")?
166//! );
167//!
168//! client.set_client_secret("client_secret");
169//! client.add_scope("read");
170//!
171//! let token = client
172//!     .exchange_password("user", "pass")
173//!     .with_reqwest_client(&reqwest_client)
174//!     .execute::<StandardToken>()
175//!     .await?;
176//!
177//! # Ok(()) }
178//! ```
179//!
180//! <br>
181//!
182//! ## Client Credentials Grant
183//!
184//! You can ask for a *client credentials* access token by calling the
185//! `Client::exchange_client_credentials` method.
186//!
187//! ```no_run
188//! use oauth2::*;
189//! use url::Url;
190//!
191//! # #[tokio::main]
192//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
193//! let reqwest_client = reqwest::Client::new();
194//! let mut client = Client::new(
195//!     "client_id",
196//!     Url::parse("http://authorize")?,
197//!     Url::parse("http://token")?
198//! );
199//!
200//! client.set_client_secret("client_secret");
201//! client.add_scope("read");
202//!
203//! let token_result = client.exchange_client_credentials()
204//!     .with_reqwest_client(&reqwest_client)
205//!     .execute::<StandardToken>();
206//!
207//! # Ok(()) }
208//! ```
209//!
210//! <br>
211//!
212//! ## Relationship to oauth2-rs
213//!
214//! This is a fork of [oauth2-rs].
215//!
216//! The main differences are:
217//! * Removal of unnecessary type parameters on Client ([see discussion here]).
218//! * Only support one client implementation ([reqwest]).
219//! * Remove most newtypes except `Scope` and the secret ones since they made the API harder to use.
220//!
221//! [RFC 6749]: https://tools.ietf.org/html/rfc6749
222//! [Google]: https://github.com/udoprog/async-oauth2/blob/master/examples/src/bin/google.rs
223//! [oauth2-rs]: https://github.com/ramosbugs/oauth2-rs
224//! [reqwest]: https://docs.rs/reqwest
225//! [see discussion here]: https://github.com/ramosbugs/oauth2-rs/issues/44#issuecomment-50158653
226//! [Spotify]: https://github.com/udoprog/async-oauth2/blob/master/examples/src/bin/spotify.rs
227//! [Twitch]: https://github.com/udoprog/async-oauth2/blob/master/examples/src/bin/twitch.rs
228
229#![allow(clippy::vec_init_then_push)]
230#![deny(missing_docs)]
231#![no_std]
232
233#[cfg(feature = "alloc")]
234extern crate alloc;
235
236#[cfg(not(feature = "alloc"))]
237compile_error!("The `alloc` feature is required for async-oauth2 to work. Please enable it in your Cargo.toml.");
238
239use core::error::Error;
240use core::fmt;
241use core::ops::Deref;
242use core::time::Duration;
243
244use alloc::borrow::{Cow, ToOwned};
245use alloc::string::{String, ToString};
246use alloc::vec::Vec;
247
248use base64::prelude::{Engine as _, BASE64_URL_SAFE_NO_PAD};
249use bytes::Bytes;
250use http::status::StatusCode;
251use serde::{Deserialize, Serialize};
252use serde_aux::prelude::*;
253use sha2::{Digest, Sha256};
254
255pub use url::Url;
256
257/// Indicates whether requests to the authorization server should use basic authentication or
258/// include the parameters in the request body for requests in which either is valid.
259///
260/// The default AuthType is *BasicAuth*, following the recommendation of
261/// [Section 2.3.1 of RFC 6749](https://tools.ietf.org/html/rfc6749#section-2.3.1).
262#[derive(Clone, Copy, Debug)]
263pub enum AuthType {
264    /// The client_id and client_secret will be included as part of the request body.
265    RequestBody,
266    /// The client_id and client_secret will be included using the basic auth authentication scheme.
267    BasicAuth,
268}
269
270macro_rules! redacted_debug {
271    ($name:ident) => {
272        impl fmt::Debug for $name {
273            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274                write!(f, concat!(stringify!($name), "([redacted])"))
275            }
276        }
277    };
278}
279
280/// borrowed newtype plumbing
281macro_rules! borrowed_newtype {
282    ($name:ident, $borrowed:ty) => {
283        impl Deref for $name {
284            type Target = $borrowed;
285
286            #[inline]
287            fn deref(&self) -> &Self::Target {
288                &self.0
289            }
290        }
291
292        impl<'a> From<&'a $name> for Cow<'a, $borrowed> {
293            #[inline]
294            fn from(value: &'a $name) -> Cow<'a, $borrowed> {
295                Cow::Borrowed(&value.0)
296            }
297        }
298
299        impl AsRef<$borrowed> for $name {
300            #[inline]
301            fn as_ref(&self) -> &$borrowed {
302                self
303            }
304        }
305    };
306}
307
308/// newtype plumbing
309macro_rules! newtype {
310    ($name:ident, $owned:ty, $borrowed:ty) => {
311        borrowed_newtype!($name, $borrowed);
312
313        impl<'a> From<&'a $borrowed> for $name {
314            #[inline]
315            fn from(value: &'a $borrowed) -> Self {
316                Self(value.to_owned())
317            }
318        }
319
320        impl From<$owned> for $name {
321            #[inline]
322            fn from(value: $owned) -> Self {
323                Self(value)
324            }
325        }
326
327        impl<'a> From<&'a $owned> for $name {
328            #[inline]
329            fn from(value: &'a $owned) -> Self {
330                Self(value.to_owned())
331            }
332        }
333
334        impl From<$name> for $owned {
335            #[inline]
336            fn from(value: $name) -> $owned {
337                value.0
338            }
339        }
340    };
341}
342
343/// Access token scope, as defined by the authorization server.
344#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
345pub struct Scope(String);
346newtype!(Scope, String, str);
347
348/// Code Challenge used for [PKCE]((https://tools.ietf.org/html/rfc7636)) protection via the
349/// `code_challenge` parameter.
350#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
351pub struct PkceCodeChallengeS256(String);
352newtype!(PkceCodeChallengeS256, String, str);
353
354/// Code Challenge Method used for [PKCE]((https://tools.ietf.org/html/rfc7636)) protection
355/// via the `code_challenge_method` parameter.
356#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
357pub struct PkceCodeChallengeMethod(String);
358newtype!(PkceCodeChallengeMethod, String, str);
359
360/// Client password issued to the client during the registration process described by
361/// [Section 2.2](https://tools.ietf.org/html/rfc6749#section-2.2).
362#[derive(Clone, Deserialize, Serialize)]
363pub struct ClientSecret(String);
364redacted_debug!(ClientSecret);
365newtype!(ClientSecret, String, str);
366
367/// Value used for [CSRF]((https://tools.ietf.org/html/rfc6749#section-10.12)) protection
368/// via the `state` parameter.
369#[must_use]
370#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
371pub struct State([u8; 16]);
372redacted_debug!(State);
373borrowed_newtype!(State, [u8]);
374
375impl State {
376    /// Generate a new random, base64-encoded 128-bit CSRF token.
377    #[cfg(feature = "rand")]
378    #[inline]
379    pub fn new_random() -> Self {
380        let mut random_bytes = [0u8; 16];
381        rand::fill(&mut random_bytes);
382        State(random_bytes)
383    }
384
385    /// Construct state from random bytes.
386    ///
387    /// This is potentially unsafe unless you can guarantee that the provided
388    /// bytes are generated from a cryptographically secure random number
389    /// generator.
390    ///
391    /// If you are not sure of this, enable the `rand` feature and use
392    /// [`State::new_random()`] instead.
393    #[inline]
394    pub fn from_random(bytes: [u8; 16]) -> Self {
395        State(bytes)
396    }
397
398    /// Convert into base64.
399    pub fn to_base64(&self) -> String {
400        BASE64_URL_SAFE_NO_PAD.encode(self.0)
401    }
402}
403
404impl serde::Serialize for State {
405    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
406    where
407        S: serde::Serializer,
408    {
409        self.to_base64().serialize(serializer)
410    }
411}
412
413impl<'de> serde::Deserialize<'de> for State {
414    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
415    where
416        D: serde::Deserializer<'de>,
417    {
418        let s = String::deserialize(deserializer)?;
419        let bytes = BASE64_URL_SAFE_NO_PAD
420            .decode(s)
421            .map_err(serde::de::Error::custom)?;
422        let mut buf = [0u8; 16];
423        buf.copy_from_slice(&bytes);
424        Ok(Self(buf))
425    }
426}
427
428/// Code Verifier used for [PKCE]((https://tools.ietf.org/html/rfc7636)) protection via the
429/// `code_verifier` parameter. The value must have a minimum length of 43 characters and a
430/// maximum length of 128 characters.  Each character must be ASCII alphanumeric or one of
431/// the characters "-" / "." / "_" / "~".
432#[derive(Deserialize, Serialize)]
433pub struct PkceCodeVerifierS256(String);
434newtype!(PkceCodeVerifierS256, String, str);
435
436impl PkceCodeVerifierS256 {
437    /// Generate a new random, base64-encoded code verifier.
438    #[cfg(feature = "rand")]
439    pub fn new_random() -> Self {
440        PkceCodeVerifierS256::new_random_len(32)
441    }
442
443    /// Generate a new random, base64-encoded code verifier.
444    ///
445    /// # Arguments
446    ///
447    /// * `num_bytes` - Number of random bytes to generate, prior to base64-encoding.
448    ///   The value must be in the range 32 to 96 inclusive in order to generate a verifier
449    ///   with a suitable length.
450    #[cfg(feature = "rand")]
451    pub fn new_random_len(num_bytes: u32) -> Self {
452        // The RFC specifies that the code verifier must have "a minimum length of 43
453        // characters and a maximum length of 128 characters".
454        // This implies 32-96 octets of random data to be base64 encoded.
455        assert!((32..=96).contains(&num_bytes));
456        let random_bytes: Vec<u8> = (0..num_bytes).map(|_| rand::random::<u8>()).collect();
457        let code = BASE64_URL_SAFE_NO_PAD.encode(random_bytes);
458        assert!(code.len() >= 43 && code.len() <= 128);
459        PkceCodeVerifierS256(code)
460    }
461
462    /// Return the code challenge for the code verifier.
463    pub fn code_challenge(&self) -> PkceCodeChallengeS256 {
464        let digest = Sha256::digest(self.as_bytes());
465        PkceCodeChallengeS256::from(BASE64_URL_SAFE_NO_PAD.encode(digest))
466    }
467
468    /// Return the code challenge method for this code verifier.
469    pub fn code_challenge_method() -> PkceCodeChallengeMethod {
470        PkceCodeChallengeMethod::from("S256".to_string())
471    }
472
473    /// Return the extension params used for authorize_url.
474    pub fn authorize_url_params(&self) -> Vec<(&'static str, String)> {
475        let mut vec = Vec::with_capacity(2);
476        vec.push((
477            "code_challenge_method",
478            PkceCodeVerifierS256::code_challenge_method().into(),
479        ));
480        vec.push(("code_challenge", self.code_challenge().into()));
481        vec
482    }
483}
484
485/// Authorization code returned from the authorization endpoint.
486#[derive(Clone, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
487pub struct AuthorizationCode(String);
488redacted_debug!(AuthorizationCode);
489newtype!(AuthorizationCode, String, str);
490
491/// Refresh token used to obtain a new access token (if supported by the authorization server).
492#[derive(Clone, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
493pub struct RefreshToken(String);
494redacted_debug!(RefreshToken);
495newtype!(RefreshToken, String, str);
496
497/// Access token returned by the token endpoint and used to access protected resources.
498#[derive(Clone, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
499pub struct AccessToken(String);
500redacted_debug!(AccessToken);
501newtype!(AccessToken, String, str);
502
503/// Resource owner's password used directly as an authorization grant to obtain an access
504/// token.
505pub struct ResourceOwnerPassword(String);
506newtype!(ResourceOwnerPassword, String, str);
507
508/// Stores the configuration for an OAuth2 client.
509#[derive(Clone, Debug)]
510pub struct Client {
511    client_id: String,
512    client_secret: Option<ClientSecret>,
513    auth_url: Url,
514    auth_type: AuthType,
515    token_url: Url,
516    scopes: Vec<Scope>,
517    redirect_url: Option<Url>,
518}
519
520impl Client {
521    /// Initializes an OAuth2 client with the fields common to most OAuth2 flows.
522    ///
523    /// # Arguments
524    ///
525    /// * `client_id` -  Client ID
526    /// * `auth_url` -  Authorization endpoint: used by the client to obtain authorization from
527    ///   the resource owner via user-agent redirection. This URL is used in all standard OAuth2
528    ///   flows except the [Resource Owner Password Credentials
529    ///   Grant](https://tools.ietf.org/html/rfc6749#section-4.3) and the
530    ///   [Client Credentials Grant](https://tools.ietf.org/html/rfc6749#section-4.4).
531    /// * `token_url` - Token endpoint: used by the client to exchange an authorization grant
532    ///   (code) for an access token, typically with client authentication. This URL is used in
533    ///   all standard OAuth2 flows except the
534    ///   [Implicit Grant](https://tools.ietf.org/html/rfc6749#section-4.2). If this value is set
535    ///   to `None`, the `exchange_*` methods will return `Err(ExecuteError::Other(_))`.
536    pub fn new(client_id: impl AsRef<str>, auth_url: Url, token_url: Url) -> Self {
537        Client {
538            client_id: client_id.as_ref().to_string(),
539            client_secret: None,
540            auth_url,
541            auth_type: AuthType::BasicAuth,
542            token_url,
543            scopes: Vec::new(),
544            redirect_url: None,
545        }
546    }
547
548    /// Configure the client secret to use.
549    pub fn set_client_secret(&mut self, client_secret: impl Into<ClientSecret>) {
550        self.client_secret = Some(client_secret.into());
551    }
552
553    /// Appends a new scope to the authorization URL.
554    pub fn add_scope(&mut self, scope: impl Into<Scope>) {
555        self.scopes.push(scope.into());
556    }
557
558    /// Configures the type of client authentication used for communicating with the authorization
559    /// server.
560    ///
561    /// The default is to use HTTP Basic authentication, as recommended in
562    /// [Section 2.3.1 of RFC 6749](https://tools.ietf.org/html/rfc6749#section-2.3.1).
563    pub fn set_auth_type(&mut self, auth_type: AuthType) {
564        self.auth_type = auth_type;
565    }
566
567    /// Sets the the redirect URL used by the authorization endpoint.
568    pub fn set_redirect_url(&mut self, redirect_url: Url) {
569        self.redirect_url = Some(redirect_url);
570    }
571
572    /// Produces the full authorization URL used by the
573    /// [Authorization Code Grant](https://tools.ietf.org/html/rfc6749#section-4.1)
574    /// flow, which is the most common OAuth2 flow.
575    ///
576    /// # Arguments
577    ///
578    /// * `state` - A state value to include in the request. The authorization
579    ///   server includes this value when redirecting the user-agent back to the
580    ///   client.
581    ///
582    /// # Security Warning
583    ///
584    /// Callers should use a fresh, unpredictable `state` for each authorization
585    /// request and verify that this value matches the `state` parameter passed
586    /// by the authorization server to the redirect URI. Doing so mitigates
587    /// [Cross-Site Request Forgery](https://tools.ietf.org/html/rfc6749#section-10.12)
588    /// attacks.
589    pub fn authorize_url(&self, state: &State) -> Url {
590        self.authorize_url_impl("code", state)
591    }
592
593    /// Produces the full authorization URL used by the
594    /// [Implicit Grant](https://tools.ietf.org/html/rfc6749#section-4.2) flow.
595    ///
596    /// # Arguments
597    ///
598    /// * `state` - A state value to include in the request. The authorization
599    ///   server includes this value when redirecting the user-agent back to the
600    ///   client.
601    ///
602    /// # Security Warning
603    ///
604    /// Callers should use a fresh, unpredictable `state` for each authorization request and verify
605    /// that this value matches the `state` parameter passed by the authorization server to the
606    /// redirect URI. Doing so mitigates
607    /// [Cross-Site Request Forgery](https://tools.ietf.org/html/rfc6749#section-10.12)
608    ///  attacks.
609    pub fn authorize_url_implicit(&self, state: &State) -> Url {
610        self.authorize_url_impl("token", state)
611    }
612
613    fn authorize_url_impl(&self, response_type: &str, state: &State) -> Url {
614        let scopes = self
615            .scopes
616            .iter()
617            .map(|s| s.to_string())
618            .collect::<Vec<_>>()
619            .join(" ");
620
621        let mut url = self.auth_url.clone();
622
623        {
624            let mut query = url.query_pairs_mut();
625
626            query.append_pair("response_type", response_type);
627            query.append_pair("client_id", &self.client_id);
628
629            if let Some(ref redirect_url) = self.redirect_url {
630                query.append_pair("redirect_uri", redirect_url.as_str());
631            }
632
633            if !scopes.is_empty() {
634                query.append_pair("scope", &scopes);
635            }
636
637            query.append_pair("state", &state.to_base64());
638        }
639
640        url
641    }
642
643    /// Exchanges a code produced by a successful authorization process with an access token.
644    ///
645    /// Acquires ownership of the `code` because authorization codes may only be used to retrieve
646    /// an access token from the authorization server.
647    ///
648    /// See https://tools.ietf.org/html/rfc6749#section-4.1.3
649    pub fn exchange_code(&self, code: impl Into<AuthorizationCode>) -> Request<'_> {
650        let code = code.into();
651
652        self.request_token()
653            .param("grant_type", "authorization_code")
654            .param("code", code.to_string())
655    }
656
657    /// Requests an access token for the *password* grant type.
658    ///
659    /// See https://tools.ietf.org/html/rfc6749#section-4.3.2
660    pub fn exchange_password(
661        &self,
662        username: impl AsRef<str>,
663        password: impl AsRef<str>,
664    ) -> Request<'_> {
665        let username = username.as_ref();
666        let password = password.as_ref();
667
668        let mut builder = self
669            .request_token()
670            .param("grant_type", "password")
671            .param("username", username.to_string())
672            .param("password", password.to_string());
673
674        // Generate the space-delimited scopes String before initializing params so that it has
675        // a long enough lifetime.
676        if !self.scopes.is_empty() {
677            let scopes = self
678                .scopes
679                .iter()
680                .map(|s| s.to_string())
681                .collect::<Vec<_>>()
682                .join(" ");
683
684            builder = builder.param("scope", scopes);
685        }
686
687        builder
688    }
689
690    /// Requests an access token for the *client credentials* grant type.
691    ///
692    /// See https://tools.ietf.org/html/rfc6749#section-4.4.2
693    pub fn exchange_client_credentials(&self) -> Request<'_> {
694        let mut builder = self
695            .request_token()
696            .param("grant_type", "client_credentials");
697
698        // Generate the space-delimited scopes String before initializing params so that it has
699        // a long enough lifetime.
700        if !self.scopes.is_empty() {
701            let scopes = self
702                .scopes
703                .iter()
704                .map(|s| s.to_string())
705                .collect::<Vec<_>>()
706                .join(" ");
707
708            builder = builder.param("scope", scopes);
709        }
710
711        builder
712    }
713
714    /// Exchanges a refresh token for an access token
715    ///
716    /// See https://tools.ietf.org/html/rfc6749#section-6
717    pub fn exchange_refresh_token(&self, refresh_token: &RefreshToken) -> Request<'_> {
718        self.request_token()
719            .param("grant_type", "refresh_token")
720            .param("refresh_token", refresh_token.to_string())
721    }
722
723    /// Construct a request builder for the token URL.
724    fn request_token(&self) -> Request<'_> {
725        Request {
726            token_url: &self.token_url,
727            auth_type: self.auth_type,
728            client_id: &self.client_id,
729            client_secret: self.client_secret.as_ref(),
730            redirect_url: self.redirect_url.as_ref(),
731            params: Vec::new(),
732        }
733    }
734}
735
736/// A request wrapped in a client, ready to be executed.
737#[cfg(feature = "reqwest")]
738pub struct ReqwestClientRequest<'a> {
739    request: Request<'a>,
740    client: &'a reqwest::Client,
741}
742
743#[cfg(feature = "reqwest")]
744impl ReqwestClientRequest<'_> {
745    /// Execute the token request.
746    pub async fn execute<T>(self) -> Result<T, ExecuteError>
747    where
748        T: for<'de> Deserialize<'de>,
749    {
750        use reqwest::{header, Method};
751
752        const CONTENT_TYPE_JSON: &str = "application/json";
753
754        fn url_encode(s: &str) -> String {
755            url::form_urlencoded::byte_serialize(s.as_bytes()).collect::<String>()
756        }
757
758        let mut request = self
759            .client
760            .request(Method::POST, self.request.token_url.clone());
761
762        // Section 5.1 of RFC 6749 (https://tools.ietf.org/html/rfc6749#section-5.1) only permits
763        // JSON responses for this request. Some providers such as GitHub have off-spec behavior
764        // and not only support different response formats, but have non-JSON defaults. Explicitly
765        // request JSON here.
766        request = request.header(
767            header::ACCEPT,
768            header::HeaderValue::from_static(CONTENT_TYPE_JSON),
769        );
770
771        let request = {
772            let mut form = url::form_urlencoded::Serializer::new(String::new());
773
774            // FIXME: add support for auth extensions? e.g., client_secret_jwt and private_key_jwt
775            match self.request.auth_type {
776                AuthType::RequestBody => {
777                    form.append_pair("client_id", self.request.client_id);
778
779                    if let Some(client_secret) = self.request.client_secret {
780                        form.append_pair("client_secret", client_secret);
781                    }
782                }
783                AuthType::BasicAuth => {
784                    // Section 2.3.1 of RFC 6749 requires separately url-encoding the id and secret
785                    // before using them as HTTP Basic auth username and password. Note that this is
786                    // not standard for ordinary Basic auth, so curl won't do it for us.
787                    let username = url_encode(self.request.client_id);
788
789                    let password = self
790                        .request
791                        .client_secret
792                        .map(|client_secret| url_encode(client_secret));
793
794                    request = request.basic_auth(username, password.as_ref());
795                }
796            }
797
798            for (key, value) in self.request.params {
799                form.append_pair(key.as_ref(), value.as_ref());
800            }
801
802            if let Some(redirect_url) = &self.request.redirect_url {
803                form.append_pair("redirect_uri", redirect_url.as_str());
804            }
805
806            request = request.header(
807                header::CONTENT_TYPE,
808                header::HeaderValue::from_static("application/x-www-form-urlencoded"),
809            );
810
811            request.body(form.finish().into_bytes())
812        };
813
814        let res = request
815            .send()
816            .await
817            .map_err(|error| ExecuteErrorKind::SendError { error })?;
818
819        let status = res.status();
820
821        let body = res
822            .bytes()
823            .await
824            .map_err(|error| ExecuteErrorKind::BytesError { error })?;
825
826        if body.is_empty() {
827            return Err(ExecuteError::from(ExecuteErrorKind::EmptyResponse {
828                status,
829            }));
830        }
831
832        if !status.is_success() {
833            let error = match serde_json::from_slice::<ErrorResponse>(body.as_ref()) {
834                Ok(error) => error,
835                Err(error) => {
836                    return Err(ExecuteError::from(ExecuteErrorKind::BadResponse {
837                        status,
838                        error,
839                        body,
840                    }));
841                }
842            };
843
844            return Err(ExecuteError::from(ExecuteErrorKind::ErrorResponse {
845                status,
846                error,
847            }));
848        }
849
850        let value = serde_json::from_slice(body.as_ref()).map_err(|error| {
851            ExecuteErrorKind::BadResponse {
852                status,
853                error,
854                body,
855            }
856        })?;
857
858        Ok(value)
859    }
860}
861
862/// A token request that is in progress.
863#[cfg_attr(not(any(feature = "reqwest")), allow(unused))]
864pub struct Request<'a> {
865    token_url: &'a Url,
866    auth_type: AuthType,
867    client_id: &'a str,
868    client_secret: Option<&'a ClientSecret>,
869    /// Configured redirect URL.
870    redirect_url: Option<&'a Url>,
871    /// Extra parameters.
872    params: Vec<(Cow<'a, str>, Cow<'a, str>)>,
873}
874
875impl<'a> Request<'a> {
876    /// Set an additional request param.
877    pub fn param(mut self, key: impl Into<Cow<'a, str>>, value: impl Into<Cow<'a, str>>) -> Self {
878        self.params.push((key.into(), value.into()));
879        self
880    }
881
882    /// Wrap the request in a client.
883    #[cfg(feature = "reqwest")]
884    pub fn with_reqwest_client(self, client: &'a reqwest::Client) -> ReqwestClientRequest<'a> {
885        ReqwestClientRequest {
886            request: self,
887            client,
888        }
889    }
890}
891
892/// Basic OAuth2 authorization token types.
893#[derive(Clone, Debug, PartialEq, Serialize)]
894#[serde(rename_all = "lowercase")]
895pub enum TokenType {
896    /// Bearer token
897    /// ([OAuth 2.0 Bearer Tokens - RFC 6750](https://tools.ietf.org/html/rfc6750)).
898    Bearer,
899    /// MAC ([OAuth 2.0 Message Authentication Code (MAC)
900    /// Tokens](https://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-05)).
901    Mac,
902}
903
904impl<'de> serde::de::Deserialize<'de> for TokenType {
905    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
906    where
907        D: serde::de::Deserializer<'de>,
908    {
909        let value = String::deserialize(deserializer)?.to_lowercase();
910
911        return match value.as_str() {
912            "bearer" => Ok(TokenType::Bearer),
913            "mac" => Ok(TokenType::Mac),
914            other => Err(serde::de::Error::custom(UnknownVariantError(
915                other.to_string(),
916            ))),
917        };
918
919        #[derive(Debug)]
920        struct UnknownVariantError(String);
921
922        impl Error for UnknownVariantError {}
923
924        impl fmt::Display for UnknownVariantError {
925            fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
926                write!(fmt, "unsupported variant: {}", self.0)
927            }
928        }
929    }
930}
931
932/// Common methods shared by all OAuth2 token implementations.
933///
934/// The methods in this trait are defined in
935/// [Section 5.1 of RFC 6749](https://tools.ietf.org/html/rfc6749#section-5.1). This trait exists
936/// separately from the `StandardToken` struct to support customization by clients,
937/// such as supporting interoperability with non-standards-complaint OAuth2 providers.
938pub trait Token
939where
940    Self: for<'a> serde::de::Deserialize<'a>,
941{
942    /// REQUIRED. The access token issued by the authorization server.
943    fn access_token(&self) -> &AccessToken;
944
945    /// REQUIRED. The type of the token issued as described in
946    /// [Section 7.1](https://tools.ietf.org/html/rfc6749#section-7.1).
947    /// Value is case insensitive and deserialized to the generic `TokenType` parameter.
948    fn token_type(&self) -> &TokenType;
949
950    /// RECOMMENDED. The lifetime in seconds of the access token. For example, the value 3600
951    /// denotes that the access token will expire in one hour from the time the response was
952    /// generated. If omitted, the authorization server SHOULD provide the expiration time via
953    /// other means or document the default value.
954    fn expires_in(&self) -> Option<Duration>;
955
956    /// OPTIONAL. The refresh token, which can be used to obtain new access tokens using the same
957    /// authorization grant as described in
958    /// [Section 6](https://tools.ietf.org/html/rfc6749#section-6).
959    fn refresh_token(&self) -> Option<&RefreshToken>;
960
961    /// OPTIONAL, if identical to the scope requested by the client; otherwise, REQUIRED. The
962    /// scipe of the access token as described by
963    /// [Section 3.3](https://tools.ietf.org/html/rfc6749#section-3.3). If included in the response,
964    /// this space-delimited field is parsed into a `Vec` of individual scopes. If omitted from
965    /// the response, this field is `None`.
966    fn scopes(&self) -> Option<&Vec<Scope>>;
967}
968
969/// Standard OAuth2 token response.
970///
971/// This struct includes the fields defined in
972/// [Section 5.1 of RFC 6749](https://tools.ietf.org/html/rfc6749#section-5.1), as well as
973/// extensions defined by the `EF` type parameter.
974#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
975pub struct StandardToken {
976    access_token: AccessToken,
977    token_type: TokenType,
978    #[serde(
979        skip_serializing_if = "Option::is_none",
980        deserialize_with = "deserialize_option_number_from_string"
981    )]
982    expires_in: Option<u64>,
983    #[serde(skip_serializing_if = "Option::is_none")]
984    refresh_token: Option<RefreshToken>,
985    #[serde(rename = "scope")]
986    #[serde(deserialize_with = "helpers::deserialize_space_delimited_vec")]
987    #[serde(serialize_with = "helpers::serialize_space_delimited_vec")]
988    #[serde(skip_serializing_if = "Option::is_none")]
989    #[serde(default)]
990    scopes: Option<Vec<Scope>>,
991}
992
993impl Token for StandardToken {
994    /// REQUIRED. The access token issued by the authorization server.
995    fn access_token(&self) -> &AccessToken {
996        &self.access_token
997    }
998
999    /// REQUIRED. The type of the token issued as described in
1000    /// [Section 7.1](https://tools.ietf.org/html/rfc6749#section-7.1).
1001    /// Value is case insensitive and deserialized to the generic `TokenType` parameter.
1002    fn token_type(&self) -> &TokenType {
1003        &self.token_type
1004    }
1005
1006    /// RECOMMENDED. The lifetime in seconds of the access token. For example, the value 3600
1007    /// denotes that the access token will expire in one hour from the time the response was
1008    /// generated. If omitted, the authorization server SHOULD provide the expiration time via
1009    /// other means or document the default value.
1010    fn expires_in(&self) -> Option<Duration> {
1011        self.expires_in.map(Duration::from_secs)
1012    }
1013
1014    /// OPTIONAL. The refresh token, which can be used to obtain new access tokens using the same
1015    /// authorization grant as described in
1016    /// [Section 6](https://tools.ietf.org/html/rfc6749#section-6).
1017    fn refresh_token(&self) -> Option<&RefreshToken> {
1018        self.refresh_token.as_ref()
1019    }
1020
1021    /// OPTIONAL, if identical to the scope requested by the client; otherwise, REQUIRED. The
1022    /// scipe of the access token as described by
1023    /// [Section 3.3](https://tools.ietf.org/html/rfc6749#section-3.3). If included in the response,
1024    /// this space-delimited field is parsed into a `Vec` of individual scopes. If omitted from
1025    /// the response, this field is `None`.
1026    fn scopes(&self) -> Option<&Vec<Scope>> {
1027        self.scopes.as_ref()
1028    }
1029}
1030
1031/// These error types are defined in
1032/// [Section 5.2 of RFC 6749](https://tools.ietf.org/html/rfc6749#section-5.2).
1033#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
1034#[serde(rename_all = "snake_case")]
1035pub enum ErrorField {
1036    /// The request is missing a required parameter, includes an unsupported parameter value
1037    /// (other than grant type), repeats a parameter, includes multiple credentials, utilizes
1038    /// more than one mechanism for authenticating the client, or is otherwise malformed.
1039    InvalidRequest,
1040    /// Client authentication failed (e.g., unknown client, no client authentication included,
1041    /// or unsupported authentication method).
1042    InvalidClient,
1043    /// The provided authorization grant (e.g., authorization code, resource owner credentials)
1044    /// or refresh token is invalid, expired, revoked, does not match the redirection URI used
1045    /// in the authorization request, or was issued to another client.
1046    InvalidGrant,
1047    /// The authenticated client is not authorized to use this authorization grant type.
1048    UnauthorizedClient,
1049    /// The authorization grant type is not supported by the authorization server.
1050    UnsupportedGrantType,
1051    /// The requested scope is invalid, unknown, malformed, or exceeds the scope granted by the
1052    /// resource owner.
1053    InvalidScope,
1054    /// Other error type.
1055    Other(String),
1056}
1057
1058impl fmt::Display for ErrorField {
1059    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
1060        use self::ErrorField::*;
1061
1062        match *self {
1063            InvalidRequest => "invalid_request".fmt(fmt),
1064            InvalidClient => "invalid_client".fmt(fmt),
1065            InvalidGrant => "invalid_grant".fmt(fmt),
1066            UnauthorizedClient => "unauthorized_client".fmt(fmt),
1067            UnsupportedGrantType => "unsupported_grant_type".fmt(fmt),
1068            InvalidScope => "invalid_scope".fmt(fmt),
1069            Other(ref value) => value.fmt(fmt),
1070        }
1071    }
1072}
1073
1074/// Error response returned by server after requesting an access token.
1075///
1076/// The fields in this structure are defined in
1077/// [Section 5.2 of RFC 6749](https://tools.ietf.org/html/rfc6749#section-5.2).
1078#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
1079pub struct ErrorResponse {
1080    /// A single ASCII error code.
1081    pub error: ErrorField,
1082    #[serde(default)]
1083    #[serde(skip_serializing_if = "Option::is_none")]
1084    /// Human-readable ASCII text providing additional information, used to assist
1085    /// the client developer in understanding the error that occurred.
1086    pub error_description: Option<String>,
1087    #[serde(default)]
1088    #[serde(skip_serializing_if = "Option::is_none")]
1089    /// A URI identifying a human-readable web page with information about the error,
1090    /// used to provide the client developer with additional information about the error.
1091    pub error_uri: Option<String>,
1092}
1093
1094impl fmt::Display for ErrorResponse {
1095    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1096        let mut formatted = self.error.to_string();
1097
1098        if let Some(error_description) = self.error_description.as_ref() {
1099            formatted.push_str(": ");
1100            formatted.push_str(error_description);
1101        }
1102
1103        if let Some(error_uri) = self.error_uri.as_ref() {
1104            formatted.push_str(" / See ");
1105            formatted.push_str(error_uri);
1106        }
1107
1108        write!(f, "{formatted}")
1109    }
1110}
1111
1112impl Error for ErrorResponse {}
1113
1114/// Errors when creating new clients.
1115pub struct NewClientError {
1116    kind: NewClientErrorKind,
1117}
1118
1119impl Error for NewClientError {
1120    #[inline]
1121    fn source(&self) -> Option<&(dyn Error + 'static)> {
1122        match self.kind {
1123            #[cfg(feature = "reqwest")]
1124            NewClientErrorKind::Reqwest { ref error } => Some(error),
1125        }
1126    }
1127}
1128
1129impl fmt::Display for NewClientError {
1130    #[inline]
1131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1132        self.kind.fmt(f)
1133    }
1134}
1135
1136impl fmt::Debug for NewClientError {
1137    #[inline]
1138    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1139        self.kind.fmt(f)
1140    }
1141}
1142
1143#[derive(Debug)]
1144enum NewClientErrorKind {
1145    /// Error creating underlying reqwest client.
1146    #[cfg(feature = "reqwest")]
1147    Reqwest { error: reqwest::Error },
1148}
1149
1150impl fmt::Display for NewClientErrorKind {
1151    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
1152        match *self {
1153            #[cfg(feature = "reqwest")]
1154            NewClientErrorKind::Reqwest { .. } => "error constructing reqwest client".fmt(_f),
1155        }
1156    }
1157}
1158
1159#[cfg(feature = "reqwest")]
1160impl From<reqwest::Error> for NewClientError {
1161    #[inline]
1162    fn from(error: reqwest::Error) -> Self {
1163        Self {
1164            kind: NewClientErrorKind::Reqwest { error },
1165        }
1166    }
1167}
1168
1169/// Error encountered while requesting access token.
1170pub struct ExecuteError {
1171    kind: ExecuteErrorKind,
1172}
1173
1174impl From<ExecuteErrorKind> for ExecuteError {
1175    #[inline]
1176    fn from(kind: ExecuteErrorKind) -> Self {
1177        Self { kind }
1178    }
1179}
1180
1181impl Error for ExecuteError {
1182    #[inline]
1183    fn source(&self) -> Option<&(dyn Error + 'static)> {
1184        match self.kind {
1185            #[cfg(feature = "reqwest")]
1186            ExecuteErrorKind::SendError { ref error } => Some(error),
1187            #[cfg(feature = "reqwest")]
1188            ExecuteErrorKind::BytesError { ref error } => Some(error),
1189            ExecuteErrorKind::BadResponse { ref error, .. } => Some(error),
1190            ExecuteErrorKind::ErrorResponse { ref error, .. } => Some(error),
1191            ExecuteErrorKind::EmptyResponse { .. } => None,
1192        }
1193    }
1194}
1195
1196impl fmt::Display for ExecuteError {
1197    #[inline]
1198    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1199        self.kind.fmt(f)
1200    }
1201}
1202
1203impl fmt::Debug for ExecuteError {
1204    #[inline]
1205    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1206        self.kind.fmt(f)
1207    }
1208}
1209
1210#[derive(Debug)]
1211enum ExecuteErrorKind {
1212    #[cfg(feature = "reqwest")]
1213    SendError {
1214        /// Original request error.
1215        error: reqwest::Error,
1216    },
1217    #[cfg(feature = "reqwest")]
1218    BytesError {
1219        /// Original request error.
1220        error: reqwest::Error,
1221    },
1222    /// Failed to parse server response. Parse errors may occur while parsing either successful
1223    /// or error responses.
1224    #[cfg_attr(not(any(feature = "reqwest")), allow(unused))]
1225    BadResponse {
1226        /// The status code associated with the response.
1227        status: StatusCode,
1228        /// The body that couldn't be deserialized.
1229        body: Bytes,
1230        /// Deserialization error.
1231        error: serde_json::error::Error,
1232    },
1233    /// Response with non-successful status code and a body that could be
1234    /// successfully deserialized as an [ErrorResponse].
1235    #[cfg_attr(not(any(feature = "reqwest")), allow(unused))]
1236    ErrorResponse {
1237        /// The status code associated with the response.
1238        status: StatusCode,
1239        /// The deserialized response.
1240        error: ErrorResponse,
1241    },
1242    /// Server response was empty.
1243    #[cfg_attr(not(any(feature = "reqwest")), allow(unused))]
1244    EmptyResponse {
1245        /// The status code associated with the empty response.
1246        status: StatusCode,
1247    },
1248}
1249
1250impl fmt::Display for ExecuteErrorKind {
1251    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1252        match *self {
1253            #[cfg(feature = "reqwest")]
1254            ExecuteErrorKind::SendError { .. } => "error sending request".fmt(f),
1255            #[cfg(feature = "reqwest")]
1256            ExecuteErrorKind::BytesError { .. } => "error reading response bytes".fmt(f),
1257            ExecuteErrorKind::BadResponse { status, .. } => {
1258                write!(f, "malformed server response: {status}")
1259            }
1260            ExecuteErrorKind::ErrorResponse { status, .. } => {
1261                write!(f, "request resulted in error response: {status}")
1262            }
1263            ExecuteErrorKind::EmptyResponse { status } => {
1264                write!(f, "request resulted in empty response: {status}")
1265            }
1266        }
1267    }
1268}
1269
1270impl ExecuteError {
1271    /// Access the status code of the error if available.
1272    #[inline]
1273    pub fn status(&self) -> Option<StatusCode> {
1274        match self.kind {
1275            #[cfg(feature = "reqwest")]
1276            ExecuteErrorKind::SendError { ref error } => error.status(),
1277            #[cfg(feature = "reqwest")]
1278            ExecuteErrorKind::BytesError { ref error } => error.status(),
1279            ExecuteErrorKind::BadResponse { status, .. } => Some(status),
1280            ExecuteErrorKind::ErrorResponse { status, .. } => Some(status),
1281            ExecuteErrorKind::EmptyResponse { status, .. } => Some(status),
1282        }
1283    }
1284
1285    /// The original response body if available.
1286    pub fn body(&self) -> Option<&Bytes> {
1287        match self.kind {
1288            ExecuteErrorKind::BadResponse { ref body, .. } => Some(body),
1289            _ => None,
1290        }
1291    }
1292}
1293
1294/// Helper methods used by OAuth2 implementations/extensions.
1295pub mod helpers {
1296    use alloc::string::{String, ToString};
1297    use alloc::vec::Vec;
1298
1299    use serde::{Deserialize, Deserializer, Serializer};
1300    use url::Url;
1301
1302    /// Serde space-delimited string deserializer for a `Vec<String>`.
1303    ///
1304    /// This function splits a JSON string at each space character into a `Vec<String>` .
1305    ///
1306    /// # Example
1307    ///
1308    /// In example below, the JSON value `{"items": "foo bar baz"}` would deserialize to:
1309    ///
1310    /// ```
1311    /// # struct GroceryBasket {
1312    /// #     items: Vec<String>,
1313    /// # }
1314    /// # fn main() {
1315    /// GroceryBasket {
1316    ///     items: vec!["foo".to_string(), "bar".to_string(), "baz".to_string()]
1317    /// };
1318    /// # }
1319    /// ```
1320    ///
1321    /// Note: this example does not compile automatically due to
1322    /// [Rust issue #29286](https://github.com/rust-lang/rust/issues/29286).
1323    ///
1324    /// ```
1325    /// # /*
1326    /// use serde::Deserialize;
1327    ///
1328    /// #[derive(Deserialize)]
1329    /// struct GroceryBasket {
1330    ///     #[serde(deserialize_with = "helpers::deserialize_space_delimited_vec")]
1331    ///     items: Vec<String>,
1332    /// }
1333    /// # */
1334    /// ```
1335    pub fn deserialize_space_delimited_vec<'de, T, D>(deserializer: D) -> Result<T, D::Error>
1336    where
1337        T: Default + Deserialize<'de>,
1338        D: Deserializer<'de>,
1339    {
1340        use serde::de::Error;
1341        use serde_json::Value;
1342
1343        if let Some(space_delimited) = Option::<String>::deserialize(deserializer)? {
1344            let entries = space_delimited
1345                .split(' ')
1346                .map(|s| Value::String(s.to_string()))
1347                .collect();
1348            return T::deserialize(Value::Array(entries)).map_err(Error::custom);
1349        }
1350
1351        // If the JSON value is null, use the default value.
1352        Ok(T::default())
1353    }
1354
1355    /// Serde space-delimited string serializer for an `Option<Vec<String>>`.
1356    ///
1357    /// This function serializes a string vector into a single space-delimited string.
1358    /// If `string_vec_opt` is `None`, the function serializes it as `None` (e.g., `null`
1359    /// in the case of JSON serialization).
1360    pub fn serialize_space_delimited_vec<T, S>(
1361        vec_opt: &Option<Vec<T>>,
1362        serializer: S,
1363    ) -> Result<S::Ok, S::Error>
1364    where
1365        T: AsRef<str>,
1366        S: Serializer,
1367    {
1368        if let Some(ref vec) = *vec_opt {
1369            let space_delimited = vec.iter().map(|s| s.as_ref()).collect::<Vec<_>>().join(" ");
1370            serializer.serialize_str(&space_delimited)
1371        } else {
1372            serializer.serialize_none()
1373        }
1374    }
1375
1376    /// Serde string deserializer for a `Url`.
1377    pub fn deserialize_url<'de, D>(deserializer: D) -> Result<Url, D::Error>
1378    where
1379        D: Deserializer<'de>,
1380    {
1381        use serde::de::Error;
1382        let url_str = String::deserialize(deserializer)?;
1383        Url::parse(url_str.as_ref()).map_err(Error::custom)
1384    }
1385
1386    /// Serde string serializer for a `Url`.
1387    pub fn serialize_url<S>(url: &Url, serializer: S) -> Result<S::Ok, S::Error>
1388    where
1389        S: Serializer,
1390    {
1391        serializer.serialize_str(url.as_str())
1392    }
1393}