jwks_client_update/
lib.rs1pub mod error;
2pub mod jwt;
3pub mod keyset;
4
5#[cfg(test)]
16mod tests {
17 use std::time::{Duration, SystemTime};
18
19 use serde::{Deserialize, Serialize};
20
21 use crate::error::{Error, Type};
22 use crate::keyset::{JwtKey, KeyStore};
23
24 const TIME_NBF: u64 = 300;
26 const TIME_SAFE: u64 = 400;
27 const TIME_EXP: u64 = 500;
28
29 fn time_nbf() -> SystemTime {
30 SystemTime::UNIX_EPOCH + Duration::new(TIME_NBF - 1, 0)
31 }
32
33 fn time_safe() -> SystemTime {
34 SystemTime::UNIX_EPOCH + Duration::new(TIME_SAFE, 0)
35 }
36
37 fn time_exp() -> SystemTime {
38 SystemTime::UNIX_EPOCH + Duration::new(TIME_EXP + 1, 0)
39 }
40
41 pub const KEY_URL: &str = "https://raw.githubusercontent.com/jfbilodeau/jwks-client/0.1.8/test/test-jwks.json";
42 pub const E: &str = "AQAB";
43 pub const N: &str = "t5N44H1mpb5Wlx_0e7CdoKTY8xt-3yMby8BgNdagVNkeCkZ4pRbmQXRWNC7qn__Zaxx9dnzHbzGCul5W0RLfd3oB3PESwsrQh-oiXVEPTYhvUPQkX0vBfCXJtg_zY2mY1DxKOIiXnZ8PaK_7Sx0aMmvR__0Yy2a5dIAWCmjPsxn-PcGZOkVUm-D5bH1-ZStcA_68r4ZSPix7Szhgl1RoHb9Q6JSekyZqM0Qfwhgb7srZVXC_9_m5PEx9wMVNYpYJBrXhD5IQm9RzE9oJS8T-Ai-4_5mNTNXI8f1rrYgffWS4wf9cvsEihrvEg9867B2f98L7ux9Llle7jsHCtwgV1w";
44 pub const N_INVALID: &str = "xt5N44H1mpb5Wlx_0e7CdoKTY8xt-3yMby8BgNdagVNkeCkZ4pRbmQXRWNC7qn__Zaxx9dnzHbzGCul5W0RLfd3oB3PESwsrQh-oiXVEPTYhvUPQkX0vBfCXJtg_zY2mY1DxKOIiXnZ8PaK_7Sx0aMmvR__0Yy2a5dIAWCmjPsxn-PcGZOkVUm-D5bH1-ZStcA_68r4ZSPix7Szhgl1RoHb9Q6JSekyZqM0Qfwhgb7srZVXC_9_m5PEx9wMVNYpYJBrXhD5IQm9RzE9oJS8T-Ai-4_5mNTNXI8f1rrYgffWS4wf9cvsEihrvEg9867B2f98L7ux9Llle7jsHCtwgV1w==";
45 pub const TOKEN: &str = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ.eyJuYW1lIjoiQWRhIExvdmVsYWNlIiwiaXNzIjoiaHR0cHM6Ly9jaHJvbm9nZWFycy5jb20vdGVzdCIsImF1ZCI6InRlc3QiLCJhdXRoX3RpbWUiOjEwMCwidXNlcl9pZCI6InVpZDEyMyIsInN1YiI6InNidTEyMyIsImlhdCI6MjAwLCJleHAiOjUwMCwibmJmIjozMDAsImVtYWlsIjoiYWxvdmVsYWNlQGNocm9ub2dlYXJzLmNvbSJ9.eTQnwXrri_uY55fS4IygseBzzbosDM1hP153EZXzNlLH5s29kdlGt2mL_KIjYmQa8hmptt9RwKJHBtw6l4KFHvIcuif86Ix-iI2fCpqNnKyGZfgERV51NXk1THkgWj0GQB6X5cvOoFIdHa9XvgPl_rVmzXSUYDgkhd2t01FOjQeeT6OL2d9KdlQHJqAsvvKVc3wnaYYoSqv2z0IluvK93Tk1dUBU2yWXH34nX3GAVGvIoFoNRiiFfZwFlnz78G0b2fQV7B5g5F8XlNRdD1xmVZXU8X2-xh9LqRpnEakdhecciFHg0u6AyC4c00rlo_HBb69wlXajQ3R4y26Kpxn7HA";
46 pub const TOKEN_INV_CERT: &str = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ.eyJuYW1lIjoiQWRhIExvdmVsYWNlIiwiaXNzIjoiaHR0cHM6Ly9jaHJvbm9nZWFycy5jb20vdGVzdCIsImF1ZCI6InRlc3QiLCJhdXRoX3RpbWUiOjEwMCwidXNlcl9pZCI6InVpZDEyMyIsInN1YiI6InNidTEyMyIsImlhdCI6MjAwLCJleHAiOjUwMCwibmJmIjozMDAsImVtYWlsIjoiYWxvdmVsYWNlQGNocm9ub2dlYXJzLmNvbSJ9.XXXeTQnwXrri_uY55fS4IygseBzzbosDM1hP153EZXzNlLH5s29kdlGt2mL_KIjYmQa8hmptt9RwKJHBtw6l4KFHvIcuif86Ix-iI2fCpqNnKyGZfgERV51NXk1THkgWj0GQB6X5cvOoFIdHa9XvgPl_rVmzXSUYDgkhd2t01FOjQeeT6OL2d9KdlQHJqAsvvKVc3wnaYYoSqv2z0IluvK93Tk1dUBU2yWXH34nX3GAVGvIoFoNRiiFfZwFlnz78G0b2fQV7B5g5F8XlNRdD1xmVZXU8X2-xh9LqRpnEakdhecciFHg0u6AyC4c00rlo_HBb69wlXajQ3R4y26Kpxn7HA";
47
48 #[derive(Debug, Serialize, Deserialize)]
49 pub struct TestPayload {
50 pub iss: String,
51 pub name: String,
52 pub email: String,
53 }
54
55 #[test]
56 fn test_new_with_url() {
57 let key_set = tokio_test::block_on(KeyStore::new_from(KEY_URL.to_owned())).unwrap();
58
59 assert_eq!(KEY_URL, key_set.key_set_url());
60 }
61
62 #[test]
63 fn test_refresh_keys() {
64 let key_set = tokio_test::block_on(KeyStore::new_from(KEY_URL.to_owned())).unwrap();
65
66 assert_eq!(KEY_URL, key_set.key_set_url());
67 assert!(key_set.keys_len() > 0);
68
69 assert!(key_set.key_by_id("1").is_some());
70 assert!(key_set.key_by_id("2").is_none());
71
72 let result = key_set.verify_time(TOKEN, time_safe());
73
74 let jwt = result.unwrap();
75
76 assert_eq!("https://chronogears.com/test", jwt.payload().iss().unwrap());
77 assert_eq!("Ada Lovelace", jwt.payload().get_str("name").unwrap());
78 assert_eq!("alovelace@chronogears.com", jwt.payload().get_str("email").unwrap());
79 }
80
81 #[test]
82 fn test_add_key() {
83 let key = JwtKey::new("1", N, E);
84
85 let mut key_set = KeyStore::new();
86
87 assert_eq!(0usize, key_set.keys_len());
88
89 key_set.add_key(&key);
90
91 assert_eq!(1usize, key_set.keys_len());
92
93 let result = key_set.key_by_id("1");
94
95 assert!(result.is_some());
96
97 let key = result.unwrap();
98
99 assert_eq!(N, key.n);
100 assert_eq!(E, key.e);
101 assert_eq!("1", key.kid);
102 }
103
104 #[test]
105 fn test_get_key() {
106 let key = JwtKey::new("1", N, E);
107
108 let mut key_set = KeyStore::new();
109
110 assert_eq!(0usize, key_set.keys_len());
111
112 key_set.add_key(&key);
113
114 assert_eq!(1usize, key_set.keys_len());
115
116 let result = key_set.key_by_id("1");
117
118 assert!(result.is_some());
119
120 let result = key_set.key_by_id("2");
121
122 assert!(result.is_none());
123 }
124
125 #[test]
126 fn test_decode_custom_payload() {
127 let key = JwtKey::new("1", N, E);
128
129 let mut key_set = KeyStore::new();
130
131 key_set.add_key(&key);
132
133 let result = key_set.decode(TOKEN);
134
135 assert!(result.is_ok());
136
137 let jwt = result.unwrap();
138
139 let payload = jwt.payload().into::<TestPayload>().unwrap();
140
141 assert_eq!("https://chronogears.com/test", payload.iss);
142 assert_eq!("Ada Lovelace", payload.name);
143 assert_eq!("alovelace@chronogears.com", payload.email);
144 }
145
146 #[test]
147 fn test_decode_json_payload() {
148 let key = JwtKey::new("1", N, E);
149
150 let mut key_set = KeyStore::new();
151
152 key_set.add_key(&key);
153
154 let result = key_set.decode(TOKEN);
155
156 assert!(result.is_ok());
157
158 let jwt = result.unwrap();
159
160 assert_eq!("https://chronogears.com/test", jwt.payload().iss().unwrap());
161 assert_eq!("Ada Lovelace", jwt.payload().get_str("name").unwrap());
162 assert_eq!("alovelace@chronogears.com", jwt.payload().get_str("email").unwrap());
163 }
164
165 #[test]
166 fn test_verify() {
167 let key = JwtKey::new("1", N, E);
168
169 let mut key_set = KeyStore::new();
170
171 key_set.add_key(&key);
172
173 let result = key_set.verify_time(TOKEN, time_safe());
174
175 assert!(result.is_ok());
176
177 let jwt = result.unwrap();
178
179 assert_eq!("https://chronogears.com/test", jwt.payload().iss().unwrap());
180 assert_eq!("Ada Lovelace", jwt.payload().get_str("name").unwrap());
181 assert_eq!("alovelace@chronogears.com", jwt.payload().get_str("email").unwrap());
182
183 let result = key_set.verify_time(TOKEN, time_nbf());
184
185 match result {
186 Ok(_) => panic!(),
187 Err(Error { msg: _, typ }) => {
188 assert_eq!(Type::Early, typ);
189 }
190 }
191
192 let result = key_set.verify_time(TOKEN, time_exp());
193
194 match result {
195 Ok(_) => panic!(),
196 Err(Error { msg: _, typ }) => {
197 assert_eq!(Type::Expired, typ);
198 }
199 }
200 }
201
202 #[test]
203 fn test_verify_invalid_certificate() {
204 let key = JwtKey::new("1", N_INVALID, E);
205
206 let mut key_set = KeyStore::new();
207
208 key_set.add_key(&key);
209
210 let result = key_set.verify(TOKEN);
211
212 assert!(result.is_err());
213 }
214
215 #[test]
216 fn test_verify_invalid_signature() {
217 let key = JwtKey::new("1", N, E);
218
219 let mut key_set = KeyStore::new();
220
221 key_set.add_key(&key);
222
223 let result = key_set.verify(TOKEN_INV_CERT);
224
225 assert!(result.is_err());
226
227 let result = key_set.decode(TOKEN_INV_CERT);
229
230 let jwt = result.unwrap();
231
232 assert_eq!("https://chronogears.com/test", jwt.payload().iss().unwrap());
233 assert_eq!("Ada Lovelace", jwt.payload().get_str("name").unwrap());
234 assert_eq!("alovelace@chronogears.com", jwt.payload().get_str("email").unwrap());
235 }
236
237 #[test]
238 fn test_expired() {
239 let key_set = KeyStore::new();
240
241 let jwk = key_set.decode(TOKEN).unwrap();
242
243 let time = SystemTime::UNIX_EPOCH + Duration::new(TIME_EXP + 1, 0);
244
245 assert!(jwk.expired_time(time).unwrap());
246 }
247
248 #[test]
249 fn test_not_expired() {
250 let key_set = KeyStore::new();
251
252 let jwk = key_set.decode(TOKEN).unwrap();
253
254 let time = SystemTime::UNIX_EPOCH + Duration::new(TIME_EXP - 1, 0);
255
256 assert!(!jwk.expired_time(time).unwrap());
257 }
258
259 #[test]
260 fn test_nbf() {
261 let key_set = KeyStore::new();
262
263 let jwk = key_set.decode(TOKEN).unwrap();
264
265 let time = SystemTime::UNIX_EPOCH + Duration::new(TIME_NBF - 1, 0);
266
267 assert!(jwk.early_time(time).unwrap());
268 }
269
270 #[test]
271 fn test_not_nbf() {
272 let key_set = KeyStore::new();
273
274 let jwk = key_set.decode(TOKEN).unwrap();
275
276 let time = SystemTime::UNIX_EPOCH + Duration::new(TIME_NBF + 1, 0);
277
278 assert!(!jwk.early_time(time).unwrap());
279 }
280
281 #[test]
282 fn test_valid_exp() {
283 let key_set = KeyStore::new();
284
285 let jwk = key_set.decode(TOKEN).unwrap();
286
287 let time = SystemTime::UNIX_EPOCH + Duration::new(TIME_NBF - 1, 0);
288
289 assert!(jwk.early_time(time).unwrap());
290 }
291
292 #[test]
293 fn test_keys_expired() {
294 let key_store = KeyStore::new();
295
296 assert_eq!(None, key_store.last_load_time());
297 assert_eq!(None, key_store.keys_expired());
298
299 let key_store = tokio_test::block_on(KeyStore::new_from(KEY_URL.to_owned())).unwrap();
300
301 assert!(key_store.last_load_time().is_some());
302 assert!(key_store.keys_expired().is_some());
303 assert_eq!(false, key_store.keys_expired().unwrap());
304 }
305
306 #[test]
307 fn test_should_refresh() {
308 let mut key_store = KeyStore::new();
309
310 assert_eq!(0.5, key_store.refresh_interval());
311 assert_eq!(None, key_store.expire_time());
312 assert_eq!(None, key_store.keys_expired());
313 assert_eq!(None, key_store.last_load_time());
314 assert_eq!(None, key_store.should_refresh());
315
316 key_store.set_refresh_interval(0.75);
317 assert_eq!(0.75, key_store.refresh_interval());
318
319 key_store.set_refresh_interval(0.5);
320
321 tokio_test::block_on(key_store.load_keys_from(KEY_URL.to_owned())).unwrap();
322
323 assert_eq!(0.5, key_store.refresh_interval());
324 assert_ne!(None, key_store.expire_time());
325 assert_ne!(None, key_store.keys_expired());
326 assert_ne!(None, key_store.last_load_time());
327 assert_eq!(Some(false), key_store.should_refresh());
328
329 let key_duration = key_store.expire_time().unwrap().duration_since(key_store.load_time().unwrap());
330 let key_duration = key_duration.unwrap();
331
332 let refresh_time = key_store.load_time().unwrap() + (key_duration / 2);
333
334 assert_eq!(Some(refresh_time), key_store.refresh_time());
335
336 let just_before = refresh_time - Duration::new(1, 0);
338 assert_eq!(Some(false), key_store.should_refresh_time(just_before));
339
340 let just_after = refresh_time + Duration::new(1, 0);
341 assert_eq!(Some(true), key_store.should_refresh_time(just_after));
342 }
343}