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}