claim169_core/model/
cwt_meta.rs

1use serde::{Deserialize, Serialize};
2
3/// CWT (CBOR Web Token) standard claims metadata
4///
5/// These are standard CWT claims defined outside of Claim 169,
6/// extracted from the CWT payload for convenience.
7#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "camelCase")]
9pub struct CwtMeta {
10    /// Issuer (CWT claim 1) - Identifier of the entity issuing the credential
11    #[serde(skip_serializing_if = "Option::is_none")]
12    pub issuer: Option<String>,
13
14    /// Subject (CWT claim 2) - Identifier of the subject of the credential
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub subject: Option<String>,
17
18    /// Expiration Time (CWT claim 4) - Unix timestamp when credential expires
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub expires_at: Option<i64>,
21
22    /// Not Before (CWT claim 5) - Unix timestamp before which credential is invalid
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub not_before: Option<i64>,
25
26    /// Issued At (CWT claim 6) - Unix timestamp when credential was issued
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub issued_at: Option<i64>,
29}
30
31impl CwtMeta {
32    pub fn new() -> Self {
33        Self::default()
34    }
35
36    pub fn with_issuer(mut self, issuer: impl Into<String>) -> Self {
37        self.issuer = Some(issuer.into());
38        self
39    }
40
41    pub fn with_subject(mut self, subject: impl Into<String>) -> Self {
42        self.subject = Some(subject.into());
43        self
44    }
45
46    pub fn with_expires_at(mut self, expires_at: i64) -> Self {
47        self.expires_at = Some(expires_at);
48        self
49    }
50
51    pub fn with_not_before(mut self, not_before: i64) -> Self {
52        self.not_before = Some(not_before);
53        self
54    }
55
56    pub fn with_issued_at(mut self, issued_at: i64) -> Self {
57        self.issued_at = Some(issued_at);
58        self
59    }
60
61    /// Check if the credential is currently valid based on timestamps
62    pub fn is_time_valid(&self, current_time: i64) -> bool {
63        // Check expiration
64        if let Some(exp) = self.expires_at {
65            if current_time > exp {
66                return false;
67            }
68        }
69
70        // Check not-before
71        if let Some(nbf) = self.not_before {
72            if current_time < nbf {
73                return false;
74            }
75        }
76
77        true
78    }
79
80    /// Check if the credential is expired
81    pub fn is_expired(&self, current_time: i64) -> bool {
82        self.expires_at
83            .map(|exp| current_time > exp)
84            .unwrap_or(false)
85    }
86
87    /// Check if the credential is not yet valid
88    pub fn is_not_yet_valid(&self, current_time: i64) -> bool {
89        self.not_before
90            .map(|nbf| current_time < nbf)
91            .unwrap_or(false)
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn test_cwt_meta_builder() {
101        let meta = CwtMeta::new()
102            .with_issuer("https://mosip.io")
103            .with_expires_at(1787912445)
104            .with_issued_at(1756376445);
105
106        assert_eq!(meta.issuer, Some("https://mosip.io".to_string()));
107        assert_eq!(meta.expires_at, Some(1787912445));
108        assert_eq!(meta.issued_at, Some(1756376445));
109    }
110
111    #[test]
112    fn test_time_validity() {
113        let meta = CwtMeta::new().with_not_before(1000).with_expires_at(2000);
114
115        assert!(!meta.is_time_valid(500)); // too early
116        assert!(meta.is_time_valid(1500)); // valid
117        assert!(!meta.is_time_valid(2500)); // expired
118    }
119
120    #[test]
121    fn test_expired_check() {
122        let meta = CwtMeta::new().with_expires_at(1000);
123
124        assert!(!meta.is_expired(500));
125        assert!(meta.is_expired(1500));
126    }
127
128    #[test]
129    fn test_json_serialization() {
130        let meta = CwtMeta::new().with_issuer("test").with_expires_at(12345);
131
132        let json = serde_json::to_string(&meta).unwrap();
133        assert!(json.contains("\"issuer\":\"test\""));
134        assert!(json.contains("\"expiresAt\":12345"));
135
136        // Fields that are None should not appear
137        assert!(!json.contains("subject"));
138    }
139}