oauth2/
types.rs

1use std::convert::Into;
2use std::fmt::Error as FormatterError;
3use std::fmt::{Debug, Formatter};
4use std::ops::Deref;
5
6use rand::{thread_rng, Rng};
7use serde::{Deserialize, Serialize};
8use sha2::{Digest, Sha256};
9use url::Url;
10
11macro_rules! new_type {
12    // Convenience pattern without an impl.
13    (
14        $(#[$attr:meta])*
15        $name:ident(
16            $(#[$type_attr:meta])*
17            $type:ty
18        )
19    ) => {
20        new_type![
21            @new_type $(#[$attr])*,
22            $name(
23                $(#[$type_attr])*
24                $type
25            ),
26            concat!(
27                "Create a new `",
28                stringify!($name),
29                "` to wrap the given `",
30                stringify!($type),
31                "`."
32            ),
33            impl {}
34        ];
35    };
36    // Main entry point with an impl.
37    (
38        $(#[$attr:meta])*
39        $name:ident(
40            $(#[$type_attr:meta])*
41            $type:ty
42        )
43        impl {
44            $($item:tt)*
45        }
46    ) => {
47        new_type![
48            @new_type $(#[$attr])*,
49            $name(
50                $(#[$type_attr])*
51                $type
52            ),
53            concat!(
54                "Create a new `",
55                stringify!($name),
56                "` to wrap the given `",
57                stringify!($type),
58                "`."
59            ),
60            impl {
61                $($item)*
62            }
63        ];
64    };
65    // Actual implementation, after stringifying the #[doc] attr.
66    (
67        @new_type $(#[$attr:meta])*,
68        $name:ident(
69            $(#[$type_attr:meta])*
70            $type:ty
71        ),
72        $new_doc:expr,
73        impl {
74            $($item:tt)*
75        }
76    ) => {
77        $(#[$attr])*
78        #[derive(Clone, Debug, PartialEq)]
79        pub struct $name(
80            $(#[$type_attr])*
81            $type
82        );
83        impl $name {
84            $($item)*
85
86            #[doc = $new_doc]
87            pub const fn new(s: $type) -> Self {
88                $name(s)
89            }
90        }
91        impl Deref for $name {
92            type Target = $type;
93            fn deref(&self) -> &$type {
94                &self.0
95            }
96        }
97        impl Into<$type> for $name {
98            fn into(self) -> $type {
99                self.0
100            }
101        }
102    }
103}
104
105macro_rules! new_secret_type {
106    (
107        $(#[$attr:meta])*
108        $name:ident($type:ty)
109    ) => {
110        new_secret_type![
111            $(#[$attr])*
112            $name($type)
113            impl {}
114        ];
115    };
116    (
117        $(#[$attr:meta])*
118        $name:ident($type:ty)
119        impl {
120            $($item:tt)*
121        }
122    ) => {
123        new_secret_type![
124            $(#[$attr])*,
125            $name($type),
126            concat!(
127                "Create a new `",
128                stringify!($name),
129                "` to wrap the given `",
130                stringify!($type),
131                "`."
132            ),
133            concat!("Get the secret contained within this `", stringify!($name), "`."),
134            impl {
135                $($item)*
136            }
137        ];
138    };
139    (
140        $(#[$attr:meta])*,
141        $name:ident($type:ty),
142        $new_doc:expr,
143        $secret_doc:expr,
144        impl {
145            $($item:tt)*
146        }
147    ) => {
148        $(
149            #[$attr]
150        )*
151        pub struct $name($type);
152        impl $name {
153            $($item)*
154
155            #[doc = $new_doc]
156            pub fn new(s: $type) -> Self {
157                $name(s)
158            }
159            ///
160            #[doc = $secret_doc]
161            ///
162            /// # Security Warning
163            ///
164            /// Leaking this value may compromise the security of the OAuth2 flow.
165            ///
166            pub fn secret(&self) -> &$type { &self.0 }
167        }
168        impl Debug for $name {
169            fn fmt(&self, f: &mut Formatter) -> Result<(), FormatterError> {
170                write!(f, concat!(stringify!($name), "([redacted])"))
171            }
172        }
173    };
174}
175
176///
177/// Creates a URL-specific new type
178///
179/// Types created by this macro enforce during construction that the contained value represents a
180/// syntactically valid URL. However, comparisons and hashes of these types are based on the string
181/// representation given during construction, disregarding any canonicalization performed by the
182/// underlying `Url` struct. OpenID Connect requires certain URLs (e.g., ID token issuers) to be
183/// compared exactly, without canonicalization.
184///
185/// In addition to the raw string representation, these types include a `url` method to retrieve a
186/// parsed `Url` struct.
187///
188macro_rules! new_url_type {
189    // Convenience pattern without an impl.
190    (
191        $(#[$attr:meta])*
192        $name:ident
193    ) => {
194        new_url_type![
195            @new_type_pub $(#[$attr])*,
196            $name,
197            concat!("Create a new `", stringify!($name), "` from a `String` to wrap a URL."),
198            concat!("Create a new `", stringify!($name), "` from a `Url` to wrap a URL."),
199            concat!("Return this `", stringify!($name), "` as a parsed `Url`."),
200            impl {}
201        ];
202    };
203    // Main entry point with an impl.
204    (
205        $(#[$attr:meta])*
206        $name:ident
207        impl {
208            $($item:tt)*
209        }
210    ) => {
211        new_url_type![
212            @new_type_pub $(#[$attr])*,
213            $name,
214            concat!("Create a new `", stringify!($name), "` from a `String` to wrap a URL."),
215            concat!("Create a new `", stringify!($name), "` from a `Url` to wrap a URL."),
216            concat!("Return this `", stringify!($name), "` as a parsed `Url`."),
217            impl {
218                $($item)*
219            }
220        ];
221    };
222    // Actual implementation, after stringifying the #[doc] attr.
223    (
224        @new_type_pub $(#[$attr:meta])*,
225        $name:ident,
226        $new_doc:expr,
227        $from_url_doc:expr,
228        $url_doc:expr,
229        impl {
230            $($item:tt)*
231        }
232    ) => {
233        $(#[$attr])*
234        #[derive(Clone)]
235        pub struct $name(Url, String);
236        impl $name {
237            #[doc = $new_doc]
238            pub fn new(url: String) -> Result<Self, ::url::ParseError> {
239                Ok($name(Url::parse(&url)?, url))
240            }
241            #[doc = $from_url_doc]
242            pub fn from_url(url: Url) -> Self {
243                let s = url.to_string();
244                Self(url, s)
245            }
246            #[doc = $url_doc]
247            pub fn url(&self) -> &Url {
248                return &self.0;
249            }
250            $($item)*
251        }
252        impl Deref for $name {
253            type Target = String;
254            fn deref(&self) -> &String {
255                &self.1
256            }
257        }
258        impl ::std::fmt::Debug for $name {
259            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
260                let mut debug_trait_builder = f.debug_tuple(stringify!($name));
261                debug_trait_builder.field(&self.1);
262                debug_trait_builder.finish()
263            }
264        }
265        impl<'de> ::serde::Deserialize<'de> for $name {
266            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
267            where
268                D: ::serde::de::Deserializer<'de>,
269            {
270                struct UrlVisitor;
271                impl<'de> ::serde::de::Visitor<'de> for UrlVisitor {
272                    type Value = $name;
273
274                    fn expecting(
275                        &self,
276                        formatter: &mut ::std::fmt::Formatter
277                    ) -> ::std::fmt::Result {
278                        formatter.write_str(stringify!($name))
279                    }
280
281                    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
282                    where
283                        E: ::serde::de::Error,
284                    {
285                        $name::new(v.to_string()).map_err(E::custom)
286                    }
287                }
288                deserializer.deserialize_str(UrlVisitor {})
289            }
290        }
291        impl ::serde::Serialize for $name {
292            fn serialize<SE>(&self, serializer: SE) -> Result<SE::Ok, SE::Error>
293            where
294                SE: ::serde::Serializer,
295            {
296                serializer.serialize_str(&self.1)
297            }
298        }
299        impl ::std::hash::Hash for $name {
300            fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) -> () {
301                ::std::hash::Hash::hash(&(self.1), state);
302            }
303        }
304        impl Ord for $name {
305            fn cmp(&self, other: &$name) -> ::std::cmp::Ordering {
306                self.1.cmp(&other.1)
307            }
308        }
309        impl PartialOrd for $name {
310            fn partial_cmp(&self, other: &$name) -> Option<::std::cmp::Ordering> {
311                Some(self.cmp(other))
312            }
313        }
314        impl PartialEq for $name {
315            fn eq(&self, other: &$name) -> bool {
316                self.1 == other.1
317            }
318        }
319        impl Eq for $name {}
320    };
321}
322
323new_type![
324    ///
325    /// Client identifier issued to the client during the registration process described by
326    /// [Section 2.2](https://tools.ietf.org/html/rfc6749#section-2.2).
327    ///
328    #[derive(Deserialize, Serialize, Eq, Hash)]
329    ClientId(String)
330];
331
332new_url_type![
333    ///
334    /// URL of the authorization server's authorization endpoint.
335    ///
336    AuthUrl
337];
338new_url_type![
339    ///
340    /// URL of the authorization server's token endpoint.
341    ///
342    TokenUrl
343];
344new_url_type![
345    ///
346    /// URL of the client's redirection endpoint.
347    ///
348    RedirectUrl
349];
350new_url_type![
351    ///
352    /// URL of the client's [RFC 7662 OAuth 2.0 Token Introspection](https://tools.ietf.org/html/rfc7662) endpoint.
353    ///
354    IntrospectionUrl
355];
356new_url_type![
357    ///
358    /// URL of the authorization server's RFC 7009 token revocation endpoint.
359    ///
360    RevocationUrl
361];
362new_url_type![
363    ///
364    /// URL of the client's device authorization endpoint.
365    ///
366    DeviceAuthorizationUrl
367];
368new_url_type![
369    ///
370    /// URL of the end-user verification URI on the authorization server.
371    ///
372    EndUserVerificationUrl
373];
374new_type![
375    ///
376    /// Authorization endpoint response (grant) type defined in
377    /// [Section 3.1.1](https://tools.ietf.org/html/rfc6749#section-3.1.1).
378    ///
379    #[derive(Deserialize, Serialize, Eq, Hash)]
380    ResponseType(String)
381];
382new_type![
383    ///
384    /// Resource owner's username used directly as an authorization grant to obtain an access
385    /// token.
386    ///
387    #[derive(Deserialize, Serialize, Eq, Hash)]
388    ResourceOwnerUsername(String)
389];
390
391new_type![
392    ///
393    /// Access token scope, as defined by the authorization server.
394    ///
395    #[derive(Deserialize, Serialize, Eq, Hash)]
396    Scope(String)
397];
398impl AsRef<str> for Scope {
399    fn as_ref(&self) -> &str {
400        self
401    }
402}
403
404new_type![
405    ///
406    /// Code Challenge Method used for [PKCE](https://tools.ietf.org/html/rfc7636) protection
407    /// via the `code_challenge_method` parameter.
408    ///
409    #[derive(Deserialize, Serialize, Eq, Hash)]
410    PkceCodeChallengeMethod(String)
411];
412// This type intentionally does not implement Clone in order to make it difficult to reuse PKCE
413// challenges across multiple requests.
414new_secret_type![
415    ///
416    /// Code Verifier used for [PKCE](https://tools.ietf.org/html/rfc7636) protection via the
417    /// `code_verifier` parameter. The value must have a minimum length of 43 characters and a
418    /// maximum length of 128 characters.  Each character must be ASCII alphanumeric or one of
419    /// the characters "-" / "." / "_" / "~".
420    ///
421    #[derive(Deserialize, Serialize)]
422    PkceCodeVerifier(String)
423];
424
425///
426/// Code Challenge used for [PKCE](https://tools.ietf.org/html/rfc7636) protection via the
427/// `code_challenge` parameter.
428///
429#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
430pub struct PkceCodeChallenge {
431    code_challenge: String,
432    code_challenge_method: PkceCodeChallengeMethod,
433}
434impl PkceCodeChallenge {
435    ///
436    /// Generate a new random, base64-encoded SHA-256 PKCE code.
437    ///
438    pub fn new_random_sha256() -> (Self, PkceCodeVerifier) {
439        Self::new_random_sha256_len(32)
440    }
441
442    ///
443    /// Generate a new random, base64-encoded SHA-256 PKCE challenge code and 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    ///
451    /// # Panics
452    ///
453    /// This method panics if the resulting PKCE code verifier is not of a suitable length
454    /// to comply with [RFC 7636](https://tools.ietf.org/html/rfc7636).
455    ///
456    pub fn new_random_sha256_len(num_bytes: u32) -> (Self, PkceCodeVerifier) {
457        let code_verifier = Self::new_random_len(num_bytes);
458        (
459            Self::from_code_verifier_sha256(&code_verifier),
460            code_verifier,
461        )
462    }
463
464    ///
465    /// Generate a new random, base64-encoded PKCE code verifier.
466    ///
467    /// # Arguments
468    ///
469    /// * `num_bytes` - Number of random bytes to generate, prior to base64-encoding.
470    ///   The value must be in the range 32 to 96 inclusive in order to generate a verifier
471    ///   with a suitable length.
472    ///
473    /// # Panics
474    ///
475    /// This method panics if the resulting PKCE code verifier is not of a suitable length
476    /// to comply with [RFC 7636](https://tools.ietf.org/html/rfc7636).
477    ///
478    fn new_random_len(num_bytes: u32) -> PkceCodeVerifier {
479        // The RFC specifies that the code verifier must have "a minimum length of 43
480        // characters and a maximum length of 128 characters".
481        // This implies 32-96 octets of random data to be base64 encoded.
482        assert!(num_bytes >= 32 && num_bytes <= 96);
483        let random_bytes: Vec<u8> = (0..num_bytes).map(|_| thread_rng().gen::<u8>()).collect();
484        PkceCodeVerifier::new(base64::encode_config(
485            &random_bytes,
486            base64::URL_SAFE_NO_PAD,
487        ))
488    }
489
490    ///
491    /// Generate a SHA-256 PKCE code challenge from the supplied PKCE code verifier.
492    ///
493    /// # Panics
494    ///
495    /// This method panics if the supplied PKCE code verifier is not of a suitable length
496    /// to comply with [RFC 7636](https://tools.ietf.org/html/rfc7636).
497    ///
498    pub fn from_code_verifier_sha256(code_verifier: &PkceCodeVerifier) -> Self {
499        // The RFC specifies that the code verifier must have "a minimum length of 43
500        // characters and a maximum length of 128 characters".
501        assert!(code_verifier.secret().len() >= 43 && code_verifier.secret().len() <= 128);
502
503        let digest = Sha256::digest(code_verifier.secret().as_bytes());
504        let code_challenge = base64::encode_config(&digest, base64::URL_SAFE_NO_PAD);
505
506        Self {
507            code_challenge,
508            code_challenge_method: PkceCodeChallengeMethod::new("S256".to_string()),
509        }
510    }
511
512    ///
513    /// Generate a new random, base64-encoded PKCE code.
514    /// Use is discouraged unless the endpoint does not support SHA-256.
515    ///
516    /// # Panics
517    ///
518    /// This method panics if the supplied PKCE code verifier is not of a suitable length
519    /// to comply with [RFC 7636](https://tools.ietf.org/html/rfc7636).
520    ///
521    #[cfg(feature = "pkce-plain")]
522    pub fn new_random_plain() -> (Self, PkceCodeVerifier) {
523        let code_verifier = Self::new_random_len(32);
524        (
525            Self::from_code_verifier_plain(&code_verifier),
526            code_verifier,
527        )
528    }
529
530    ///
531    /// Generate a plain PKCE code challenge from the supplied PKCE code verifier.
532    /// Use is discouraged unless the endpoint does not support SHA-256.
533    ///
534    /// # Panics
535    ///
536    /// This method panics if the supplied PKCE code verifier is not of a suitable length
537    /// to comply with [RFC 7636](https://tools.ietf.org/html/rfc7636).
538    ///
539    #[cfg(feature = "pkce-plain")]
540    pub fn from_code_verifier_plain(code_verifier: &PkceCodeVerifier) -> Self {
541        // The RFC specifies that the code verifier must have "a minimum length of 43
542        // characters and a maximum length of 128 characters".
543        assert!(code_verifier.secret().len() >= 43 && code_verifier.secret().len() <= 128);
544
545        let code_challenge = code_verifier.secret().clone();
546
547        Self {
548            code_challenge,
549            code_challenge_method: PkceCodeChallengeMethod::new("plain".to_string()),
550        }
551    }
552
553    ///
554    /// Returns the PKCE code challenge as a string.
555    ///
556    pub fn as_str(&self) -> &str {
557        &self.code_challenge
558    }
559
560    ///
561    /// Returns the PKCE code challenge method as a string.
562    ///
563    pub fn method(&self) -> &PkceCodeChallengeMethod {
564        &self.code_challenge_method
565    }
566}
567
568new_secret_type![
569    ///
570    /// Client password issued to the client during the registration process described by
571    /// [Section 2.2](https://tools.ietf.org/html/rfc6749#section-2.2).
572    ///
573    #[derive(Clone, Deserialize, Serialize)]
574    ClientSecret(String)
575];
576new_secret_type![
577    ///
578    /// Value used for [CSRF](https://tools.ietf.org/html/rfc6749#section-10.12) protection
579    /// via the `state` parameter.
580    ///
581    #[must_use]
582    #[derive(Clone, Deserialize, Serialize)]
583    CsrfToken(String)
584    impl {
585        ///
586        /// Generate a new random, base64-encoded 128-bit CSRF token.
587        ///
588        pub fn new_random() -> Self {
589            CsrfToken::new_random_len(16)
590        }
591        ///
592        /// Generate a new random, base64-encoded CSRF token of the specified length.
593        ///
594        /// # Arguments
595        ///
596        /// * `num_bytes` - Number of random bytes to generate, prior to base64-encoding.
597        ///
598        pub fn new_random_len(num_bytes: u32) -> Self {
599            let random_bytes: Vec<u8> = (0..num_bytes).map(|_| thread_rng().gen::<u8>()).collect();
600            CsrfToken::new(base64::encode_config(&random_bytes, base64::URL_SAFE_NO_PAD))
601        }
602    }
603];
604new_secret_type![
605    ///
606    /// Authorization code returned from the authorization endpoint.
607    ///
608    #[derive(Clone, Deserialize, Serialize)]
609    AuthorizationCode(String)
610];
611new_secret_type![
612    ///
613    /// Refresh token used to obtain a new access token (if supported by the authorization server).
614    ///
615    #[derive(Clone, Deserialize, Serialize)]
616    RefreshToken(String)
617];
618new_secret_type![
619    ///
620    /// Access token returned by the token endpoint and used to access protected resources.
621    ///
622    #[derive(Clone, Deserialize, Serialize)]
623    AccessToken(String)
624];
625new_secret_type![
626    ///
627    /// Resource owner's password used directly as an authorization grant to obtain an access
628    /// token.
629    ///
630    #[derive(Clone)]
631    ResourceOwnerPassword(String)
632];
633new_secret_type![
634    ///
635    /// Device code returned by the device authorization endpoint and used to query the token endpoint.
636    ///
637    #[derive(Clone, Deserialize, Serialize)]
638    DeviceCode(String)
639];
640new_secret_type![
641    ///
642    /// Verification URI returned by the device authorization endpoint and visited by the user
643    /// to authorize.  Contains the user code.
644    ///
645    #[derive(Clone, Deserialize, Serialize)]
646    VerificationUriComplete(String)
647];
648new_secret_type![
649    ///
650    /// User code returned by the device authorization endpoint and used by the user to authorize at
651    /// the verification URI.
652    ///
653    #[derive(Clone, Deserialize, Serialize)]
654    UserCode(String)
655];