1use std::collections::HashMap;
5use std::sync::Arc;
6use std::time::{Duration, SystemTime, UNIX_EPOCH};
7
8use base64::Engine;
9use base64::engine::general_purpose::STANDARD as STANDARD_BASE64;
10use jsonwebtoken::jwk::KeyAlgorithm;
11pub use jsonwebtoken::{Algorithm, Validation};
12use jsonwebtoken::{
13 DecodingKey, EncodingKey, Header as JwtHeader, TokenData, decode, decode_header, encode,
14 jwk::Jwk,
15};
16
17use parking_lot::RwLock;
18use schemars::JsonSchema;
19use serde::de::DeserializeOwned;
20use serde::{Deserialize, Serialize};
21
22use crate::errors::AuthError;
23use crate::file_watcher::FileWatcher;
24use crate::metadata::MetadataMap;
25use crate::resolver::KeyResolver;
26use crate::traits::{Signer, StandardClaims, TokenProvider, Verifier};
27
28#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, JsonSchema)]
29#[serde(rename_all = "lowercase")]
30pub enum KeyFormat {
31 Pem,
32 Jwk,
33 Jwks,
34}
35
36#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, JsonSchema)]
38#[serde(rename_all = "lowercase")]
39pub enum KeyData {
40 Data(String),
42 File(String),
44}
45
46#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, JsonSchema)]
48pub struct Key {
49 #[schemars(with = "AlgorithmRepr")]
51 pub algorithm: Algorithm,
52
53 pub format: KeyFormat,
55
56 pub key: KeyData,
58}
59
60#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, JsonSchema)]
63pub enum AlgorithmRepr {
64 HS256,
65 HS384,
66 HS512,
67 ES256,
68 ES384,
69 RS256,
70 RS384,
71 RS512,
72 PS256,
73 PS384,
74 PS512,
75 EdDSA,
76}
77
78fn key_alg_to_algorithm(key: &KeyAlgorithm) -> Result<Algorithm, AuthError> {
79 match key {
80 KeyAlgorithm::ES256 => Ok(Algorithm::ES256),
81 KeyAlgorithm::ES384 => Ok(Algorithm::ES384),
82 KeyAlgorithm::HS256 => Ok(Algorithm::HS256),
83 KeyAlgorithm::HS384 => Ok(Algorithm::HS384),
84 KeyAlgorithm::HS512 => Ok(Algorithm::HS512),
85 KeyAlgorithm::RS256 => Ok(Algorithm::RS256),
86 KeyAlgorithm::RS384 => Ok(Algorithm::RS384),
87 KeyAlgorithm::RS512 => Ok(Algorithm::RS512),
88 KeyAlgorithm::PS256 => Ok(Algorithm::PS256),
89 KeyAlgorithm::PS384 => Ok(Algorithm::PS384),
90 KeyAlgorithm::PS512 => Ok(Algorithm::PS512),
91 KeyAlgorithm::EdDSA => Ok(Algorithm::EdDSA),
92 _ => Err(AuthError::JwtUnsupportedKeyAlgorithm(*key)),
93 }
94}
95
96pub fn algorithm_from_jwk(jwk: &str) -> Result<Algorithm, AuthError> {
97 let jwk: Jwk = serde_json::from_str(jwk)?;
98
99 tracing::info!(?jwk, "JWK parsed successfully");
100
101 let alg = jwk
102 .common
103 .key_algorithm
104 .ok_or(AuthError::JwtMissingKeyAlgorithm)?;
105
106 key_alg_to_algorithm(&alg)
107}
108
109#[derive(Debug, Clone)]
111struct TokenCacheEntry {
112 expiry: u64,
114}
115
116#[derive(Debug)]
118struct TokenCache {
119 entries: RwLock<HashMap<String, TokenCacheEntry>>,
121}
122
123impl TokenCache {
124 fn new() -> Self {
126 TokenCache {
127 entries: RwLock::new(HashMap::new()),
128 }
129 }
130
131 fn store(&self, token: impl Into<String>, expiry: u64) {
133 let entry = TokenCacheEntry { expiry };
134 self.entries.write().insert(token.into(), entry);
135 }
136
137 fn get(&self, token: impl Into<String>) -> Option<String> {
139 let now = SystemTime::now()
140 .duration_since(UNIX_EPOCH)
141 .unwrap_or(Duration::from_secs(0))
142 .as_secs();
143
144 let token = token.into();
145 if let Some(entry) = self.entries.read().get(&token)
146 && entry.expiry > now
147 {
148 let parts: Vec<&str> = token.split('.').collect();
150 if parts.len() == 3 {
151 return Some(parts[1].to_string());
152 }
153 }
154
155 None
156 }
157}
158
159pub type SignerJwt = Jwt<S>;
160pub type VerifierJwt = Jwt<V>;
161pub type StaticTokenProvider = Jwt<P>;
162
163#[derive(Debug, Serialize, Deserialize, Clone)]
164struct ExpClaim {
165 exp: u64,
167}
168
169#[derive(Clone)]
171pub struct Jwt<T> {
172 claims: StandardClaims,
173 token_duration: Duration,
174 validation: Validation,
175 encoding_key: Option<Arc<RwLock<EncodingKey>>>,
176 decoding_key: Option<Arc<RwLock<DecodingKey>>>,
177 key_resolver: Option<Arc<KeyResolver>>,
178 token_cache: std::sync::Arc<TokenCache>,
179 watchers: Arc<Vec<FileWatcher>>,
180
181 static_token: Option<Arc<RwLock<String>>>,
183
184 signature_keys: (Vec<u8>, Vec<u8>),
187
188 _phantom: std::marker::PhantomData<T>,
189}
190
191impl<T> Jwt<T> {
192 pub fn new(
204 claims: StandardClaims,
205 token_duration: Duration,
206 validation: Validation,
207 ) -> Result<Self, AuthError> {
208 Ok(Self {
209 claims,
210 token_duration,
211 validation,
212 encoding_key: None,
213 decoding_key: None,
214 key_resolver: None,
215 watchers: Arc::new(Vec::new()),
216 static_token: None,
217 token_cache: std::sync::Arc::new(TokenCache::new()),
218 signature_keys: (vec![], vec![]),
219 _phantom: std::marker::PhantomData,
220 })
221 }
222
223 pub fn with_watcher(mut self, w: FileWatcher) -> Self {
224 let watchers =
226 Arc::get_mut(&mut self.watchers).expect("Failed to get mutable reference to watchers");
227 watchers.push(w);
228
229 Self { ..self }
230 }
231
232 pub fn with_encoding_key(self, encoding_key: Arc<RwLock<EncodingKey>>) -> SignerJwt {
233 SignerJwt {
234 claims: self.claims,
235 token_duration: self.token_duration,
236 validation: self.validation,
237 encoding_key: Some(encoding_key),
238 decoding_key: None,
239 key_resolver: None,
240 watchers: Arc::new(Vec::new()),
241 static_token: None,
242 token_cache: self.token_cache,
243 signature_keys: self.signature_keys,
244 _phantom: std::marker::PhantomData,
245 }
246 }
247
248 pub fn with_decoding_key(self, decoding_key: Arc<RwLock<DecodingKey>>) -> VerifierJwt {
249 VerifierJwt {
250 claims: self.claims,
251 token_duration: self.token_duration,
252 validation: self.validation,
253 encoding_key: None,
254 decoding_key: Some(decoding_key),
255 key_resolver: None,
256 watchers: Arc::new(Vec::new()),
257 static_token: None,
258 token_cache: self.token_cache,
259 signature_keys: self.signature_keys,
260 _phantom: std::marker::PhantomData,
261 }
262 }
263
264 pub fn with_key_resolver(self, key_resolver: KeyResolver) -> VerifierJwt {
265 VerifierJwt {
266 claims: self.claims,
267 token_duration: self.token_duration,
268 validation: self.validation,
269 encoding_key: None,
270 decoding_key: None,
271 key_resolver: Some(Arc::new(key_resolver)),
272 watchers: Arc::new(Vec::new()),
273 static_token: None,
274 token_cache: self.token_cache,
275 signature_keys: self.signature_keys,
276 _phantom: std::marker::PhantomData,
277 }
278 }
279
280 pub fn with_static_token(self, token: Arc<RwLock<String>>) -> StaticTokenProvider {
281 StaticTokenProvider {
282 claims: self.claims,
283 token_duration: self.token_duration,
284 validation: self.validation,
285 encoding_key: None,
286 decoding_key: None,
287 key_resolver: None,
288 watchers: self.watchers,
289 static_token: Some(token),
290 token_cache: self.token_cache,
291 signature_keys: self.signature_keys,
292 _phantom: std::marker::PhantomData,
293 }
294 }
295}
296
297#[derive(Clone)]
298pub struct S {}
299
300impl<S> Jwt<S> {
301 pub fn create_claims(&self) -> StandardClaims {
303 let now = SystemTime::now()
304 .duration_since(UNIX_EPOCH)
305 .unwrap_or(Duration::from_secs(0))
306 .as_secs();
307
308 let expiration = now + self.token_duration.as_secs();
309
310 StandardClaims {
311 exp: expiration,
312 iat: Some(now),
313 nbf: Some(now),
314 ..self.claims.clone()
315 }
316 }
317
318 pub fn create_claims_with_custom(&self, custom_claims: MetadataMap) -> StandardClaims {
320 let now = SystemTime::now()
321 .duration_since(UNIX_EPOCH)
322 .unwrap_or(Duration::from_secs(0))
323 .as_secs();
324
325 let expiration = now + self.token_duration.as_secs();
326
327 let mut merged_claims = self.claims.custom_claims.clone();
328 merged_claims.extend(custom_claims);
329
330 StandardClaims {
331 exp: expiration,
332 iat: Some(now),
333 nbf: Some(now),
334 custom_claims: merged_claims,
335 ..self.claims.clone()
336 }
337 }
338
339 fn sign_claims<Claims: Serialize>(&self, claims: &Claims) -> Result<String, AuthError> {
340 let encoding_key = self
342 .encoding_key
343 .as_ref()
344 .ok_or(AuthError::JwtMissingPrivateKey)?;
345
346 let header = JwtHeader::new(self.validation.algorithms[0]);
348
349 let token = encode(&header, claims, &encoding_key.read())?;
351 Ok(token)
352 }
353
354 fn sign_internal_claims(&self) -> Result<String, AuthError> {
355 let claims = self.create_claims();
356 self.sign_claims(&claims)
357 }
358
359 fn sign_internal_claims_with_custom(
360 &self,
361 custom_claims: MetadataMap,
362 ) -> Result<String, AuthError> {
363 let claims = self.create_claims_with_custom(custom_claims);
364 self.sign_claims(&claims)
365 }
366}
367
368#[derive(Clone)]
369pub struct P {}
370impl<P> Jwt<P> {
371 fn get_token(&self) -> Result<String, AuthError> {
373 Ok(self
374 .static_token
375 .as_ref()
376 .ok_or(AuthError::GetTokenError)?
377 .read()
378 .clone())
379 }
380}
381
382#[derive(Clone)]
383pub struct V {}
384
385impl<V> Jwt<V> {
386 fn try_verify_claims<Claims: serde::de::DeserializeOwned>(
387 &self,
388 token: impl AsRef<str>,
389 ) -> Result<Claims, AuthError> {
390 let token = token.as_ref();
391
392 if let Some(cached_claims) = self.get_cached_claims::<Claims>(token) {
394 return Ok(cached_claims);
395 }
396
397 let decoding_key = self.decoding_key(token)?;
399
400 self.verify_internal::<Claims>(token, decoding_key)
402 }
403
404 async fn verify_claims<Claims: serde::de::DeserializeOwned>(
405 &self,
406 token: impl AsRef<str>,
407 ) -> Result<Claims, AuthError> {
408 let token = token.as_ref();
409
410 if let Some(cached_claims) = self.get_cached_claims::<Claims>(token) {
412 return Ok(cached_claims);
413 }
414
415 let decoding_key = self.resolve_decoding_key(token).await?;
417
418 self.verify_internal::<Claims>(token, decoding_key)
419 }
420
421 fn verify_internal<Claims: serde::de::DeserializeOwned>(
422 &self,
423 token: impl AsRef<str>,
424 decoding_key: DecodingKey,
425 ) -> Result<Claims, AuthError> {
426 let token = token.as_ref();
427
428 let token_header = decode_header(token)?;
430
431 let validation = self.get_validation(token_header.alg);
433
434 let token_data: TokenData<Claims> = decode(token, &decoding_key, &validation)?;
436
437 let token_exp_data: TokenData<ExpClaim> = jsonwebtoken::dangerous::insecure_decode(token)?;
439
440 self.cache(token.to_owned(), token_exp_data.claims.exp);
442
443 Ok(token_data.claims)
445 }
446
447 fn get_cached_claims<Claims: serde::de::DeserializeOwned>(
449 &self,
450 token: &str,
451 ) -> Option<Claims> {
452 if let Some(_cached_claims) = self.token_cache.get(token) {
454 let token_data: TokenData<Claims> =
456 jsonwebtoken::dangerous::insecure_decode(token).ok()?;
457
458 return Some(token_data.claims);
460 }
461
462 None
463 }
464
465 fn cache(&self, token: impl Into<String>, expiry: u64) {
466 self.token_cache.store(token, expiry);
468 }
469
470 fn get_validation(&self, alg: Algorithm) -> Validation {
471 let mut ret = self.validation.clone();
473 ret.algorithms[0] = alg;
474
475 ret
476 }
477
478 fn unsecure_get_token_data<T: DeserializeOwned>(
479 &self,
480 token: &str,
481 ) -> Result<TokenData<T>, AuthError> {
482 let ret = jsonwebtoken::dangerous::insecure_decode(token)?;
484
485 Ok(ret)
486 }
487
488 fn decoding_key(&self, token: &str) -> Result<DecodingKey, AuthError> {
490 {
492 if let Some(key) = &self.decoding_key {
493 return Ok(key.read().clone());
494 }
495 }
496
497 if let Some(resolver) = &self.key_resolver {
499 let token_data: TokenData<StandardClaims> =
501 jsonwebtoken::dangerous::insecure_decode(token)?;
502
503 let issuer = token_data
504 .claims
505 .iss
506 .as_ref()
507 .ok_or(AuthError::JwtMissingIssuer)?;
508
509 match resolver.get_cached_key(issuer, &token_data.header) {
510 Ok(k) => return Ok(k),
511 Err(_e) => {
512 return Err(AuthError::WouldBlockOn);
514 }
515 }
516 }
517
518 Err(AuthError::JwtNoKeyResolver)
520 }
521
522 async fn resolve_decoding_key(&self, token: &str) -> Result<DecodingKey, AuthError> {
524 {
526 if let Some(key) = &self.decoding_key {
527 return Ok(key.read().clone());
528 }
529 }
530
531 let resolver = self
534 .key_resolver
535 .as_ref()
536 .ok_or(AuthError::JwtNoKeyResolver)?;
537
538 let token_data = self.unsecure_get_token_data::<StandardClaims>(token)?;
540
541 let issuer = token_data
542 .claims
543 .iss
544 .as_ref()
545 .ok_or(AuthError::JwtMissingIssuer)?;
546
547 resolver.resolve_key(issuer, &token_data.header).await
549 }
550}
551
552impl Signer for SignerJwt {
553 fn sign<Claims>(&self, claims: &Claims) -> Result<String, AuthError>
554 where
555 Claims: Serialize,
556 {
557 self.sign_claims(claims)
558 }
559
560 fn sign_standard_claims(&self) -> Result<String, AuthError> {
561 self.sign_internal_claims()
562 }
563}
564
565impl TokenProvider for SignerJwt {
566 async fn initialize(&mut self) -> Result<(), AuthError> {
567 Ok(())
569 }
570
571 fn get_token(&self) -> Result<String, AuthError> {
572 let pub_key_b64 = STANDARD_BASE64.encode(&self.signature_keys.1);
573 let mut claims_map = MetadataMap::new();
574 claims_map.insert("pubkey".to_string(), pub_key_b64);
575 self.sign_internal_claims_with_custom(claims_map)
576 }
577
578 fn get_id(&self) -> Result<String, AuthError> {
579 self.claims
580 .sub
581 .clone()
582 .ok_or(AuthError::TokenInvalidMissingSub)
583 }
584
585 fn get_signature_secret_key(&self) -> Result<Vec<u8>, AuthError> {
586 Ok(self.signature_keys.0.clone())
587 }
588
589 fn get_signature_public_key(&self) -> Result<Vec<u8>, AuthError> {
590 Ok(self.signature_keys.1.clone())
591 }
592
593 async fn set_signature_keys(
594 &mut self,
595 private_key: Vec<u8>,
596 public_key: Vec<u8>,
597 ) -> Result<(), AuthError> {
598 self.signature_keys = (private_key, public_key);
599 Ok(())
600 }
601}
602
603impl TokenProvider for StaticTokenProvider {
604 async fn initialize(&mut self) -> Result<(), AuthError> {
605 Ok(())
607 }
608
609 fn get_token(&self) -> Result<String, AuthError> {
610 self.static_token
611 .as_ref()
612 .ok_or_else(|| AuthError::JwtNoStaticTokenConfigured)
613 .map(|token| token.read().clone())
614 }
615
616 fn get_id(&self) -> Result<String, AuthError> {
617 let token = self.get_token()?;
618 extract_sub_claim_unsafe(&token)
619 }
620
621 async fn set_signature_keys(
622 &mut self,
623 _private_key: Vec<u8>,
624 _public_key: Vec<u8>,
625 ) -> Result<(), AuthError> {
626 Err(AuthError::MlsNotSupported)
627 }
628}
629
630impl Verifier for VerifierJwt {
631 async fn initialize(&mut self) -> Result<(), AuthError> {
632 Ok(()) }
634
635 async fn verify(&self, token: impl AsRef<str> + Send) -> Result<(), AuthError> {
636 self.verify_claims::<StandardClaims>(token)
638 .await
639 .map(|_| ())
640 }
641
642 fn try_verify(&self, token: impl AsRef<str>) -> Result<(), AuthError> {
643 self.try_verify_claims::<StandardClaims>(token).map(|_| ())
645 }
646
647 async fn get_claims<Claims>(&self, token: impl AsRef<str> + Send) -> Result<Claims, AuthError>
648 where
649 Claims: serde::de::DeserializeOwned + Send,
650 {
651 self.verify_claims(token).await
652 }
653
654 fn try_get_claims<Claims>(&self, token: impl AsRef<str>) -> Result<Claims, AuthError>
655 where
656 Claims: serde::de::DeserializeOwned + Send,
657 {
658 self.try_verify_claims(token)
659 }
660}
661
662pub(crate) fn extract_sub_claim_unsafe(token: &str) -> Result<String, AuthError> {
664 let token_data = jsonwebtoken::dangerous::insecure_decode::<serde_json::Value>(token)?;
666
667 token_data
669 .claims
670 .get("sub")
671 .and_then(|v| v.as_str())
672 .map(|s| s.to_string())
673 .ok_or(AuthError::TokenInvalidMissingSub)
674}
675
676#[cfg(test)]
677mod tests {
678 use std::fs::{File, OpenOptions};
679 use std::io::{Seek, SeekFrom, Write};
680 use std::path::Path;
681 use std::path::PathBuf;
682 use std::process;
683 use std::{env, fs, vec};
684
685 use super::*;
686 use jsonwebtoken::{Algorithm, Header};
687 use tokio::time;
688 use tracing_test::traced_test;
689
690 use slim_config::tls::provider::initialize_crypto_provider;
691
692 use crate::builder::JwtBuilder;
693 use slim_testing::utils::setup_test_jwt_resolver;
694
695 fn temp_file_path(prefix: &str) -> PathBuf {
696 let mut path = env::temp_dir();
697 let unique = std::time::SystemTime::now()
698 .duration_since(std::time::UNIX_EPOCH)
699 .expect("time went backwards")
700 .as_nanos();
701 path.push(format!("{prefix}_{}_{}.txt", process::id(), unique));
702 path
703 }
704
705 fn create_file(file_path: &Path, content: &str) -> std::io::Result<()> {
706 let mut file = File::create(file_path)?;
707 file.write_all(content.as_bytes())?;
708 Ok(())
709 }
710
711 fn modify_file(file_path: &Path, new_content: &str) -> std::io::Result<()> {
712 let mut file = OpenOptions::new().write(true).open(file_path)?;
713 file.seek(SeekFrom::Start(0))?;
714 file.write_all(new_content.as_bytes())?;
715 Ok(())
716 }
717
718 fn delete_file(file_path: &Path) {
719 let _ = fs::remove_file(file_path);
720 }
721
722 #[tokio::test]
723 #[traced_test]
724 async fn test_jwt_singer_update_key_from_file() {
725 let file_path = temp_file_path("key_file_signer");
727 let file_name = file_path.to_string_lossy().to_string();
728 let first_key = "test-key";
729 create_file(&file_path, first_key).expect("failed to create file");
730
731 let mut jwt = JwtBuilder::new()
733 .issuer("test-issuer")
734 .audience(&["test-audience"])
735 .subject("test-subject")
736 .private_key(&Key {
737 algorithm: Algorithm::HS512,
738 format: KeyFormat::Pem,
739 key: KeyData::File(file_name.to_string()),
740 })
741 .build()
742 .unwrap();
743
744 let _ = jwt.initialize().await;
745 let claims = jwt.create_claims();
746
747 assert_eq!(claims.iss.unwrap(), "test-issuer");
748 assert_eq!(claims.aud.unwrap(), ["test-audience"]);
749 assert_eq!(claims.sub.unwrap(), "test-subject");
750
751 assert!(jwt.decoding_key.is_none());
752 {
753 let expected = EncodingKey::from_secret(first_key.as_bytes());
754 assert!(jwt.encoding_key.is_some());
755 let k = jwt.encoding_key.as_ref().unwrap();
756
757 #[derive(Debug, Serialize, Deserialize)]
758 struct Claims {
759 sub: String,
760 company: String,
761 }
762
763 let my_claims = Claims {
764 sub: "b@b.com".to_owned(),
765 company: "ACME".to_owned(),
766 };
767
768 let token_1 = encode(&Header::default(), &my_claims, &expected).unwrap();
769 let token_2 = encode(&Header::default(), &my_claims, &k.read()).unwrap();
770 assert_eq!(token_1, token_2);
771 }
772
773 let second_key = "another-test-key";
774 modify_file(&file_path, second_key).expect("failed to create file");
775 time::sleep(Duration::from_millis(100)).await;
776
777 assert!(jwt.decoding_key.is_none());
778 {
779 let expected = EncodingKey::from_secret(second_key.as_bytes());
780 assert!(jwt.encoding_key.is_some());
781 let k = jwt.encoding_key.as_ref().unwrap();
782
783 #[derive(Debug, Serialize, Deserialize)]
784 struct Claims {
785 sub: String,
786 company: String,
787 }
788
789 let my_claims = Claims {
790 sub: "b@b.com".to_owned(),
791 company: "ACME".to_owned(),
792 };
793
794 let token_1 = encode(&Header::default(), &my_claims, &expected).unwrap();
795 let token_2 = encode(&Header::default(), &my_claims, &k.read()).unwrap();
796 assert_eq!(token_1, token_2);
797 }
798
799 delete_file(&file_path);
800 }
801
802 #[test]
803 fn test_jwt_try_verify_would_block_on_missing_cached_key_valid_token() {
804 let verifier = JwtBuilder::new()
806 .issuer("test-issuer")
807 .audience(&["test-audience"])
808 .subject("test-subject")
809 .auto_resolve_keys(true)
810 .build()
811 .unwrap();
812 let token = slim_testing::utils::generate_test_token(
814 "test-issuer",
815 "test-audience",
816 "test-subject",
817 );
818
819 let res = verifier.try_verify(&token);
820 assert!(
821 matches!(res, Err(AuthError::WouldBlockOn)),
822 "Expected WouldBlockOn, got {:?}",
823 res
824 );
825 }
826
827 #[tokio::test]
828 #[traced_test]
829 async fn test_jwt_verifier_update_key_from_file() {
830 let file_path = temp_file_path("key_file_verifier");
832 let file_name = file_path.to_string_lossy().to_string();
833 let first_key = "test-key";
834 create_file(&file_path, first_key).expect("failed to create file");
835
836 let jwt = JwtBuilder::new()
838 .issuer("test-issuer")
839 .audience(&["test-audience"])
840 .subject("test-subject")
841 .public_key(&Key {
842 algorithm: Algorithm::HS512,
843 format: KeyFormat::Pem,
844 key: KeyData::File(file_name.to_string()),
845 })
846 .build()
847 .unwrap();
848
849 assert!(jwt.decoding_key.is_some());
850 assert!(jwt.encoding_key.is_none());
851
852 let signer = JwtBuilder::new()
854 .issuer("test-issuer")
855 .audience(&["test-audience"])
856 .subject("test-subject")
857 .private_key(&Key {
858 algorithm: Algorithm::HS512,
859 format: KeyFormat::Pem,
860 key: KeyData::Data(String::from(first_key)),
861 })
862 .build()
863 .unwrap();
864
865 let claims = signer.create_claims();
866 let token = signer.sign(&claims).unwrap();
867
868 let verified_claims: StandardClaims = jwt.get_claims(token.clone()).await.unwrap();
869
870 assert_eq!(verified_claims.iss.unwrap(), "test-issuer");
871 assert_eq!(verified_claims.aud.unwrap(), &["test-audience"]);
872 assert_eq!(verified_claims.sub.unwrap(), "test-subject");
873
874 let second_key = "another-test-key";
875 modify_file(&file_path, second_key).expect("failed to create file");
876 time::sleep(Duration::from_millis(100)).await;
877
878 assert!(jwt.decoding_key.is_some());
879 assert!(jwt.encoding_key.is_none());
880
881 let signer = JwtBuilder::new()
883 .issuer("test-issuer")
884 .audience(&["test-audience"])
885 .subject("test-subject")
886 .private_key(&Key {
887 algorithm: Algorithm::HS512,
888 format: KeyFormat::Pem,
889 key: KeyData::Data(String::from(second_key)),
890 })
891 .build()
892 .unwrap();
893
894 let claims = signer.create_claims();
895 let token = signer.sign(&claims).unwrap();
896
897 let verified_claims: StandardClaims = jwt.get_claims(token.clone()).await.unwrap();
898
899 assert_eq!(verified_claims.iss.unwrap(), "test-issuer");
900 assert_eq!(verified_claims.aud.unwrap(), &["test-audience"]);
901 assert_eq!(verified_claims.sub.unwrap(), "test-subject");
902
903 delete_file(&file_path);
904 }
905
906 #[tokio::test]
907 async fn test_jwt_sign_and_verify() {
908 let signer = JwtBuilder::new()
909 .issuer("test-issuer")
910 .audience(&["test-audience"])
911 .subject("test-subject")
912 .private_key(&Key {
913 algorithm: Algorithm::HS512,
914 format: KeyFormat::Pem,
915 key: KeyData::Data("secret-key".to_string()),
916 })
917 .build()
918 .unwrap();
919
920 let verifier = JwtBuilder::new()
921 .issuer("test-issuer")
922 .audience(&["test-audience"])
923 .subject("test-subject")
924 .public_key(&Key {
925 algorithm: Algorithm::HS512,
926 format: KeyFormat::Pem,
927 key: KeyData::Data("secret-key".to_string()),
928 })
929 .build()
930 .unwrap();
931
932 let claims = signer.create_claims();
933 let token = signer.sign_claims(&claims).unwrap();
934
935 let verified_claims: StandardClaims = verifier.get_claims(token.clone()).await.unwrap();
936
937 assert_eq!(verified_claims.iss.unwrap(), "test-issuer");
938 assert_eq!(verified_claims.aud.unwrap(), ["test-audience"]);
939 assert_eq!(verified_claims.sub.unwrap(), "test-subject");
940
941 let invalid_token = "invalid.token.string";
943 let result: Result<StandardClaims, AuthError> =
944 verifier.get_claims(invalid_token.to_string()).await;
945 assert!(
946 result.is_err(),
947 "Expected verification to fail for invalid token"
948 );
949
950 let wrong_verifier = JwtBuilder::new()
952 .issuer("test-issuer")
953 .audience(&["test-audience"])
954 .subject("test-subject")
955 .public_key(&Key {
956 algorithm: Algorithm::HS512,
957 format: KeyFormat::Pem,
958 key: KeyData::Data("wrong-secret-key".to_string()),
959 })
960 .build()
961 .unwrap();
962 let wrong_result: Result<StandardClaims, AuthError> =
963 wrong_verifier.get_claims(token).await;
964 assert!(
965 wrong_result.is_err(),
966 "Expected verification to fail with wrong key"
967 );
968 }
969
970 #[tokio::test]
971 async fn test_jwt_sign_and_verify_custom_claims() {
972 let signer = JwtBuilder::new()
973 .issuer("test-issuer")
974 .audience(&["test-audience"])
975 .subject("test-subject")
976 .private_key(&Key {
977 algorithm: Algorithm::HS512,
978 format: KeyFormat::Pem,
979 key: KeyData::Data("secret-key".to_string()),
980 })
981 .build()
982 .unwrap();
983
984 let verifier = JwtBuilder::new()
985 .issuer("test-issuer")
986 .audience(&["test-audience"])
987 .subject("test-subject")
988 .public_key(&Key {
989 algorithm: Algorithm::HS512,
990 format: KeyFormat::Pem,
991 key: KeyData::Data("secret-key".to_string()),
992 })
993 .build()
994 .unwrap();
995
996 let mut custom_claims = MetadataMap::new();
997 custom_claims.insert("role".to_string(), "admin".to_string());
998 custom_claims.insert(
999 "permissions".to_string(),
1000 vec!["read".to_string(), "write".to_string()],
1001 );
1002
1003 let mut claims = signer.create_claims();
1004 claims.custom_claims = custom_claims;
1005 let token = signer.sign_claims(&claims).unwrap();
1006
1007 let verified_claims: StandardClaims = verifier.get_claims(token).await.unwrap();
1008
1009 let role: String = verified_claims
1010 .custom_claims
1011 .get("role")
1012 .unwrap()
1013 .try_into()
1014 .unwrap();
1015 assert_eq!(&role, "admin");
1016
1017 let permissions: Vec<String> = verified_claims
1018 .custom_claims
1019 .get("permissions")
1020 .unwrap()
1021 .as_list()
1022 .unwrap()
1023 .iter()
1024 .map(|v| v.try_into().unwrap())
1025 .collect();
1026 assert_eq!(
1027 permissions,
1028 vec![
1029 serde_json::Value::String("read".to_string()),
1030 serde_json::Value::String("write".to_string())
1031 ]
1032 );
1033 }
1034
1035 #[tokio::test]
1036 async fn test_validate_jwt_with_provided_key() {}
1037
1038 async fn test_jwt_resolve_with_algorithm(algorithm: Algorithm) {
1039 initialize_crypto_provider();
1040
1041 let (test_key, mock_server, _alg_str) = setup_test_jwt_resolver(algorithm).await;
1042
1043 let signer = JwtBuilder::new()
1045 .issuer(mock_server.uri())
1046 .audience(&["test-audience"])
1047 .subject("test-subject")
1048 .private_key(&Key {
1049 algorithm,
1050 format: KeyFormat::Pem,
1051 key: KeyData::Data(test_key),
1052 })
1053 .build()
1054 .unwrap();
1055
1056 let verifier = JwtBuilder::new()
1057 .issuer(mock_server.uri())
1058 .audience(&["test-audience"])
1059 .subject("test-subject")
1060 .auto_resolve_keys(true)
1061 .build()
1062 .unwrap();
1063
1064 let token = signer.sign_claims(&signer.create_claims()).unwrap();
1066 let claims: StandardClaims = verifier.get_claims(token).await.unwrap();
1067
1068 assert_eq!(claims.iss.unwrap(), mock_server.uri());
1070 assert_eq!(claims.aud.unwrap(), vec!["test-audience"]);
1071 assert_eq!(claims.sub.unwrap(), "test-subject");
1072 }
1073
1074 #[tokio::test]
1075 async fn test_jwt_resolve_decoding_key_rs256() {
1076 test_jwt_resolve_with_algorithm(Algorithm::RS256).await;
1077 }
1078
1079 #[tokio::test]
1080 async fn test_jwt_resolve_decoding_key_rs384() {
1081 test_jwt_resolve_with_algorithm(Algorithm::RS384).await;
1082 }
1083
1084 #[tokio::test]
1085 async fn test_jwt_resolve_decoding_key_rs512() {
1086 test_jwt_resolve_with_algorithm(Algorithm::RS512).await;
1087 }
1088
1089 #[tokio::test]
1090 async fn test_jwt_resolve_decoding_key_ps256() {
1091 test_jwt_resolve_with_algorithm(Algorithm::PS256).await;
1093 }
1094
1095 #[tokio::test]
1096 async fn test_jwt_resolve_decoding_key_ps384() {
1097 test_jwt_resolve_with_algorithm(Algorithm::PS384).await;
1098 }
1099
1100 #[tokio::test]
1101 async fn test_jwt_resolve_decoding_key_ps512() {
1102 test_jwt_resolve_with_algorithm(Algorithm::PS512).await;
1103 }
1104
1105 #[tokio::test]
1106 async fn test_jwt_resolve_decoding_key_es256() {
1107 test_jwt_resolve_with_algorithm(Algorithm::ES256).await;
1108 }
1109
1110 #[tokio::test]
1111 async fn test_jwt_resolve_decoding_key_es384() {
1112 test_jwt_resolve_with_algorithm(Algorithm::ES384).await;
1113 }
1114
1115 #[tokio::test]
1116 async fn test_jwt_resolve_decoding_key_eddsa() {
1117 test_jwt_resolve_with_algorithm(Algorithm::EdDSA).await;
1118 }
1119
1120 #[tokio::test]
1121 async fn test_jwt_verify_caching() {
1122 let signer = JwtBuilder::new()
1124 .issuer("test-issuer")
1125 .audience(&["test-audience"])
1126 .subject("test-subject")
1127 .private_key(&Key {
1128 algorithm: Algorithm::HS512,
1129 format: KeyFormat::Pem,
1130 key: KeyData::Data("secret-key".to_string()),
1131 })
1132 .build()
1133 .unwrap();
1134
1135 let mut verifier = JwtBuilder::new()
1136 .issuer("test-issuer")
1137 .audience(&["test-audience"])
1138 .subject("test-subject")
1139 .public_key(&Key {
1140 algorithm: Algorithm::HS512,
1141 format: KeyFormat::Pem,
1142 key: KeyData::Data("secret-key".to_string()),
1143 })
1144 .build()
1145 .unwrap();
1146
1147 let claims = signer.create_claims();
1148 let token = signer.sign_claims(&claims).unwrap();
1149
1150 let first_result: StandardClaims = verifier.try_get_claims(token.clone()).unwrap();
1152
1153 verifier.decoding_key = None;
1156
1157 let second_result: StandardClaims = verifier.try_get_claims(token.clone()).unwrap();
1159
1160 assert_eq!(first_result.iss, second_result.iss);
1162 assert_eq!(first_result.aud, second_result.aud);
1163 assert_eq!(first_result.sub, second_result.sub);
1164 assert_eq!(first_result.exp, second_result.exp);
1165
1166 let mut custom_claims = MetadataMap::new();
1168 custom_claims.insert("role", "admin");
1169
1170 let mut claims2 = signer.create_claims();
1171 claims2.custom_claims = custom_claims;
1172
1173 let token2 = signer.sign_claims(&claims2).unwrap();
1174
1175 let result = verifier.try_get_claims::<StandardClaims>(token2);
1177 assert!(
1178 result.is_err(),
1179 "Should have failed due to missing decoding key"
1180 );
1181 }
1182
1183 #[tokio::test]
1184 async fn test_signer_jwt_get_id() {
1185 let signer = JwtBuilder::new()
1186 .issuer("test-issuer")
1187 .audience(&["test-audience"])
1188 .subject("test-user-123")
1189 .private_key(&Key {
1190 algorithm: Algorithm::HS256,
1191 format: KeyFormat::Pem,
1192 key: KeyData::Data("secret".to_string()),
1193 })
1194 .build()
1195 .unwrap();
1196
1197 let id = signer.get_id().unwrap();
1198 assert_eq!(id, "test-user-123");
1199 }
1200
1201 #[tokio::test]
1202 async fn test_signer_jwt_get_id_missing_sub() {
1203 let signer = JwtBuilder::new()
1204 .issuer("test-issuer")
1205 .audience(&["test-audience"])
1206 .private_key(&Key {
1208 algorithm: Algorithm::HS256,
1209 format: KeyFormat::Pem,
1210 key: KeyData::Data("secret".to_string()),
1211 })
1212 .build()
1213 .unwrap();
1214
1215 let result = signer.get_id();
1216 assert!(result.is_err());
1217 assert!(
1218 result
1219 .unwrap_err()
1220 .to_string()
1221 .contains("missing subject claim")
1222 );
1223 }
1224
1225 #[tokio::test]
1226 async fn test_static_token_provider_get_id() {
1227 let header = JwtHeader::new(Algorithm::HS256);
1229 let claims = serde_json::json!({
1230 "sub": "static-user-456",
1231 "exp": (SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 3600)
1232 });
1233 let key = EncodingKey::from_secret(b"secret");
1234 let token = encode(&header, &claims, &key).unwrap();
1235
1236 let provider: StaticTokenProvider = Jwt::<P>::new(
1237 StandardClaims::new(
1238 SystemTime::now()
1239 .duration_since(UNIX_EPOCH)
1240 .unwrap()
1241 .as_secs()
1242 + 3600,
1243 ),
1244 Duration::from_secs(3600),
1245 Validation::default(),
1246 )
1247 .unwrap()
1248 .with_static_token(Arc::new(RwLock::new(token)));
1249
1250 let id = provider.get_id().unwrap();
1251 assert_eq!(id, "static-user-456");
1252 }
1253
1254 #[tokio::test]
1255 async fn test_static_token_provider_get_id_invalid_token() {
1256 let provider: StaticTokenProvider = Jwt::<P>::new(
1257 StandardClaims::new(
1258 SystemTime::now()
1259 .duration_since(UNIX_EPOCH)
1260 .unwrap()
1261 .as_secs()
1262 + 3600,
1263 ),
1264 Duration::from_secs(3600),
1265 Validation::default(),
1266 )
1267 .unwrap()
1268 .with_static_token(Arc::new(RwLock::new("invalid.token.here".to_string())));
1269
1270 let result = provider.get_id();
1271 assert!(result.is_err_and(|e| matches!(e, AuthError::JwtTokenInvalid(_))));
1272 }
1273
1274 #[tokio::test]
1275 async fn test_static_token_provider_get_id_missing_sub() {
1276 let header = JwtHeader::new(Algorithm::HS256);
1278 let claims = serde_json::json!({
1279 "exp": (SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 3600)
1280 });
1281 let key = EncodingKey::from_secret(b"secret");
1282 let token = encode(&header, &claims, &key).unwrap();
1283
1284 let provider: StaticTokenProvider = Jwt::<P>::new(
1285 StandardClaims::new(
1286 SystemTime::now()
1287 .duration_since(UNIX_EPOCH)
1288 .unwrap()
1289 .as_secs()
1290 + 3600,
1291 ),
1292 Duration::from_secs(3600),
1293 Validation::default(),
1294 )
1295 .unwrap()
1296 .with_static_token(Arc::new(RwLock::new(token)));
1297
1298 let result = provider.get_id();
1299 assert!(result.is_err_and(|e| matches!(e, AuthError::TokenInvalidMissingSub)));
1300 }
1301
1302 #[tokio::test]
1303 async fn test_initialize_jwt_signer() {
1304 let jwt = JwtBuilder::new()
1305 .issuer("test-issuer")
1306 .audience(&["aud"])
1307 .subject("sub")
1308 .private_key(&Key {
1309 algorithm: Algorithm::HS256,
1310 format: KeyFormat::Pem,
1311 key: KeyData::Data("secret-key".into()),
1312 })
1313 .build()
1314 .unwrap();
1315 let mut signer: SignerJwt = jwt;
1316 let _ = signer.initialize().await; assert!(signer.encoding_key.is_some());
1319 assert!(signer.decoding_key.is_none());
1320
1321 let claims = signer.create_claims();
1323 assert_eq!(claims.iss.as_ref().unwrap(), "test-issuer");
1324 assert_eq!(claims.aud.as_ref().unwrap(), &["aud"]);
1325 assert_eq!(claims.sub.as_ref().unwrap(), "sub");
1326 assert!(claims.exp > 0);
1327 assert!(claims.iat.is_some());
1328 assert!(claims.nbf.is_some());
1329 let token = signer.sign(&claims).unwrap();
1330 assert!(!token.is_empty());
1331 assert_eq!(token.split('.').count(), 3); }
1333
1334 #[tokio::test]
1335 async fn test_verifier_jwt_verify_async() {
1336 use crate::traits::Verifier;
1337
1338 let signer = JwtBuilder::new()
1339 .issuer("test-issuer")
1340 .audience(&["test-audience"])
1341 .subject("test-subject")
1342 .private_key(&Key {
1343 algorithm: Algorithm::HS256,
1344 format: KeyFormat::Pem,
1345 key: KeyData::Data("secret-key".to_string()),
1346 })
1347 .build()
1348 .unwrap();
1349
1350 let verifier = JwtBuilder::new()
1351 .issuer("test-issuer")
1352 .audience(&["test-audience"])
1353 .subject("test-subject")
1354 .public_key(&Key {
1355 algorithm: Algorithm::HS256,
1356 format: KeyFormat::Pem,
1357 key: KeyData::Data("secret-key".to_string()),
1358 })
1359 .build()
1360 .unwrap();
1361
1362 let claims = signer.create_claims();
1363 let token = signer.sign_claims(&claims).unwrap();
1364
1365 verifier.verify(token).await.unwrap();
1367 }
1368}