1#![allow(
52 missing_copy_implementations,
53 missing_debug_implementations,
54 unknown_lints
55)]
56#![allow(
57 clippy::try_err,
58 clippy::needless_doctest_main,
59 clippy::upper_case_acronyms
60)]
61#![deny(
62 arithmetic_overflow,
63 bad_style,
64 dead_code,
65 improper_ctypes,
66 missing_docs,
67 mutable_transmutes,
68 no_mangle_const_items,
69 non_camel_case_types,
70 non_shorthand_field_patterns,
71 non_upper_case_globals,
72 overflowing_literals,
73 path_statements,
74 patterns_in_fns_without_body,
75 stable_features,
76 trivial_casts,
77 trivial_numeric_casts,
78 unconditional_recursion,
79 unknown_crate_types,
80 unreachable_code,
81 unused_allocation,
82 unused_assignments,
83 unused_attributes,
84 unused_comparisons,
85 unused_extern_crates,
86 unused_features,
87 unused_import_braces,
88 unused_imports,
89 unused_must_use,
90 unused_mut,
91 unused_parens,
92 unused_qualifications,
93 unused_results,
94 unused_unsafe,
95 unused_variables,
96 variant_size_differences,
97 while_true
98)]
99#![doc(test(attr(allow(unused_variables), deny(warnings))))]
100#![cfg_attr(feature = "strict", deny(warnings))]
101#![cfg_attr(feature = "strict", allow(unused_braces))]
103#![cfg_attr(feature = "strict", allow(deprecated))]
105#![cfg_attr(feature = "strict", allow(clippy::large_enum_variant))]
107
108use std::borrow::Borrow;
109use std::fmt::{self, Debug, Display};
110use std::iter;
111use std::ops::Deref;
112use std::str::{self, FromStr};
113
114use chrono::{DateTime, Duration, NaiveDateTime, Utc};
115use data_encoding::BASE64URL_NOPAD;
116use serde::de::{self, DeserializeOwned};
117use serde::{Deserialize, Deserializer, Serialize, Serializer};
118
119mod helpers;
120pub use crate::helpers::*;
121
122#[cfg(test)]
123#[macro_use]
124mod test;
125
126#[macro_use]
127mod serde_custom;
128
129#[macro_use]
130mod macros;
131
132pub mod errors;
133pub mod jwa;
134pub mod jwe;
135pub mod jwk;
136pub mod jws;
137
138pub mod digest;
139
140use crate::errors::{Error, ValidationError};
141
142pub type JWT<T, H> = jws::Compact<ClaimsSet<T>, H>;
207
208pub type JWE<T, H, I> = jwe::Compact<JWT<T, H>, I>;
324
325#[derive(Debug, Eq, PartialEq, Clone, Copy, Serialize, Deserialize, Default)]
359pub struct Empty {}
360
361impl CompactJson for Empty {}
362
363pub trait CompactPart {
369 fn to_bytes(&self) -> Result<Vec<u8>, Error>;
371
372 fn from_bytes(bytes: &[u8]) -> Result<Self, Error>
374 where
375 Self: Sized;
376
377 fn from_base64<B: AsRef<[u8]>>(encoded: &B) -> Result<Self, Error>
379 where
380 Self: Sized,
381 {
382 let decoded = BASE64URL_NOPAD.decode(encoded.as_ref())?;
383 Self::from_bytes(&decoded)
384 }
385
386 fn to_base64(&self) -> Result<Base64Url, Error> {
388 let bytes = self.to_bytes()?;
389 Ok(Base64Url(BASE64URL_NOPAD.encode(bytes.as_ref())))
390 }
391}
392
393pub trait CompactJson: Serialize + DeserializeOwned {}
397
398impl<T> CompactPart for T
399where
400 T: CompactJson,
401{
402 fn to_bytes(&self) -> Result<Vec<u8>, Error> {
404 Ok(serde_json::to_vec(&self)?)
405 }
406
407 fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
408 Ok(serde_json::from_slice(bytes)?)
409 }
410}
411
412impl CompactPart for Vec<u8> {
413 fn to_bytes(&self) -> Result<Vec<u8>, Error> {
414 Ok(self.clone())
415 }
416
417 fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
419 Ok(bytes.to_vec())
420 }
421}
422
423#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
425pub struct Base64Url(String);
426
427impl Base64Url {
428 pub fn unwrap(self) -> String {
430 let Base64Url(string) = self;
431 string
432 }
433
434 pub fn str(&self) -> &str {
436 &self.0
437 }
438}
439
440impl Deref for Base64Url {
441 type Target = str;
442
443 fn deref(&self) -> &str {
444 &self.0
445 }
446}
447
448impl FromStr for Base64Url {
449 type Err = Error;
450
451 fn from_str(s: &str) -> Result<Self, Self::Err> {
453 Ok(Base64Url(s.to_string()))
454 }
455}
456
457impl Borrow<str> for Base64Url {
458 fn borrow(&self) -> &str {
459 self.str()
460 }
461}
462
463impl CompactPart for Base64Url {
464 fn to_bytes(&self) -> Result<Vec<u8>, Error> {
465 Ok(BASE64URL_NOPAD.decode(self.as_ref())?)
466 }
467
468 fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
470 let string = str::from_utf8(bytes)?;
471 Ok(Base64Url(string.to_string()))
472 }
473
474 fn to_base64(&self) -> Result<Base64Url, Error> {
475 Ok((*self).clone())
476 }
477
478 fn from_base64<B: AsRef<[u8]>>(encoded: &B) -> Result<Self, Error> {
479 Self::from_bytes(encoded.as_ref())
480 }
481}
482
483impl AsRef<[u8]> for Base64Url {
484 fn as_ref(&self) -> &[u8] {
485 self.0.as_ref()
486 }
487}
488
489#[derive(Debug, Eq, PartialEq, Clone)]
491pub struct Compact {
492 pub parts: Vec<Base64Url>,
494}
495
496impl Compact {
497 pub fn new() -> Self {
499 Self { parts: vec![] }
500 }
501
502 pub fn with_capacity(capacity: usize) -> Self {
504 Self {
505 parts: Vec::with_capacity(capacity),
506 }
507 }
508
509 pub fn push(&mut self, part: &dyn CompactPart) -> Result<(), Error> {
511 let base64 = part.to_base64()?;
512 self.parts.push(base64);
513 Ok(())
514 }
515
516 pub fn len(&self) -> usize {
518 self.parts.len()
519 }
520
521 pub fn is_empty(&self) -> bool {
523 self.parts.is_empty()
524 }
525
526 pub fn encode(&self) -> String {
529 let strings: Vec<&str> = self.parts.iter().map(Deref::deref).collect();
530 strings.join(".")
531 }
532
533 pub fn decode(encoded: &str) -> Self {
535 let parts = encoded
537 .split('.')
538 .map(|s| FromStr::from_str(s).unwrap())
539 .collect();
540 Self { parts }
541 }
542
543 pub fn part<T: CompactPart>(&self, index: usize) -> Result<T, Error> {
545 let part = self
546 .parts
547 .get(index)
548 .ok_or_else(|| "Out of bounds".to_string())?;
549 CompactPart::from_base64(part)
550 }
551}
552
553impl Default for Compact {
554 fn default() -> Self {
555 Compact::new()
556 }
557}
558
559impl Display for Compact {
560 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
561 write!(f, "{}", self.encode())
562 }
563}
564
565impl Serialize for Compact {
566 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
567 where
568 S: Serializer,
569 {
570 serializer.serialize_str(&self.encode())
571 }
572}
573
574impl<'de> Deserialize<'de> for Compact {
575 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
576 where
577 D: Deserializer<'de>,
578 {
579 struct CompactVisitor;
580
581 impl<'de> de::Visitor<'de> for CompactVisitor {
582 type Value = Compact;
583
584 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
585 formatter.write_str("a string containing a compact JOSE representation")
586 }
587
588 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
589 where
590 E: de::Error,
591 {
592 Ok(Compact::decode(value))
593 }
594 }
595
596 deserializer.deserialize_str(CompactVisitor)
597 }
598}
599
600#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
641#[serde(untagged)]
642pub enum SingleOrMultiple<T> {
643 Single(T),
645 Multiple(Vec<T>),
647}
648
649impl<T> SingleOrMultiple<T>
650where
651 T: Clone + Debug + Eq + PartialEq + Serialize + DeserializeOwned + Send + Sync,
652{
653 pub fn contains<Q: ?Sized>(&self, value: &Q) -> bool
655 where
656 T: Borrow<Q>,
657 Q: PartialEq,
658 {
659 match *self {
660 SingleOrMultiple::Single(ref single) => single.borrow() == value,
661 SingleOrMultiple::Multiple(ref vector) => {
662 vector.iter().map(Borrow::borrow).any(|v| v == value)
663 }
664 }
665 }
666
667 pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = &'a T> + 'a> {
669 match *self {
670 SingleOrMultiple::Single(ref single) => Box::new(iter::once(single)),
671 SingleOrMultiple::Multiple(ref vector) => Box::new(vector.iter()),
672 }
673 }
674}
675
676#[derive(Clone, Copy, Debug, Eq, PartialEq)]
678pub struct Timestamp(DateTime<Utc>);
679
680impl Deref for Timestamp {
681 type Target = DateTime<Utc>;
682 fn deref(&self) -> &Self::Target {
683 &self.0
684 }
685}
686
687impl From<DateTime<Utc>> for Timestamp {
688 fn from(datetime: DateTime<Utc>) -> Self {
689 Timestamp(datetime)
690 }
691}
692
693impl From<Timestamp> for DateTime<Utc> {
694 fn from(ts: Timestamp) -> Self {
695 ts.0
696 }
697}
698
699impl From<i64> for Timestamp {
700 fn from(timestamp: i64) -> Self {
701 DateTime::<Utc>::from_utc(
702 NaiveDateTime::from_timestamp_opt(timestamp, 0).unwrap(),
703 Utc,
704 )
705 .into()
706 }
707}
708
709impl Serialize for Timestamp {
710 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
711 where
712 S: Serializer,
713 {
714 serializer.serialize_i64(self.timestamp())
715 }
716}
717
718impl<'de> Deserialize<'de> for Timestamp {
719 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
720 where
721 D: Deserializer<'de>,
722 {
723 let timestamp = i64::deserialize(deserializer)?;
724 Ok(Timestamp(DateTime::<Utc>::from_utc(
725 NaiveDateTime::from_timestamp_opt(timestamp, 0).unwrap(),
726 Utc,
727 )))
728 }
729}
730
731#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default)]
733pub struct RegisteredClaims {
734 #[serde(rename = "iss", skip_serializing_if = "Option::is_none")]
736 pub issuer: Option<String>,
737
738 #[serde(rename = "sub", skip_serializing_if = "Option::is_none")]
740 pub subject: Option<String>,
741
742 #[serde(rename = "aud", skip_serializing_if = "Option::is_none")]
744 pub audience: Option<SingleOrMultiple<String>>,
745
746 #[serde(rename = "exp", skip_serializing_if = "Option::is_none")]
748 pub expiry: Option<Timestamp>,
749
750 #[serde(rename = "nbf", skip_serializing_if = "Option::is_none")]
752 pub not_before: Option<Timestamp>,
753
754 #[serde(rename = "iat", skip_serializing_if = "Option::is_none")]
756 pub issued_at: Option<Timestamp>,
757
758 #[serde(rename = "jti", skip_serializing_if = "Option::is_none")]
760 pub id: Option<String>,
761}
762
763#[derive(Debug, Eq, PartialEq, Clone, Copy, Default)]
764pub struct ClaimPresenceOptions {
769 pub issued_at: Presence,
771 pub not_before: Presence,
773 pub expiry: Presence,
775 pub issuer: Presence,
777 pub audience: Presence,
779 pub subject: Presence,
781 pub id: Presence,
783}
784
785impl ClaimPresenceOptions {
786 pub fn strict() -> Self {
788 use crate::Presence::*;
789 ClaimPresenceOptions {
790 issued_at: Required,
791 not_before: Required,
792 expiry: Required,
793 issuer: Required,
794 audience: Required,
795 subject: Required,
796 id: Required,
797 }
798 }
799}
800
801#[derive(Eq, PartialEq, Clone)]
802pub struct ValidationOptions {
815 pub claim_presence_options: ClaimPresenceOptions,
817
818 pub temporal_options: TemporalOptions,
820
821 pub issued_at: Validation<Duration>,
826 pub not_before: Validation<()>,
828 pub expiry: Validation<()>,
830
831 pub issuer: Validation<String>,
834
835 pub audience: Validation<String>,
838}
839
840impl Default for ValidationOptions {
841 fn default() -> Self {
842 ValidationOptions {
843 expiry: Validation::Validate(()),
844 not_before: Validation::Validate(()),
845 issued_at: Validation::Validate(Duration::max_value()),
846
847 claim_presence_options: Default::default(),
848 temporal_options: Default::default(),
849 audience: Default::default(),
850 issuer: Default::default(),
851 }
852 }
853}
854
855impl RegisteredClaims {
856 pub fn validate_claim_presence(
858 &self,
859 options: ClaimPresenceOptions,
860 ) -> Result<(), ValidationError> {
861 use crate::Presence::Required;
862
863 let mut missing_claims: Vec<&str> = vec![];
864
865 if options.expiry == Required && self.expiry.is_none() {
866 missing_claims.push("exp");
867 }
868
869 if options.not_before == Required && self.not_before.is_none() {
870 missing_claims.push("nbf");
871 }
872
873 if options.issued_at == Required && self.issued_at.is_none() {
874 missing_claims.push("iat");
875 }
876
877 if options.audience == Required && self.audience.is_none() {
878 missing_claims.push("aud");
879 }
880
881 if options.issuer == Required && self.issuer.is_none() {
882 missing_claims.push("iss");
883 }
884
885 if options.subject == Required && self.subject.is_none() {
886 missing_claims.push("sub");
887 }
888
889 if options.id == Required && self.id.is_none() {
890 missing_claims.push("jti");
891 }
892
893 if missing_claims.is_empty() {
894 Ok(())
895 } else {
896 Err(ValidationError::MissingRequiredClaims(
897 missing_claims.into_iter().map(|v| v.into()).collect(),
898 ))
899 }
900 }
901
902 pub fn validate_exp(
904 &self,
905 validation: Validation<TemporalOptions>,
906 ) -> Result<(), ValidationError> {
907 match validation {
908 Validation::Ignored => Ok(()),
909 Validation::Validate(temporal_options) => {
910 let now = temporal_options.now.unwrap_or_else(Utc::now);
911
912 match self.expiry {
913 Some(Timestamp(expiry)) if now - expiry > temporal_options.epsilon => {
914 Err(ValidationError::Expired(now - expiry))
915 }
916 _ => Ok(()),
917 }
918 }
919 }
920 }
921
922 pub fn validate_nbf(
924 &self,
925 validation: Validation<TemporalOptions>,
926 ) -> Result<(), ValidationError> {
927 match validation {
928 Validation::Ignored => Ok(()),
929 Validation::Validate(temporal_options) => {
930 let now = temporal_options.now.unwrap_or_else(Utc::now);
931
932 match self.not_before {
933 Some(Timestamp(nbf)) if nbf - now > temporal_options.epsilon => {
934 Err(ValidationError::NotYetValid(nbf - now))
935 }
936 _ => Ok(()),
937 }
938 }
939 }
940 }
941
942 pub fn validate_iat(
944 &self,
945 validation: Validation<(Duration, TemporalOptions)>,
946 ) -> Result<(), ValidationError> {
947 match validation {
948 Validation::Ignored => Ok(()),
949 Validation::Validate((max_age, temporal_options)) => {
950 let now = temporal_options.now.unwrap_or_else(Utc::now);
951
952 match self.issued_at {
953 Some(Timestamp(iat)) if iat - now > temporal_options.epsilon => {
954 Err(ValidationError::NotYetValid(iat - now))
955 }
956 Some(Timestamp(iat)) if now - iat > max_age - temporal_options.epsilon => {
957 Err(ValidationError::TooOld(now - iat - max_age))
958 }
959 _ => Ok(()),
960 }
961 }
962 }
963 }
964
965 pub fn validate_aud(&self, validation: Validation<String>) -> Result<(), ValidationError> {
967 match validation {
968 Validation::Ignored => Ok(()),
969 Validation::Validate(expected_aud) => match self.audience {
970 Some(SingleOrMultiple::Single(ref audience)) if audience != &expected_aud => Err(
971 ValidationError::InvalidAudience(self.audience.clone().unwrap()),
972 ),
973 Some(SingleOrMultiple::Multiple(ref audiences))
974 if !audiences.contains(&expected_aud) =>
975 {
976 Err(ValidationError::InvalidAudience(
977 self.audience.clone().unwrap(),
978 ))
979 }
980 _ => Ok(()),
981 },
982 }
983 }
984
985 pub fn validate_iss(&self, validation: Validation<String>) -> Result<(), ValidationError> {
987 match validation {
988 Validation::Ignored => Ok(()),
989 Validation::Validate(expected_issuer) => match self.issuer {
990 Some(ref iss) if iss != &expected_issuer => {
991 Err(ValidationError::InvalidIssuer(self.issuer.clone().unwrap()))
992 }
993 _ => Ok(()),
994 },
995 }
996 }
997
998 pub fn validate(&self, options: ValidationOptions) -> Result<(), ValidationError> {
1004 self.validate_claim_presence(options.claim_presence_options)?;
1005 self.validate_exp(options.expiry.map(|_| options.temporal_options))?;
1006 self.validate_nbf(options.not_before.map(|_| options.temporal_options))?;
1007 self.validate_iat(options.issued_at.map(|dur| (dur, options.temporal_options)))?;
1008
1009 self.validate_iss(options.issuer)?;
1010 self.validate_aud(options.audience)?;
1011
1012 Ok(())
1016 }
1017}
1018
1019#[derive(Debug, Eq, PartialEq, Clone, Default, Serialize, Deserialize)]
1022pub struct ClaimsSet<T> {
1023 #[serde(flatten)]
1025 pub registered: RegisteredClaims,
1026 #[serde(flatten)]
1028 pub private: T,
1029}
1030
1031impl<T> CompactJson for ClaimsSet<T> where T: Serialize + DeserializeOwned {}
1032
1033#[cfg(test)]
1034mod tests {
1035 use std::str::{self, FromStr};
1036
1037 use chrono::{Duration, TimeZone, Utc};
1038
1039 use super::*;
1040
1041 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1042 struct PrivateClaims {
1043 company: String,
1044 department: String,
1045 }
1046
1047 impl CompactJson for PrivateClaims {}
1048
1049 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1050 struct InvalidPrivateClaim {
1051 sub: String,
1052 company: String,
1053 }
1054
1055 #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
1056 struct SingleOrMultipleStrings {
1057 values: SingleOrMultiple<String>,
1058 }
1059
1060 #[test]
1061 fn single_string_serialization_round_trip() {
1062 let test = SingleOrMultipleStrings {
1063 values: SingleOrMultiple::Single("foobar".to_string()),
1064 };
1065 let expected_json = r#"{"values":"foobar"}"#;
1066
1067 let serialized = not_err!(serde_json::to_string(&test));
1068 assert_eq!(expected_json, serialized);
1069
1070 let deserialized: SingleOrMultipleStrings = not_err!(serde_json::from_str(&serialized));
1071 assert_eq!(deserialized, test);
1072 assert!(deserialized.values.contains("foobar"));
1073 assert!(!deserialized.values.contains("does not exist"));
1074 }
1075
1076 #[test]
1077 fn multiple_strings_serialization_round_trip() {
1078 let test = SingleOrMultipleStrings {
1079 values: SingleOrMultiple::Multiple(vec![
1080 "foo".to_string(),
1081 "bar".to_string(),
1082 "baz".to_string(),
1083 ]),
1084 };
1085 let expected_json = r#"{"values":["foo","bar","baz"]}"#;
1086
1087 let serialized = not_err!(serde_json::to_string(&test));
1088 assert_eq!(expected_json, serialized);
1089
1090 let deserialized: SingleOrMultipleStrings = not_err!(serde_json::from_str(&serialized));
1091 assert_eq!(deserialized, test);
1092 assert!(deserialized.values.contains("foo"));
1093 assert!(deserialized.values.contains("bar"));
1094 assert!(deserialized.values.contains("baz"));
1095 assert!(!deserialized.values.contains("does not exist"));
1096 }
1097
1098 #[test]
1099 fn single_string_or_uri_string_serialization_round_trip() {
1100 let test = SingleOrMultipleStrings {
1101 values: SingleOrMultiple::Single(not_err!(FromStr::from_str("foobar"))),
1102 };
1103 let expected_json = r#"{"values":"foobar"}"#;
1104
1105 let serialized = not_err!(serde_json::to_string(&test));
1106 assert_eq!(expected_json, serialized);
1107
1108 let deserialized: SingleOrMultipleStrings = not_err!(serde_json::from_str(&serialized));
1109 assert_eq!(deserialized, test);
1110 assert!(deserialized.values.contains("foobar"));
1111 assert!(!deserialized.values.contains("does not exist"));
1112 }
1113
1114 #[test]
1115 fn single_string_or_uri_uri_serialization_round_trip() {
1116 let test = SingleOrMultipleStrings {
1117 values: SingleOrMultiple::Single(not_err!(FromStr::from_str(
1118 "https://www.examples.com/"
1119 ))),
1120 };
1121 let expected_json = r#"{"values":"https://www.examples.com/"}"#;
1122
1123 let serialized = not_err!(serde_json::to_string(&test));
1124 assert_eq!(expected_json, serialized);
1125
1126 let deserialized: SingleOrMultipleStrings = not_err!(serde_json::from_str(&serialized));
1127 assert_eq!(deserialized, test);
1128 assert!(deserialized.values.contains("https://www.examples.com/"));
1129 assert!(!deserialized.values.contains("https://ecorp.com"));
1130 }
1131
1132 #[test]
1133 fn multiple_string_or_uri_serialization_round_trip() {
1134 let test = SingleOrMultipleStrings {
1135 values: SingleOrMultiple::Multiple(vec![
1136 not_err!(FromStr::from_str("foo")),
1137 not_err!(FromStr::from_str("https://www.example.com/")),
1138 not_err!(FromStr::from_str("data:text/plain,Hello?World#")),
1139 not_err!(FromStr::from_str("http://[::1]/")),
1140 not_err!(FromStr::from_str("baz")),
1141 ]),
1142 };
1143 let expected_json = r#"{"values":["foo","https://www.example.com/","data:text/plain,Hello?World#","http://[::1]/","baz"]}"#;
1144
1145 let serialized = not_err!(serde_json::to_string(&test));
1146 assert_eq!(expected_json, serialized);
1147
1148 let deserialized: SingleOrMultipleStrings = not_err!(serde_json::from_str(&serialized));
1149 assert_eq!(deserialized, test);
1150
1151 assert!(deserialized.values.contains("foo"));
1152 assert!(deserialized.values.contains("https://www.example.com/"));
1153 assert!(deserialized.values.contains("data:text/plain,Hello?World#"));
1154 assert!(deserialized.values.contains("http://[::1]/"));
1155 assert!(deserialized.values.contains("baz"));
1156 assert!(!deserialized.values.contains("https://ecorp.com"));
1157 }
1158
1159 #[test]
1160 fn timestamp_serialization_roundtrip() {
1161 use chrono::Timelike;
1162
1163 let now: Timestamp = Utc::now().with_nanosecond(0).unwrap().into();
1164 let serialized = not_err!(serde_json::to_string(&now));
1165 let deserialized = not_err!(serde_json::from_str(&serialized));
1166 assert_eq!(now, deserialized);
1167
1168 let fixed_time: Timestamp = 1000.into();
1169 let serialized = not_err!(serde_json::to_string(&fixed_time));
1170 assert_eq!(serialized, "1000");
1171 let deserialized = not_err!(serde_json::from_str(&serialized));
1172 assert_eq!(fixed_time, deserialized);
1173 }
1174
1175 #[test]
1176 fn empty_registered_claims_serialization_round_trip() {
1177 let claim = RegisteredClaims::default();
1178 let expected_json = "{}";
1179
1180 let serialized = not_err!(serde_json::to_string(&claim));
1181 assert_eq!(expected_json, serialized);
1182
1183 let deserialized: RegisteredClaims = not_err!(serde_json::from_str(&serialized));
1184 assert_eq!(deserialized, claim);
1185 }
1186
1187 #[test]
1188 fn registered_claims_serialization_round_trip() {
1189 let claim = RegisteredClaims {
1190 issuer: Some(not_err!(FromStr::from_str("https://www.acme.com/"))),
1191 audience: Some(SingleOrMultiple::Single(not_err!(FromStr::from_str(
1192 "htts://acme-customer.com/"
1193 )))),
1194 not_before: Some(1234.into()),
1195 ..Default::default()
1196 };
1197 let expected_json =
1198 r#"{"iss":"https://www.acme.com/","aud":"htts://acme-customer.com/","nbf":1234}"#;
1199
1200 let serialized = not_err!(serde_json::to_string(&claim));
1201 assert_eq!(expected_json, serialized);
1202
1203 let deserialized: RegisteredClaims = not_err!(serde_json::from_str(&serialized));
1204 assert_eq!(deserialized, claim);
1205 }
1206
1207 #[test]
1208 fn claims_set_serialization_round_trip() {
1209 let claim = ClaimsSet::<PrivateClaims> {
1210 registered: RegisteredClaims {
1211 issuer: Some(not_err!(FromStr::from_str("https://www.acme.com/"))),
1212 subject: Some(not_err!(FromStr::from_str("John Doe"))),
1213 audience: Some(SingleOrMultiple::Single(not_err!(FromStr::from_str(
1214 "htts://acme-customer.com/"
1215 )))),
1216 not_before: Some(1234.into()),
1217 ..Default::default()
1218 },
1219 private: PrivateClaims {
1220 department: "Toilet Cleaning".to_string(),
1221 company: "ACME".to_string(),
1222 },
1223 };
1224
1225 let expected_json = "{\"iss\":\"https://www.acme.com/\",\"sub\":\"John Doe\",\
1226 \"aud\":\"htts://acme-customer.com/\",\
1227 \"nbf\":1234,\"company\":\"ACME\",\"department\":\"Toilet Cleaning\"}";
1228
1229 let serialized = not_err!(serde_json::to_string(&claim));
1230 assert_eq!(expected_json, serialized);
1231
1232 let deserialized: ClaimsSet<PrivateClaims> = not_err!(serde_json::from_str(&serialized));
1233 assert_eq!(deserialized, claim);
1234 }
1235
1236 #[test]
1237 fn duplicate_claims_round_trip() {
1239 let claim = ClaimsSet::<InvalidPrivateClaim> {
1240 registered: RegisteredClaims {
1241 issuer: Some(not_err!(FromStr::from_str("https://www.acme.com"))),
1242 subject: Some(not_err!(FromStr::from_str("John Doe"))),
1243 audience: Some(SingleOrMultiple::Single(not_err!(FromStr::from_str(
1244 "htts://acme-customer.com"
1245 )))),
1246 not_before: Some(1234.into()),
1247 ..Default::default()
1248 },
1249 private: InvalidPrivateClaim {
1250 sub: "John Doe".to_string(),
1251 company: "ACME".to_string(),
1252 },
1253 };
1254
1255 let json = serde_json::to_string(&claim).unwrap();
1256 assert_eq!(2, json.matches("\"sub\"").count());
1257
1258 let duplicate: Result<ClaimsSet<InvalidPrivateClaim>, _> = serde_json::from_str(&json);
1259 assert!(duplicate.is_err());
1260 let error = duplicate.unwrap_err().to_string();
1261 assert!(error.contains("duplicate field `sub`"));
1262 }
1263
1264 #[test]
1265 #[should_panic(expected = "MissingRequiredClaims([\"iat\"])")]
1266 fn validate_times_missing_iat() {
1267 let registered_claims: RegisteredClaims = Default::default();
1268 let options = ClaimPresenceOptions {
1269 issued_at: Presence::Required,
1270 ..Default::default()
1271 };
1272 registered_claims.validate_claim_presence(options).unwrap();
1273 }
1274
1275 #[test]
1276 #[should_panic(expected = "MissingRequiredClaims([\"exp\"])")]
1277 fn validate_times_missing_exp() {
1278 let registered_claims: RegisteredClaims = Default::default();
1279 let options = ClaimPresenceOptions {
1280 expiry: Presence::Required,
1281 ..Default::default()
1282 };
1283 registered_claims.validate_claim_presence(options).unwrap();
1284 }
1285
1286 #[test]
1287 #[should_panic(expected = "MissingRequiredClaims([\"nbf\"])")]
1288 fn validate_times_missing_nbf() {
1289 let registered_claims: RegisteredClaims = Default::default();
1290 let options = ClaimPresenceOptions {
1291 not_before: Presence::Required,
1292 ..Default::default()
1293 };
1294 registered_claims.validate_claim_presence(options).unwrap();
1295 }
1296
1297 #[test]
1298 #[should_panic(expected = "MissingRequiredClaims([\"aud\"])")]
1299 fn validate_times_missing_aud() {
1300 let registered_claims: RegisteredClaims = Default::default();
1301 let options = ClaimPresenceOptions {
1302 audience: Presence::Required,
1303 ..Default::default()
1304 };
1305 registered_claims.validate_claim_presence(options).unwrap();
1306 }
1307
1308 #[test]
1309 #[should_panic(expected = "MissingRequiredClaims([\"iss\"])")]
1310 fn validate_times_missing_iss() {
1311 let registered_claims: RegisteredClaims = Default::default();
1312 let options = ClaimPresenceOptions {
1313 issuer: Presence::Required,
1314 ..Default::default()
1315 };
1316 registered_claims.validate_claim_presence(options).unwrap();
1317 }
1318
1319 #[test]
1320 #[should_panic(expected = "MissingRequiredClaims([\"sub\"])")]
1321 fn validate_times_missing_sub() {
1322 let registered_claims: RegisteredClaims = Default::default();
1323 let options = ClaimPresenceOptions {
1324 subject: Presence::Required,
1325 ..Default::default()
1326 };
1327 registered_claims.validate_claim_presence(options).unwrap();
1328 }
1329
1330 #[test]
1331 #[should_panic(
1332 expected = "MissingRequiredClaims([\"exp\", \"nbf\", \"iat\", \"aud\", \"iss\", \"sub\", \"jti\"])"
1333 )]
1334 fn validate_times_missing_all() {
1335 let registered_claims: RegisteredClaims = Default::default();
1336 let options = ClaimPresenceOptions::strict();
1337 registered_claims.validate_claim_presence(options).unwrap();
1338 }
1339
1340 #[test]
1341 fn validate_times_catch_future_token() {
1342 let temporal_options = TemporalOptions {
1343 now: Some(Utc.timestamp_opt(0, 0).unwrap()),
1344 ..Default::default()
1345 };
1346
1347 let registered_claims = RegisteredClaims {
1348 issued_at: Some(10.into()),
1349 ..Default::default()
1350 };
1351
1352 assert_eq!(
1353 Err(ValidationError::NotYetValid(Duration::seconds(10))),
1354 registered_claims.validate_iat(Validation::Validate((
1355 Duration::seconds(0),
1356 temporal_options
1357 )))
1358 );
1359 }
1360
1361 #[test]
1362 fn validate_times_catch_too_old_token() {
1363 let temporal_options = TemporalOptions {
1364 now: Some(Utc.timestamp_opt(40, 0).unwrap()),
1365 ..Default::default()
1366 };
1367
1368 let registered_claims = RegisteredClaims {
1369 issued_at: Some(10.into()),
1370 ..Default::default()
1371 };
1372
1373 assert_eq!(
1374 Err(ValidationError::TooOld(Duration::seconds(5))),
1375 registered_claims.validate_iat(Validation::Validate((
1376 Duration::seconds(25),
1377 temporal_options
1378 )))
1379 );
1380 }
1381
1382 #[test]
1383 fn validate_times_catch_expired_token() {
1384 let temporal_options = TemporalOptions {
1385 now: Some(Utc.timestamp_opt(2, 0).unwrap()),
1386 ..Default::default()
1387 };
1388
1389 let registered_claims = RegisteredClaims {
1390 expiry: Some(1.into()),
1391 ..Default::default()
1392 };
1393
1394 assert_eq!(
1395 Err(ValidationError::Expired(Duration::seconds(1))),
1396 registered_claims.validate_exp(Validation::Validate(temporal_options))
1397 );
1398 }
1399
1400 #[test]
1401 fn validate_times_catch_early_token() {
1402 let temporal_options = TemporalOptions {
1403 now: Some(Utc.timestamp_opt(0, 0).unwrap()),
1404 ..Default::default()
1405 };
1406
1407 let registered_claims = RegisteredClaims {
1408 not_before: Some(1.into()),
1409 ..Default::default()
1410 };
1411
1412 assert_eq!(
1413 Err(ValidationError::NotYetValid(Duration::seconds(1))),
1414 registered_claims.validate_nbf(Validation::Validate(temporal_options))
1415 );
1416 }
1417
1418 #[test]
1419 fn validate_times_valid_token_with_default_options() {
1420 let registered_claims = RegisteredClaims {
1421 not_before: Some(Timestamp(Utc::now() - Duration::days(2))),
1422 issued_at: Some(Timestamp(Utc::now() - Duration::days(1))),
1423 expiry: Some(Timestamp(Utc::now() + Duration::days(1))),
1424 ..Default::default()
1425 };
1426
1427 let validation_options = ValidationOptions {
1428 temporal_options: Default::default(),
1429 claim_presence_options: Default::default(),
1430
1431 expiry: Validation::Validate(()),
1432 not_before: Validation::Validate(()),
1433 issued_at: Validation::Validate(Duration::max_value()),
1434
1435 ..Default::default()
1436 };
1437
1438 not_err!(registered_claims.validate(validation_options));
1439 }
1440
1441 #[test]
1442 fn validate_issuer_catch_mismatch() {
1443 let registered_claims = RegisteredClaims {
1444 issuer: Some("issuer".to_string()),
1445 ..Default::default()
1446 };
1447
1448 assert_eq!(
1449 Err(ValidationError::InvalidIssuer("issuer".to_string())),
1450 registered_claims.validate_iss(Validation::Validate("http://issuer".to_string()))
1451 );
1452 }
1453
1454 #[test]
1455 fn validate_audience_when_single() {
1456 let aud = SingleOrMultiple::Single("audience".to_string());
1457
1458 let registered_claims = RegisteredClaims {
1459 audience: Some(aud.clone()),
1460 ..Default::default()
1461 };
1462
1463 assert_eq!(
1464 Err(ValidationError::InvalidAudience(aud.clone())),
1465 registered_claims.validate_aud(Validation::Validate("http://audience".to_string()))
1466 );
1467
1468 assert_eq!(
1469 Err(ValidationError::InvalidAudience(aud)),
1470 registered_claims.validate_aud(Validation::Validate("audience2".to_string()))
1471 );
1472
1473 assert_eq!(
1474 Ok(()),
1475 registered_claims.validate_aud(Validation::Validate("audience".to_string()))
1476 );
1477 }
1478
1479 #[test]
1480 fn validate_audience_when_multiple() {
1481 let aud =
1482 SingleOrMultiple::Multiple(vec!["audience".to_string(), "http://audience".to_string()]);
1483
1484 let registered_claims = RegisteredClaims {
1485 audience: Some(aud.clone()),
1486 ..Default::default()
1487 };
1488
1489 assert_eq!(
1490 Ok(()),
1491 registered_claims.validate_aud(Validation::Validate("http://audience".to_string()))
1492 );
1493
1494 assert_eq!(
1495 Err(ValidationError::InvalidAudience(aud.clone())),
1496 registered_claims.validate_aud(Validation::Validate("audience2".to_string()))
1497 );
1498
1499 assert_eq!(
1500 Err(ValidationError::InvalidAudience(aud)),
1501 registered_claims.validate_aud(Validation::Validate("https://audience".to_string()))
1502 );
1503
1504 assert_eq!(
1505 Ok(()),
1506 registered_claims.validate_aud(Validation::Validate("audience".to_string()))
1507 );
1508 }
1509
1510 #[test]
1511 fn validate_valid_token_with_all_required() {
1512 let registered_claims = RegisteredClaims {
1513 expiry: Some(999.into()),
1514 not_before: Some(1.into()),
1515 issued_at: Some(95.into()),
1516 subject: Some("subject".to_string()),
1517 issuer: Some("issuer".to_string()),
1518 audience: Some(SingleOrMultiple::Multiple(vec![
1519 "http://audience".to_string(),
1520 "audience".to_string(),
1521 ])),
1522 id: Some("id".into()),
1523 };
1524
1525 let temporal_options = TemporalOptions {
1526 now: Some(Utc.timestamp_opt(100, 0).unwrap()),
1527 ..Default::default()
1528 };
1529
1530 let validation_options = ValidationOptions {
1531 temporal_options,
1532 claim_presence_options: ClaimPresenceOptions::strict(),
1533
1534 expiry: Validation::Validate(()),
1535 not_before: Validation::Validate(()),
1536 issued_at: Validation::Validate(Duration::max_value()),
1537 audience: Validation::Validate("audience".to_string()),
1538 issuer: Validation::Validate("issuer".to_string()),
1539 };
1540
1541 not_err!(registered_claims.validate(validation_options));
1542 }
1543
1544 #[test]
1545 fn validate_times_valid_token_with_epsilon() {
1546 let registered_claims = RegisteredClaims {
1547 expiry: Some(99.into()),
1548 not_before: Some(96.into()),
1549 issued_at: Some(96.into()),
1550 ..Default::default()
1551 };
1552
1553 let temporal_options = TemporalOptions {
1554 now: Some(Utc.timestamp_opt(100, 0).unwrap()),
1555 epsilon: Duration::seconds(10),
1556 };
1557
1558 let validation_options = ValidationOptions {
1559 temporal_options,
1560 claim_presence_options: Default::default(),
1561
1562 expiry: Validation::Validate(()),
1563 not_before: Validation::Validate(()),
1564 issued_at: Validation::Validate(Duration::max_value()),
1565
1566 ..Default::default()
1567 };
1568
1569 not_err!(registered_claims.validate(validation_options));
1570 }
1571
1572 #[test]
1573 fn compact_part_round_trip() {
1574 let test_value = PrivateClaims {
1575 department: "Toilet Cleaning".to_string(),
1576 company: "ACME".to_string(),
1577 };
1578
1579 let base64 = not_err!(test_value.to_base64());
1580 let expected_base64 = "eyJjb21wYW55IjoiQUNNRSIsImRlcGFydG1lbnQiOiJUb2lsZXQgQ2xlYW5pbmcifQ";
1581 assert_eq!(base64.str(), expected_base64);
1582
1583 let actual_value = not_err!(PrivateClaims::from_base64(&base64));
1584 assert_eq!(actual_value, test_value);
1585 }
1586
1587 #[test]
1588 fn compact_part_vec_u8_round_trip() {
1589 let test_value: Vec<u8> = vec![1, 2, 3, 4, 5];
1590
1591 let base64 = not_err!(test_value.to_base64());
1592 let expected_base64 = "AQIDBAU";
1593 assert_eq!(base64.str(), expected_base64);
1594
1595 let actual_value = not_err!(Vec::<u8>::from_base64(&base64));
1596 assert_eq!(actual_value, test_value);
1597 }
1598
1599 #[test]
1600 fn compact_part_base64_url_round_trip() {
1601 let test_value = Base64Url("AQIDBAU".to_string());
1602
1603 let base64 = not_err!(test_value.to_base64());
1604 let expected_base64 = "AQIDBAU";
1605 assert_eq!(base64.str(), expected_base64);
1606
1607 let actual_value = not_err!(Base64Url::from_base64(&base64));
1608 assert_eq!(actual_value, test_value);
1609 }
1610}