1use std::borrow::Cow;
2use std::collections::HashSet;
3use std::fmt;
4use std::marker::PhantomData;
5
6use serde::de::{self, Visitor};
7use serde::{Deserialize, Deserializer};
8
9use crate::algorithms::Algorithm;
10use crate::errors::{ErrorKind, Result, new_error};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct Validation {
32 pub required_spec_claims: HashSet<String>,
39 pub leeway: u64,
44 pub reject_tokens_expiring_in_less_than: u64,
51 pub validate_exp: bool,
57 pub validate_nbf: bool,
66 pub validate_aud: bool,
75 pub aud: Option<HashSet<String>>,
84 pub iss: Option<HashSet<String>>,
93 pub sub: Option<String>,
101 pub algorithms: Vec<Algorithm>,
106
107 pub(crate) validate_signature: bool,
109}
110
111impl Validation {
112 pub fn new(alg: Algorithm) -> Validation {
114 let mut required_claims = HashSet::with_capacity(1);
115 required_claims.insert("exp".to_owned());
116
117 Validation {
118 required_spec_claims: required_claims,
119 algorithms: vec![alg],
120 leeway: 60,
121 reject_tokens_expiring_in_less_than: 0,
122
123 validate_exp: true,
124 validate_nbf: false,
125 validate_aud: true,
126
127 iss: None,
128 sub: None,
129 aud: None,
130
131 validate_signature: true,
132 }
133 }
134
135 pub fn set_audience<T: ToString>(&mut self, items: &[T]) {
138 self.aud = Some(items.iter().map(|x| x.to_string()).collect())
139 }
140
141 pub fn set_issuer<T: ToString>(&mut self, items: &[T]) {
144 self.iss = Some(items.iter().map(|x| x.to_string()).collect())
145 }
146
147 pub fn set_required_spec_claims<T: ToString>(&mut self, items: &[T]) {
153 self.required_spec_claims = items.iter().map(|x| x.to_string()).collect();
154 }
155
156 #[deprecated(
160 since = "10.1.0",
161 note = "Use `jsonwebtoken::dangerous::insecure_decode` if you require this functionality."
162 )]
163 pub fn insecure_disable_signature_validation(&mut self) {
164 self.validate_signature = false;
165 }
166}
167
168impl Default for Validation {
169 fn default() -> Self {
170 Self::new(Algorithm::HS256)
171 }
172}
173
174#[cfg(not(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi")))))]
176#[must_use]
177pub fn get_current_timestamp() -> u64 {
178 let start = std::time::SystemTime::now();
179 start.duration_since(std::time::UNIX_EPOCH).expect("Time went backwards").as_secs()
180}
181
182#[cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))]
184#[must_use]
185pub fn get_current_timestamp() -> u64 {
186 js_sys::Date::new_0().get_time() as u64 / 1000
187}
188
189#[derive(Deserialize)]
190pub(crate) struct ClaimsForValidation<'a> {
191 #[serde(deserialize_with = "numeric_type", default)]
192 exp: TryParse<u64>,
193 #[serde(deserialize_with = "numeric_type", default)]
194 nbf: TryParse<u64>,
195 #[serde(borrow)]
196 sub: TryParse<Cow<'a, str>>,
197 #[serde(borrow)]
198 iss: TryParse<Issuer<'a>>,
199 #[serde(borrow)]
200 aud: TryParse<Audience<'a>>,
201}
202
203#[derive(Debug)]
204enum TryParse<T> {
205 Parsed(T),
206 FailedToParse,
207 NotPresent,
208}
209
210impl<'de, T: Deserialize<'de>> Deserialize<'de> for TryParse<T> {
211 fn deserialize<D: serde::Deserializer<'de>>(
212 deserializer: D,
213 ) -> std::result::Result<Self, D::Error> {
214 Ok(match Option::<T>::deserialize(deserializer) {
215 Ok(Some(value)) => TryParse::Parsed(value),
216 Ok(None) => TryParse::NotPresent,
217 Err(_) => TryParse::FailedToParse,
218 })
219 }
220}
221
222impl<T> Default for TryParse<T> {
223 fn default() -> Self {
224 Self::NotPresent
225 }
226}
227
228#[derive(Deserialize)]
229#[serde(untagged)]
230enum Audience<'a> {
231 Single(#[serde(borrow)] Cow<'a, str>),
232 Multiple(#[serde(borrow)] HashSet<BorrowedCowIfPossible<'a>>),
233}
234
235#[derive(Deserialize)]
236#[serde(untagged)]
237enum Issuer<'a> {
238 Single(#[serde(borrow)] Cow<'a, str>),
239 Multiple(#[serde(borrow)] HashSet<BorrowedCowIfPossible<'a>>),
240}
241
242#[derive(Deserialize, PartialEq, Eq, Hash)]
246struct BorrowedCowIfPossible<'a>(#[serde(borrow)] Cow<'a, str>);
247
248impl std::borrow::Borrow<str> for BorrowedCowIfPossible<'_> {
249 fn borrow(&self) -> &str {
250 &self.0
251 }
252}
253
254fn is_subset(reference: &HashSet<String>, given: &HashSet<BorrowedCowIfPossible<'_>>) -> bool {
255 if reference.len() < given.len() {
257 reference.iter().any(|a| given.contains(&**a))
258 } else {
259 given.iter().any(|a| reference.contains(&*a.0))
260 }
261}
262
263pub(crate) fn validate(claims: ClaimsForValidation, options: &Validation) -> Result<()> {
264 for required_claim in &options.required_spec_claims {
265 let present = match required_claim.as_str() {
266 "exp" => matches!(claims.exp, TryParse::Parsed(_)),
267 "sub" => matches!(claims.sub, TryParse::Parsed(_)),
268 "iss" => matches!(claims.iss, TryParse::Parsed(_)),
269 "aud" => matches!(claims.aud, TryParse::Parsed(_)),
270 "nbf" => matches!(claims.nbf, TryParse::Parsed(_)),
271 _ => continue,
272 };
273
274 if !present {
275 return Err(new_error(ErrorKind::MissingRequiredClaim(required_claim.clone())));
276 }
277 }
278
279 if options.validate_exp || options.validate_nbf {
280 let now = get_current_timestamp();
281
282 if matches!(claims.exp, TryParse::Parsed(exp) if exp < options.reject_tokens_expiring_in_less_than)
283 {
284 return Err(new_error(ErrorKind::InvalidToken));
285 }
286
287 if matches!(claims.exp, TryParse::Parsed(exp) if options.validate_exp
288 && exp - options.reject_tokens_expiring_in_less_than < now - options.leeway )
289 {
290 return Err(new_error(ErrorKind::ExpiredSignature));
291 }
292
293 if matches!(claims.nbf, TryParse::Parsed(nbf) if options.validate_nbf && nbf > now + options.leeway)
294 {
295 return Err(new_error(ErrorKind::ImmatureSignature));
296 }
297 }
298
299 if let (TryParse::Parsed(sub), Some(correct_sub)) = (claims.sub, options.sub.as_deref()) {
300 if sub != correct_sub {
301 return Err(new_error(ErrorKind::InvalidSubject));
302 }
303 }
304
305 match (claims.iss, options.iss.as_ref()) {
306 (TryParse::Parsed(Issuer::Single(iss)), Some(correct_iss)) => {
307 if !correct_iss.contains(&*iss) {
308 return Err(new_error(ErrorKind::InvalidIssuer));
309 }
310 }
311 (TryParse::Parsed(Issuer::Multiple(iss)), Some(correct_iss)) => {
312 if !is_subset(correct_iss, &iss) {
313 return Err(new_error(ErrorKind::InvalidIssuer));
314 }
315 }
316 _ => {}
317 }
318
319 if !options.validate_aud {
320 return Ok(());
321 }
322 match (claims.aud, options.aud.as_ref()) {
323 (TryParse::Parsed(Audience::Multiple(aud)), None) => {
329 if !aud.is_empty() {
330 return Err(new_error(ErrorKind::InvalidAudience));
331 }
332 }
333 (TryParse::Parsed(_), None) => {
334 return Err(new_error(ErrorKind::InvalidAudience));
335 }
336 (TryParse::Parsed(Audience::Single(aud)), Some(correct_aud)) => {
337 if !correct_aud.contains(&*aud) {
338 return Err(new_error(ErrorKind::InvalidAudience));
339 }
340 }
341 (TryParse::Parsed(Audience::Multiple(aud)), Some(correct_aud)) => {
342 if !is_subset(correct_aud, &aud) {
343 return Err(new_error(ErrorKind::InvalidAudience));
344 }
345 }
346 _ => {}
347 }
348
349 Ok(())
350}
351
352fn numeric_type<'de, D>(deserializer: D) -> std::result::Result<TryParse<u64>, D::Error>
353where
354 D: Deserializer<'de>,
355{
356 struct NumericType(PhantomData<fn() -> TryParse<u64>>);
357
358 impl Visitor<'_> for NumericType {
359 type Value = TryParse<u64>;
360
361 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
362 formatter.write_str("A NumericType that can be reasonably coerced into a u64")
363 }
364
365 fn visit_u64<E>(self, value: u64) -> std::result::Result<Self::Value, E>
366 where
367 E: de::Error,
368 {
369 Ok(TryParse::Parsed(value))
370 }
371
372 fn visit_f64<E>(self, value: f64) -> std::result::Result<Self::Value, E>
373 where
374 E: de::Error,
375 {
376 if value.is_finite() && value >= 0.0 && value < (u64::MAX as f64) {
377 Ok(TryParse::Parsed(value.round() as u64))
378 } else {
379 Err(serde::de::Error::custom("NumericType must be representable as a u64"))
380 }
381 }
382 }
383
384 match deserializer.deserialize_any(NumericType(PhantomData)) {
385 Ok(ok) => Ok(ok),
386 Err(_) => Ok(TryParse::FailedToParse),
387 }
388}
389
390#[cfg(test)]
391mod tests {
392 use std::collections::HashSet;
393
394 use serde_json::json;
395 use wasm_bindgen_test::wasm_bindgen_test;
396
397 use crate::Algorithm;
398 use crate::errors::ErrorKind;
399
400 use super::{ClaimsForValidation, Validation, get_current_timestamp, validate};
401
402 fn deserialize_claims(claims: &serde_json::Value) -> ClaimsForValidation<'_> {
403 serde::Deserialize::deserialize(claims).unwrap()
404 }
405
406 #[test]
407 #[wasm_bindgen_test]
408 fn exp_in_future_ok() {
409 let claims = json!({ "exp": get_current_timestamp() + 10000 });
410 let res = validate(deserialize_claims(&claims), &Validation::new(Algorithm::HS256));
411 assert!(res.is_ok());
412 }
413
414 #[test]
415 #[wasm_bindgen_test]
416 fn exp_in_future_but_in_rejection_period_fails() {
417 let claims = json!({ "exp": get_current_timestamp() + 500 });
418 let mut validation = Validation::new(Algorithm::HS256);
419 validation.leeway = 0;
420 validation.reject_tokens_expiring_in_less_than = 501;
421 let res = validate(deserialize_claims(&claims), &validation);
422 assert!(res.is_err());
423 }
424
425 #[test]
426 #[wasm_bindgen_test]
427 fn exp_float_in_future_ok() {
428 let claims = json!({ "exp": (get_current_timestamp() as f64) + 10000.123 });
429 let res = validate(deserialize_claims(&claims), &Validation::new(Algorithm::HS256));
430 assert!(res.is_ok());
431 }
432
433 #[test]
434 #[wasm_bindgen_test]
435 fn exp_float_in_future_but_in_rejection_period_fails() {
436 let claims = json!({ "exp": (get_current_timestamp() as f64) + 500.123 });
437 let mut validation = Validation::new(Algorithm::HS256);
438 validation.leeway = 0;
439 validation.reject_tokens_expiring_in_less_than = 501;
440 let res = validate(deserialize_claims(&claims), &validation);
441 assert!(res.is_err());
442 }
443
444 #[test]
445 #[wasm_bindgen_test]
446 fn exp_in_past_fails() {
447 let claims = json!({ "exp": get_current_timestamp() - 100000 });
448 let res = validate(deserialize_claims(&claims), &Validation::new(Algorithm::HS256));
449 assert!(res.is_err());
450
451 match res.unwrap_err().kind() {
452 ErrorKind::ExpiredSignature => (),
453 _ => unreachable!(),
454 };
455 }
456
457 #[test]
458 #[wasm_bindgen_test]
459 fn exp_float_in_past_fails() {
460 let claims = json!({ "exp": (get_current_timestamp() as f64) - 100000.1234 });
461 let res = validate(deserialize_claims(&claims), &Validation::new(Algorithm::HS256));
462 assert!(res.is_err());
463
464 match res.unwrap_err().kind() {
465 ErrorKind::ExpiredSignature => (),
466 _ => unreachable!(),
467 };
468 }
469
470 #[test]
471 #[wasm_bindgen_test]
472 fn exp_in_past_but_in_leeway_ok() {
473 let claims = json!({ "exp": get_current_timestamp() - 500 });
474 let mut validation = Validation::new(Algorithm::HS256);
475 validation.leeway = 1000 * 60;
476 let res = validate(deserialize_claims(&claims), &validation);
477 assert!(res.is_ok());
478 }
479
480 #[test]
482 #[wasm_bindgen_test]
483 fn validate_required_fields_are_present() {
484 for spec_claim in ["exp", "nbf", "aud", "iss", "sub"] {
485 let claims = json!({});
486 let mut validation = Validation::new(Algorithm::HS256);
487 validation.set_required_spec_claims(&[spec_claim]);
488 let res = validate(deserialize_claims(&claims), &validation).unwrap_err();
489 assert_eq!(res.kind(), &ErrorKind::MissingRequiredClaim(spec_claim.to_owned()));
490 }
491 }
492
493 #[test]
494 #[wasm_bindgen_test]
495 fn exp_validated_but_not_required_ok() {
496 let claims = json!({});
497 let mut validation = Validation::new(Algorithm::HS256);
498 validation.required_spec_claims = HashSet::new();
499 validation.validate_exp = true;
500 let res = validate(deserialize_claims(&claims), &validation);
501 assert!(res.is_ok());
502 }
503
504 #[test]
505 #[wasm_bindgen_test]
506 fn exp_validated_but_not_required_fails() {
507 let claims = json!({ "exp": (get_current_timestamp() as f64) - 100000.1234 });
508 let mut validation = Validation::new(Algorithm::HS256);
509 validation.required_spec_claims = HashSet::new();
510 validation.validate_exp = true;
511 let res = validate(deserialize_claims(&claims), &validation);
512 assert!(res.is_err());
513 }
514
515 #[test]
516 #[wasm_bindgen_test]
517 fn exp_required_but_not_validated_ok() {
518 let claims = json!({ "exp": (get_current_timestamp() as f64) - 100000.1234 });
519 let mut validation = Validation::new(Algorithm::HS256);
520 validation.set_required_spec_claims(&["exp"]);
521 validation.validate_exp = false;
522 let res = validate(deserialize_claims(&claims), &validation);
523 assert!(res.is_ok());
524 }
525
526 #[test]
527 #[wasm_bindgen_test]
528 fn exp_required_but_not_validated_fails() {
529 let claims = json!({});
530 let mut validation = Validation::new(Algorithm::HS256);
531 validation.set_required_spec_claims(&["exp"]);
532 validation.validate_exp = false;
533 let res = validate(deserialize_claims(&claims), &validation);
534 assert!(res.is_err());
535 }
536
537 #[test]
538 #[wasm_bindgen_test]
539 fn nbf_in_past_ok() {
540 let claims = json!({ "nbf": get_current_timestamp() - 10000 });
541 let mut validation = Validation::new(Algorithm::HS256);
542 validation.required_spec_claims = HashSet::new();
543 validation.validate_exp = false;
544 validation.validate_nbf = true;
545 let res = validate(deserialize_claims(&claims), &validation);
546 assert!(res.is_ok());
547 }
548
549 #[test]
550 #[wasm_bindgen_test]
551 fn nbf_float_in_past_ok() {
552 let claims = json!({ "nbf": (get_current_timestamp() as f64) - 10000.1234 });
553 let mut validation = Validation::new(Algorithm::HS256);
554 validation.required_spec_claims = HashSet::new();
555 validation.validate_exp = false;
556 validation.validate_nbf = true;
557 let res = validate(deserialize_claims(&claims), &validation);
558 assert!(res.is_ok());
559 }
560
561 #[test]
562 #[wasm_bindgen_test]
563 fn nbf_in_future_fails() {
564 let claims = json!({ "nbf": get_current_timestamp() + 100000 });
565 let mut validation = Validation::new(Algorithm::HS256);
566 validation.required_spec_claims = HashSet::new();
567 validation.validate_exp = false;
568 validation.validate_nbf = true;
569 let res = validate(deserialize_claims(&claims), &validation);
570 assert!(res.is_err());
571
572 match res.unwrap_err().kind() {
573 ErrorKind::ImmatureSignature => (),
574 _ => unreachable!(),
575 };
576 }
577
578 #[test]
579 #[wasm_bindgen_test]
580 fn nbf_in_future_but_in_leeway_ok() {
581 let claims = json!({ "nbf": get_current_timestamp() + 500 });
582 let mut validation = Validation::new(Algorithm::HS256);
583 validation.required_spec_claims = HashSet::new();
584 validation.validate_exp = false;
585 validation.validate_nbf = true;
586 validation.leeway = 1000 * 60;
587 let res = validate(deserialize_claims(&claims), &validation);
588 assert!(res.is_ok());
589 }
590
591 #[test]
592 #[wasm_bindgen_test]
593 fn iss_string_ok() {
594 let claims = json!({"iss": ["Keats"]});
595 let mut validation = Validation::new(Algorithm::HS256);
596 validation.required_spec_claims = HashSet::new();
597 validation.validate_exp = false;
598 validation.set_issuer(&["Keats"]);
599 let res = validate(deserialize_claims(&claims), &validation);
600 assert!(res.is_ok());
601 }
602
603 #[test]
604 #[wasm_bindgen_test]
605 fn iss_array_of_string_ok() {
606 let claims = json!({"iss": ["UserA", "UserB"]});
607 let mut validation = Validation::new(Algorithm::HS256);
608 validation.required_spec_claims = HashSet::new();
609 validation.validate_exp = false;
610 validation.set_issuer(&["UserA", "UserB"]);
611 let res = validate(deserialize_claims(&claims), &validation);
612 assert!(res.is_ok());
613 }
614
615 #[test]
616 #[wasm_bindgen_test]
617 fn iss_not_matching_fails() {
618 let claims = json!({"iss": "Hacked"});
619
620 let mut validation = Validation::new(Algorithm::HS256);
621 validation.required_spec_claims = HashSet::new();
622 validation.validate_exp = false;
623 validation.set_issuer(&["Keats"]);
624 let res = validate(deserialize_claims(&claims), &validation);
625 assert!(res.is_err());
626
627 match res.unwrap_err().kind() {
628 ErrorKind::InvalidIssuer => (),
629 _ => unreachable!(),
630 };
631 }
632
633 #[test]
634 #[wasm_bindgen_test]
635 fn iss_missing_fails() {
636 let claims = json!({});
637
638 let mut validation = Validation::new(Algorithm::HS256);
639 validation.set_required_spec_claims(&["iss"]);
640 validation.validate_exp = false;
641 validation.set_issuer(&["Keats"]);
642 let res = validate(deserialize_claims(&claims), &validation);
643
644 match res.unwrap_err().kind() {
645 ErrorKind::MissingRequiredClaim(claim) => assert_eq!(claim, "iss"),
646 _ => unreachable!(),
647 };
648 }
649
650 #[test]
651 #[wasm_bindgen_test]
652 fn sub_ok() {
653 let claims = json!({"sub": "Keats"});
654 let mut validation = Validation::new(Algorithm::HS256);
655 validation.required_spec_claims = HashSet::new();
656 validation.validate_exp = false;
657 validation.sub = Some("Keats".to_owned());
658 let res = validate(deserialize_claims(&claims), &validation);
659 assert!(res.is_ok());
660 }
661
662 #[test]
663 #[wasm_bindgen_test]
664 fn sub_not_matching_fails() {
665 let claims = json!({"sub": "Hacked"});
666 let mut validation = Validation::new(Algorithm::HS256);
667 validation.required_spec_claims = HashSet::new();
668 validation.validate_exp = false;
669 validation.sub = Some("Keats".to_owned());
670 let res = validate(deserialize_claims(&claims), &validation);
671 assert!(res.is_err());
672
673 match res.unwrap_err().kind() {
674 ErrorKind::InvalidSubject => (),
675 _ => unreachable!(),
676 };
677 }
678
679 #[test]
680 #[wasm_bindgen_test]
681 fn sub_missing_fails() {
682 let claims = json!({});
683 let mut validation = Validation::new(Algorithm::HS256);
684 validation.validate_exp = false;
685 validation.set_required_spec_claims(&["sub"]);
686 validation.sub = Some("Keats".to_owned());
687 let res = validate(deserialize_claims(&claims), &validation);
688 assert!(res.is_err());
689
690 match res.unwrap_err().kind() {
691 ErrorKind::MissingRequiredClaim(claim) => assert_eq!(claim, "sub"),
692 _ => unreachable!(),
693 };
694 }
695
696 #[test]
697 #[wasm_bindgen_test]
698 fn aud_string_ok() {
699 let claims = json!({"aud": "Everyone"});
700 let mut validation = Validation::new(Algorithm::HS256);
701 validation.validate_exp = false;
702 validation.required_spec_claims = HashSet::new();
703 validation.set_audience(&["Everyone"]);
704 let res = validate(deserialize_claims(&claims), &validation);
705 assert!(res.is_ok());
706 }
707
708 #[test]
709 #[wasm_bindgen_test]
710 fn aud_array_of_string_ok() {
711 let claims = json!({"aud": ["UserA", "UserB"]});
712 let mut validation = Validation::new(Algorithm::HS256);
713 validation.validate_exp = false;
714 validation.required_spec_claims = HashSet::new();
715 validation.set_audience(&["UserA", "UserB"]);
716 let res = validate(deserialize_claims(&claims), &validation);
717 assert!(res.is_ok());
718 }
719
720 #[test]
721 #[wasm_bindgen_test]
722 fn aud_type_mismatch_fails() {
723 let claims = json!({"aud": ["Everyone"]});
724 let mut validation = Validation::new(Algorithm::HS256);
725 validation.validate_exp = false;
726 validation.required_spec_claims = HashSet::new();
727 validation.set_audience(&["UserA", "UserB"]);
728 let res = validate(deserialize_claims(&claims), &validation);
729 assert!(res.is_err());
730
731 match res.unwrap_err().kind() {
732 ErrorKind::InvalidAudience => (),
733 _ => unreachable!(),
734 };
735 }
736
737 #[test]
738 #[wasm_bindgen_test]
739 fn aud_correct_type_not_matching_fails() {
740 let claims = json!({"aud": ["Everyone"]});
741 let mut validation = Validation::new(Algorithm::HS256);
742 validation.validate_exp = false;
743 validation.required_spec_claims = HashSet::new();
744 validation.set_audience(&["None"]);
745 let res = validate(deserialize_claims(&claims), &validation);
746 assert!(res.is_err());
747
748 match res.unwrap_err().kind() {
749 ErrorKind::InvalidAudience => (),
750 _ => unreachable!(),
751 };
752 }
753
754 #[test]
755 #[wasm_bindgen_test]
756 fn aud_none_fails() {
757 let claims = json!({"aud": ["Everyone"]});
758 let mut validation = Validation::new(Algorithm::HS256);
759 validation.validate_exp = false;
760 validation.required_spec_claims = HashSet::new();
761 validation.aud = None;
762 let res = validate(deserialize_claims(&claims), &validation);
763 assert!(res.is_err());
764
765 match res.unwrap_err().kind() {
766 ErrorKind::InvalidAudience => (),
767 _ => unreachable!(),
768 };
769 }
770
771 #[test]
772 #[wasm_bindgen_test]
773 fn aud_validation_skipped() {
774 let claims = json!({"aud": ["Everyone"]});
775 let mut validation = Validation::new(Algorithm::HS256);
776 validation.validate_exp = false;
777 validation.validate_aud = false;
778 validation.required_spec_claims = HashSet::new();
779 validation.aud = None;
780 let res = validate(deserialize_claims(&claims), &validation);
781 assert!(res.is_ok());
782 }
783
784 #[test]
785 #[wasm_bindgen_test]
786 fn aud_missing_fails() {
787 let claims = json!({});
788 let mut validation = Validation::new(Algorithm::HS256);
789 validation.validate_exp = false;
790 validation.set_required_spec_claims(&["aud"]);
791 validation.set_audience(&["None"]);
792 let res = validate(deserialize_claims(&claims), &validation);
793 assert!(res.is_err());
794
795 match res.unwrap_err().kind() {
796 ErrorKind::MissingRequiredClaim(claim) => assert_eq!(claim, "aud"),
797 _ => unreachable!(),
798 };
799 }
800
801 #[test]
803 #[wasm_bindgen_test]
804 fn does_validation_in_right_order() {
805 let claims = json!({ "exp": get_current_timestamp() + 10000 });
806
807 let mut validation = Validation::new(Algorithm::HS256);
808 validation.set_required_spec_claims(&["exp", "iss"]);
809 validation.leeway = 5;
810 validation.set_issuer(&["iss no check"]);
811 validation.set_audience(&["iss no check"]);
812
813 let res = validate(deserialize_claims(&claims), &validation);
814 assert!(res.is_err());
816 match res.unwrap_err().kind() {
817 ErrorKind::MissingRequiredClaim(claim) => assert_eq!(claim, "iss"),
818 t => panic!("{:?}", t),
819 };
820 }
821
822 #[test]
824 #[wasm_bindgen_test]
825 fn aud_use_validation_struct() {
826 let claims = json!({"aud": "my-googleclientid1234.apps.googleusercontent.com"});
827
828 let aud = "my-googleclientid1234.apps.googleusercontent.com".to_string();
829 let mut aud_hashset = std::collections::HashSet::new();
830 aud_hashset.insert(aud);
831 let mut validation = Validation::new(Algorithm::HS256);
832 validation.validate_exp = false;
833 validation.required_spec_claims = HashSet::new();
834 validation.set_audience(&["my-googleclientid1234.apps.googleusercontent.com"]);
835
836 let res = validate(deserialize_claims(&claims), &validation);
837 assert!(res.is_ok());
838 }
839
840 #[test]
842 #[wasm_bindgen_test]
843 fn doesnt_panic_with_leeway_overflow() {
844 let claims = json!({ "exp": 1 });
845
846 let mut validation = Validation::new(Algorithm::HS256);
847 validation.reject_tokens_expiring_in_less_than = 100;
848
849 let res = validate(deserialize_claims(&claims), &validation);
850 assert!(res.is_err());
851 }
852}