1use regex::Regex;
2use serde_json::Value;
3use std::collections::HashSet;
4use std::fmt;
5use std::ops::RangeInclusive;
6
7use crate::claims::CognitoJwtClaims;
8
9pub trait ClaimValidator: Send + Sync {
11 fn validate(&self, claims: &CognitoJwtClaims) -> Result<(), String>;
13}
14
15pub struct ExistenceValidator {
17 claim_name: String,
19}
20
21impl ExistenceValidator {
22 pub fn new(claim_name: &str) -> Self {
24 Self {
25 claim_name: claim_name.to_string(),
26 }
27 }
28}
29
30impl ClaimValidator for ExistenceValidator {
31 fn validate(&self, claims: &CognitoJwtClaims) -> Result<(), String> {
32 if claims.custom_claims.contains_key(&self.claim_name) {
33 Ok(())
34 } else {
35 Err(format!("Claim '{}' is required", self.claim_name))
36 }
37 }
38}
39
40impl fmt::Debug for ExistenceValidator {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 f.debug_struct("ExistenceValidator")
43 .field("claim_name", &self.claim_name)
44 .finish()
45 }
46}
47
48pub struct StringValueValidator {
50 claim_name: String,
52 expected_value: String,
54}
55
56impl StringValueValidator {
57 pub fn new(claim_name: &str, expected_value: &str) -> Self {
59 Self {
60 claim_name: claim_name.to_string(),
61 expected_value: expected_value.to_string(),
62 }
63 }
64}
65
66impl ClaimValidator for StringValueValidator {
67 fn validate(&self, claims: &CognitoJwtClaims) -> Result<(), String> {
68 if let Some(Value::String(value)) = claims.custom_claims.get(&self.claim_name) {
69 if value == &self.expected_value {
70 Ok(())
71 } else {
72 Err(format!(
73 "Claim '{}' has invalid value: expected '{}', got '{}'",
74 self.claim_name, self.expected_value, value
75 ))
76 }
77 } else {
78 Err(format!(
79 "Claim '{}' is missing or not a string",
80 self.claim_name
81 ))
82 }
83 }
84}
85
86impl fmt::Debug for StringValueValidator {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 f.debug_struct("StringValueValidator")
89 .field("claim_name", &self.claim_name)
90 .field("expected_value", &self.expected_value)
91 .finish()
92 }
93}
94
95pub struct AllowedValuesValidator {
97 claim_name: String,
99 allowed_values: HashSet<String>,
101}
102
103impl AllowedValuesValidator {
104 pub fn new(claim_name: &str, allowed_values: &[&str]) -> Self {
106 Self {
107 claim_name: claim_name.to_string(),
108 allowed_values: allowed_values.iter().map(|s| s.to_string()).collect(),
109 }
110 }
111}
112
113impl ClaimValidator for AllowedValuesValidator {
114 fn validate(&self, claims: &CognitoJwtClaims) -> Result<(), String> {
115 if let Some(Value::String(value)) = claims.custom_claims.get(&self.claim_name) {
116 if self.allowed_values.contains(value) {
117 Ok(())
118 } else {
119 Err(format!(
120 "Claim '{}' has invalid value: '{}' is not one of the allowed values",
121 self.claim_name, value
122 ))
123 }
124 } else {
125 Err(format!(
126 "Claim '{}' is missing or not a string",
127 self.claim_name
128 ))
129 }
130 }
131}
132
133impl fmt::Debug for AllowedValuesValidator {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 f.debug_struct("AllowedValuesValidator")
136 .field("claim_name", &self.claim_name)
137 .field("allowed_values", &self.allowed_values)
138 .finish()
139 }
140}
141
142pub struct NumericRangeValidator {
144 claim_name: String,
146 range: RangeInclusive<f64>,
148}
149
150impl NumericRangeValidator {
151 pub fn new(claim_name: &str, min: f64, max: f64) -> Self {
153 Self {
154 claim_name: claim_name.to_string(),
155 range: min..=max,
156 }
157 }
158}
159
160impl ClaimValidator for NumericRangeValidator {
161 fn validate(&self, claims: &CognitoJwtClaims) -> Result<(), String> {
162 if let Some(value) = claims.custom_claims.get(&self.claim_name) {
163 let num = match value {
164 Value::Number(n) => n.as_f64().unwrap_or(0.0),
165 Value::String(s) => s.parse::<f64>().unwrap_or(0.0),
166 _ => {
167 return Err(format!("Claim '{}' is not a number", self.claim_name));
168 }
169 };
170
171 if self.range.contains(&num) {
172 Ok(())
173 } else {
174 Err(format!(
175 "Claim '{}' is outside the allowed range: {} not in {:?}",
176 self.claim_name, num, self.range
177 ))
178 }
179 } else {
180 Err(format!("Claim '{}' is missing", self.claim_name))
181 }
182 }
183}
184
185impl fmt::Debug for NumericRangeValidator {
186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187 f.debug_struct("NumericRangeValidator")
188 .field("claim_name", &self.claim_name)
189 .field("range", &format!("{:?}", self.range))
190 .finish()
191 }
192}
193
194pub struct BooleanValidator {
196 claim_name: String,
198 expected_value: bool,
200}
201
202impl BooleanValidator {
203 pub fn new(claim_name: &str, expected_value: bool) -> Self {
205 Self {
206 claim_name: claim_name.to_string(),
207 expected_value,
208 }
209 }
210}
211
212impl ClaimValidator for BooleanValidator {
213 fn validate(&self, claims: &CognitoJwtClaims) -> Result<(), String> {
214 if let Some(Value::Bool(value)) = claims.custom_claims.get(&self.claim_name) {
215 if *value == self.expected_value {
216 Ok(())
217 } else {
218 Err(format!(
219 "Claim '{}' has invalid value: expected {}, got {}",
220 self.claim_name, self.expected_value, value
221 ))
222 }
223 } else {
224 Err(format!(
225 "Claim '{}' is missing or not a boolean",
226 self.claim_name
227 ))
228 }
229 }
230}
231
232impl fmt::Debug for BooleanValidator {
233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234 f.debug_struct("BooleanValidator")
235 .field("claim_name", &self.claim_name)
236 .field("expected_value", &self.expected_value)
237 .finish()
238 }
239}
240
241pub struct RegexValidator {
243 claim_name: String,
245 pattern: Regex,
247 pattern_str: String,
249}
250
251impl RegexValidator {
252 pub fn new(claim_name: &str, pattern: &str) -> Result<Self, regex::Error> {
254 Ok(Self {
255 claim_name: claim_name.to_string(),
256 pattern: Regex::new(pattern)?,
257 pattern_str: pattern.to_string(),
258 })
259 }
260}
261
262impl ClaimValidator for RegexValidator {
263 fn validate(&self, claims: &CognitoJwtClaims) -> Result<(), String> {
264 if let Some(Value::String(value)) = claims.custom_claims.get(&self.claim_name) {
265 if self.pattern.is_match(value) {
266 Ok(())
267 } else {
268 Err(format!(
269 "Claim '{}' does not match pattern: '{}' does not match '{}'",
270 self.claim_name, value, self.pattern_str
271 ))
272 }
273 } else {
274 Err(format!(
275 "Claim '{}' is missing or not a string",
276 self.claim_name
277 ))
278 }
279 }
280}
281
282impl fmt::Debug for RegexValidator {
283 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284 f.debug_struct("RegexValidator")
285 .field("claim_name", &self.claim_name)
286 .field("pattern", &self.pattern_str)
287 .finish()
288 }
289}
290
291pub struct ArrayContainsValidator {
293 claim_name: String,
295 value: Value,
297}
298
299impl ArrayContainsValidator {
300 pub fn new_string(claim_name: &str, value: &str) -> Self {
302 Self {
303 claim_name: claim_name.to_string(),
304 value: Value::String(value.to_string()),
305 }
306 }
307
308 pub fn new_number(claim_name: &str, value: f64) -> Self {
310 Self {
311 claim_name: claim_name.to_string(),
312 value: Value::Number(serde_json::Number::from_f64(value).unwrap()),
313 }
314 }
315
316 pub fn new_bool(claim_name: &str, value: bool) -> Self {
318 Self {
319 claim_name: claim_name.to_string(),
320 value: Value::Bool(value),
321 }
322 }
323}
324
325impl ClaimValidator for ArrayContainsValidator {
326 fn validate(&self, claims: &CognitoJwtClaims) -> Result<(), String> {
327 if let Some(Value::Array(array)) = claims.custom_claims.get(&self.claim_name) {
328 if array.contains(&self.value) {
329 Ok(())
330 } else {
331 Err(format!(
332 "Claim '{}' does not contain expected value: {:?}",
333 self.claim_name, self.value
334 ))
335 }
336 } else {
337 Err(format!(
338 "Claim '{}' is missing or not an array",
339 self.claim_name
340 ))
341 }
342 }
343}
344
345impl fmt::Debug for ArrayContainsValidator {
346 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347 f.debug_struct("ArrayContainsValidator")
348 .field("claim_name", &self.claim_name)
349 .field("value", &self.value)
350 .finish()
351 }
352}
353
354pub struct AndValidator {
356 validators: Vec<Box<dyn ClaimValidator>>,
358}
359
360impl AndValidator {
361 pub fn new(validators: Vec<Box<dyn ClaimValidator>>) -> Self {
363 Self { validators }
364 }
365}
366
367impl ClaimValidator for AndValidator {
368 fn validate(&self, claims: &CognitoJwtClaims) -> Result<(), String> {
369 for validator in &self.validators {
370 if let Err(reason) = validator.validate(claims) {
371 return Err(reason);
372 }
373 }
374 Ok(())
375 }
376}
377
378impl fmt::Debug for AndValidator {
379 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380 f.debug_struct("AndValidator")
381 .field("validators_count", &self.validators.len())
382 .finish()
383 }
384}
385
386pub struct OrValidator {
388 validators: Vec<Box<dyn ClaimValidator>>,
390}
391
392impl OrValidator {
393 pub fn new(validators: Vec<Box<dyn ClaimValidator>>) -> Self {
395 Self { validators }
396 }
397}
398
399impl ClaimValidator for OrValidator {
400 fn validate(&self, claims: &CognitoJwtClaims) -> Result<(), String> {
401 let mut errors = Vec::new();
402 for validator in &self.validators {
403 match validator.validate(claims) {
404 Ok(()) => return Ok(()),
405 Err(reason) => errors.push(reason),
406 }
407 }
408 Err(format!(
409 "None of the validators passed: {}",
410 errors.join(", ")
411 ))
412 }
413}
414
415impl fmt::Debug for OrValidator {
416 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
417 f.debug_struct("OrValidator")
418 .field("validators_count", &self.validators.len())
419 .finish()
420 }
421}
422
423#[cfg(test)]
424mod tests {
425 use super::*;
426 use std::collections::HashMap;
427
428 #[test]
429 fn test_existence_validator() {
430 let validator = ExistenceValidator::new("test_claim");
431
432 let mut claims = CognitoJwtClaims {
434 sub: "user123".to_string(),
435 iss: "https://example.com".to_string(),
436 client_id: "client123".to_string(),
437 origin_jti: None,
438 event_id: None,
439 token_use: "id".to_string(),
440 scope: None,
441 auth_time: 0,
442 exp: 0,
443 iat: 0,
444 jti: "jti123".to_string(),
445 username: None,
446 custom_claims: HashMap::new(),
447 };
448
449 claims
450 .custom_claims
451 .insert("test_claim".to_string(), Value::String("value".to_string()));
452
453 assert!(validator.validate(&claims).is_ok());
454
455 let claims = CognitoJwtClaims {
457 sub: "user123".to_string(),
458 iss: "https://example.com".to_string(),
459 client_id: "client123".to_string(),
460 origin_jti: None,
461 event_id: None,
462 token_use: "id".to_string(),
463 scope: None,
464 auth_time: 0,
465 exp: 0,
466 iat: 0,
467 jti: "jti123".to_string(),
468 username: None,
469 custom_claims: HashMap::new(),
470 };
471
472 assert!(validator.validate(&claims).is_err());
473 }
474
475 #[test]
476 fn test_string_value_validator() {
477 let validator = StringValueValidator::new("test_claim", "expected_value");
478
479 let mut claims = CognitoJwtClaims {
481 sub: "user123".to_string(),
482 iss: "https://example.com".to_string(),
483 client_id: "client123".to_string(),
484 origin_jti: None,
485 event_id: None,
486 token_use: "id".to_string(),
487 scope: None,
488 auth_time: 0,
489 exp: 0,
490 iat: 0,
491 jti: "jti123".to_string(),
492 username: None,
493 custom_claims: HashMap::new(),
494 };
495
496 claims.custom_claims.insert(
497 "test_claim".to_string(),
498 Value::String("expected_value".to_string()),
499 );
500
501 assert!(validator.validate(&claims).is_ok());
502
503 let mut claims = CognitoJwtClaims {
505 sub: "user123".to_string(),
506 iss: "https://example.com".to_string(),
507 client_id: "client123".to_string(),
508 origin_jti: None,
509 event_id: None,
510 token_use: "id".to_string(),
511 scope: None,
512 auth_time: 0,
513 exp: 0,
514 iat: 0,
515 jti: "jti123".to_string(),
516 username: None,
517 custom_claims: HashMap::new(),
518 };
519
520 claims.custom_claims.insert(
521 "test_claim".to_string(),
522 Value::String("wrong_value".to_string()),
523 );
524
525 assert!(validator.validate(&claims).is_err());
526
527 let claims = CognitoJwtClaims {
529 sub: "user123".to_string(),
530 iss: "https://example.com".to_string(),
531 client_id: "client123".to_string(),
532 origin_jti: None,
533 event_id: None,
534 token_use: "id".to_string(),
535 scope: None,
536 auth_time: 0,
537 exp: 0,
538 iat: 0,
539 jti: "jti123".to_string(),
540 username: None,
541 custom_claims: HashMap::new(),
542 };
543
544 assert!(validator.validate(&claims).is_err());
545 }
546
547 #[test]
548 fn test_allowed_values_validator() {
549 let validator = AllowedValuesValidator::new("test_claim", &["value1", "value2", "value3"]);
550
551 let mut claims = CognitoJwtClaims {
553 sub: "user123".to_string(),
554 iss: "https://example.com".to_string(),
555 client_id: "client123".to_string(),
556 origin_jti: None,
557 event_id: None,
558 token_use: "id".to_string(),
559 scope: None,
560 auth_time: 0,
561 exp: 0,
562 iat: 0,
563 jti: "jti123".to_string(),
564 username: None,
565 custom_claims: HashMap::new(),
566 };
567
568 claims.custom_claims.insert(
569 "test_claim".to_string(),
570 Value::String("value2".to_string()),
571 );
572
573 assert!(validator.validate(&claims).is_ok());
574
575 let mut claims = CognitoJwtClaims {
577 sub: "user123".to_string(),
578 iss: "https://example.com".to_string(),
579 client_id: "client123".to_string(),
580 origin_jti: None,
581 event_id: None,
582 token_use: "id".to_string(),
583 scope: None,
584 auth_time: 0,
585 exp: 0,
586 iat: 0,
587 jti: "jti123".to_string(),
588 username: None,
589 custom_claims: HashMap::new(),
590 };
591
592 claims.custom_claims.insert(
593 "test_claim".to_string(),
594 Value::String("value4".to_string()),
595 );
596
597 assert!(validator.validate(&claims).is_err());
598 }
599
600 #[test]
601 fn test_numeric_range_validator() {
602 let validator = NumericRangeValidator::new("test_claim", 10.0, 20.0);
603
604 let mut claims = CognitoJwtClaims {
606 sub: "user123".to_string(),
607 iss: "https://example.com".to_string(),
608 client_id: "client123".to_string(),
609 origin_jti: None,
610 event_id: None,
611 token_use: "id".to_string(),
612 scope: None,
613 auth_time: 0,
614 exp: 0,
615 iat: 0,
616 jti: "jti123".to_string(),
617 username: None,
618 custom_claims: HashMap::new(),
619 };
620
621 claims.custom_claims.insert(
622 "test_claim".to_string(),
623 Value::Number(serde_json::Number::from_f64(15.0).unwrap()),
624 );
625
626 assert!(validator.validate(&claims).is_ok());
627
628 let mut claims = CognitoJwtClaims {
630 sub: "user123".to_string(),
631 iss: "https://example.com".to_string(),
632 client_id: "client123".to_string(),
633 origin_jti: None,
634 event_id: None,
635 token_use: "id".to_string(),
636 scope: None,
637 auth_time: 0,
638 exp: 0,
639 iat: 0,
640 jti: "jti123".to_string(),
641 username: None,
642 custom_claims: HashMap::new(),
643 };
644
645 claims.custom_claims.insert(
646 "test_claim".to_string(),
647 Value::Number(serde_json::Number::from_f64(25.0).unwrap()),
648 );
649
650 assert!(validator.validate(&claims).is_err());
651 }
652
653 #[test]
654 fn test_boolean_validator() {
655 let validator = BooleanValidator::new("test_claim", true);
656
657 let mut claims = CognitoJwtClaims {
659 sub: "user123".to_string(),
660 iss: "https://example.com".to_string(),
661 client_id: "client123".to_string(),
662 origin_jti: None,
663 event_id: None,
664 token_use: "id".to_string(),
665 scope: None,
666 auth_time: 0,
667 exp: 0,
668 iat: 0,
669 jti: "jti123".to_string(),
670 username: None,
671 custom_claims: HashMap::new(),
672 };
673
674 claims
675 .custom_claims
676 .insert("test_claim".to_string(), Value::Bool(true));
677
678 assert!(validator.validate(&claims).is_ok());
679
680 let mut claims = CognitoJwtClaims {
682 sub: "user123".to_string(),
683 iss: "https://example.com".to_string(),
684 client_id: "client123".to_string(),
685 origin_jti: None,
686 event_id: None,
687 token_use: "id".to_string(),
688 scope: None,
689 auth_time: 0,
690 exp: 0,
691 iat: 0,
692 jti: "jti123".to_string(),
693 username: None,
694 custom_claims: HashMap::new(),
695 };
696
697 claims
698 .custom_claims
699 .insert("test_claim".to_string(), Value::Bool(false));
700
701 assert!(validator.validate(&claims).is_err());
702 }
703
704 #[test]
705 fn test_regex_validator() {
706 let validator = RegexValidator::new("test_claim", r"^[a-z]{3}\d{2}$").unwrap();
707
708 let mut claims = CognitoJwtClaims {
710 sub: "user123".to_string(),
711 iss: "https://example.com".to_string(),
712 client_id: "client123".to_string(),
713 origin_jti: None,
714 event_id: None,
715 token_use: "id".to_string(),
716 scope: None,
717 auth_time: 0,
718 exp: 0,
719 iat: 0,
720 jti: "jti123".to_string(),
721 username: None,
722 custom_claims: HashMap::new(),
723 };
724
725 claims
726 .custom_claims
727 .insert("test_claim".to_string(), Value::String("abc12".to_string()));
728
729 assert!(validator.validate(&claims).is_ok());
730
731 let mut claims = CognitoJwtClaims {
733 sub: "user123".to_string(),
734 iss: "https://example.com".to_string(),
735 client_id: "client123".to_string(),
736 origin_jti: None,
737 event_id: None,
738 token_use: "id".to_string(),
739 scope: None,
740 auth_time: 0,
741 exp: 0,
742 iat: 0,
743 jti: "jti123".to_string(),
744 username: None,
745 custom_claims: HashMap::new(),
746 };
747
748 claims
749 .custom_claims
750 .insert("test_claim".to_string(), Value::String("ABC12".to_string()));
751
752 assert!(validator.validate(&claims).is_err());
753 }
754
755 #[test]
756 fn test_array_contains_validator() {
757 let validator = ArrayContainsValidator::new_string("test_claim", "value2");
758
759 let mut claims = CognitoJwtClaims {
761 sub: "user123".to_string(),
762 iss: "https://example.com".to_string(),
763 client_id: "client123".to_string(),
764 origin_jti: None,
765 event_id: None,
766 token_use: "id".to_string(),
767 scope: None,
768 auth_time: 0,
769 exp: 0,
770 iat: 0,
771 jti: "jti123".to_string(),
772 username: None,
773 custom_claims: HashMap::new(),
774 };
775
776 let array = vec![
777 Value::String("value1".to_string()),
778 Value::String("value2".to_string()),
779 Value::String("value3".to_string()),
780 ];
781
782 claims
783 .custom_claims
784 .insert("test_claim".to_string(), Value::Array(array));
785
786 assert!(validator.validate(&claims).is_ok());
787
788 let mut claims = CognitoJwtClaims {
790 sub: "user123".to_string(),
791 iss: "https://example.com".to_string(),
792 client_id: "client123".to_string(),
793 origin_jti: None,
794 event_id: None,
795 token_use: "id".to_string(),
796 scope: None,
797 auth_time: 0,
798 exp: 0,
799 iat: 0,
800 jti: "jti123".to_string(),
801 username: None,
802 custom_claims: HashMap::new(),
803 };
804
805 let array = vec![
806 Value::String("value1".to_string()),
807 Value::String("value3".to_string()),
808 Value::String("value4".to_string()),
809 ];
810
811 claims
812 .custom_claims
813 .insert("test_claim".to_string(), Value::Array(array));
814
815 assert!(validator.validate(&claims).is_err());
816 }
817
818 #[test]
819 fn test_and_validator() {
820 let validator1 = Box::new(ExistenceValidator::new("claim1"));
821 let validator2 = Box::new(ExistenceValidator::new("claim2"));
822
823 let and_validator = AndValidator::new(vec![validator1, validator2]);
824
825 let mut claims = CognitoJwtClaims {
827 sub: "user123".to_string(),
828 iss: "https://example.com".to_string(),
829 client_id: "client123".to_string(),
830 origin_jti: None,
831 event_id: None,
832 token_use: "id".to_string(),
833 scope: None,
834 auth_time: 0,
835 exp: 0,
836 iat: 0,
837 jti: "jti123".to_string(),
838 username: None,
839 custom_claims: HashMap::new(),
840 };
841
842 claims
843 .custom_claims
844 .insert("claim1".to_string(), Value::String("value1".to_string()));
845 claims
846 .custom_claims
847 .insert("claim2".to_string(), Value::String("value2".to_string()));
848
849 assert!(and_validator.validate(&claims).is_ok());
850
851 let mut claims = CognitoJwtClaims {
853 sub: "user123".to_string(),
854 iss: "https://example.com".to_string(),
855 client_id: "client123".to_string(),
856 origin_jti: None,
857 event_id: None,
858 token_use: "id".to_string(),
859 scope: None,
860 auth_time: 0,
861 exp: 0,
862 iat: 0,
863 jti: "jti123".to_string(),
864 username: None,
865 custom_claims: HashMap::new(),
866 };
867
868 claims
869 .custom_claims
870 .insert("claim1".to_string(), Value::String("value1".to_string()));
871
872 assert!(and_validator.validate(&claims).is_err());
873 }
874
875 #[test]
876 fn test_or_validator() {
877 let validator1 = Box::new(ExistenceValidator::new("claim1"));
878 let validator2 = Box::new(ExistenceValidator::new("claim2"));
879
880 let or_validator = OrValidator::new(vec![validator1, validator2]);
881
882 let mut claims = CognitoJwtClaims {
884 sub: "user123".to_string(),
885 iss: "https://example.com".to_string(),
886 client_id: "client123".to_string(),
887 origin_jti: None,
888 event_id: None,
889 token_use: "id".to_string(),
890 scope: None,
891 auth_time: 0,
892 exp: 0,
893 iat: 0,
894 jti: "jti123".to_string(),
895 username: None,
896 custom_claims: HashMap::new(),
897 };
898
899 claims
900 .custom_claims
901 .insert("claim1".to_string(), Value::String("value1".to_string()));
902 claims
903 .custom_claims
904 .insert("claim2".to_string(), Value::String("value2".to_string()));
905
906 assert!(or_validator.validate(&claims).is_ok());
907
908 let mut claims = CognitoJwtClaims {
910 sub: "user123".to_string(),
911 iss: "https://example.com".to_string(),
912 client_id: "client123".to_string(),
913 origin_jti: None,
914 event_id: None,
915 token_use: "id".to_string(),
916 scope: None,
917 auth_time: 0,
918 exp: 0,
919 iat: 0,
920 jti: "jti123".to_string(),
921 username: None,
922 custom_claims: HashMap::new(),
923 };
924
925 claims
926 .custom_claims
927 .insert("claim2".to_string(), Value::String("value2".to_string()));
928
929 assert!(or_validator.validate(&claims).is_ok());
930
931 let claims = CognitoJwtClaims {
933 sub: "user123".to_string(),
934 iss: "https://example.com".to_string(),
935 client_id: "client123".to_string(),
936 origin_jti: None,
937 event_id: None,
938 token_use: "id".to_string(),
939 scope: None,
940 auth_time: 0,
941 exp: 0,
942 iat: 0,
943 jti: "jti123".to_string(),
944 username: None,
945 custom_claims: HashMap::new(),
946 };
947
948 assert!(or_validator.validate(&claims).is_err());
949 }
950}