rusty_paseto/prelude/
paseto_builder.rs

1use crate::generic::*;
2use core::marker::PhantomData;
3use std::collections::HashSet;
4use std::convert::TryFrom;
5use time::format_description::well_known::Rfc3339;
6 ///The PasetoBuilder is created at compile time by specifying a PASETO version and purpose and
7///providing a key of the same version and purpose. This structure allows setting [PASETO claims](https://github.com/paseto-standard/paseto-spec/blob/master/docs/02-Implementation-Guide/04-Claims.md),
8///your own [custom claims](CustomClaim), an optional [footer](Footer) and in the case of V3/V4 tokens, an optional [implicit
9///assertion](ImplicitAssertion).
10///
11///The PasetoBuilder wraps the [GenericBuilder] with JWT style claims and business rules which align
12///with the PASETO standard.
13/// For most users, this batteries-included struct
14/// will be all they need.
15///
16///# Usage
17///
18///```
19///# #[cfg(all(feature = "prelude", feature="v2_local"))]
20///# {
21///   use rusty_paseto::prelude::*;
22 ///     let key = PasetoSymmetricKey::<V2, Local>::from(Key::<32>::from(*b"wubbalubbadubdubwubbalubbadubdub"));
23 ///     let footer = Footer::from("some footer");
24 ///     //create a builder, add some claims and then build the token with the key
25///     let token = PasetoBuilder::<V2, Local>::default()
26///       .set_claim(AudienceClaim::from("customers"))
27///       .set_claim(SubjectClaim::from("loyal subjects"))
28///       .set_claim(IssuerClaim::from("me"))
29///       .set_claim(TokenIdentifierClaim::from("me"))
30///       .set_claim(IssuedAtClaim::try_from("2019-01-01T00:00:00+00:00")?)
31///       .set_claim(NotBeforeClaim::try_from("2019-01-01T00:00:00+00:00")?)
32///       .set_claim(ExpirationClaim::try_from("2019-01-01T00:00:00+00:00")?)
33///       .set_claim(CustomClaim::try_from(("data", "this is a secret message"))?)
34///       .set_claim(CustomClaim::try_from(("seats", 4))?)
35///       .set_claim(CustomClaim::try_from(("pi to 6 digits", 3.141526))?)
36///       .set_footer(footer)
37///       .try_encrypt(&key)?;
38 ///     //now let's decrypt the token and verify the values
39///     let json = PasetoParser::<V2, Local>::default()
40///       .set_footer(footer)
41///       .parse(&token, &key)?;
42 ///     assert_eq!(json["aud"], "customers");
43///     assert_eq!(json["jti"], "me");
44///     assert_eq!(json["iss"], "me");
45///     assert_eq!(json["data"], "this is a secret message");
46///     assert_eq!(json["exp"], "2019-01-01T00:00:00+00:00");
47///     assert_eq!(json["iat"], "2019-01-01T00:00:00+00:00");
48///     assert_eq!(json["nbf"], "2019-01-01T00:00:00+00:00");
49///     assert_eq!(json["sub"], "loyal subjects");
50///     assert_eq!(json["pi to 6 digits"], 3.141526);
51///     assert_eq!(json["seats"], 4);
52///  # }
53/// # Ok::<(),anyhow::Error>(())
54///   ```
55pub struct PasetoBuilder<'a, Version, Purpose> {
56  version: PhantomData<Version>,
57  purpose: PhantomData<Purpose>,
58  builder: GenericBuilder<'a, 'a, Version, Purpose>,
59  top_level_claims: HashSet<String>,
60  dup_top_level_found: (bool, String),
61  non_expiring_token: bool,
62}
63
64impl<'a, Version, Purpose> PasetoBuilder<'a, Version, Purpose> {
65  fn new() -> Self {
66    PasetoBuilder::<Version, Purpose> {
67      version: PhantomData::<Version>,
68      purpose: PhantomData::<Purpose>,
69      builder: GenericBuilder::default(),
70      top_level_claims: HashSet::new(),
71      non_expiring_token: false,
72      dup_top_level_found: (false, String::default()),
73    }
74  }
75 /// Given a [PasetoClaim], attempts to add it to the builder for inclusion in the payload of the
76  /// token.
77  /// claims provided to the GenericBuilder. Overwrites the default 'nbf' (not before) claim if
78  /// provided. Prevents duplicate claims from being added.
79  ///
80  /// Returns a mutable reference to the builder on success.
81  ///
82  /// # Errors
83  ///
84  /// none
85  ///
86  /// # Example
87  ///```
88  ///# #[cfg(all(feature = "prelude", feature="v2_local"))]
89  ///# {
90  ///   use rusty_paseto::prelude::*;
91  ///     let key = PasetoSymmetricKey::<V2, Local>::from(Key::<32>::from(*b"wubbalubbadubdubwubbalubbadubdub"));
92  ///     //create a builder, add some claims and then build the token with the key
93  ///     let token = PasetoBuilder::<V2, Local>::default()
94  ///       .set_claim(AudienceClaim::from("customers"))
95  ///       .try_encrypt(&key)?;
96  ///  #   //now let's decrypt the token and verify the values
97  ///  #  let json = PasetoParser::<V2, Local>::default().parse(&token, &key)?;
98  ///  # assert_eq!(json["aud"], "customers");
99  /// # }
100  /// # Ok::<(),anyhow::Error>(())
101  ///   ```
102  pub fn set_claim<T: PasetoClaim + erased_serde::Serialize + Sized + 'a>(&mut self, value: T) -> &mut Self {
103    //we need to inspect all the claims and verify there are no duplicates
104    //overwrite nbf default claim if provided
105    if value.get_key() == "nbf" {
106      //remove the existing claim
107      self.builder.remove_claim(value.get_key());
108    }
109    if !self.top_level_claims.insert(value.get_key().to_string()) {
110      self.dup_top_level_found = (true, value.get_key().to_string());
111    }
112
113    self.builder.set_claim(value);
114    self
115  }
116 /// Sets the token to have no expiration date.
117  /// A **1 hour** ExpirationClaim is set by default because the use case for non-expiring tokens in the world of security tokens is fairly limited.
118  ///  Omitting an expiration claim or forgetting to require one when processing them
119  ///  is almost certainly an oversight rather than a deliberate choice.  
120  ///  When it is a deliberate choice, you have the opportunity to deliberately remove this claim from the Builder.
121  ///  This method call ensures readers of the code understand the implicit risk.
122  ///
123  /// Returns a mutable reference to the builder on success.
124  ///
125  /// # Errors
126  /// none
127  ///
128  /// # Example
129  ///```
130  ///# #[cfg(all(feature = "prelude", feature="v2_local"))]
131  ///# {
132  ///   use rusty_paseto::prelude::*;
133  ///     let key = PasetoSymmetricKey::<V2, Local>::from(Key::<32>::from(*b"wubbalubbadubdubwubbalubbadubdub"));
134  ///  let token = PasetoBuilder::<V4, Local>::default()
135  ///    .set_claim(ExpirationClaim::try_from(in_5_minutes)?)
136  ///    // even if you set an expiration claim (as above) it will be ignored
137  ///    // due to the method call below
138  ///    .set_no_expiration_danger_acknowledged()
139  ///    .build(&key)?;
140  /// # }
141  /// # Ok::<(),anyhow::Error>(())
142  ///   ```
143  pub fn set_no_expiration_danger_acknowledged(&mut self) -> &mut Self {
144    self.top_level_claims.insert("exp".to_string());
145    self.non_expiring_token = true;
146    self
147  }
148 /// Sets an optional [Footer] on the token.
149  ///
150  /// Returns a mutable reference to the builder on success.
151  ///
152  /// # Errors
153  ///none
154  ///
155  /// # Example
156  ///```
157  ///# #[cfg(all(feature = "prelude", feature="v2_local"))]
158  ///# {
159  ///   use rusty_paseto::prelude::*;
160  ///     let key = PasetoSymmetricKey::<V2, Local>::from(Key::<32>::from(*b"wubbalubbadubdubwubbalubbadubdub"));
161  ///  let token = PasetoBuilder::<V2, Local>::default()
162  ///    .set_footer(Footer::from("Some footer"))
163  ///    .build(&key)?;
164  /// # }
165  /// # Ok::<(),anyhow::Error>(())
166  ///   ```
167  pub fn set_footer(&mut self, footer: Footer<'a>) -> &mut Self {
168    self.builder.set_footer(footer);
169    self
170  }
171
172  fn verify_ready_to_build(&mut self) -> Result<(), GenericBuilderError> {
173    if self.non_expiring_token {
174      self.builder.remove_claim("exp");
175    }
176    //  //raise an error if there were duplicates
177    let (dup_found, dup_key) = &self.dup_top_level_found;
178    if *dup_found {
179      return Err(GenericBuilderError::DuplicateTopLevelPayloadClaim(dup_key.to_string()));
180    }
181    Ok(())
182  }
183}
184impl<'a, Version, Purpose> PasetoBuilder<'a, Version, Purpose>
185where
186  Version: ImplicitAssertionCapable,
187{
188  /// Sets an optional [ImplicitAssertion] on the token. ([V3] or [V4] tokens only)
189  ///
190  /// Returns a mutable reference to the builder on success.
191  ///
192  /// # Errors
193  ///none
194  ///
195  /// # Example
196  ///```
197  ///# #[cfg(all(feature = "prelude", feature="v3_local"))]
198  ///# {
199  ///   use rusty_paseto::prelude::*;
200  ///     let key = PasetoSymmetricKey::<V3, Local>::from(Key::<32>::from(*b"wubbalubbadubdubwubbalubbadubdub"));
201  ///  let token = PasetoBuilder::<V3, Local>::default()
202  ///    .set_implicit_assertion(ImplicitAssertion::from("Some assertion"))
203  ///    .build(&key)?;
204  /// # }
205  /// # Ok::<(),anyhow::Error>(())
206  ///   ```
207  pub fn set_implicit_assertion(&mut self, implicit_assertion: ImplicitAssertion<'a>) -> &mut Self {
208    self.builder.set_implicit_assertion(implicit_assertion);
209    self
210  }
211}
212
213impl<'a, Version, Purpose> Default for PasetoBuilder<'a, Version, Purpose> {
214  fn default() -> Self {
215    //the unwraps in this function should be Infallible
216    let mut new_builder = Self::new();
217    let now = time::OffsetDateTime::now_utc();
218    let in_one_hour = now + time::Duration::hours(1);
219
220    let expiration_time = in_one_hour.format(&Rfc3339).unwrap();
221    let current_time = now.format(&Rfc3339).unwrap();
222    //set some defaults
223    new_builder
224      .builder
225      .set_claim(ExpirationClaim::try_from(expiration_time).unwrap())
226      .set_claim(IssuedAtClaim::try_from(current_time.clone()).unwrap())
227      .set_claim(NotBeforeClaim::try_from(current_time).unwrap());
228
229    new_builder
230  }
231}
232
233#[cfg(feature = "v1_local")]
234impl PasetoBuilder<'_, V1, Local> {
235  /// Attempts to validate claims meet PASETO standard requirements and then encrypt the token.
236  ///
237  /// Returns Ok(String) where the string is the encrypted PASETO token.
238  ///
239  /// # Errors
240  /// [GenericBuilderError] if there are [claim](PasetoClaim) or encryption issues.
241  ///
242  /// # Example
243  ///```
244  ///# #[cfg(all(feature = "prelude", feature="v1_local"))]
245  ///# {
246  ///   use rusty_paseto::prelude::*;
247  ///     let key = PasetoSymmetricKey::<V1, Local>::from(Key::<32>::from(*b"wubbalubbadubdubwubbalubbadubdub"));
248 ///     let footer = Footer::from("some footer");
249  ///     //create a builder, add some claims and then build the token with the key
250  ///     let token = PasetoBuilder::<V1, Local>::default()
251  ///       .set_claim(AudienceClaim::from("customers"))
252  ///       .set_claim(SubjectClaim::from("loyal subjects"))
253  ///       .set_claim(IssuerClaim::from("me"))
254  ///       .set_claim(TokenIdentifierClaim::from("me"))
255  ///       .set_claim(IssuedAtClaim::try_from("2019-01-01T00:00:00+00:00")?)
256  ///       .set_claim(NotBeforeClaim::try_from("2019-01-01T00:00:00+00:00")?)
257  ///       .set_claim(ExpirationClaim::try_from("2019-01-01T00:00:00+00:00")?)
258  ///       .set_claim(CustomClaim::try_from(("data", "this is a secret message"))?)
259  ///       .set_claim(CustomClaim::try_from(("seats", 4))?)
260  ///       .set_claim(CustomClaim::try_from(("pi to 6 digits", 3.141526))?)
261  ///       .set_footer(footer)
262  ///       .build(&key)?;
263  ///     //now let's decrypt the token and verify the values
264  ///     let json = PasetoParser::<V1, Local>::default()
265  ///       .set_footer(footer)
266  ///       .parse(&token, &key)?;
267  ///     assert_eq!(json["aud"], "customers");
268  ///     assert_eq!(json["jti"], "me");
269  ///     assert_eq!(json["iss"], "me");
270  ///     assert_eq!(json["data"], "this is a secret message");
271  ///     assert_eq!(json["exp"], "2019-01-01T00:00:00+00:00");
272  ///     assert_eq!(json["iat"], "2019-01-01T00:00:00+00:00");
273  ///     assert_eq!(json["nbf"], "2019-01-01T00:00:00+00:00");
274  ///     assert_eq!(json["sub"], "loyal subjects");
275  ///     assert_eq!(json["pi to 6 digits"], 3.141526);
276  ///     assert_eq!(json["seats"], 4);
277  ///  # }
278  /// # Ok::<(),anyhow::Error>(())
279  ///   ```
280  pub fn build(&mut self, key: &PasetoSymmetricKey<V1, Local>) -> Result<String, GenericBuilderError> {
281    self.verify_ready_to_build()?;
282    self.builder.try_encrypt(key)
283  }
284}
285
286#[cfg(feature = "v2_local")]
287impl PasetoBuilder<'_, V2, Local> {
288  /// Attempts to validate claims meet PASETO standard requirements and then encrypt the token.
289  ///
290  /// Returns Ok(String) where the string is the encrypted PASETO token.
291  ///
292  /// # Errors
293  /// [GenericBuilderError] if there are [claim](PasetoClaim) or encryption issues.
294  ///
295  /// # Example
296  ///
297  ///
298  ///```
299  ///# #[cfg(all(feature = "prelude", feature="v2_local"))]
300  ///# {
301  ///   use rusty_paseto::prelude::*;
302  ///     let key = PasetoSymmetricKey::<V2, Local>::from(Key::<32>::from(*b"wubbalubbadubdubwubbalubbadubdub"));
303 ///     let footer = Footer::from("some footer");
304  ///     //create a builder, add some claims and then build the token with the key
305  ///     let token = PasetoBuilder::<V2, Local>::default()
306  ///       .set_claim(AudienceClaim::from("customers"))
307  ///       .set_claim(SubjectClaim::from("loyal subjects"))
308  ///       .set_claim(IssuerClaim::from("me"))
309  ///       .set_claim(TokenIdentifierClaim::from("me"))
310  ///       .set_claim(IssuedAtClaim::try_from("2019-01-01T00:00:00+00:00")?)
311  ///       .set_claim(NotBeforeClaim::try_from("2019-01-01T00:00:00+00:00")?)
312  ///       .set_claim(ExpirationClaim::try_from("2019-01-01T00:00:00+00:00")?)
313  ///       .set_claim(CustomClaim::try_from(("data", "this is a secret message"))?)
314  ///       .set_claim(CustomClaim::try_from(("seats", 4))?)
315  ///       .set_claim(CustomClaim::try_from(("pi to 6 digits", 3.141526))?)
316  ///       .set_footer(footer)
317  ///       .try_encrypt(&key)?;
318  ///     //now let's decrypt the token and verify the values
319  ///     let json = PasetoParser::<V2, Local>::default()
320  ///       .set_footer(footer)
321  ///       .parse(&token, &key)?;
322  ///     assert_eq!(json["aud"], "customers");
323  ///     assert_eq!(json["jti"], "me");
324  ///     assert_eq!(json["iss"], "me");
325  ///     assert_eq!(json["data"], "this is a secret message");
326  ///     assert_eq!(json["exp"], "2019-01-01T00:00:00+00:00");
327  ///     assert_eq!(json["iat"], "2019-01-01T00:00:00+00:00");
328  ///     assert_eq!(json["nbf"], "2019-01-01T00:00:00+00:00");
329  ///     assert_eq!(json["sub"], "loyal subjects");
330  ///     assert_eq!(json["pi to 6 digits"], 3.141526);
331  ///     assert_eq!(json["seats"], 4);
332  ///  # }
333  /// # Ok::<(),anyhow::Error>(())
334  ///   ```
335  pub fn build(&mut self, key: &PasetoSymmetricKey<V2, Local>) -> Result<String, GenericBuilderError> {
336    self.verify_ready_to_build()?;
337    self.builder.try_encrypt(key)
338  }
339}
340
341#[cfg(feature = "v3_local")]
342impl PasetoBuilder<'_, V3, Local> {
343  /// Attempts to validate claims meet PASETO standard requirements and then encrypt the token.
344  ///
345  /// Returns Ok(String) where the string is the encrypted PASETO token.
346  ///
347  /// # Errors
348  /// [GenericBuilderError] if there are [claim](PasetoClaim) or encryption issues.
349  ///
350  /// # Example
351  ///
352  ///```
353  ///# #[cfg(all(feature = "prelude", feature="v3_local"))]
354  ///# {
355  ///   use rusty_paseto::prelude::*;
356  ///     let key = PasetoSymmetricKey::<V3, Local>::from(Key::<32>::from(*b"wubbalubbadubdubwubbalubbadubdub"));
357 ///     let footer = Footer::from("some footer");
358  ///     let implicit_assertion = ImplicitAssertion::from("some assertion");
359  ///     //create a builder, add some claims and then build the token with the key
360  ///     let token = PasetoBuilder::<V3, Local>::default()
361  ///       .set_claim(AudienceClaim::from("customers"))
362  ///       .set_claim(SubjectClaim::from("loyal subjects"))
363  ///       .set_claim(IssuerClaim::from("me"))
364  ///       .set_claim(TokenIdentifierClaim::from("me"))
365  ///       .set_claim(IssuedAtClaim::try_from("2019-01-01T00:00:00+00:00")?)
366  ///       .set_claim(NotBeforeClaim::try_from("2019-01-01T00:00:00+00:00")?)
367  ///       .set_claim(ExpirationClaim::try_from("2019-01-01T00:00:00+00:00")?)
368  ///       .set_claim(CustomClaim::try_from(("data", "this is a secret message"))?)
369  ///       .set_claim(CustomClaim::try_from(("seats", 4))?)
370  ///       .set_claim(CustomClaim::try_from(("pi to 6 digits", 3.141526))?)
371  ///       .set_footer(footer)
372  ///       .set_implicit_assertion(implicit_assertion)
373  ///       .try_encrypt(&key)?;
374  ///     //now let's decrypt the token and verify the values
375  ///     let json = PasetoParser::<V3, Local>::default()
376  ///       .set_footer(footer)
377  ///       .set_implicit_assertion(implicit_assertion)
378  ///       .parse(&token, &key)?;
379  ///     assert_eq!(json["aud"], "customers");
380  ///     assert_eq!(json["jti"], "me");
381  ///     assert_eq!(json["iss"], "me");
382  ///     assert_eq!(json["data"], "this is a secret message");
383  ///     assert_eq!(json["exp"], "2019-01-01T00:00:00+00:00");
384  ///     assert_eq!(json["iat"], "2019-01-01T00:00:00+00:00");
385  ///     assert_eq!(json["nbf"], "2019-01-01T00:00:00+00:00");
386  ///     assert_eq!(json["sub"], "loyal subjects");
387  ///     assert_eq!(json["pi to 6 digits"], 3.141526);
388  ///     assert_eq!(json["seats"], 4);
389  ///  # }
390  /// # Ok::<(),anyhow::Error>(())
391  ///   ```
392  pub fn build(&mut self, key: &PasetoSymmetricKey<V3, Local>) -> Result<String, GenericBuilderError> {
393    self.verify_ready_to_build()?;
394    self.builder.try_encrypt(key)
395  }
396}
397
398#[cfg(feature = "v4_local")]
399impl PasetoBuilder<'_, V4, Local> {
400  /// Attempts to validate claims meet PASETO standard requirements and then encrypt the token.
401  ///
402  /// Returns Ok(String) where the string is the encrypted PASETO token.
403  ///
404  /// # Errors
405  /// [GenericBuilderError] if there are [claim](PasetoClaim) or encryption issues.
406  ///
407  /// # Example
408  ///
409  ///```
410  ///# #[cfg(all(feature = "prelude", feature="v4_local"))]
411  ///# {
412  ///   use rusty_paseto::prelude::*;
413  ///     let key = PasetoSymmetricKey::<V4, Local>::from(Key::<32>::from(*b"wubbalubbadubdubwubbalubbadubdub"));
414 ///     let footer = Footer::from("some footer");
415  ///     let implicit_assertion = ImplicitAssertion::from("some assertion");
416  ///     //create a builder, add some claims and then build the token with the key
417  ///     let token = PasetoBuilder::<V4, Local>::default()
418  ///       .set_claim(AudienceClaim::from("customers"))
419  ///       .set_claim(SubjectClaim::from("loyal subjects"))
420  ///       .set_claim(IssuerClaim::from("me"))
421  ///       .set_claim(TokenIdentifierClaim::from("me"))
422  ///       .set_claim(IssuedAtClaim::try_from("2019-01-01T00:00:00+00:00")?)
423  ///       .set_claim(NotBeforeClaim::try_from("2019-01-01T00:00:00+00:00")?)
424  ///       .set_claim(ExpirationClaim::try_from("2019-01-01T00:00:00+00:00")?)
425  ///       .set_claim(CustomClaim::try_from(("data", "this is a secret message"))?)
426  ///       .set_claim(CustomClaim::try_from(("seats", 4))?)
427  ///       .set_claim(CustomClaim::try_from(("pi to 6 digits", 3.141526))?)
428  ///       .set_footer(footer)
429  ///       .set_implicit_assertion(implicit_assertion)
430  ///       .try_encrypt(&key)?;
431  ///     //now let's decrypt the token and verify the values
432  ///     let json = PasetoParser::<V4, Local>::default()
433  ///       .set_footer(footer)
434  ///       .set_implicit_assertion(implicit_assertion)
435  ///       .parse(&token, &key)?;
436  ///     assert_eq!(json["aud"], "customers");
437  ///     assert_eq!(json["jti"], "me");
438  ///     assert_eq!(json["iss"], "me");
439  ///     assert_eq!(json["data"], "this is a secret message");
440  ///     assert_eq!(json["exp"], "2019-01-01T00:00:00+00:00");
441  ///     assert_eq!(json["iat"], "2019-01-01T00:00:00+00:00");
442  ///     assert_eq!(json["nbf"], "2019-01-01T00:00:00+00:00");
443  ///     assert_eq!(json["sub"], "loyal subjects");
444  ///     assert_eq!(json["pi to 6 digits"], 3.141526);
445  ///     assert_eq!(json["seats"], 4);
446  ///  # }
447  /// # Ok::<(),anyhow::Error>(())
448  ///   ```
449  pub fn build(&mut self, key: &PasetoSymmetricKey<V4, Local>) -> Result<String, GenericBuilderError> {
450    self.verify_ready_to_build()?;
451    self.builder.try_encrypt(key)
452  }
453}
454
455#[cfg(feature = "v1_public")]
456impl PasetoBuilder<'_, V1, Public> {
457  /// Given a [PasetoAsymmetricPrivateKey], attempts to validate claims meet PASETO standard requirements and then sign the token.
458  ///
459  /// Returns Ok(String) where the string is the signed PASETO token.
460  ///
461  /// # Errors
462  /// [GenericBuilderError] if there are [claim](PasetoClaim) or signing issues.
463  ///
464  /// # Example
465  ///
466  ///```
467  ///# #[cfg(all(feature = "prelude", feature="v1_public"))]
468  ///# {
469  ///   # use rusty_paseto::prelude::*;
470  ///    //obtain a private key (pk)
471  ///   # let private_key = include_bytes!("../../../tests/v1_public_test_vectors_private_key.pk8");
472  ///   # let pk: &[u8] = private_key;
473  ///    let private_key = PasetoAsymmetricPrivateKey::<V1, Public>::from(pk);
474  ///     let footer = Footer::from("some footer");
475 ///     //sign a public V1 token
476  ///     let token = PasetoBuilder::<V1, Public>::default()
477  ///       .set_claim(AudienceClaim::from("customers"))
478  ///       .set_claim(SubjectClaim::from("loyal subjects"))
479  ///       .set_claim(IssuerClaim::from("me"))
480  ///       .set_claim(TokenIdentifierClaim::from("me"))
481  ///       .set_claim(IssuedAtClaim::try_from("2019-01-01T00:00:00+00:00")?)
482  ///       .set_claim(NotBeforeClaim::try_from("2019-01-01T00:00:00+00:00")?)
483  ///       .set_claim(ExpirationClaim::try_from("2019-01-01T00:00:00+00:00")?)
484  ///       .set_claim(CustomClaim::try_from(("data", "this is a secret message"))?)
485  ///       .set_claim(CustomClaim::try_from(("seats", 4))?)
486  ///       .set_claim(CustomClaim::try_from(("pi to 6 digits", 3.141526))?)
487  ///       .set_footer(footer)
488  ///       .try_sign(&private_key)?;
489  /// //obtain a public key (pubk)
490  ///   #  let public_key = include_bytes!("../../../tests/v1_public_test_vectors_public_key.der");
491  ///   #  let pubk: &[u8] = public_key;
492  ///     let public_key = PasetoAsymmetricPublicKey::<V1, Public>::from(pubk);
493  ///     //now let's try to verify it
494  ///     let json = PasetoParser::<V1, Public>::default()
495  ///       .set_footer(footer)
496  ///       .check_claim(AudienceClaim::from("customers"))
497  ///       .check_claim(SubjectClaim::from("loyal subjects"))
498  ///       .check_claim(IssuerClaim::from("me"))
499  ///       .check_claim(TokenIdentifierClaim::from("me"))
500  ///       .check_claim(IssuedAtClaim::try_from("2019-01-01T00:00:00+00:00")?)
501  ///       .check_claim(NotBeforeClaim::try_from("2019-01-01T00:00:00+00:00")?)
502  ///       .check_claim(ExpirationClaim::try_from("2019-01-01T00:00:00+00:00")?)
503  ///       .check_claim(CustomClaim::try_from(("data", "this is a secret message"))?)
504  ///       .check_claim(CustomClaim::try_from(("seats", 4))?)
505  ///       .check_claim(CustomClaim::try_from(("pi to 6 digits", 3.141526))?)
506  ///       .parse(&token, &public_key)?;
507  ///     // we can access all the values from the serde Value object returned by the parser
508  ///     assert_eq!(json["aud"], "customers");
509  ///     assert_eq!(json["jti"], "me");
510  ///     assert_eq!(json["iss"], "me");
511  ///     assert_eq!(json["data"], "this is a secret message");
512  ///     assert_eq!(json["exp"], "2019-01-01T00:00:00+00:00");
513  ///     assert_eq!(json["iat"], "2019-01-01T00:00:00+00:00");
514  ///     assert_eq!(json["nbf"], "2019-01-01T00:00:00+00:00");
515  ///     assert_eq!(json["sub"], "loyal subjects");
516  ///     assert_eq!(json["pi to 6 digits"], 3.141526);
517  ///     assert_eq!(json["seats"], 4);
518  ///  # }
519  /// # Ok::<(),anyhow::Error>(())
520  ///```
521  pub fn build(&mut self, key: &PasetoAsymmetricPrivateKey<V1, Public>) -> Result<String, GenericBuilderError> {
522    self.verify_ready_to_build()?;
523    self.builder.try_sign(key)
524  }
525}
526
527#[cfg(feature = "v2_public")]
528impl PasetoBuilder<'_, V2, Public> {
529  /// Given a [PasetoAsymmetricPrivateKey], attempts to validate claims meet PASETO standard requirements and then sign the token.
530  ///
531  /// Returns Ok(String) where the string is the signed PASETO token.
532  ///
533  /// # Errors
534  /// [GenericBuilderError] if there are [claim](PasetoClaim) or signing issues.
535  ///
536  /// # Example
537  ///```
538  ///# #[cfg(all(feature = "prelude", feature="v2_public"))]
539  ///# {
540  ///   # use rusty_paseto::prelude::*;
541  ///    //obtain a key
542  /// let private_key = Key::<64>::try_from("b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a37741eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2")?;
543  /// let private_key = PasetoAsymmetricPrivateKey::<V2, Public>::from(&private_key);
544  /// let public_key = Key::<32>::try_from("1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2")?;
545  /// let public_key = PasetoAsymmetricPublicKey::<V2, Public>::from(&public_key);
546  /// let footer = Footer::from("some footer");
547 /// //sign a public V2 token
548  /// let token = PasetoBuilder::<V2, Public>::default()
549  ///   .set_claim(AudienceClaim::from("customers"))
550  ///   .set_claim(SubjectClaim::from("loyal subjects"))
551  ///   .set_claim(IssuerClaim::from("me"))
552  ///   .set_claim(TokenIdentifierClaim::from("me"))
553  ///   .set_claim(IssuedAtClaim::try_from("2019-01-01T00:00:00+00:00")?)
554  ///   .set_claim(NotBeforeClaim::try_from("2019-01-01T00:00:00+00:00")?)
555  ///   .set_claim(ExpirationClaim::try_from("2019-01-01T00:00:00+00:00")?)
556  ///   .set_claim(CustomClaim::try_from(("data", "this is a secret message"))?)
557  ///   .set_claim(CustomClaim::try_from(("seats", 4))?)
558  ///   .set_claim(CustomClaim::try_from(("pi to 6 digits", 3.141526))?)
559  ///   .set_footer(footer)
560  ///   .try_sign(&private_key)?;
561  /// //now let's try to verify it
562  /// let json = PasetoParser::<V2, Public>::default()
563  ///   .set_footer(footer)
564  ///   .check_claim(AudienceClaim::from("customers"))
565  ///   .check_claim(SubjectClaim::from("loyal subjects"))
566  ///   .check_claim(IssuerClaim::from("me"))
567  ///   .check_claim(TokenIdentifierClaim::from("me"))
568  ///   .check_claim(IssuedAtClaim::try_from("2019-01-01T00:00:00+00:00")?)
569  ///   .check_claim(NotBeforeClaim::try_from("2019-01-01T00:00:00+00:00")?)
570  ///   .check_claim(ExpirationClaim::try_from("2019-01-01T00:00:00+00:00")?)
571  ///   .check_claim(CustomClaim::try_from(("data", "this is a secret message"))?)
572  ///   .check_claim(CustomClaim::try_from(("seats", 4))?)
573  ///   .check_claim(CustomClaim::try_from(("pi to 6 digits", 3.141526))?)
574  ///   .parse(&token, &public_key)?;
575  /// // we can access all the values from the serde Value object returned by the parser
576  /// assert_eq!(json["aud"], "customers");
577  /// assert_eq!(json["jti"], "me");
578  /// assert_eq!(json["iss"], "me");
579  /// assert_eq!(json["data"], "this is a secret message");
580  /// assert_eq!(json["exp"], "2019-01-01T00:00:00+00:00");
581  /// assert_eq!(json["iat"], "2019-01-01T00:00:00+00:00");
582  /// assert_eq!(json["nbf"], "2019-01-01T00:00:00+00:00");
583  /// assert_eq!(json["sub"], "loyal subjects");
584  /// assert_eq!(json["pi to 6 digits"], 3.141526);
585  /// assert_eq!(json["seats"], 4);
586  ///  # }
587  /// # Ok::<(),anyhow::Error>(())
588  ///```
589  pub fn build(&mut self, key: &PasetoAsymmetricPrivateKey<V2, Public>) -> Result<String, GenericBuilderError> {
590    self.verify_ready_to_build()?;
591    self.builder.try_sign(key)
592  }
593}
594
595#[cfg(feature = "v3_public")]
596impl PasetoBuilder<'_, V3, Public> {
597  /// Given a [PasetoAsymmetricPrivateKey], attempts to validate claims meet PASETO standard requirements and then sign the token.
598  ///
599  /// Returns Ok(String) where the string is the signed PASETO token.
600  ///
601  /// # Errors
602  /// [GenericBuilderError] if there are [claim](PasetoClaim) or signing issues.
603  ///
604  /// # Example
605  ///```
606  ///# #[cfg(all(feature = "prelude", feature="v3_public"))]
607  ///# {
608  ///   # use rusty_paseto::prelude::*;
609  ///    //obtain a key
610  /// let private_key = Key::<48>::try_from(
611  ///   "20347609607477aca8fbfbc5e6218455f3199669792ef8b466faa87bdc67798144c848dd03661eed5ac62461340cea96",
612  /// )?;
613  /// let private_key = PasetoAsymmetricPrivateKey::<V3, Public>::from(&private_key);
614  /// let public_key = Key::<49>::try_from(
615  ///   "02fbcb7c69ee1c60579be7a334134878d9c5c5bf35d552dab63c0140397ed14cef637d7720925c44699ea30e72874c72fb",
616  /// )?;
617  /// let public_key = PasetoAsymmetricPublicKey::<V3, Public>::try_from(&public_key)?;
618  /// let footer = Footer::from("some footer");
619 /// let implicit_assertion = ImplicitAssertion::from("some assertion");
620  /// //sign a public V3 token
621  /// let token = PasetoBuilder::<V3, Public>::default()
622  ///   .set_claim(AudienceClaim::from("customers"))
623  ///   .set_claim(SubjectClaim::from("loyal subjects"))
624  ///   .set_claim(IssuerClaim::from("me"))
625  ///   .set_claim(TokenIdentifierClaim::from("me"))
626  ///   .set_claim(IssuedAtClaim::try_from("2019-01-01T00:00:00+00:00")?)
627  ///   .set_claim(NotBeforeClaim::try_from("2019-01-01T00:00:00+00:00")?)
628  ///   .set_claim(ExpirationClaim::try_from("2019-01-01T00:00:00+00:00")?)
629  ///   .set_claim(CustomClaim::try_from(("data", "this is a secret message"))?)
630  ///   .set_claim(CustomClaim::try_from(("seats", 4))?)
631  ///   .set_claim(CustomClaim::try_from(("pi to 6 digits", 3.141526))?)
632  ///   .set_footer(footer)
633  ///   .set_implicit_assertion(implicit_assertion)
634  ///   .try_sign(&private_key)?;
635  /// //now let's try to verify it
636  /// let json = PasetoParser::<V3, Public>::default()
637  ///   .set_footer(footer)
638  ///   .check_claim(AudienceClaim::from("customers"))
639  ///   .set_implicit_assertion(implicit_assertion)
640  ///   .check_claim(SubjectClaim::from("loyal subjects"))
641  ///   .check_claim(IssuerClaim::from("me"))
642  ///   .check_claim(TokenIdentifierClaim::from("me"))
643  ///   .check_claim(IssuedAtClaim::try_from("2019-01-01T00:00:00+00:00")?)
644  ///   .check_claim(NotBeforeClaim::try_from("2019-01-01T00:00:00+00:00")?)
645  ///   .check_claim(ExpirationClaim::try_from("2019-01-01T00:00:00+00:00")?)
646  ///   .check_claim(CustomClaim::try_from(("data", "this is a secret message"))?)
647  ///   .check_claim(CustomClaim::try_from(("seats", 4))?)
648  ///   .check_claim(CustomClaim::try_from(("pi to 6 digits", 3.141526))?)
649  ///   .parse(&token, &public_key)?;
650  /// // we can access all the values from the serde Value object returned by the parser
651  /// assert_eq!(json["aud"], "customers");
652  /// assert_eq!(json["jti"], "me");
653  /// assert_eq!(json["iss"], "me");
654  /// assert_eq!(json["data"], "this is a secret message");
655  /// assert_eq!(json["exp"], "2019-01-01T00:00:00+00:00");
656  /// assert_eq!(json["iat"], "2019-01-01T00:00:00+00:00");
657  /// assert_eq!(json["nbf"], "2019-01-01T00:00:00+00:00");
658  /// assert_eq!(json["sub"], "loyal subjects");
659  /// assert_eq!(json["pi to 6 digits"], 3.141526);
660  /// assert_eq!(json["seats"], 4);
661  ///  # }
662  /// # Ok::<(),anyhow::Error>(())
663  ///```
664  pub fn build(&mut self, key: &PasetoAsymmetricPrivateKey<V3, Public>) -> Result<String, GenericBuilderError> {
665    self.verify_ready_to_build()?;
666    self.builder.try_sign(key)
667  }
668}
669
670#[cfg(feature = "v4_public")]
671impl PasetoBuilder<'_, V4, Public> {
672  /// Given a [PasetoAsymmetricPrivateKey], attempts to validate claims meet PASETO standard requirements and then sign the token.
673  ///
674  /// Returns Ok(String) where the string is the signed PASETO token.
675  ///
676  /// # Errors
677  /// [GenericBuilderError] if there are [claim](PasetoClaim) or signing issues.
678  ///
679  /// # Example
680  ///```
681  ///# #[cfg(all(feature = "prelude", feature="v4_public"))]
682  ///# {
683  ///   # use rusty_paseto::prelude::*;
684  /// //create a key
685  /// let private_key = Key::<64>::try_from("b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a37741eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2")?;
686  /// let pk: &[u8] = private_key.as_slice();
687  /// let private_key = PasetoAsymmetricPrivateKey::<V4, Public>::from(pk);
688  /// let public_key = Key::<32>::try_from("1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2")?;
689  /// let public_key = PasetoAsymmetricPublicKey::<V4, Public>::from(&public_key);
690  /// let footer = Footer::from("some footer");
691  /// let implicit_assertion = ImplicitAssertion::from("some assertion");
692  /// //sign a public V4 token
693  /// let token = PasetoBuilder::<V4, Public>::default()
694  ///   .set_claim(AudienceClaim::from("customers"))
695  ///   .set_claim(SubjectClaim::from("loyal subjects"))
696  ///   .set_claim(IssuerClaim::from("me"))
697  ///   .set_claim(TokenIdentifierClaim::from("me"))
698  ///   .set_claim(IssuedAtClaim::try_from("2019-01-01T00:00:00+00:00")?)
699  ///   .set_claim(NotBeforeClaim::try_from("2019-01-01T00:00:00+00:00")?)
700  ///   .set_claim(ExpirationClaim::try_from("2019-01-01T00:00:00+00:00")?)
701  ///   .set_claim(CustomClaim::try_from(("data", "this is a secret message"))?)
702  ///   .set_claim(CustomClaim::try_from(("seats", 4))?)
703  ///   .set_claim(CustomClaim::try_from(("pi to 6 digits", 3.141526))?)
704  ///   .set_footer(footer)
705  ///   .set_implicit_assertion(implicit_assertion)
706  ///   .try_sign(&private_key)?;
707  /// //now let's try to verify it
708  /// let json = PasetoParser::<V4, Public>::default()
709  ///   .set_footer(footer)
710  ///   .set_implicit_assertion(implicit_assertion)
711  ///   .check_claim(AudienceClaim::from("customers"))
712  ///   .check_claim(SubjectClaim::from("loyal subjects"))
713  ///   .check_claim(IssuerClaim::from("me"))
714  ///   .check_claim(TokenIdentifierClaim::from("me"))
715  ///   .check_claim(IssuedAtClaim::try_from("2019-01-01T00:00:00+00:00")?)
716  ///   .check_claim(NotBeforeClaim::try_from("2019-01-01T00:00:00+00:00")?)
717  ///   .check_claim(ExpirationClaim::try_from("2019-01-01T00:00:00+00:00")?)
718  ///   .check_claim(CustomClaim::try_from(("data", "this is a secret message"))?)
719  ///   .check_claim(CustomClaim::try_from(("seats", 4))?)
720  ///   .check_claim(CustomClaim::try_from(("pi to 6 digits", 3.141526))?)
721  ///   .parse(&token, &public_key)?;
722  /// // we can access all the values from the serde Value object returned by the parser
723  /// assert_eq!(json["aud"], "customers");
724  /// assert_eq!(json["jti"], "me");
725  /// assert_eq!(json["iss"], "me");
726  /// assert_eq!(json["data"], "this is a secret message");
727  /// assert_eq!(json["exp"], "2019-01-01T00:00:00+00:00");
728  /// assert_eq!(json["iat"], "2019-01-01T00:00:00+00:00");
729  /// assert_eq!(json["nbf"], "2019-01-01T00:00:00+00:00");
730  /// assert_eq!(json["sub"], "loyal subjects");
731  /// assert_eq!(json["pi to 6 digits"], 3.141526);
732  /// assert_eq!(json["seats"], 4);
733  ///  # }
734  /// # Ok::<(),anyhow::Error>(())
735  ///```
736  pub fn build(&mut self, key: &PasetoAsymmetricPrivateKey<V4, Public>) -> Result<String, GenericBuilderError> {
737    self.verify_ready_to_build()?;
738    self.builder.try_sign(key)
739  }
740}
741
742#[cfg(all(test, feature = "v2_local"))]
743mod paseto_builder {
744
745  use crate::prelude::*;
746  use anyhow::Result;
747  use std::convert::TryFrom;
748  use time::format_description::well_known::Rfc3339;
749
750  #[test]
751  fn duplicate_top_level_claim_test() -> Result<()> {
752    //create a key
753
754    let key = PasetoSymmetricKey::<V2, Local>::from(Key::from(*b"wubbalubbadubdubwubbalubbadubdub"));
755    let tomorrow = (time::OffsetDateTime::now_utc() + time::Duration::days(1)).format(&Rfc3339)?;
756
757    //let tomorrow = (Utc::now() + Duration::days(1)).to_rfc3339();
758
759    //create a builder, with default IssuedAtClaim
760    let expected_error = format!(
761      "{}",
762      PasetoBuilder::<V2, Local>::default()
763        .set_claim(IssuedAtClaim::try_from(tomorrow.as_str()).unwrap())
764        .set_claim(IssuedAtClaim::try_from(tomorrow.as_str()).unwrap())
765        .build(&key)
766        .unwrap_err()
767    );
768
769    assert_eq!(
770      expected_error,
771      "The claim 'iat' appears more than once in the top level payload json"
772    );
773
774    Ok(())
775  }
776
777  #[test]
778  fn update_default_not_before_claim_test() -> Result<()> {
779    //create a key
780
781    let key = PasetoSymmetricKey::<V2, Local>::from(Key::from(*b"wubbalubbadubdubwubbalubbadubdub"));
782    let tomorrow = (time::OffsetDateTime::now_utc() + time::Duration::days(1)).format(&Rfc3339)?;
783
784    //create a builder, with default IssuedAtClaim
785    let token = PasetoBuilder::<V2, Local>::default()
786      .set_claim(NotBeforeClaim::try_from(tomorrow).unwrap())
787      .build(&key)?;
788
789    //now let's decrypt the token and verify the values
790    //the IssuedAtClaim should exist and the date should be set to tomorrow
791    let token_error = PasetoParser::<V2, Local>::default().parse(&token, &key).err().unwrap();
792
793    assert!(token_error.to_string().starts_with("The token cannot be used before "));
794
795    Ok(())
796  }
797
798  #[test]
799  fn update_default_issued_at_claim_test() -> Result<()> {
800    //create a key
801
802    let key = PasetoSymmetricKey::<V2, Local>::from(Key::from(*b"wubbalubbadubdubwubbalubbadubdub"));
803    let tomorrow = (time::OffsetDateTime::now_utc() + time::Duration::days(1)).format(&Rfc3339)?;
804
805    //create a builder, with default IssuedAtClaim
806    let token = PasetoBuilder::<V2, Local>::default()
807      .set_claim(IssuedAtClaim::try_from(tomorrow.as_str()).unwrap())
808      .build(&key)?;
809
810    //now let's decrypt the token and verify the values
811    //the IssuedAtClaim should exist and the date should be set to tomorrow
812    GenericParser::<V2, Local>::default()
813      .validate_claim(IssuedAtClaim::default(), &|key, value| {
814        //let's get the value
815        let val = value
816          .as_str()
817          .ok_or_else(|| PasetoClaimError::Unexpected(key.to_string()))?;
818
819        let datetime = iso8601::datetime(val).unwrap();
820        let tomorrow = (time::OffsetDateTime::now_utc() + time::Duration::days(1))
821          .date()
822          .to_string();
823
824        //the claimm should exist
825        assert_eq!(key, "iat");
826        //date should be tomorrow
827        assert_eq!(datetime.date.to_string(), tomorrow);
828
829        Ok(())
830      })
831      .parse(&token, &key)?;
832
833    Ok(())
834  }
835
836  #[test]
837  fn check_for_default_issued_at_claim_test() -> Result<()> {
838    //create a key
839
840    let key = PasetoSymmetricKey::<V2, Local>::from(Key::from(*b"wubbalubbadubdubwubbalubbadubdub"));
841    //create a builder, with default IssuedAtClaim
842    let token = PasetoBuilder::<V2, Local>::default().build(&key)?;
843
844    //now let's decrypt the token and verify the values
845    //the IssuedAtClaim should exist
846    GenericParser::<V2, Local>::default()
847      .validate_claim(IssuedAtClaim::default(), &|key, value| {
848        //let's get the value
849        let val = value
850          .as_str()
851          .ok_or_else(|| PasetoClaimError::Unexpected(key.to_string()))?;
852
853        let datetime = iso8601::datetime(val).unwrap();
854
855        let now = time::OffsetDateTime::now_utc().date().to_string();
856        //the claimm should exist
857        assert_eq!(key, "iat");
858        //date should be today
859        assert_eq!(datetime.date.to_string(), now);
860
861        Ok(())
862      })
863      .parse(&token, &key)?;
864
865    Ok(())
866  }
867
868  #[test]
869  fn update_default_expiration_claim_test() -> Result<()> {
870    //create a key
871
872    let key = PasetoSymmetricKey::<V2, Local>::from(Key::from(*b"wubbalubbadubdubwubbalubbadubdub"));
873    //let in_4_days = (Utc::now() + Duration::days(4)).to_rfc3339();
874    let in_4_days = (time::OffsetDateTime::now_utc() + time::Duration::days(4)).format(&Rfc3339)?;
875
876    //create a builder, with default IssuedAtClaim
877    let token = PasetoBuilder::<V2, Local>::default()
878      .set_claim(ExpirationClaim::try_from(in_4_days).unwrap())
879      .build(&key)?;
880
881    //now let's decrypt the token and verify the values
882    //the IssuedAtClaim should exist and the date should be set to tomorrow
883    GenericParser::<V2, Local>::default()
884      .validate_claim(ExpirationClaim::default(), &|key, value| {
885        //let's get the value
886        let val = value
887          .as_str()
888          .ok_or_else(|| PasetoClaimError::Unexpected(key.to_string()))?;
889
890        let datetime = iso8601::datetime(val).unwrap();
891
892        let in_4_days = (time::OffsetDateTime::now_utc() + time::Duration::days(4))
893          .date()
894          .to_string();
895        //let in_4_days = Utc::now() + Duration::days(4);
896        //the claimm should exist
897        assert_eq!(key, "exp");
898        //date should be tomorrow
899        assert_eq!(datetime.date.to_string(), in_4_days);
900
901        Ok(())
902      })
903      .parse(&token, &key)?;
904
905    Ok(())
906  }
907
908  #[test]
909  fn check_for_default_expiration_claim_test() -> Result<()> {
910    //create a key
911
912    let key = PasetoSymmetricKey::<V2, Local>::from(Key::from(*b"wubbalubbadubdubwubbalubbadubdub"));
913    //create a builder, with default IssuedAtClaim
914    let token = PasetoBuilder::<V2, Local>::default().build(&key)?;
915
916    //now let's decrypt the token and verify the values
917    //the IssuedAtClaim should exist
918    GenericParser::<V2, Local>::default()
919      .validate_claim(ExpirationClaim::default(), &|key, value| {
920        //let's get the value
921        let val = value
922          .as_str()
923          .ok_or_else(|| PasetoClaimError::Unexpected(key.to_string()))?;
924
925        let datetime = iso8601::datetime(val).unwrap();
926        let expires = (time::OffsetDateTime::now_utc() + time::Duration::hours(1))
927          .date()
928          .to_string();
929
930        //let tomorrow = Utc::now() + Duration::hours(1);
931        //the claimm should exist
932        assert_eq!(key, "exp");
933        //date should be today
934        assert_eq!(datetime.date.to_string(), expires);
935
936        Ok(())
937      })
938      .parse(&token, &key)?;
939
940    Ok(())
941  }
942
943  #[test]
944  fn full_paseto_builder_test() -> Result<()> {
945    //create a key
946
947    let key = PasetoSymmetricKey::<V2, Local>::from(Key::from(*b"wubbalubbadubdubwubbalubbadubdub"));
948    let footer = Footer::from("some footer");
949
950    //create a builder, add some claims and then build the token with the key
951    let token = PasetoBuilder::<V2, Local>::default()
952      .set_claim(AudienceClaim::from("customers"))
953      .set_claim(SubjectClaim::from("loyal subjects"))
954      .set_claim(IssuerClaim::from("me"))
955      .set_claim(TokenIdentifierClaim::from("me"))
956      .set_claim(IssuedAtClaim::try_from("2019-01-01T00:00:00+00:00")?)
957      .set_claim(NotBeforeClaim::try_from("2019-01-01T00:00:00+00:00")?)
958      .set_claim(ExpirationClaim::try_from("2019-01-01T00:00:00+00:00")?)
959      .set_claim(CustomClaim::try_from(("data", "this is a secret message"))?)
960      .set_claim(CustomClaim::try_from(("seats", 4))?)
961      .set_claim(CustomClaim::try_from(("pi to 6 digits", 3.141526))?)
962      .set_footer(footer)
963      .build(&key)?;
964
965    //now let's decrypt the token and verify the values
966    let json = GenericParser::<V2, Local>::default()
967      .check_claim(AudienceClaim::from("customers"))
968      .check_claim(SubjectClaim::from("loyal subjects"))
969      .check_claim(IssuerClaim::from("me"))
970      .check_claim(TokenIdentifierClaim::from("me"))
971      .check_claim(IssuedAtClaim::try_from("2019-01-01T00:00:00+00:00")?)
972      .check_claim(NotBeforeClaim::try_from("2019-01-01T00:00:00+00:00")?)
973      .check_claim(ExpirationClaim::try_from("2019-01-01T00:00:00+00:00")?)
974      .check_claim(CustomClaim::try_from(("data", "this is a secret message"))?)
975      .check_claim(CustomClaim::try_from(("seats", 4))?)
976      .check_claim(CustomClaim::try_from(("pi to 6 digits", 3.141526))?)
977      .set_footer(footer)
978      .parse(&token, &key)?;
979
980    // we can access all the values from the serde Value object returned by the parser
981    assert_eq!(json["aud"], "customers");
982    assert_eq!(json["jti"], "me");
983    assert_eq!(json["iss"], "me");
984    assert_eq!(json["data"], "this is a secret message");
985    assert_eq!(json["exp"], "2019-01-01T00:00:00+00:00");
986    assert_eq!(json["iat"], "2019-01-01T00:00:00+00:00");
987    assert_eq!(json["nbf"], "2019-01-01T00:00:00+00:00");
988    assert_eq!(json["sub"], "loyal subjects");
989    assert_eq!(json["pi to 6 digits"], 3.141526);
990    assert_eq!(json["seats"], 4);
991    Ok(())
992  }
993}