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.
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 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.
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 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 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.
572 ///
573 #[derive(Clone, Deserialize, Serialize)]
574 ClientSecret(String)
575];
576new_secret_type 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];