did_crypto/
jwt.rs

1use crate::{
2    algorithms::Algorithm,
3    crypto::{SignFromKey, VerifyFromKey},
4    errors::Error,
5    log,
6    signer::sign,
7    verifier::verify,
8};
9use base64::{engine::general_purpose::STANDARD, Engine};
10use chrono::{DateTime, Utc};
11use serde::{de::DeserializeOwned, Deserialize, Serialize};
12use serde_json::Value;
13
14trait Base64Encode
15where
16    Self: Serialize + ToString,
17{
18    fn to_base64_encoded(&self) -> String {
19        STANDARD.encode(self.to_string())
20    }
21}
22
23trait FromBase64Encoded
24where
25    Self: DeserializeOwned,
26{
27    fn from_base64_encoded(base64_encoded_str: &str) -> Result<Self, Error> {
28        let base64_decoded = match STANDARD.decode(base64_encoded_str) {
29            Ok(val) => match String::from_utf8(val) {
30                Ok(val) => val,
31                Err(error) => {
32                    log::error(error.to_string().as_str());
33                    return Err(Error::JWT_UTF8_ERROR);
34                }
35            },
36            Err(error) => {
37                log::error(error.to_string().as_str());
38                return Err(Error::DECODING_ERROR);
39            }
40        };
41
42        match serde_json::from_str(base64_decoded.as_str()) {
43            Ok(val) => Ok(val),
44            Err(error) => {
45                log::error(error.to_string().as_str());
46                return Err(Error::JWT_HEADER_DESERIALIZING_ERROR);
47            }
48        }
49    }
50}
51
52#[derive(Serialize, Deserialize)]
53pub struct Header {
54    pub typ: String,
55    pub alg: Algorithm,
56    pub kid: String,
57}
58
59impl Header {
60    pub fn new(kid: String, alg: Algorithm) -> Self {
61        Header {
62            kid,
63            alg,
64            typ: String::from("JWT"),
65        }
66    }
67}
68
69impl ToString for Header {
70    fn to_string(&self) -> String {
71        match serde_json::to_string(self) {
72            Ok(val) => val,
73            Err(error) => {
74                log::error(error.to_string().as_str());
75                panic!()
76            }
77        }
78    }
79}
80
81impl FromBase64Encoded for Header {}
82impl Base64Encode for Header {}
83
84#[derive(Serialize, Deserialize)]
85pub struct Payload(pub Value);
86
87impl ToString for Payload {
88    fn to_string(&self) -> String {
89        match serde_json::to_string(self) {
90            Ok(val) => val,
91            Err(error) => {
92                log::error(error.to_string().as_str());
93                panic!()
94            }
95        }
96    }
97}
98
99impl FromBase64Encoded for Payload {}
100impl Base64Encode for Payload {}
101
102#[derive(Serialize, Deserialize, Clone)]
103pub struct Signature(String);
104
105impl ToString for Signature {
106    fn to_string(&self) -> String {
107        self.0.clone()
108    }
109}
110
111#[derive(Serialize, Deserialize)]
112pub struct JWT {
113    pub header: Header,
114    pub payload: Payload,
115    pub signature: Option<Signature>,
116}
117
118impl JWT {
119    pub fn to_token(&self) -> Result<String, Error> {
120        if self.signature.is_none() {
121            return Err(Error::JWT_TOKEN_NOT_SIGNED);
122        } else {
123            let sig = self.signature.as_ref().unwrap();
124            Ok(format!(
125                "{}.{}.{}",
126                self.header.to_base64_encoded(),
127                self.payload.to_base64_encoded(),
128                sig.to_string()
129            ))
130        }
131    }
132
133    pub fn sign(&mut self, private_key: impl SignFromKey) -> Result<(), Error> {
134        let content = format!(
135            "{}.{}",
136            self.header.to_base64_encoded(),
137            self.payload.to_base64_encoded()
138        );
139
140        match sign(content.clone(), private_key, self.header.alg) {
141            Ok(val) => {
142                self.signature = Some(Signature(val));
143                Ok(())
144            }
145            Err(error) => Err(error),
146        }
147    }
148
149    pub fn from_token(token: &str) -> Result<Self, Error> {
150        let token_content: Vec<&str> = token.split(".").collect();
151
152        let header = match Header::from_base64_encoded(token_content[0]) {
153            Ok(val) => val,
154            Err(error) => return Err(error),
155        };
156
157        let payload = match Payload::from_base64_encoded(token_content[1]) {
158            Ok(val) => val,
159            Err(error) => return Err(error),
160        };
161
162        let signature: Signature = Signature(String::from(token_content[2]));
163
164        Ok(JWT {
165            header,
166            payload,
167            signature: Some(signature),
168        })
169    }
170
171    fn check_if_expired(timestamp_secs: i64) -> Result<bool, Error> {
172        let now = Utc::now();
173        let exp_time = match DateTime::from_timestamp_millis(timestamp_secs * 1000) {
174            Some(val) => val,
175            None => {
176                return Err(Error::FAILED_TO_CONVERT_TIMESTAMP_TO_DATETTIME);
177            }
178        };
179
180        Ok(now < exp_time)
181    }
182
183    pub fn validate(&self, public_key: impl VerifyFromKey) -> Result<bool, Error> {
184        let algorithm = self.header.alg;
185
186        let signature = match &self.signature {
187            Some(val) => val.clone(),
188            None => return Err(Error::JWT_NO_SIGNATURE_FOUND),
189        };
190
191        let verified = match verify(
192            format!(
193                "{}.{}",
194                self.header.to_base64_encoded(),
195                self.payload.to_base64_encoded()
196            ),
197            signature.0,
198            public_key,
199            algorithm,
200        ) {
201            Ok(val) => val,
202            Err(error) => return Err(error),
203        };
204
205        if !verified {
206            return Ok(false);
207        }
208
209        let exp = match self.payload.0.get("exp") {
210            Some(val) => match val.as_i64() {
211                Some(val) => val,
212                None => return Err(Error::JWT_PAYLOAD_FIELD_EXP_IDENTIFICATION_ERROR),
213            },
214            None => return Err(Error::JWT_PAYLOAD_MISSING_FIELD_EXP),
215        };
216
217        Self::check_if_expired(exp)
218    }
219
220    pub fn validate_token(
221        token_str: &str,
222        public_key: impl VerifyFromKey,
223    ) -> Result<(Self, bool), Error> {
224        let token = match Self::from_token(token_str) {
225            Ok(val) => val,
226            Err(error) => return Err(error),
227        };
228
229        let verified = match token.validate(public_key) {
230            Ok(val) => val,
231            Err(error) => return Err(error),
232        };
233
234        Ok((token, verified))
235    }
236}