1#![warn(clippy::pedantic)]
118#![allow(
119 clippy::module_name_repetitions,
120 clippy::missing_errors_doc,
121 clippy::missing_panics_doc,
122 clippy::must_use_candidate,
123 clippy::default_trait_access,
124 clippy::similar_names,
125 clippy::enum_glob_use
126)]
127
128use std::borrow::Cow;
129use std::fmt::Debug;
130use std::ops::Deref;
131
132use serde::de::DeserializeOwned;
133use serde::{Deserialize, Deserializer, Serialize, Serializer};
134use time::{Duration, OffsetDateTime};
135
136mod helpers;
137pub use crate::helpers::*;
138
139#[cfg(test)]
140#[macro_use]
141mod test;
142
143#[macro_use]
144mod serde_custom;
145
146#[macro_use]
147mod macros;
148
149pub mod errors;
150pub mod jwa;
151pub mod jwe;
152pub mod jwk;
153pub mod jws;
154
155use crate::errors::{Error, ValidationError};
156
157pub trait FromCompactPart: Sized {
159 fn from_bytes(b: &[u8]) -> Result<Self, Error>;
161}
162
163pub trait ToCompactPart: Sized {
165 fn to_bytes(&self) -> Result<Cow<'_, [u8]>, Error>;
167}
168
169pub struct Json<T>(pub T);
171
172impl<T: DeserializeOwned> FromCompactPart for Json<T> {
173 fn from_bytes(b: &[u8]) -> Result<Self, Error> {
174 Ok(Json(serde_json::from_slice(b)?))
175 }
176}
177impl<T: Serialize> ToCompactPart for Json<T> {
178 fn to_bytes(&self) -> Result<Cow<'_, [u8]>, Error> {
179 Ok(serde_json::to_vec(&self.0)?.into())
180 }
181}
182
183impl FromCompactPart for Vec<u8> {
184 fn from_bytes(b: &[u8]) -> Result<Self, Error> {
185 Ok(b.to_vec())
186 }
187}
188impl ToCompactPart for Vec<u8> {
189 fn to_bytes(&self) -> Result<Cow<'_, [u8]>, Error> {
190 Ok(self.as_slice().into())
191 }
192}
193
194impl FromCompactPart for () {
195 fn from_bytes(b: &[u8]) -> Result<Self, Error> {
196 if b.is_empty() {
197 Ok(())
198 } else {
199 Err(Error::DecodeError(errors::DecodeError::PartsLengthError {
200 expected: 0,
201 actual: b.len(),
202 }))
203 }
204 }
205}
206impl ToCompactPart for () {
207 fn to_bytes(&self) -> Result<Cow<'_, [u8]>, Error> {
208 Ok(Cow::Borrowed(&[]))
209 }
210}
211
212pub type JWT<T> = jws::Verified<ClaimsSet<T>, ()>;
265
266pub type JWE<T> = jwe::Decrypted<jws::Unverified<ClaimsSet<T>>, ()>;
358
359#[derive(Clone, Debug, Eq, PartialEq)]
383pub enum SingleOrMultiple {
384 Single([String; 1]),
386 Multiple(Vec<String>),
388}
389
390mod serde_impls {
391 use super::SingleOrMultiple;
392
393 impl serde::Serialize for SingleOrMultiple {
394 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
395 where
396 S: serde::Serializer,
397 {
398 match self {
399 SingleOrMultiple::Single([field]) => field.serialize(serializer),
400 SingleOrMultiple::Multiple(ref field) => field.serialize(serializer),
401 }
402 }
403 }
404
405 struct Visitor;
406 impl<'de> serde::de::Visitor<'de> for Visitor {
407 type Value = SingleOrMultiple;
408 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
409 formatter.write_str("single or multiple strings")
410 }
411
412 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
413 where
414 E: serde::de::Error,
415 {
416 Ok(v.into())
417 }
418
419 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
420 where
421 E: serde::de::Error,
422 {
423 Ok(v.into())
424 }
425
426 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
427 where
428 A: serde::de::SeqAccess<'de>,
429 {
430 let mut values = Vec::new();
431 while let Some(value) = seq.next_element()? {
432 values.push(value);
433 }
434 Ok(SingleOrMultiple::Multiple(values))
435 }
436 }
437 impl<'de> serde::Deserialize<'de> for SingleOrMultiple {
438 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
439 where
440 D: serde::Deserializer<'de>,
441 {
442 deserializer.deserialize_any(Visitor)
443 }
444 }
445}
446
447impl From<&str> for SingleOrMultiple {
448 fn from(t: &str) -> Self {
449 Self::Single([t.to_owned()])
450 }
451}
452impl From<&[&str]> for SingleOrMultiple {
453 fn from(t: &[&str]) -> Self {
454 Self::Multiple(t.iter().map(|&s| s.to_owned()).collect())
455 }
456}
457impl From<String> for SingleOrMultiple {
458 fn from(t: String) -> Self {
459 Self::Single([t])
460 }
461}
462impl From<Vec<String>> for SingleOrMultiple {
463 fn from(t: Vec<String>) -> Self {
464 Self::Multiple(t)
465 }
466}
467
468impl SingleOrMultiple {
469 pub fn contains(&self, value: &str) -> bool {
471 match self {
472 Self::Single([single]) => single == value,
473 Self::Multiple(vector) => vector.iter().any(|v| v == value),
474 }
475 }
476
477 pub fn iter(&self) -> std::slice::Iter<String> {
479 match self {
480 Self::Single(single) => single.iter(),
481 Self::Multiple(vector) => vector.iter(),
482 }
483 }
484}
485
486#[derive(Clone, Copy, Debug, Eq, PartialEq)]
488pub struct Timestamp(OffsetDateTime);
489
490impl Deref for Timestamp {
491 type Target = OffsetDateTime;
492 fn deref(&self) -> &Self::Target {
493 &self.0
494 }
495}
496
497impl From<OffsetDateTime> for Timestamp {
498 fn from(datetime: OffsetDateTime) -> Self {
499 Timestamp(datetime)
500 }
501}
502
503impl From<Timestamp> for OffsetDateTime {
504 fn from(ts: Timestamp) -> Self {
505 ts.0
506 }
507}
508
509impl TryFrom<i64> for Timestamp {
510 type Error = time::error::ComponentRange;
511
512 fn try_from(value: i64) -> Result<Self, Self::Error> {
513 OffsetDateTime::from_unix_timestamp(value).map(Self)
514 }
515}
516
517impl Serialize for Timestamp {
518 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
519 where
520 S: Serializer,
521 {
522 time::serde::timestamp::serialize(&self.0, serializer)
523 }
524}
525
526impl<'de> Deserialize<'de> for Timestamp {
527 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
528 where
529 D: Deserializer<'de>,
530 {
531 time::serde::timestamp::deserialize(deserializer).map(Self)
532 }
533}
534
535#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default)]
537pub struct RegisteredClaims {
538 #[serde(rename = "iss", skip_serializing_if = "Option::is_none")]
540 pub issuer: Option<String>,
541
542 #[serde(rename = "sub", skip_serializing_if = "Option::is_none")]
544 pub subject: Option<String>,
545
546 #[serde(rename = "aud", skip_serializing_if = "Option::is_none")]
548 pub audience: Option<SingleOrMultiple>,
549
550 #[serde(rename = "exp", skip_serializing_if = "Option::is_none")]
552 pub expiry: Option<Timestamp>,
553
554 #[serde(rename = "nbf", skip_serializing_if = "Option::is_none")]
556 pub not_before: Option<Timestamp>,
557
558 #[serde(rename = "iat", skip_serializing_if = "Option::is_none")]
560 pub issued_at: Option<Timestamp>,
561
562 #[serde(rename = "jti", skip_serializing_if = "Option::is_none")]
564 pub id: Option<String>,
565}
566
567#[derive(Debug, Eq, PartialEq, Clone, Copy, Default)]
568pub struct ClaimPresenceOptions {
573 pub issued_at: Presence,
575 pub not_before: Presence,
577 pub expiry: Presence,
579 pub issuer: Presence,
581 pub audience: Presence,
583 pub subject: Presence,
585 pub id: Presence,
587}
588
589impl ClaimPresenceOptions {
590 pub fn strict() -> Self {
592 use Presence::Required;
593 ClaimPresenceOptions {
594 issued_at: Required,
595 not_before: Required,
596 expiry: Required,
597 issuer: Required,
598 audience: Required,
599 subject: Required,
600 id: Required,
601 }
602 }
603}
604
605#[derive(Eq, PartialEq, Clone)]
606pub struct ValidationOptions {
619 pub claim_presence_options: ClaimPresenceOptions,
621
622 pub temporal_options: TemporalOptions,
624
625 pub issued_at: Validation<Duration>,
630 pub not_before: Validation<()>,
632 pub expiry: Validation<()>,
634
635 pub issuer: Validation<String>,
638
639 pub audience: Validation<String>,
642}
643
644impl Default for ValidationOptions {
645 fn default() -> Self {
646 ValidationOptions {
647 expiry: Validation::Validate(()),
648 not_before: Validation::Validate(()),
649 issued_at: Validation::Validate(Duration::MAX),
650
651 claim_presence_options: Default::default(),
652 temporal_options: Default::default(),
653 audience: Default::default(),
654 issuer: Default::default(),
655 }
656 }
657}
658
659impl RegisteredClaims {
660 pub fn validate_claim_presence(
662 &self,
663 options: ClaimPresenceOptions,
664 ) -> Result<(), ValidationError> {
665 use crate::Presence::Required;
666
667 let mut missing_claims: Vec<&str> = vec![];
668
669 if options.expiry == Required && self.expiry.is_none() {
670 missing_claims.push("exp");
671 }
672
673 if options.not_before == Required && self.not_before.is_none() {
674 missing_claims.push("nbf");
675 }
676
677 if options.issued_at == Required && self.issued_at.is_none() {
678 missing_claims.push("iat");
679 }
680
681 if options.audience == Required && self.audience.is_none() {
682 missing_claims.push("aud");
683 }
684
685 if options.issuer == Required && self.issuer.is_none() {
686 missing_claims.push("iss");
687 }
688
689 if options.subject == Required && self.subject.is_none() {
690 missing_claims.push("sub");
691 }
692
693 if options.id == Required && self.id.is_none() {
694 missing_claims.push("jti");
695 }
696
697 if missing_claims.is_empty() {
698 Ok(())
699 } else {
700 Err(ValidationError::MissingRequiredClaims(
701 missing_claims.into_iter().map(Into::into).collect(),
702 ))
703 }
704 }
705
706 pub fn validate_exp(
708 &self,
709 validation: Validation<TemporalOptions>,
710 ) -> Result<(), ValidationError> {
711 match validation {
712 Validation::Ignored => Ok(()),
713 Validation::Validate(temporal_options) => {
714 let now = temporal_options.now.unwrap_or_else(OffsetDateTime::now_utc);
715
716 match self.expiry {
717 Some(Timestamp(expiry)) if now - expiry > temporal_options.epsilon => {
718 Err(ValidationError::Expired(now - expiry))
719 }
720 _ => Ok(()),
721 }
722 }
723 }
724 }
725
726 pub fn validate_nbf(
728 &self,
729 validation: Validation<TemporalOptions>,
730 ) -> Result<(), ValidationError> {
731 match validation {
732 Validation::Ignored => Ok(()),
733 Validation::Validate(temporal_options) => {
734 let now = temporal_options.now.unwrap_or_else(OffsetDateTime::now_utc);
735
736 match self.not_before {
737 Some(Timestamp(nbf)) if nbf - now > temporal_options.epsilon => {
738 Err(ValidationError::NotYetValid(nbf - now))
739 }
740 _ => Ok(()),
741 }
742 }
743 }
744 }
745
746 pub fn validate_iat(
748 &self,
749 validation: Validation<(Duration, TemporalOptions)>,
750 ) -> Result<(), ValidationError> {
751 match validation {
752 Validation::Ignored => Ok(()),
753 Validation::Validate((max_age, temporal_options)) => {
754 let now = temporal_options.now.unwrap_or_else(OffsetDateTime::now_utc);
755
756 match self.issued_at {
757 Some(Timestamp(iat)) if iat - now > temporal_options.epsilon => {
758 Err(ValidationError::NotYetValid(iat - now))
759 }
760 Some(Timestamp(iat)) if now - iat > max_age - temporal_options.epsilon => {
761 Err(ValidationError::TooOld(now - iat - max_age))
762 }
763 _ => Ok(()),
764 }
765 }
766 }
767 }
768
769 pub fn validate_aud(&self, validation: Validation<String>) -> Result<(), ValidationError> {
771 match validation {
772 Validation::Ignored => Ok(()),
773 Validation::Validate(expected_aud) => match &self.audience {
774 Some(SingleOrMultiple::Single([audience])) if audience != &expected_aud => Err(
775 ValidationError::InvalidAudience(SingleOrMultiple::Single([audience.clone()])),
776 ),
777 Some(SingleOrMultiple::Multiple(audiences))
778 if !audiences.contains(&expected_aud) =>
779 {
780 Err(ValidationError::InvalidAudience(
781 SingleOrMultiple::Multiple(audiences.clone()),
782 ))
783 }
784 _ => Ok(()),
785 },
786 }
787 }
788
789 pub fn validate_iss(&self, validation: Validation<String>) -> Result<(), ValidationError> {
791 match validation {
792 Validation::Ignored => Ok(()),
793 Validation::Validate(expected_issuer) => match self.issuer {
794 Some(ref iss) if iss != &expected_issuer => {
795 Err(ValidationError::InvalidIssuer(iss.clone()))
796 }
797 _ => Ok(()),
798 },
799 }
800 }
801
802 pub fn validate(&self, options: ValidationOptions) -> Result<(), ValidationError> {
808 self.validate_claim_presence(options.claim_presence_options)?;
809 self.validate_exp(options.expiry.map(|_| options.temporal_options))?;
810 self.validate_nbf(options.not_before.map(|_| options.temporal_options))?;
811 self.validate_iat(options.issued_at.map(|dur| (dur, options.temporal_options)))?;
812
813 self.validate_iss(options.issuer)?;
814 self.validate_aud(options.audience)?;
815
816 Ok(())
820 }
821}
822
823#[derive(Debug, Eq, PartialEq, Clone, Default, Serialize, Deserialize)]
826pub struct ClaimsSet<T> {
827 #[serde(flatten)]
829 pub registered: RegisteredClaims,
830 #[serde(flatten)]
832 pub private: T,
833}
834
835impl<T: DeserializeOwned> FromCompactPart for ClaimsSet<T> {
836 fn from_bytes(b: &[u8]) -> Result<Self, Error> {
837 Ok(serde_json::from_slice(b)?)
838 }
839}
840impl<T: Serialize> ToCompactPart for ClaimsSet<T> {
841 fn to_bytes(&self) -> Result<Cow<'_, [u8]>, Error> {
842 Ok(serde_json::to_vec(&self)?.into())
843 }
844}
845
846type B64 = base64ct::Base64UrlUnpadded;
847
848#[cfg(test)]
849mod tests {
850 use super::*;
851 use time::Duration;
852
853 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
854 struct PrivateClaims {
855 company: String,
856 department: String,
857 }
858
859 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
862 struct InvalidPrivateClaim {
863 sub: String,
864 company: String,
865 }
866
867 #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
868 struct SingleOrMultipleStrings {
869 values: SingleOrMultiple,
870 }
871
872 #[test]
873 fn single_string_serialization_round_trip() {
874 let test = SingleOrMultipleStrings {
875 values: "foobar".into(),
876 };
877 let expected_json = r#"{"values":"foobar"}"#;
878
879 let serialized = serde_json::to_string(&test).unwrap();
880 assert_eq!(expected_json, serialized);
881
882 let deserialized: SingleOrMultipleStrings = serde_json::from_str(&serialized).unwrap();
883 assert_eq!(deserialized, test);
884 assert!(deserialized.values.contains("foobar"));
885 assert!(!deserialized.values.contains("does not exist"));
886 }
887
888 #[test]
889 fn multiple_strings_serialization_round_trip() {
890 let test = SingleOrMultipleStrings {
891 values: ["foo", "bar", "baz"].as_slice().into(),
892 };
893 let expected_json = r#"{"values":["foo","bar","baz"]}"#;
894
895 let serialized = serde_json::to_string(&test).unwrap();
896 assert_eq!(expected_json, serialized);
897
898 let deserialized: SingleOrMultipleStrings = serde_json::from_str(&serialized).unwrap();
899 assert_eq!(deserialized, test);
900 assert!(deserialized.values.contains("foo"));
901 assert!(deserialized.values.contains("bar"));
902 assert!(deserialized.values.contains("baz"));
903 assert!(!deserialized.values.contains("does not exist"));
904 }
905
906 #[test]
907 fn single_string_or_uri_string_serialization_round_trip() {
908 let test = SingleOrMultipleStrings {
909 values: "foobar".into(),
910 };
911 let expected_json = r#"{"values":"foobar"}"#;
912
913 let serialized = serde_json::to_string(&test).unwrap();
914 assert_eq!(expected_json, serialized);
915
916 let deserialized: SingleOrMultipleStrings = serde_json::from_str(&serialized).unwrap();
917 assert_eq!(deserialized, test);
918 assert!(deserialized.values.contains("foobar"));
919 assert!(!deserialized.values.contains("does not exist"));
920 }
921
922 #[test]
923 fn single_string_or_uri_uri_serialization_round_trip() {
924 let test = SingleOrMultipleStrings {
925 values: "https://www.examples.com/".into(),
926 };
927 let expected_json = r#"{"values":"https://www.examples.com/"}"#;
928
929 let serialized = serde_json::to_string(&test).unwrap();
930 assert_eq!(expected_json, serialized);
931
932 let deserialized: SingleOrMultipleStrings = serde_json::from_str(&serialized).unwrap();
933 assert_eq!(deserialized, test);
934 assert!(deserialized.values.contains("https://www.examples.com/"));
935 assert!(!deserialized.values.contains("https://ecorp.com"));
936 }
937
938 #[test]
939 fn multiple_string_or_uri_serialization_round_trip() {
940 let test = SingleOrMultipleStrings {
941 values: [
942 "foo",
943 "https://www.example.com/",
944 "data:text/plain,Hello?World#",
945 "http://[::1]/",
946 "baz",
947 ]
948 .as_slice()
949 .into(),
950 };
951 let expected_json = r#"{"values":["foo","https://www.example.com/","data:text/plain,Hello?World#","http://[::1]/","baz"]}"#;
952
953 let serialized = serde_json::to_string(&test).unwrap();
954 assert_eq!(expected_json, serialized);
955
956 let deserialized: SingleOrMultipleStrings = serde_json::from_str(&serialized).unwrap();
957 assert_eq!(deserialized, test);
958
959 assert!(deserialized.values.contains("foo"));
960 assert!(deserialized.values.contains("https://www.example.com/"));
961 assert!(deserialized.values.contains("data:text/plain,Hello?World#"));
962 assert!(deserialized.values.contains("http://[::1]/"));
963 assert!(deserialized.values.contains("baz"));
964 assert!(!deserialized.values.contains("https://ecorp.com"));
965 }
966
967 #[test]
968 fn timestamp_serialization_roundtrip() {
969 let now: Timestamp = OffsetDateTime::now_utc()
970 .replace_nanosecond(0)
971 .unwrap()
972 .into();
973 let serialized = serde_json::to_string(&now).unwrap();
974 let deserialized = serde_json::from_str(&serialized).unwrap();
975 assert_eq!(now, deserialized);
976
977 let fixed_time: Timestamp = 1000.try_into().unwrap();
978 let serialized = serde_json::to_string(&fixed_time).unwrap();
979 assert_eq!(serialized, "1000");
980 let deserialized = serde_json::from_str(&serialized).unwrap();
981 assert_eq!(fixed_time, deserialized);
982 }
983
984 #[test]
985 fn empty_registered_claims_serialization_round_trip() {
986 let claim = RegisteredClaims::default();
987 let expected_json = "{}";
988
989 let serialized = serde_json::to_string(&claim).unwrap();
990 assert_eq!(expected_json, serialized);
991
992 let deserialized: RegisteredClaims = serde_json::from_str(&serialized).unwrap();
993 assert_eq!(deserialized, claim);
994 }
995
996 #[test]
997 fn registered_claims_serialization_round_trip() {
998 let claim = RegisteredClaims {
999 issuer: Some("https://www.acme.com/".into()),
1000 audience: Some("htts://acme-customer.com/".into()),
1001 not_before: Some(1234.try_into().unwrap()),
1002 ..Default::default()
1003 };
1004 let expected_json =
1005 r#"{"iss":"https://www.acme.com/","aud":"htts://acme-customer.com/","nbf":1234}"#;
1006
1007 let serialized = serde_json::to_string(&claim).unwrap();
1008 assert_eq!(expected_json, serialized);
1009
1010 let deserialized: RegisteredClaims = serde_json::from_str(&serialized).unwrap();
1011 assert_eq!(deserialized, claim);
1012 }
1013
1014 #[test]
1015 fn claims_set_serialization_round_trip() {
1016 let claim = ClaimsSet::<PrivateClaims> {
1017 registered: RegisteredClaims {
1018 issuer: Some("https://www.acme.com/".into()),
1019 subject: Some("John Doe".into()),
1020 audience: Some("htts://acme-customer.com/".into()),
1021 not_before: Some(1234.try_into().unwrap()),
1022 ..Default::default()
1023 },
1024 private: PrivateClaims {
1025 department: "Toilet Cleaning".to_string(),
1026 company: "ACME".to_string(),
1027 },
1028 };
1029
1030 let expected_json = "{\"iss\":\"https://www.acme.com/\",\"sub\":\"John Doe\",\
1031 \"aud\":\"htts://acme-customer.com/\",\
1032 \"nbf\":1234,\"company\":\"ACME\",\"department\":\"Toilet Cleaning\"}";
1033
1034 let serialized = serde_json::to_string(&claim).unwrap();
1035 assert_eq!(expected_json, serialized);
1036
1037 let deserialized: ClaimsSet<PrivateClaims> = serde_json::from_str(&serialized).unwrap();
1038 assert_eq!(deserialized, claim);
1039 }
1040
1041 #[test]
1042 fn duplicate_claims_round_trip() {
1044 let claim = ClaimsSet::<InvalidPrivateClaim> {
1045 registered: RegisteredClaims {
1046 issuer: Some("https://www.acme.com".into()),
1047 subject: Some("John Doe".into()),
1048 audience: Some("htts://acme-customer.com".into()),
1049 not_before: Some(1234.try_into().unwrap()),
1050 ..Default::default()
1051 },
1052 private: InvalidPrivateClaim {
1053 sub: "John Doe".to_string(),
1054 company: "ACME".to_string(),
1055 },
1056 };
1057
1058 let json = serde_json::to_string(&claim).unwrap();
1059 assert_eq!(2, json.matches("\"sub\"").count());
1060
1061 let duplicate: Result<ClaimsSet<InvalidPrivateClaim>, _> = serde_json::from_str(&json);
1062 assert!(duplicate.is_err());
1063 let error = duplicate.unwrap_err().to_string();
1064 assert!(error.contains("duplicate field `sub`"));
1065 }
1066
1067 #[test]
1068 #[should_panic(expected = "MissingRequiredClaims([\"iat\"])")]
1069 fn validate_times_missing_iat() {
1070 let registered_claims = RegisteredClaims::default();
1071 let options = ClaimPresenceOptions {
1072 issued_at: Presence::Required,
1073 ..Default::default()
1074 };
1075 registered_claims.validate_claim_presence(options).unwrap();
1076 }
1077
1078 #[test]
1079 #[should_panic(expected = "MissingRequiredClaims([\"exp\"])")]
1080 fn validate_times_missing_exp() {
1081 let registered_claims = RegisteredClaims::default();
1082 let options = ClaimPresenceOptions {
1083 expiry: Presence::Required,
1084 ..Default::default()
1085 };
1086 registered_claims.validate_claim_presence(options).unwrap();
1087 }
1088
1089 #[test]
1090 #[should_panic(expected = "MissingRequiredClaims([\"nbf\"])")]
1091 fn validate_times_missing_nbf() {
1092 let registered_claims = RegisteredClaims::default();
1093 let options = ClaimPresenceOptions {
1094 not_before: Presence::Required,
1095 ..Default::default()
1096 };
1097 registered_claims.validate_claim_presence(options).unwrap();
1098 }
1099
1100 #[test]
1101 #[should_panic(expected = "MissingRequiredClaims([\"aud\"])")]
1102 fn validate_times_missing_aud() {
1103 let registered_claims = RegisteredClaims::default();
1104 let options = ClaimPresenceOptions {
1105 audience: Presence::Required,
1106 ..Default::default()
1107 };
1108 registered_claims.validate_claim_presence(options).unwrap();
1109 }
1110
1111 #[test]
1112 #[should_panic(expected = "MissingRequiredClaims([\"iss\"])")]
1113 fn validate_times_missing_iss() {
1114 let registered_claims = RegisteredClaims::default();
1115 let options = ClaimPresenceOptions {
1116 issuer: Presence::Required,
1117 ..Default::default()
1118 };
1119 registered_claims.validate_claim_presence(options).unwrap();
1120 }
1121
1122 #[test]
1123 #[should_panic(expected = "MissingRequiredClaims([\"sub\"])")]
1124 fn validate_times_missing_sub() {
1125 let registered_claims = RegisteredClaims::default();
1126 let options = ClaimPresenceOptions {
1127 subject: Presence::Required,
1128 ..Default::default()
1129 };
1130 registered_claims.validate_claim_presence(options).unwrap();
1131 }
1132
1133 #[test]
1134 #[should_panic(
1135 expected = "MissingRequiredClaims([\"exp\", \"nbf\", \"iat\", \"aud\", \"iss\", \"sub\", \"jti\"])"
1136 )]
1137 fn validate_times_missing_all() {
1138 let registered_claims = RegisteredClaims::default();
1139 let options = ClaimPresenceOptions::strict();
1140 registered_claims.validate_claim_presence(options).unwrap();
1141 }
1142
1143 #[test]
1144 fn validate_times_catch_future_token() {
1145 let temporal_options = TemporalOptions {
1146 now: Some(OffsetDateTime::from_unix_timestamp(0).unwrap()),
1147 ..Default::default()
1148 };
1149
1150 let registered_claims = RegisteredClaims {
1151 issued_at: Some(10.try_into().unwrap()),
1152 ..Default::default()
1153 };
1154
1155 assert_eq!(
1156 Err(ValidationError::NotYetValid(Duration::seconds(10))),
1157 registered_claims.validate_iat(Validation::Validate((
1158 Duration::seconds(0),
1159 temporal_options
1160 )))
1161 );
1162 }
1163
1164 #[test]
1165 fn validate_times_catch_too_old_token() {
1166 let temporal_options = TemporalOptions {
1167 now: Some(OffsetDateTime::from_unix_timestamp(40).unwrap()),
1168 ..Default::default()
1169 };
1170
1171 let registered_claims = RegisteredClaims {
1172 issued_at: Some(10.try_into().unwrap()),
1173 ..Default::default()
1174 };
1175
1176 assert_eq!(
1177 Err(ValidationError::TooOld(Duration::seconds(5))),
1178 registered_claims.validate_iat(Validation::Validate((
1179 Duration::seconds(25),
1180 temporal_options
1181 )))
1182 );
1183 }
1184
1185 #[test]
1186 fn validate_times_catch_expired_token() {
1187 let temporal_options = TemporalOptions {
1188 now: Some(OffsetDateTime::from_unix_timestamp(2).unwrap()),
1189 ..Default::default()
1190 };
1191
1192 let registered_claims = RegisteredClaims {
1193 expiry: Some(1.try_into().unwrap()),
1194 ..Default::default()
1195 };
1196
1197 assert_eq!(
1198 Err(ValidationError::Expired(Duration::seconds(1))),
1199 registered_claims.validate_exp(Validation::Validate(temporal_options))
1200 );
1201 }
1202
1203 #[test]
1204 fn validate_times_catch_early_token() {
1205 let temporal_options = TemporalOptions {
1206 now: Some(OffsetDateTime::from_unix_timestamp(0).unwrap()),
1207 ..Default::default()
1208 };
1209
1210 let registered_claims = RegisteredClaims {
1211 not_before: Some(1.try_into().unwrap()),
1212 ..Default::default()
1213 };
1214
1215 assert_eq!(
1216 Err(ValidationError::NotYetValid(Duration::seconds(1))),
1217 registered_claims.validate_nbf(Validation::Validate(temporal_options))
1218 );
1219 }
1220
1221 #[test]
1222 fn validate_times_valid_token_with_default_options() {
1223 let registered_claims = RegisteredClaims {
1224 not_before: Some(Timestamp(OffsetDateTime::now_utc() - Duration::days(2))),
1225 issued_at: Some(Timestamp(OffsetDateTime::now_utc() - Duration::days(1))),
1226 expiry: Some(Timestamp(OffsetDateTime::now_utc() + Duration::days(1))),
1227 ..Default::default()
1228 };
1229
1230 let validation_options = ValidationOptions {
1231 temporal_options: Default::default(),
1232 claim_presence_options: Default::default(),
1233
1234 expiry: Validation::Validate(()),
1235 not_before: Validation::Validate(()),
1236 issued_at: Validation::Validate(Duration::MAX),
1237
1238 ..Default::default()
1239 };
1240
1241 registered_claims.validate(validation_options).unwrap();
1242 }
1243
1244 #[test]
1245 fn validate_issuer_catch_mismatch() {
1246 let registered_claims = RegisteredClaims {
1247 issuer: Some("issuer".to_string()),
1248 ..Default::default()
1249 };
1250
1251 assert_eq!(
1252 Err(ValidationError::InvalidIssuer("issuer".to_string())),
1253 registered_claims.validate_iss(Validation::Validate("http://issuer".to_string()))
1254 );
1255 }
1256
1257 #[test]
1258 fn validate_audience_when_single() {
1259 let aud: SingleOrMultiple = "audience".into();
1260
1261 let registered_claims = RegisteredClaims {
1262 audience: Some(aud.clone()),
1263 ..Default::default()
1264 };
1265
1266 assert_eq!(
1267 Err(ValidationError::InvalidAudience(aud.clone())),
1268 registered_claims.validate_aud(Validation::Validate("http://audience".to_string()))
1269 );
1270
1271 assert_eq!(
1272 Err(ValidationError::InvalidAudience(aud)),
1273 registered_claims.validate_aud(Validation::Validate("audience2".to_string()))
1274 );
1275
1276 assert_eq!(
1277 Ok(()),
1278 registered_claims.validate_aud(Validation::Validate("audience".to_string()))
1279 );
1280 }
1281
1282 #[test]
1283 fn validate_audience_when_multiple() {
1284 let aud =
1285 SingleOrMultiple::Multiple(vec!["audience".to_string(), "http://audience".to_string()]);
1286
1287 let registered_claims = RegisteredClaims {
1288 audience: Some(aud.clone()),
1289 ..Default::default()
1290 };
1291
1292 assert_eq!(
1293 Ok(()),
1294 registered_claims.validate_aud(Validation::Validate("http://audience".to_string()))
1295 );
1296
1297 assert_eq!(
1298 Err(ValidationError::InvalidAudience(aud.clone())),
1299 registered_claims.validate_aud(Validation::Validate("audience2".to_string()))
1300 );
1301
1302 assert_eq!(
1303 Err(ValidationError::InvalidAudience(aud)),
1304 registered_claims.validate_aud(Validation::Validate("https://audience".to_string()))
1305 );
1306
1307 assert_eq!(
1308 Ok(()),
1309 registered_claims.validate_aud(Validation::Validate("audience".to_string()))
1310 );
1311 }
1312
1313 #[test]
1314 fn validate_valid_token_with_all_required() {
1315 let registered_claims = RegisteredClaims {
1316 expiry: Some(999.try_into().unwrap()),
1317 not_before: Some(1.try_into().unwrap()),
1318 issued_at: Some(95.try_into().unwrap()),
1319 subject: Some("subject".to_string()),
1320 issuer: Some("issuer".to_string()),
1321 audience: Some(SingleOrMultiple::Multiple(vec![
1322 "http://audience".to_string(),
1323 "audience".to_string(),
1324 ])),
1325 id: Some("id".into()),
1326 };
1327
1328 let temporal_options = TemporalOptions {
1329 now: Some(OffsetDateTime::from_unix_timestamp(100).unwrap()),
1330 ..Default::default()
1331 };
1332
1333 let validation_options = ValidationOptions {
1334 temporal_options,
1335 claim_presence_options: ClaimPresenceOptions::strict(),
1336
1337 expiry: Validation::Validate(()),
1338 not_before: Validation::Validate(()),
1339 issued_at: Validation::Validate(Duration::MAX),
1340 audience: Validation::Validate("audience".to_string()),
1341 issuer: Validation::Validate("issuer".to_string()),
1342 };
1343
1344 registered_claims.validate(validation_options).unwrap();
1345 }
1346
1347 #[test]
1348 fn validate_times_valid_token_with_epsilon() {
1349 let registered_claims = RegisteredClaims {
1350 expiry: Some(99.try_into().unwrap()),
1351 not_before: Some(96.try_into().unwrap()),
1352 issued_at: Some(96.try_into().unwrap()),
1353 ..Default::default()
1354 };
1355
1356 let temporal_options = TemporalOptions {
1357 now: Some(OffsetDateTime::from_unix_timestamp(100).unwrap()),
1358 epsilon: Duration::seconds(10),
1359 };
1360
1361 let validation_options = ValidationOptions {
1362 temporal_options,
1363 claim_presence_options: Default::default(),
1364
1365 expiry: Validation::Validate(()),
1366 not_before: Validation::Validate(()),
1367 issued_at: Validation::Validate(Duration::MAX),
1368
1369 ..Default::default()
1370 };
1371
1372 registered_claims.validate(validation_options).unwrap();
1373 }
1374}