biscuit/
lib.rs

1//! [![Build Status](https://travis-ci.org/lawliet89/biscuit.svg)](https://travis-ci.org/lawliet89/biscuit)
2//! [![Crates.io](https://img.shields.io/crates/v/biscuit.svg)](https://crates.io/crates/biscuit)
3//! [![Repository](https://img.shields.io/github/tag/lawliet89/biscuit.svg)](https://github.com/lawliet89/biscuit)
4//! [![Documentation](https://docs.rs/biscuit/badge.svg)](https://docs.rs/biscuit)
5//! [![dependency status](https://deps.rs/repo/github/lawliet89/biscuit/status.svg)](https://deps.rs/repo/github/lawliet89/biscuit)
6//!
7//! - Documentation:  [stable](https://docs.rs/biscuit/)
8//! - Changelog: [Link](https://github.com/lawliet89/biscuit/blob/master/CHANGELOG.md)
9//!
10//! A library to work with Javascript Object Signing and Encryption(JOSE),
11//! including JSON Web Tokens (JWT), JSON Web Signature (JWS) and JSON Web Encryption (JWE)
12//!
13//! This was based off [`Keats/rust-jwt`](https://github.com/Keats/rust-jwt).
14//!
15//! ## Installation
16//!
17//! Add the following to Cargo.toml:
18//!
19//! ```toml
20//! biscuit = "0.7.0"
21//! ```
22//!
23//! To use the latest `master` branch, for example:
24//!
25//! ```toml
26//! biscuit = { git = "https://github.com/lawliet89/biscuit", branch = "master" }
27//! ```
28//!
29//! See [`JWT`] for common usage examples.
30//!
31//! ## Supported Features
32//! The crate does not support all, and probably will never support all of
33//! the features described in the various RFCs, including some algorithms and verification.
34//!
35//! See the [documentation](https://github.com/lawliet89/biscuit/blob/master/doc/supported.md) for more information.
36//!
37//! ## References
38//! - [JWT Handbook](https://auth0.com/e-books/jwt-handbook) — great introduction to JWT
39//! - [IANA JOSE Registry](https://www.iana.org/assignments/jose/jose.xhtml)
40//!
41//! ### RFCs
42//! - [JSON Web Tokens RFC](https://tools.ietf.org/html/rfc7519)
43//! - [JSON Web Signature RFC](https://tools.ietf.org/html/rfc7515)
44//! - [JSON Web Algorithms RFC](https://tools.ietf.org/html/rfc7518)
45//! - [JSON Web Encryption RFC](https://tools.ietf.org/html/rfc7516)
46//! - [JSON Web Signature (JWS) Unencoded Payload Option](https://tools.ietf.org/html/rfc7797)
47//! - [CFRG Elliptic Curve Diffie-Hellman (ECDH) and Signatures in JOSE](https://tools.ietf.org/html/rfc8037)
48//! - [JWS Unencoded Payload Option](https://tools.ietf.org/html/rfc7797)
49//! - [JWK Thumbprint](https://tools.ietf.org/html/rfc7638)
50
51#![allow(
52    missing_copy_implementations,
53    missing_debug_implementations,
54    unknown_lints
55)]
56#![allow(
57    clippy::try_err,
58    clippy::needless_doctest_main,
59    clippy::upper_case_acronyms
60)]
61#![deny(
62    arithmetic_overflow,
63    bad_style,
64    dead_code,
65    improper_ctypes,
66    missing_docs,
67    mutable_transmutes,
68    no_mangle_const_items,
69    non_camel_case_types,
70    non_shorthand_field_patterns,
71    non_upper_case_globals,
72    overflowing_literals,
73    path_statements,
74    patterns_in_fns_without_body,
75    stable_features,
76    trivial_casts,
77    trivial_numeric_casts,
78    unconditional_recursion,
79    unknown_crate_types,
80    unreachable_code,
81    unused_allocation,
82    unused_assignments,
83    unused_attributes,
84    unused_comparisons,
85    unused_extern_crates,
86    unused_features,
87    unused_import_braces,
88    unused_imports,
89    unused_must_use,
90    unused_mut,
91    unused_parens,
92    unused_qualifications,
93    unused_results,
94    unused_unsafe,
95    unused_variables,
96    variant_size_differences,
97    while_true
98)]
99#![doc(test(attr(allow(unused_variables), deny(warnings))))]
100#![cfg_attr(feature = "strict", deny(warnings))]
101// See regression in nightly: https://github.com/rust-lang/rust/issues/70814
102#![cfg_attr(feature = "strict", allow(unused_braces))]
103// FIXME
104#![cfg_attr(feature = "strict", allow(deprecated))]
105// FIXME
106#![cfg_attr(feature = "strict", allow(clippy::large_enum_variant))]
107
108use std::borrow::Borrow;
109use std::fmt::{self, Debug, Display};
110use std::iter;
111use std::ops::Deref;
112use std::str::{self, FromStr};
113
114use chrono::{DateTime, Duration, NaiveDateTime, Utc};
115use data_encoding::BASE64URL_NOPAD;
116use serde::de::{self, DeserializeOwned};
117use serde::{Deserialize, Deserializer, Serialize, Serializer};
118
119mod helpers;
120pub use crate::helpers::*;
121
122#[cfg(test)]
123#[macro_use]
124mod test;
125
126#[macro_use]
127mod serde_custom;
128
129#[macro_use]
130mod macros;
131
132pub mod errors;
133pub mod jwa;
134pub mod jwe;
135pub mod jwk;
136pub mod jws;
137
138pub mod digest;
139
140use crate::errors::{Error, ValidationError};
141
142/// A convenience type alias of the common "JWT" which is a secured/unsecured compact JWS.
143/// Type `T` is the type of the private claims, and type `H` is the type of private header fields
144///
145/// # Examples
146/// ## Encoding and decoding with HS256
147///
148/// ```
149/// use biscuit::*;
150/// use biscuit::jws::*;
151/// use biscuit::jwa::*;
152/// use serde::{Serialize, Deserialize};
153///
154/// # fn main() {
155///
156/// // Define our own private claims
157/// #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
158/// struct PrivateClaims {
159///     company: String,
160///     department: String,
161/// }
162///
163/// let signing_secret = Secret::Bytes("secret".to_string().into_bytes());
164///
165/// let expected_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.\
166///        eyJpc3MiOiJodHRwczovL3d3dy5hY21lLmNvbS8iLCJzdWIiOiJKb2huIERvZSIsImF1ZCI6Imh0dHBzOi8vYWNtZ\
167///        S1jdXN0b21lci5jb20vIiwibmJmIjoxMjM0LCJjb21wYW55IjoiQUNNRSIsImRlcGFydG1lbnQiOiJUb2lsZXQgQ2\
168///        xlYW5pbmcifQ.VFCl2un1Kc17odzOe2Ehf4DVrWddu3U4Ux3GFpOZHtc";
169///
170/// let expected_claims = ClaimsSet::<PrivateClaims> {
171///     registered: RegisteredClaims {
172///         issuer: Some("https://www.acme.com/".to_string()),
173///         subject: Some("John Doe".to_string()),
174///         audience:
175///             Some(SingleOrMultiple::Single("https://acme-customer.com/".to_string())),
176///         not_before: Some(1234.into()),
177///         ..Default::default()
178///     },
179///     private: PrivateClaims {
180///         department: "Toilet Cleaning".to_string(),
181///         company: "ACME".to_string(),
182///     },
183/// };
184///
185/// let expected_jwt = JWT::new_decoded(From::from(
186///                                         RegisteredHeader {
187///                                             algorithm: SignatureAlgorithm::HS256,
188///                                             ..Default::default()
189///                                         }),
190///                                     expected_claims.clone());
191///
192/// let token = expected_jwt
193///     .into_encoded(&signing_secret).unwrap();
194/// let token = token.unwrap_encoded().to_string();
195/// assert_eq!(expected_token, token);
196/// // Now, send `token` to your clients
197///
198/// // ... some time later, we get token back!
199///
200/// let token = JWT::<_, biscuit::Empty>::new_encoded(&token);
201/// let token = token.into_decoded(&signing_secret,
202///     SignatureAlgorithm::HS256).unwrap();
203/// assert_eq!(*token.payload().unwrap(), expected_claims);
204/// # }
205/// ```
206pub type JWT<T, H> = jws::Compact<ClaimsSet<T>, H>;
207
208/// A convenience type alias of a "JWE" which is a compact JWE that contains a signed/unsigned compact JWS.
209///
210/// Type `T` is the type of private claims for the encapsulated JWT, and type `H` is the type of the private
211/// header fields of the encapsulated JWT. Type `I` is the private header fields fo the encapsulating JWE.
212///
213/// Usually, you would set `H` and `I` to `biscuit::Empty` because you usually do not need any private header fields.
214///
215/// In general, you should [sign a JWT claims set, then encrypt it](http://crypto.stackexchange.com/a/5466),
216/// although there is nothing stopping you from doing it the other way round.
217///
218/// # Examples
219/// ## Sign with HS256, then encrypt with A256GCMKW and A256GCM
220///
221/// ```rust
222/// use std::str::FromStr;
223/// use biscuit::{ClaimsSet, RegisteredClaims, Empty, SingleOrMultiple, JWT, JWE};
224/// use biscuit::jwk::JWK;
225/// use biscuit::jws::{self, Secret};
226/// use biscuit::jwe;
227/// use biscuit::jwa::{EncryptionOptions, SignatureAlgorithm, KeyManagementAlgorithm,
228///                    ContentEncryptionAlgorithm};
229/// use serde::{Serialize, Deserialize};
230///
231/// // Define our own private claims
232/// #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
233/// struct PrivateClaims {
234///     company: String,
235///     department: String,
236/// }
237///
238/// #[allow(unused_assignments)]
239/// # fn main() {
240/// // Craft our JWS
241/// let expected_claims = ClaimsSet::<PrivateClaims> {
242///     registered: RegisteredClaims {
243///         issuer: Some(FromStr::from_str("https://www.acme.com").unwrap()),
244///         subject: Some(FromStr::from_str("John Doe").unwrap()),
245///         audience: Some(SingleOrMultiple::Single(
246///             FromStr::from_str("htts://acme-customer.com").unwrap(),
247///         )),
248///         not_before: Some(1234.into()),
249///         ..Default::default()
250///     },
251///     private: PrivateClaims {
252///         department: "Toilet Cleaning".to_string(),
253///         company: "ACME".to_string(),
254///     },
255/// };
256///
257/// let expected_jwt = JWT::new_decoded(
258///     From::from(jws::RegisteredHeader {
259///         algorithm: SignatureAlgorithm::HS256,
260///         ..Default::default()
261///     }),
262///     expected_claims.clone(),
263/// );
264///
265/// let jws = expected_jwt
266///     .into_encoded(&Secret::Bytes("secret".to_string().into_bytes()))
267///     .unwrap();
268///
269/// // Encrypt the token
270///
271/// // You would usually have your own AES key for this, but we will use a zeroed key as an example
272/// let key: JWK<Empty> = JWK::new_octet_key(&vec![0; 256 / 8], Default::default());
273///
274/// // We need to create an `EncryptionOptions` with a nonce for AES GCM encryption.
275/// // You must take care NOT to reuse the nonce. You can simply treat the nonce as a 96 bit
276/// // counter that is incremented after every use
277/// let mut nonce_counter = num_bigint::BigUint::from_bytes_le(&vec![0; 96 / 8]);
278/// // Make sure it's no more than 96 bits!
279/// assert!(nonce_counter.bits() <= 96);
280/// let mut nonce_bytes = nonce_counter.to_bytes_le();
281/// // We need to ensure it is exactly 96 bits
282/// nonce_bytes.resize(96 / 8, 0);
283/// let options = EncryptionOptions::AES_GCM { nonce: nonce_bytes };
284///
285/// // Construct the JWE
286/// let jwe = JWE::new_decrypted(
287///     From::from(jwe::RegisteredHeader {
288///         cek_algorithm: KeyManagementAlgorithm::A256GCMKW,
289///         enc_algorithm: ContentEncryptionAlgorithm::A256GCM,
290///         media_type: Some("JOSE".to_string()),
291///         content_type: Some("JOSE".to_string()),
292///         ..Default::default()
293///     }),
294///     jws.clone(),
295/// );
296///
297/// // Encrypt
298/// let encrypted_jwe = jwe.encrypt(&key, &options).unwrap();
299///
300/// let token = encrypted_jwe.unwrap_encrypted().to_string();
301///
302/// // Now, send `token` to your clients
303///
304/// // ... some time later, we get token back!
305/// let token: JWE<PrivateClaims, Empty, Empty> = JWE::new_encrypted(&token);
306///
307/// // Decrypt
308/// let decrypted_jwe = token
309///     .into_decrypted(
310///         &key,
311///         KeyManagementAlgorithm::A256GCMKW,
312///         ContentEncryptionAlgorithm::A256GCM,
313///     )
314///     .unwrap();
315///
316/// let decrypted_jws = decrypted_jwe.payload().unwrap();
317/// assert_eq!(jws, *decrypted_jws);
318///
319/// // Don't forget to increment the nonce!
320/// nonce_counter = nonce_counter + 1u8;
321/// # }
322/// ```
323pub type JWE<T, H, I> = jwe::Compact<JWT<T, H>, I>;
324
325/// An empty struct that derives Serialize and Deserialize. Can be used, for example, in places where a type
326/// for custom values (such as private claims in a `ClaimsSet`) is required but you have nothing to implement.
327///
328/// # Examples
329/// ```
330/// use std::str::FromStr;
331/// use biscuit::*;
332/// use biscuit::jws::*;
333/// use biscuit::jwa::*;
334///
335/// # fn main() {
336///
337/// let claims_set = ClaimsSet::<biscuit::Empty> {
338///     registered: RegisteredClaims {
339///         issuer: Some(FromStr::from_str("https://www.acme.com").unwrap()),
340///         subject: Some(FromStr::from_str("John Doe").unwrap()),
341///         audience:
342///             Some(SingleOrMultiple::Single(FromStr::from_str("htts://acme-customer.com").unwrap())),
343///         not_before: Some(1234.into()),
344///         ..Default::default()
345///     },
346///     private: Default::default(),
347/// };
348///
349/// let expected_jwt = JWT::new_decoded(From::from(
350///                                         RegisteredHeader {
351///                                             algorithm: SignatureAlgorithm::HS256,
352///                                             ..Default::default()
353///                                     }),
354///                                     claims_set);
355///
356/// # }
357/// ```
358#[derive(Debug, Eq, PartialEq, Clone, Copy, Serialize, Deserialize, Default)]
359pub struct Empty {}
360
361impl CompactJson for Empty {}
362
363/// A "part" of the compact representation of JWT/JWS/JWE. Parts are first serialized to some form and then
364/// base64 encoded and separated by periods.
365///
366/// An automatic implementation for any `T` that implements the marker trait `CompactJson` is provided.
367/// This implementation will serialize/deserialize `T` to JSON via serde.
368pub trait CompactPart {
369    /// Convert this part into bytes
370    fn to_bytes(&self) -> Result<Vec<u8>, Error>;
371
372    /// Convert a sequence of bytes into Self
373    fn from_bytes(bytes: &[u8]) -> Result<Self, Error>
374    where
375        Self: Sized;
376
377    /// Base64 decode into Self
378    fn from_base64<B: AsRef<[u8]>>(encoded: &B) -> Result<Self, Error>
379    where
380        Self: Sized,
381    {
382        let decoded = BASE64URL_NOPAD.decode(encoded.as_ref())?;
383        Self::from_bytes(&decoded)
384    }
385
386    /// Serialize `Self` to some form and then base64URL Encode
387    fn to_base64(&self) -> Result<Base64Url, Error> {
388        let bytes = self.to_bytes()?;
389        Ok(Base64Url(BASE64URL_NOPAD.encode(bytes.as_ref())))
390    }
391}
392
393/// A marker trait that indicates that the object is to be serialized to JSON and deserialized from JSON.
394/// This is primarily used in conjunction with the `CompactPart` trait which will serialize structs to JSON before
395/// base64 encoding, and vice-versa.
396pub trait CompactJson: Serialize + DeserializeOwned {}
397
398impl<T> CompactPart for T
399where
400    T: CompactJson,
401{
402    /// JSON serialize the part and return the JSON string bytes
403    fn to_bytes(&self) -> Result<Vec<u8>, Error> {
404        Ok(serde_json::to_vec(&self)?)
405    }
406
407    fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
408        Ok(serde_json::from_slice(bytes)?)
409    }
410}
411
412impl CompactPart for Vec<u8> {
413    fn to_bytes(&self) -> Result<Vec<u8>, Error> {
414        Ok(self.clone())
415    }
416
417    /// Convert a sequence of bytes into Self
418    fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
419        Ok(bytes.to_vec())
420    }
421}
422
423/// A newtype wrapper around a string to indicate it's base64 URL encoded
424#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
425pub struct Base64Url(String);
426
427impl Base64Url {
428    /// Unwrap the embedded string, consuming self in the process
429    pub fn unwrap(self) -> String {
430        let Base64Url(string) = self;
431        string
432    }
433
434    /// "Borrow" the string
435    pub fn str(&self) -> &str {
436        &self.0
437    }
438}
439
440impl Deref for Base64Url {
441    type Target = str;
442
443    fn deref(&self) -> &str {
444        &self.0
445    }
446}
447
448impl FromStr for Base64Url {
449    type Err = Error;
450
451    /// This never fails
452    fn from_str(s: &str) -> Result<Self, Self::Err> {
453        Ok(Base64Url(s.to_string()))
454    }
455}
456
457impl Borrow<str> for Base64Url {
458    fn borrow(&self) -> &str {
459        self.str()
460    }
461}
462
463impl CompactPart for Base64Url {
464    fn to_bytes(&self) -> Result<Vec<u8>, Error> {
465        Ok(BASE64URL_NOPAD.decode(self.as_ref())?)
466    }
467
468    /// Convert a sequence of bytes into Self
469    fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
470        let string = str::from_utf8(bytes)?;
471        Ok(Base64Url(string.to_string()))
472    }
473
474    fn to_base64(&self) -> Result<Base64Url, Error> {
475        Ok((*self).clone())
476    }
477
478    fn from_base64<B: AsRef<[u8]>>(encoded: &B) -> Result<Self, Error> {
479        Self::from_bytes(encoded.as_ref())
480    }
481}
482
483impl AsRef<[u8]> for Base64Url {
484    fn as_ref(&self) -> &[u8] {
485        self.0.as_ref()
486    }
487}
488
489/// A collection of `CompactPart`s that have been converted to `Base64Url`
490#[derive(Debug, Eq, PartialEq, Clone)]
491pub struct Compact {
492    /// Parts of the compact representation
493    pub parts: Vec<Base64Url>,
494}
495
496impl Compact {
497    /// Create an empty struct
498    pub fn new() -> Self {
499        Self { parts: vec![] }
500    }
501
502    /// Create an empty struct with some expected capacity
503    pub fn with_capacity(capacity: usize) -> Self {
504        Self {
505            parts: Vec::with_capacity(capacity),
506        }
507    }
508
509    /// Push a `CompactPart` to the end
510    pub fn push(&mut self, part: &dyn CompactPart) -> Result<(), Error> {
511        let base64 = part.to_base64()?;
512        self.parts.push(base64);
513        Ok(())
514    }
515
516    /// Returns the number of parts
517    pub fn len(&self) -> usize {
518        self.parts.len()
519    }
520
521    /// Returns whether there are no parts
522    pub fn is_empty(&self) -> bool {
523        self.parts.is_empty()
524    }
525
526    /// Encodes the various parts into Base64 URL encoding and then concatenates them with period '.'
527    /// This corresponds to the various `Compact` representation in JWE and JWS, for example
528    pub fn encode(&self) -> String {
529        let strings: Vec<&str> = self.parts.iter().map(Deref::deref).collect();
530        strings.join(".")
531    }
532
533    /// Convenience function to split an encoded compact representation into a list of `Base64Url`.
534    pub fn decode(encoded: &str) -> Self {
535        // Never fails
536        let parts = encoded
537            .split('.')
538            .map(|s| FromStr::from_str(s).unwrap())
539            .collect();
540        Self { parts }
541    }
542
543    /// Convenience function to retrieve a part at a certain index and decode into the type desired
544    pub fn part<T: CompactPart>(&self, index: usize) -> Result<T, Error> {
545        let part = self
546            .parts
547            .get(index)
548            .ok_or_else(|| "Out of bounds".to_string())?;
549        CompactPart::from_base64(part)
550    }
551}
552
553impl Default for Compact {
554    fn default() -> Self {
555        Compact::new()
556    }
557}
558
559impl Display for Compact {
560    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
561        write!(f, "{}", self.encode())
562    }
563}
564
565impl Serialize for Compact {
566    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
567    where
568        S: Serializer,
569    {
570        serializer.serialize_str(&self.encode())
571    }
572}
573
574impl<'de> Deserialize<'de> for Compact {
575    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
576    where
577        D: Deserializer<'de>,
578    {
579        struct CompactVisitor;
580
581        impl<'de> de::Visitor<'de> for CompactVisitor {
582            type Value = Compact;
583
584            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
585                formatter.write_str("a string containing a compact JOSE representation")
586            }
587
588            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
589            where
590                E: de::Error,
591            {
592                Ok(Compact::decode(value))
593            }
594        }
595
596        deserializer.deserialize_str(CompactVisitor)
597    }
598}
599
600/// Represents a choice between a single value or multiple values.
601/// This value is serialized by serde [untagged](https://serde.rs/enum-representations.html).
602///
603/// # Examples
604/// ```
605/// use biscuit::SingleOrMultiple;
606/// use serde::{Serialize, Deserialize};
607///
608/// #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
609/// struct SingleOrMultipleStrings {
610///     values: SingleOrMultiple<String>,
611/// }
612///
613/// # fn main() {
614/// let single = SingleOrMultipleStrings {
615///     values: SingleOrMultiple::Single("foobar".to_string())
616/// };
617/// let expected_json = r#"{"values":"foobar"}"#;
618///
619/// let serialized = serde_json::to_string(&single).unwrap();
620/// assert_eq!(expected_json, serialized);
621///
622/// let deserialized: SingleOrMultipleStrings = serde_json::from_str(&serialized).unwrap();
623/// assert_eq!(deserialized, single);
624///
625/// let multiple = SingleOrMultipleStrings {
626///     values: SingleOrMultiple::Multiple(vec!["foo".to_string(),
627///                                             "bar".to_string(),
628///                                             "baz".to_string()]),
629/// };
630///
631/// let expected_json = r#"{"values":["foo","bar","baz"]}"#;
632///
633/// let serialized = serde_json::to_string(&multiple).unwrap();
634/// assert_eq!(expected_json, serialized);
635///
636/// let deserialized: SingleOrMultipleStrings = serde_json::from_str(&serialized).unwrap();
637/// assert_eq!(deserialized, multiple);
638/// # }
639/// ```
640#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
641#[serde(untagged)]
642pub enum SingleOrMultiple<T> {
643    /// One single value
644    Single(T),
645    /// Multiple values
646    Multiple(Vec<T>),
647}
648
649impl<T> SingleOrMultiple<T>
650where
651    T: Clone + Debug + Eq + PartialEq + Serialize + DeserializeOwned + Send + Sync,
652{
653    /// Checks whether this enum, regardless of single or multiple value contains `value`.
654    pub fn contains<Q: ?Sized>(&self, value: &Q) -> bool
655    where
656        T: Borrow<Q>,
657        Q: PartialEq,
658    {
659        match *self {
660            SingleOrMultiple::Single(ref single) => single.borrow() == value,
661            SingleOrMultiple::Multiple(ref vector) => {
662                vector.iter().map(Borrow::borrow).any(|v| v == value)
663            }
664        }
665    }
666
667    /// Yields an iterator for the single value or the list
668    pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = &'a T> + 'a> {
669        match *self {
670            SingleOrMultiple::Single(ref single) => Box::new(iter::once(single)),
671            SingleOrMultiple::Multiple(ref vector) => Box::new(vector.iter()),
672        }
673    }
674}
675
676/// Wrapper around `DateTime<Utc>` to allow us to do custom de(serialization)
677#[derive(Clone, Copy, Debug, Eq, PartialEq)]
678pub struct Timestamp(DateTime<Utc>);
679
680impl Deref for Timestamp {
681    type Target = DateTime<Utc>;
682    fn deref(&self) -> &Self::Target {
683        &self.0
684    }
685}
686
687impl From<DateTime<Utc>> for Timestamp {
688    fn from(datetime: DateTime<Utc>) -> Self {
689        Timestamp(datetime)
690    }
691}
692
693impl From<Timestamp> for DateTime<Utc> {
694    fn from(ts: Timestamp) -> Self {
695        ts.0
696    }
697}
698
699impl From<i64> for Timestamp {
700    fn from(timestamp: i64) -> Self {
701        DateTime::<Utc>::from_utc(
702            NaiveDateTime::from_timestamp_opt(timestamp, 0).unwrap(),
703            Utc,
704        )
705        .into()
706    }
707}
708
709impl Serialize for Timestamp {
710    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
711    where
712        S: Serializer,
713    {
714        serializer.serialize_i64(self.timestamp())
715    }
716}
717
718impl<'de> Deserialize<'de> for Timestamp {
719    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
720    where
721        D: Deserializer<'de>,
722    {
723        let timestamp = i64::deserialize(deserializer)?;
724        Ok(Timestamp(DateTime::<Utc>::from_utc(
725            NaiveDateTime::from_timestamp_opt(timestamp, 0).unwrap(),
726            Utc,
727        )))
728    }
729}
730
731/// Registered claims defined by [RFC7519#4.1](https://tools.ietf.org/html/rfc7519#section-4.1)
732#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default)]
733pub struct RegisteredClaims {
734    /// Token issuer. Serialized to `iss`.
735    #[serde(rename = "iss", skip_serializing_if = "Option::is_none")]
736    pub issuer: Option<String>,
737
738    /// Subject where the JWT is referring to. Serialized to `sub`
739    #[serde(rename = "sub", skip_serializing_if = "Option::is_none")]
740    pub subject: Option<String>,
741
742    /// Audience intended for the JWT. Serialized to `aud`
743    #[serde(rename = "aud", skip_serializing_if = "Option::is_none")]
744    pub audience: Option<SingleOrMultiple<String>>,
745
746    /// Expiration time in seconds since Unix Epoch. Serialized to `exp`
747    #[serde(rename = "exp", skip_serializing_if = "Option::is_none")]
748    pub expiry: Option<Timestamp>,
749
750    /// Not before time in seconds since Unix Epoch. Serialized to `nbf`
751    #[serde(rename = "nbf", skip_serializing_if = "Option::is_none")]
752    pub not_before: Option<Timestamp>,
753
754    /// Issued at Time in seconds since Unix Epoch. Serialized to `iat`
755    #[serde(rename = "iat", skip_serializing_if = "Option::is_none")]
756    pub issued_at: Option<Timestamp>,
757
758    /// Application specific JWT ID. Serialized to `jti`
759    #[serde(rename = "jti", skip_serializing_if = "Option::is_none")]
760    pub id: Option<String>,
761}
762
763#[derive(Debug, Eq, PartialEq, Clone, Copy, Default)]
764/// Options for claims presence validation
765///
766/// By default, no claims (namely `iat`, `exp`, `nbf`, `iss`, `aud`)
767/// are required, and they pass validation if they are missing.
768pub struct ClaimPresenceOptions {
769    /// Whether the `iat` or `Issued At` field is required
770    pub issued_at: Presence,
771    /// Whether the `nbf` or `Not Before` field is required
772    pub not_before: Presence,
773    /// Whether the `exp` or `Expiry` field is required
774    pub expiry: Presence,
775    /// Whether the `iss` or `Issuer` field is required
776    pub issuer: Presence,
777    /// Whether the `aud` or `Audience` field is required
778    pub audience: Presence,
779    /// Whether the `sub` or `Subject` field is required
780    pub subject: Presence,
781    /// Whether the `jti` or `JWT ID` field is required
782    pub id: Presence,
783}
784
785impl ClaimPresenceOptions {
786    /// Returns a ClaimPresenceOptions where every claim is required as per [RFC7523](https://tools.ietf.org/html/rfc7523#section-3)
787    pub fn strict() -> Self {
788        use crate::Presence::*;
789        ClaimPresenceOptions {
790            issued_at: Required,
791            not_before: Required,
792            expiry: Required,
793            issuer: Required,
794            audience: Required,
795            subject: Required,
796            id: Required,
797        }
798    }
799}
800
801#[derive(Eq, PartialEq, Clone)]
802/// Options for claims validation
803///
804/// If a claim is missing, it passes validation unless the claim is marked as required within the
805/// `claim_presence_options`.
806///
807/// By default, no claims are required. If there are present, only expiry-related claims are validated
808/// (namely `exp`, `nbf`, `iat`) with zero epsilon and maximum age duration.
809///
810/// Should any temporal claims be required, set the appropriate fields.
811///
812/// To deal with clock drifts, you might want to provide an `epsilon` error margin in the form of a
813/// `std::time::Duration` to allow time comparisons to fall within the margin.
814pub struct ValidationOptions {
815    /// Claims marked as required will trigger a validation failure if they are missing
816    pub claim_presence_options: ClaimPresenceOptions,
817
818    /// Define how to validate temporal claims
819    pub temporal_options: TemporalOptions,
820
821    /// Validation options for `iat` or `Issued At` claim if present
822    /// Parameter shows the maximum age of a token to be accepted,
823    /// use [```Duration::max_value()```] if you do not want to skip validating
824    /// the age of the token, and only validate it was not issued in the future
825    pub issued_at: Validation<Duration>,
826    /// Validation options for `nbf` or `Not Before` claim if present
827    pub not_before: Validation<()>,
828    /// Validation options for `exp` or `Expiry` claim if present
829    pub expiry: Validation<()>,
830
831    /// Validation options for `iss` or `Issuer` claim if present
832    /// Parameter must match the issuer in the token exactly.
833    pub issuer: Validation<String>,
834
835    /// Validation options for `aud` or `Audience` claim if present
836    /// Token must include an audience with the value of the parameter
837    pub audience: Validation<String>,
838}
839
840impl Default for ValidationOptions {
841    fn default() -> Self {
842        ValidationOptions {
843            expiry: Validation::Validate(()),
844            not_before: Validation::Validate(()),
845            issued_at: Validation::Validate(Duration::max_value()),
846
847            claim_presence_options: Default::default(),
848            temporal_options: Default::default(),
849            audience: Default::default(),
850            issuer: Default::default(),
851        }
852    }
853}
854
855impl RegisteredClaims {
856    /// Validates that the token contains the claims defined as required
857    pub fn validate_claim_presence(
858        &self,
859        options: ClaimPresenceOptions,
860    ) -> Result<(), ValidationError> {
861        use crate::Presence::Required;
862
863        let mut missing_claims: Vec<&str> = vec![];
864
865        if options.expiry == Required && self.expiry.is_none() {
866            missing_claims.push("exp");
867        }
868
869        if options.not_before == Required && self.not_before.is_none() {
870            missing_claims.push("nbf");
871        }
872
873        if options.issued_at == Required && self.issued_at.is_none() {
874            missing_claims.push("iat");
875        }
876
877        if options.audience == Required && self.audience.is_none() {
878            missing_claims.push("aud");
879        }
880
881        if options.issuer == Required && self.issuer.is_none() {
882            missing_claims.push("iss");
883        }
884
885        if options.subject == Required && self.subject.is_none() {
886            missing_claims.push("sub");
887        }
888
889        if options.id == Required && self.id.is_none() {
890            missing_claims.push("jti");
891        }
892
893        if missing_claims.is_empty() {
894            Ok(())
895        } else {
896            Err(ValidationError::MissingRequiredClaims(
897                missing_claims.into_iter().map(|v| v.into()).collect(),
898            ))
899        }
900    }
901
902    /// Validates that if the token has an `exp` claim, it has not passed.
903    pub fn validate_exp(
904        &self,
905        validation: Validation<TemporalOptions>,
906    ) -> Result<(), ValidationError> {
907        match validation {
908            Validation::Ignored => Ok(()),
909            Validation::Validate(temporal_options) => {
910                let now = temporal_options.now.unwrap_or_else(Utc::now);
911
912                match self.expiry {
913                    Some(Timestamp(expiry)) if now - expiry > temporal_options.epsilon => {
914                        Err(ValidationError::Expired(now - expiry))
915                    }
916                    _ => Ok(()),
917                }
918            }
919        }
920    }
921
922    /// Validates that if the token has an `nbf` claim, it has passed.
923    pub fn validate_nbf(
924        &self,
925        validation: Validation<TemporalOptions>,
926    ) -> Result<(), ValidationError> {
927        match validation {
928            Validation::Ignored => Ok(()),
929            Validation::Validate(temporal_options) => {
930                let now = temporal_options.now.unwrap_or_else(Utc::now);
931
932                match self.not_before {
933                    Some(Timestamp(nbf)) if nbf - now > temporal_options.epsilon => {
934                        Err(ValidationError::NotYetValid(nbf - now))
935                    }
936                    _ => Ok(()),
937                }
938            }
939        }
940    }
941
942    /// Validates that if the token has an `iat` claim, it is not in the future and not older than the Duration
943    pub fn validate_iat(
944        &self,
945        validation: Validation<(Duration, TemporalOptions)>,
946    ) -> Result<(), ValidationError> {
947        match validation {
948            Validation::Ignored => Ok(()),
949            Validation::Validate((max_age, temporal_options)) => {
950                let now = temporal_options.now.unwrap_or_else(Utc::now);
951
952                match self.issued_at {
953                    Some(Timestamp(iat)) if iat - now > temporal_options.epsilon => {
954                        Err(ValidationError::NotYetValid(iat - now))
955                    }
956                    Some(Timestamp(iat)) if now - iat > max_age - temporal_options.epsilon => {
957                        Err(ValidationError::TooOld(now - iat - max_age))
958                    }
959                    _ => Ok(()),
960                }
961            }
962        }
963    }
964
965    /// Validates that if the token has an `aud` claim, it contains an entry which matches the expected audience
966    pub fn validate_aud(&self, validation: Validation<String>) -> Result<(), ValidationError> {
967        match validation {
968            Validation::Ignored => Ok(()),
969            Validation::Validate(expected_aud) => match self.audience {
970                Some(SingleOrMultiple::Single(ref audience)) if audience != &expected_aud => Err(
971                    ValidationError::InvalidAudience(self.audience.clone().unwrap()),
972                ),
973                Some(SingleOrMultiple::Multiple(ref audiences))
974                    if !audiences.contains(&expected_aud) =>
975                {
976                    Err(ValidationError::InvalidAudience(
977                        self.audience.clone().unwrap(),
978                    ))
979                }
980                _ => Ok(()),
981            },
982        }
983    }
984
985    /// Validates that if the token has an `iss` claim, it matches the expected issuer
986    pub fn validate_iss(&self, validation: Validation<String>) -> Result<(), ValidationError> {
987        match validation {
988            Validation::Ignored => Ok(()),
989            Validation::Validate(expected_issuer) => match self.issuer {
990                Some(ref iss) if iss != &expected_issuer => {
991                    Err(ValidationError::InvalidIssuer(self.issuer.clone().unwrap()))
992                }
993                _ => Ok(()),
994            },
995        }
996    }
997
998    /// Performs full validation of the token according to the `ValidationOptions` supplied
999    ///
1000    /// First it validates that all claims marked as required are present
1001    /// Then it validates each claim marked to be validated if they are present in the token
1002    /// (even those that are not marked as required, but are present).
1003    pub fn validate(&self, options: ValidationOptions) -> Result<(), ValidationError> {
1004        self.validate_claim_presence(options.claim_presence_options)?;
1005        self.validate_exp(options.expiry.map(|_| options.temporal_options))?;
1006        self.validate_nbf(options.not_before.map(|_| options.temporal_options))?;
1007        self.validate_iat(options.issued_at.map(|dur| (dur, options.temporal_options)))?;
1008
1009        self.validate_iss(options.issuer)?;
1010        self.validate_aud(options.audience)?;
1011
1012        //        self.validate_sub(options.subject_validated)?;
1013        //        self.validate_custom(options.custom_validation)?;
1014
1015        Ok(())
1016    }
1017}
1018
1019/// A collection of claims, both [registered](https://tools.ietf.org/html/rfc7519#section-4.1) and your custom
1020/// private claims.
1021#[derive(Debug, Eq, PartialEq, Clone, Default, Serialize, Deserialize)]
1022pub struct ClaimsSet<T> {
1023    /// Registered claims defined by the RFC
1024    #[serde(flatten)]
1025    pub registered: RegisteredClaims,
1026    /// Application specific claims
1027    #[serde(flatten)]
1028    pub private: T,
1029}
1030
1031impl<T> CompactJson for ClaimsSet<T> where T: Serialize + DeserializeOwned {}
1032
1033#[cfg(test)]
1034mod tests {
1035    use std::str::{self, FromStr};
1036
1037    use chrono::{Duration, TimeZone, Utc};
1038
1039    use super::*;
1040
1041    #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1042    struct PrivateClaims {
1043        company: String,
1044        department: String,
1045    }
1046
1047    impl CompactJson for PrivateClaims {}
1048
1049    #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1050    struct InvalidPrivateClaim {
1051        sub: String,
1052        company: String,
1053    }
1054
1055    #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
1056    struct SingleOrMultipleStrings {
1057        values: SingleOrMultiple<String>,
1058    }
1059
1060    #[test]
1061    fn single_string_serialization_round_trip() {
1062        let test = SingleOrMultipleStrings {
1063            values: SingleOrMultiple::Single("foobar".to_string()),
1064        };
1065        let expected_json = r#"{"values":"foobar"}"#;
1066
1067        let serialized = not_err!(serde_json::to_string(&test));
1068        assert_eq!(expected_json, serialized);
1069
1070        let deserialized: SingleOrMultipleStrings = not_err!(serde_json::from_str(&serialized));
1071        assert_eq!(deserialized, test);
1072        assert!(deserialized.values.contains("foobar"));
1073        assert!(!deserialized.values.contains("does not exist"));
1074    }
1075
1076    #[test]
1077    fn multiple_strings_serialization_round_trip() {
1078        let test = SingleOrMultipleStrings {
1079            values: SingleOrMultiple::Multiple(vec![
1080                "foo".to_string(),
1081                "bar".to_string(),
1082                "baz".to_string(),
1083            ]),
1084        };
1085        let expected_json = r#"{"values":["foo","bar","baz"]}"#;
1086
1087        let serialized = not_err!(serde_json::to_string(&test));
1088        assert_eq!(expected_json, serialized);
1089
1090        let deserialized: SingleOrMultipleStrings = not_err!(serde_json::from_str(&serialized));
1091        assert_eq!(deserialized, test);
1092        assert!(deserialized.values.contains("foo"));
1093        assert!(deserialized.values.contains("bar"));
1094        assert!(deserialized.values.contains("baz"));
1095        assert!(!deserialized.values.contains("does not exist"));
1096    }
1097
1098    #[test]
1099    fn single_string_or_uri_string_serialization_round_trip() {
1100        let test = SingleOrMultipleStrings {
1101            values: SingleOrMultiple::Single(not_err!(FromStr::from_str("foobar"))),
1102        };
1103        let expected_json = r#"{"values":"foobar"}"#;
1104
1105        let serialized = not_err!(serde_json::to_string(&test));
1106        assert_eq!(expected_json, serialized);
1107
1108        let deserialized: SingleOrMultipleStrings = not_err!(serde_json::from_str(&serialized));
1109        assert_eq!(deserialized, test);
1110        assert!(deserialized.values.contains("foobar"));
1111        assert!(!deserialized.values.contains("does not exist"));
1112    }
1113
1114    #[test]
1115    fn single_string_or_uri_uri_serialization_round_trip() {
1116        let test = SingleOrMultipleStrings {
1117            values: SingleOrMultiple::Single(not_err!(FromStr::from_str(
1118                "https://www.examples.com/"
1119            ))),
1120        };
1121        let expected_json = r#"{"values":"https://www.examples.com/"}"#;
1122
1123        let serialized = not_err!(serde_json::to_string(&test));
1124        assert_eq!(expected_json, serialized);
1125
1126        let deserialized: SingleOrMultipleStrings = not_err!(serde_json::from_str(&serialized));
1127        assert_eq!(deserialized, test);
1128        assert!(deserialized.values.contains("https://www.examples.com/"));
1129        assert!(!deserialized.values.contains("https://ecorp.com"));
1130    }
1131
1132    #[test]
1133    fn multiple_string_or_uri_serialization_round_trip() {
1134        let test = SingleOrMultipleStrings {
1135            values: SingleOrMultiple::Multiple(vec![
1136                not_err!(FromStr::from_str("foo")),
1137                not_err!(FromStr::from_str("https://www.example.com/")),
1138                not_err!(FromStr::from_str("data:text/plain,Hello?World#")),
1139                not_err!(FromStr::from_str("http://[::1]/")),
1140                not_err!(FromStr::from_str("baz")),
1141            ]),
1142        };
1143        let expected_json = r#"{"values":["foo","https://www.example.com/","data:text/plain,Hello?World#","http://[::1]/","baz"]}"#;
1144
1145        let serialized = not_err!(serde_json::to_string(&test));
1146        assert_eq!(expected_json, serialized);
1147
1148        let deserialized: SingleOrMultipleStrings = not_err!(serde_json::from_str(&serialized));
1149        assert_eq!(deserialized, test);
1150
1151        assert!(deserialized.values.contains("foo"));
1152        assert!(deserialized.values.contains("https://www.example.com/"));
1153        assert!(deserialized.values.contains("data:text/plain,Hello?World#"));
1154        assert!(deserialized.values.contains("http://[::1]/"));
1155        assert!(deserialized.values.contains("baz"));
1156        assert!(!deserialized.values.contains("https://ecorp.com"));
1157    }
1158
1159    #[test]
1160    fn timestamp_serialization_roundtrip() {
1161        use chrono::Timelike;
1162
1163        let now: Timestamp = Utc::now().with_nanosecond(0).unwrap().into();
1164        let serialized = not_err!(serde_json::to_string(&now));
1165        let deserialized = not_err!(serde_json::from_str(&serialized));
1166        assert_eq!(now, deserialized);
1167
1168        let fixed_time: Timestamp = 1000.into();
1169        let serialized = not_err!(serde_json::to_string(&fixed_time));
1170        assert_eq!(serialized, "1000");
1171        let deserialized = not_err!(serde_json::from_str(&serialized));
1172        assert_eq!(fixed_time, deserialized);
1173    }
1174
1175    #[test]
1176    fn empty_registered_claims_serialization_round_trip() {
1177        let claim = RegisteredClaims::default();
1178        let expected_json = "{}";
1179
1180        let serialized = not_err!(serde_json::to_string(&claim));
1181        assert_eq!(expected_json, serialized);
1182
1183        let deserialized: RegisteredClaims = not_err!(serde_json::from_str(&serialized));
1184        assert_eq!(deserialized, claim);
1185    }
1186
1187    #[test]
1188    fn registered_claims_serialization_round_trip() {
1189        let claim = RegisteredClaims {
1190            issuer: Some(not_err!(FromStr::from_str("https://www.acme.com/"))),
1191            audience: Some(SingleOrMultiple::Single(not_err!(FromStr::from_str(
1192                "htts://acme-customer.com/"
1193            )))),
1194            not_before: Some(1234.into()),
1195            ..Default::default()
1196        };
1197        let expected_json =
1198            r#"{"iss":"https://www.acme.com/","aud":"htts://acme-customer.com/","nbf":1234}"#;
1199
1200        let serialized = not_err!(serde_json::to_string(&claim));
1201        assert_eq!(expected_json, serialized);
1202
1203        let deserialized: RegisteredClaims = not_err!(serde_json::from_str(&serialized));
1204        assert_eq!(deserialized, claim);
1205    }
1206
1207    #[test]
1208    fn claims_set_serialization_round_trip() {
1209        let claim = ClaimsSet::<PrivateClaims> {
1210            registered: RegisteredClaims {
1211                issuer: Some(not_err!(FromStr::from_str("https://www.acme.com/"))),
1212                subject: Some(not_err!(FromStr::from_str("John Doe"))),
1213                audience: Some(SingleOrMultiple::Single(not_err!(FromStr::from_str(
1214                    "htts://acme-customer.com/"
1215                )))),
1216                not_before: Some(1234.into()),
1217                ..Default::default()
1218            },
1219            private: PrivateClaims {
1220                department: "Toilet Cleaning".to_string(),
1221                company: "ACME".to_string(),
1222            },
1223        };
1224
1225        let expected_json = "{\"iss\":\"https://www.acme.com/\",\"sub\":\"John Doe\",\
1226                             \"aud\":\"htts://acme-customer.com/\",\
1227                             \"nbf\":1234,\"company\":\"ACME\",\"department\":\"Toilet Cleaning\"}";
1228
1229        let serialized = not_err!(serde_json::to_string(&claim));
1230        assert_eq!(expected_json, serialized);
1231
1232        let deserialized: ClaimsSet<PrivateClaims> = not_err!(serde_json::from_str(&serialized));
1233        assert_eq!(deserialized, claim);
1234    }
1235
1236    #[test]
1237    // serde's flatten will serialize them twice
1238    fn duplicate_claims_round_trip() {
1239        let claim = ClaimsSet::<InvalidPrivateClaim> {
1240            registered: RegisteredClaims {
1241                issuer: Some(not_err!(FromStr::from_str("https://www.acme.com"))),
1242                subject: Some(not_err!(FromStr::from_str("John Doe"))),
1243                audience: Some(SingleOrMultiple::Single(not_err!(FromStr::from_str(
1244                    "htts://acme-customer.com"
1245                )))),
1246                not_before: Some(1234.into()),
1247                ..Default::default()
1248            },
1249            private: InvalidPrivateClaim {
1250                sub: "John Doe".to_string(),
1251                company: "ACME".to_string(),
1252            },
1253        };
1254
1255        let json = serde_json::to_string(&claim).unwrap();
1256        assert_eq!(2, json.matches("\"sub\"").count());
1257
1258        let duplicate: Result<ClaimsSet<InvalidPrivateClaim>, _> = serde_json::from_str(&json);
1259        assert!(duplicate.is_err());
1260        let error = duplicate.unwrap_err().to_string();
1261        assert!(error.contains("duplicate field `sub`"));
1262    }
1263
1264    #[test]
1265    #[should_panic(expected = "MissingRequiredClaims([\"iat\"])")]
1266    fn validate_times_missing_iat() {
1267        let registered_claims: RegisteredClaims = Default::default();
1268        let options = ClaimPresenceOptions {
1269            issued_at: Presence::Required,
1270            ..Default::default()
1271        };
1272        registered_claims.validate_claim_presence(options).unwrap();
1273    }
1274
1275    #[test]
1276    #[should_panic(expected = "MissingRequiredClaims([\"exp\"])")]
1277    fn validate_times_missing_exp() {
1278        let registered_claims: RegisteredClaims = Default::default();
1279        let options = ClaimPresenceOptions {
1280            expiry: Presence::Required,
1281            ..Default::default()
1282        };
1283        registered_claims.validate_claim_presence(options).unwrap();
1284    }
1285
1286    #[test]
1287    #[should_panic(expected = "MissingRequiredClaims([\"nbf\"])")]
1288    fn validate_times_missing_nbf() {
1289        let registered_claims: RegisteredClaims = Default::default();
1290        let options = ClaimPresenceOptions {
1291            not_before: Presence::Required,
1292            ..Default::default()
1293        };
1294        registered_claims.validate_claim_presence(options).unwrap();
1295    }
1296
1297    #[test]
1298    #[should_panic(expected = "MissingRequiredClaims([\"aud\"])")]
1299    fn validate_times_missing_aud() {
1300        let registered_claims: RegisteredClaims = Default::default();
1301        let options = ClaimPresenceOptions {
1302            audience: Presence::Required,
1303            ..Default::default()
1304        };
1305        registered_claims.validate_claim_presence(options).unwrap();
1306    }
1307
1308    #[test]
1309    #[should_panic(expected = "MissingRequiredClaims([\"iss\"])")]
1310    fn validate_times_missing_iss() {
1311        let registered_claims: RegisteredClaims = Default::default();
1312        let options = ClaimPresenceOptions {
1313            issuer: Presence::Required,
1314            ..Default::default()
1315        };
1316        registered_claims.validate_claim_presence(options).unwrap();
1317    }
1318
1319    #[test]
1320    #[should_panic(expected = "MissingRequiredClaims([\"sub\"])")]
1321    fn validate_times_missing_sub() {
1322        let registered_claims: RegisteredClaims = Default::default();
1323        let options = ClaimPresenceOptions {
1324            subject: Presence::Required,
1325            ..Default::default()
1326        };
1327        registered_claims.validate_claim_presence(options).unwrap();
1328    }
1329
1330    #[test]
1331    #[should_panic(
1332        expected = "MissingRequiredClaims([\"exp\", \"nbf\", \"iat\", \"aud\", \"iss\", \"sub\", \"jti\"])"
1333    )]
1334    fn validate_times_missing_all() {
1335        let registered_claims: RegisteredClaims = Default::default();
1336        let options = ClaimPresenceOptions::strict();
1337        registered_claims.validate_claim_presence(options).unwrap();
1338    }
1339
1340    #[test]
1341    fn validate_times_catch_future_token() {
1342        let temporal_options = TemporalOptions {
1343            now: Some(Utc.timestamp_opt(0, 0).unwrap()),
1344            ..Default::default()
1345        };
1346
1347        let registered_claims = RegisteredClaims {
1348            issued_at: Some(10.into()),
1349            ..Default::default()
1350        };
1351
1352        assert_eq!(
1353            Err(ValidationError::NotYetValid(Duration::seconds(10))),
1354            registered_claims.validate_iat(Validation::Validate((
1355                Duration::seconds(0),
1356                temporal_options
1357            )))
1358        );
1359    }
1360
1361    #[test]
1362    fn validate_times_catch_too_old_token() {
1363        let temporal_options = TemporalOptions {
1364            now: Some(Utc.timestamp_opt(40, 0).unwrap()),
1365            ..Default::default()
1366        };
1367
1368        let registered_claims = RegisteredClaims {
1369            issued_at: Some(10.into()),
1370            ..Default::default()
1371        };
1372
1373        assert_eq!(
1374            Err(ValidationError::TooOld(Duration::seconds(5))),
1375            registered_claims.validate_iat(Validation::Validate((
1376                Duration::seconds(25),
1377                temporal_options
1378            )))
1379        );
1380    }
1381
1382    #[test]
1383    fn validate_times_catch_expired_token() {
1384        let temporal_options = TemporalOptions {
1385            now: Some(Utc.timestamp_opt(2, 0).unwrap()),
1386            ..Default::default()
1387        };
1388
1389        let registered_claims = RegisteredClaims {
1390            expiry: Some(1.into()),
1391            ..Default::default()
1392        };
1393
1394        assert_eq!(
1395            Err(ValidationError::Expired(Duration::seconds(1))),
1396            registered_claims.validate_exp(Validation::Validate(temporal_options))
1397        );
1398    }
1399
1400    #[test]
1401    fn validate_times_catch_early_token() {
1402        let temporal_options = TemporalOptions {
1403            now: Some(Utc.timestamp_opt(0, 0).unwrap()),
1404            ..Default::default()
1405        };
1406
1407        let registered_claims = RegisteredClaims {
1408            not_before: Some(1.into()),
1409            ..Default::default()
1410        };
1411
1412        assert_eq!(
1413            Err(ValidationError::NotYetValid(Duration::seconds(1))),
1414            registered_claims.validate_nbf(Validation::Validate(temporal_options))
1415        );
1416    }
1417
1418    #[test]
1419    fn validate_times_valid_token_with_default_options() {
1420        let registered_claims = RegisteredClaims {
1421            not_before: Some(Timestamp(Utc::now() - Duration::days(2))),
1422            issued_at: Some(Timestamp(Utc::now() - Duration::days(1))),
1423            expiry: Some(Timestamp(Utc::now() + Duration::days(1))),
1424            ..Default::default()
1425        };
1426
1427        let validation_options = ValidationOptions {
1428            temporal_options: Default::default(),
1429            claim_presence_options: Default::default(),
1430
1431            expiry: Validation::Validate(()),
1432            not_before: Validation::Validate(()),
1433            issued_at: Validation::Validate(Duration::max_value()),
1434
1435            ..Default::default()
1436        };
1437
1438        not_err!(registered_claims.validate(validation_options));
1439    }
1440
1441    #[test]
1442    fn validate_issuer_catch_mismatch() {
1443        let registered_claims = RegisteredClaims {
1444            issuer: Some("issuer".to_string()),
1445            ..Default::default()
1446        };
1447
1448        assert_eq!(
1449            Err(ValidationError::InvalidIssuer("issuer".to_string())),
1450            registered_claims.validate_iss(Validation::Validate("http://issuer".to_string()))
1451        );
1452    }
1453
1454    #[test]
1455    fn validate_audience_when_single() {
1456        let aud = SingleOrMultiple::Single("audience".to_string());
1457
1458        let registered_claims = RegisteredClaims {
1459            audience: Some(aud.clone()),
1460            ..Default::default()
1461        };
1462
1463        assert_eq!(
1464            Err(ValidationError::InvalidAudience(aud.clone())),
1465            registered_claims.validate_aud(Validation::Validate("http://audience".to_string()))
1466        );
1467
1468        assert_eq!(
1469            Err(ValidationError::InvalidAudience(aud)),
1470            registered_claims.validate_aud(Validation::Validate("audience2".to_string()))
1471        );
1472
1473        assert_eq!(
1474            Ok(()),
1475            registered_claims.validate_aud(Validation::Validate("audience".to_string()))
1476        );
1477    }
1478
1479    #[test]
1480    fn validate_audience_when_multiple() {
1481        let aud =
1482            SingleOrMultiple::Multiple(vec!["audience".to_string(), "http://audience".to_string()]);
1483
1484        let registered_claims = RegisteredClaims {
1485            audience: Some(aud.clone()),
1486            ..Default::default()
1487        };
1488
1489        assert_eq!(
1490            Ok(()),
1491            registered_claims.validate_aud(Validation::Validate("http://audience".to_string()))
1492        );
1493
1494        assert_eq!(
1495            Err(ValidationError::InvalidAudience(aud.clone())),
1496            registered_claims.validate_aud(Validation::Validate("audience2".to_string()))
1497        );
1498
1499        assert_eq!(
1500            Err(ValidationError::InvalidAudience(aud)),
1501            registered_claims.validate_aud(Validation::Validate("https://audience".to_string()))
1502        );
1503
1504        assert_eq!(
1505            Ok(()),
1506            registered_claims.validate_aud(Validation::Validate("audience".to_string()))
1507        );
1508    }
1509
1510    #[test]
1511    fn validate_valid_token_with_all_required() {
1512        let registered_claims = RegisteredClaims {
1513            expiry: Some(999.into()),
1514            not_before: Some(1.into()),
1515            issued_at: Some(95.into()),
1516            subject: Some("subject".to_string()),
1517            issuer: Some("issuer".to_string()),
1518            audience: Some(SingleOrMultiple::Multiple(vec![
1519                "http://audience".to_string(),
1520                "audience".to_string(),
1521            ])),
1522            id: Some("id".into()),
1523        };
1524
1525        let temporal_options = TemporalOptions {
1526            now: Some(Utc.timestamp_opt(100, 0).unwrap()),
1527            ..Default::default()
1528        };
1529
1530        let validation_options = ValidationOptions {
1531            temporal_options,
1532            claim_presence_options: ClaimPresenceOptions::strict(),
1533
1534            expiry: Validation::Validate(()),
1535            not_before: Validation::Validate(()),
1536            issued_at: Validation::Validate(Duration::max_value()),
1537            audience: Validation::Validate("audience".to_string()),
1538            issuer: Validation::Validate("issuer".to_string()),
1539        };
1540
1541        not_err!(registered_claims.validate(validation_options));
1542    }
1543
1544    #[test]
1545    fn validate_times_valid_token_with_epsilon() {
1546        let registered_claims = RegisteredClaims {
1547            expiry: Some(99.into()),
1548            not_before: Some(96.into()),
1549            issued_at: Some(96.into()),
1550            ..Default::default()
1551        };
1552
1553        let temporal_options = TemporalOptions {
1554            now: Some(Utc.timestamp_opt(100, 0).unwrap()),
1555            epsilon: Duration::seconds(10),
1556        };
1557
1558        let validation_options = ValidationOptions {
1559            temporal_options,
1560            claim_presence_options: Default::default(),
1561
1562            expiry: Validation::Validate(()),
1563            not_before: Validation::Validate(()),
1564            issued_at: Validation::Validate(Duration::max_value()),
1565
1566            ..Default::default()
1567        };
1568
1569        not_err!(registered_claims.validate(validation_options));
1570    }
1571
1572    #[test]
1573    fn compact_part_round_trip() {
1574        let test_value = PrivateClaims {
1575            department: "Toilet Cleaning".to_string(),
1576            company: "ACME".to_string(),
1577        };
1578
1579        let base64 = not_err!(test_value.to_base64());
1580        let expected_base64 = "eyJjb21wYW55IjoiQUNNRSIsImRlcGFydG1lbnQiOiJUb2lsZXQgQ2xlYW5pbmcifQ";
1581        assert_eq!(base64.str(), expected_base64);
1582
1583        let actual_value = not_err!(PrivateClaims::from_base64(&base64));
1584        assert_eq!(actual_value, test_value);
1585    }
1586
1587    #[test]
1588    fn compact_part_vec_u8_round_trip() {
1589        let test_value: Vec<u8> = vec![1, 2, 3, 4, 5];
1590
1591        let base64 = not_err!(test_value.to_base64());
1592        let expected_base64 = "AQIDBAU";
1593        assert_eq!(base64.str(), expected_base64);
1594
1595        let actual_value = not_err!(Vec::<u8>::from_base64(&base64));
1596        assert_eq!(actual_value, test_value);
1597    }
1598
1599    #[test]
1600    fn compact_part_base64_url_round_trip() {
1601        let test_value = Base64Url("AQIDBAU".to_string());
1602
1603        let base64 = not_err!(test_value.to_base64());
1604        let expected_base64 = "AQIDBAU";
1605        assert_eq!(base64.str(), expected_base64);
1606
1607        let actual_value = not_err!(Base64Url::from_base64(&base64));
1608        assert_eq!(actual_value, test_value);
1609    }
1610}