rusttls_jwt_authorizer/
claims.rs

1use serde::{Deserialize, Serialize};
2
3/// The number of seconds from 1970-01-01T00:00:00Z UTC until the specified UTC date/time ignoring leap seconds.
4/// (<https://www.rfc-editor.org/rfc/rfc7519#section-2>)
5#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Debug)]
6pub struct NumericDate(pub i64);
7
8/// accesses the underlying value
9impl From<NumericDate> for i64 {
10    fn from(t: NumericDate) -> Self {
11        t.0
12    }
13}
14
15#[cfg(feature = "chrono")]
16use chrono::{DateTime, TimeZone, Utc};
17
18#[cfg(feature = "chrono")]
19impl From<NumericDate> for DateTime<Utc> {
20    fn from(t: NumericDate) -> Self {
21        Utc.timestamp_opt(t.0, 0).unwrap()
22    }
23}
24
25#[cfg(feature = "time")]
26use time::OffsetDateTime;
27
28#[cfg(feature = "time")]
29impl From<NumericDate> for OffsetDateTime {
30    fn from(t: NumericDate) -> Self {
31        OffsetDateTime::from_unix_timestamp(t.0).unwrap()
32    }
33}
34
35#[derive(PartialEq, Debug, Clone, Deserialize, Serialize)]
36#[serde(untagged)]
37pub enum OneOrArray<T> {
38    One(T),
39    Array(Vec<T>),
40}
41
42impl<T> OneOrArray<T> {
43    pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = &'a T> + 'a> {
44        match self {
45            OneOrArray::One(v) => Box::new(std::iter::once(v)),
46            OneOrArray::Array(vector) => Box::new(vector.iter()),
47        }
48    }
49}
50
51#[derive(PartialEq, Debug, Clone)]
52pub struct StringList(Vec<String>);
53
54/// Claims mentioned in the JWT specifications.
55///
56/// <https://www.rfc-editor.org/rfc/rfc7519#section-4.1>
57#[derive(Deserialize, Serialize, Clone, Debug)]
58pub struct RegisteredClaims {
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub iss: Option<String>,
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub sub: Option<String>,
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub aud: Option<OneOrArray<String>>,
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub exp: Option<NumericDate>,
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub nbf: Option<NumericDate>,
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub iat: Option<NumericDate>,
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub jti: Option<String>,
73}
74
75#[cfg(test)]
76mod tests {
77
78    use chrono::{DateTime, TimeZone, Utc};
79    use serde::Deserialize;
80    use serde_json::json;
81
82    use crate::claims::{NumericDate, OneOrArray, RegisteredClaims};
83
84    #[derive(Deserialize)]
85    struct TestStruct {
86        v: OneOrArray<String>,
87    }
88
89    #[test]
90    fn one_or_array_iter() {
91        let o = OneOrArray::One("aaa".to_owned());
92        let mut i = o.iter();
93        assert_eq!(Some(&"aaa".to_owned()), i.next());
94
95        let a = OneOrArray::Array(vec!["aaa".to_owned()]);
96        let mut i = a.iter();
97        assert_eq!(Some(&"aaa".to_owned()), i.next());
98
99        let a = OneOrArray::Array(vec!["aaa".to_owned(), "bbb".to_owned()]);
100        let mut i = a.iter();
101        assert_eq!(Some(&"aaa".to_owned()), i.next());
102        assert_eq!(Some(&"bbb".to_owned()), i.next());
103        assert_eq!(None, i.next());
104    }
105
106    #[test]
107    fn rfc_claims_aud() {
108        let a: TestStruct = serde_json::from_str(r#"{"v":"a"}"#).unwrap();
109        assert_eq!(a.v, OneOrArray::One("a".to_owned()));
110
111        let a: TestStruct = serde_json::from_str(r#"{"v":["a", "b"]}"#).unwrap();
112        assert_eq!(a.v, OneOrArray::Array(vec!["a".to_owned(), "b".to_owned()]));
113    }
114
115    #[test]
116    fn from_numeric_date() {
117        let exp: i64 = NumericDate(1516239022).into();
118        assert_eq!(exp, 1516239022);
119    }
120
121    #[test]
122    fn chrono_from_numeric_date() {
123        let exp: DateTime<Utc> = NumericDate(1516239022).into();
124        assert_eq!(exp, Utc.timestamp_opt(1516239022, 0).unwrap());
125        assert_eq!(exp, DateTime::parse_from_rfc3339("2018-01-18T01:30:22.000Z").unwrap());
126    }
127
128    #[cfg(feature = "time")]
129    #[test]
130    fn time_from_numeric_date() {
131        use time::macros::datetime;
132        use time::OffsetDateTime;
133
134        let exp: OffsetDateTime = NumericDate(1516239022).into();
135        assert_eq!(exp, datetime!(2018-01-18 01:30:22 UTC));
136    }
137
138    #[test]
139    fn rfc_claims() {
140        let jwt_json = json!({
141                    "iss": "http://localhost:3001",
142                    "aud": ["aud1", "aud2"],
143                    "sub": "bob",
144                    "exp": 1516240122,
145                    "iat": 1516239022,
146                }
147        );
148
149        let claims: RegisteredClaims = serde_json::from_value(jwt_json).expect("Failed RfcClaims deserialisation");
150        assert_eq!(claims.iss.unwrap(), "http://localhost:3001");
151        assert_eq!(
152            claims.aud.unwrap(),
153            OneOrArray::Array(vec!["aud1".to_owned(), "aud2".to_owned()])
154        );
155        assert_eq!(claims.exp.unwrap(), NumericDate(1516240122));
156        assert_eq!(claims.nbf, None);
157
158        let dt: DateTime<Utc> = claims.iat.unwrap().into();
159        assert_eq!(dt, Utc.timestamp_opt(1516239022, 0).unwrap());
160    }
161
162    #[test]
163    fn rfc_claims_serde() {
164        let claims_str = r#"{
165                    "iss": "http://localhost:3001",
166                    "sub": "bob",
167                    "aud": ["aud1", "aud2"],
168                    "exp": 1516240122,
169                    "iat": 1516239022
170                }"#;
171
172        let claims: RegisteredClaims = serde_json::from_str(claims_str).expect("Failed RfcClaims deserialisation");
173
174        let jwt_serd = serde_json::to_string(&claims).unwrap();
175        let mut trimed_claims = claims_str.to_owned();
176        trimed_claims.retain(|c| !c.is_whitespace());
177        assert_eq!(trimed_claims, jwt_serd);
178    }
179}