Skip to main content

slim_auth/
jwt.rs

1// Copyright AGNTCY Contributors (https://github.com/agntcy)
2// SPDX-License-Identifier: Apache-2.0
3
4use 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/// Enum representing key data types
37#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, JsonSchema)]
38#[serde(rename_all = "lowercase")]
39pub enum KeyData {
40    /// String with encoded key(s)
41    Data(String),
42    /// File path to the key(s)
43    File(String),
44}
45
46/// Represents a key
47#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, JsonSchema)]
48pub struct Key {
49    /// Algorithm used for signing the JWT
50    #[schemars(with = "AlgorithmRepr")]
51    pub algorithm: Algorithm,
52
53    /// Key format - PEM, JWK or JWKS
54    pub format: KeyFormat,
55
56    /// Encoded key or file path
57    pub key: KeyData,
58}
59
60/// Local enum used only for JSON Schema generation of the `algorithm` field.
61/// Remote schema representation of jsonwebtoken::Algorithm
62#[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/// Cache entry for validated tokens
110#[derive(Debug, Clone)]
111struct TokenCacheEntry {
112    /// Expiration time in seconds since UNIX epoch
113    expiry: u64,
114}
115
116/// Cache for validated tokens to avoid repeated signature verification
117#[derive(Debug)]
118struct TokenCache {
119    /// Map from token string to cache entry
120    entries: RwLock<HashMap<String, TokenCacheEntry>>,
121}
122
123impl TokenCache {
124    /// Create a new token cache
125    fn new() -> Self {
126        TokenCache {
127            entries: RwLock::new(HashMap::new()),
128        }
129    }
130
131    /// Store a token and its claims in the cache
132    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    /// Retrieve a token's claims from the cache if it exists and is still valid
138    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            // Decode the claims part of the token
149            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    /// Expiration time in seconds since UNIX epoch
166    exp: u64,
167}
168
169/// JWT implementation that uses the jsonwebtoken crate.
170#[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 from file
182    static_token: Option<Arc<RwLock<String>>>,
183
184    /// MLS signature key pair: (secret_key_bytes, public_key_bytes).
185    /// Each instance (and clone) holds its own independent copy of the keys.
186    signature_keys: (Vec<u8>, Vec<u8>),
187
188    _phantom: std::marker::PhantomData<T>,
189}
190
191impl<T> Jwt<T> {
192    /// Internal constructor used by the builder.
193    ///
194    /// This should not be called directly. Use the builder pattern instead:
195    /// ```
196    /// let jwt = Jwt::builder()
197    ///     .issuer("my-issuer")
198    ///     .audience("my-audience")
199    ///     .subject("user-123")
200    ///     .private_key("secret-key")
201    ///     .build()?;
202    /// ```
203    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        // Clone the Arc, get a mutable reference to the Vec, and push to it
225        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    /// Creates a StandardClaims object with the default values.
302    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    /// Creates a StandardClaims object with custom claims merged in.
319    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        // Ensure we have an encoding key for signing
341        let encoding_key = self
342            .encoding_key
343            .as_ref()
344            .ok_or(AuthError::JwtMissingPrivateKey)?;
345
346        // Create the JWT header
347        let header = JwtHeader::new(self.validation.algorithms[0]);
348
349        // Encode the claims into a JWT token (use #[from] JwtError for propagation)
350        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    /// Get token
372    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        // Check if the token is in the cache first for cacheable claim types
393        if let Some(cached_claims) = self.get_cached_claims::<Claims>(token) {
394            return Ok(cached_claims);
395        }
396
397        // Try to decode the key from the cache first; if this would require async, return WouldBlockOn
398        let decoding_key = self.decoding_key(token)?;
399
400        // If we have a decoding key, proceed with verification
401        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        // Check if the token is in the cache first for cacheable claim types
411        if let Some(cached_claims) = self.get_cached_claims::<Claims>(token) {
412            return Ok(cached_claims);
413        }
414
415        // Resolve the decoding key for verification
416        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        // Get the token header (propagate underlying jsonwebtoken error directly)
429        let token_header = decode_header(token)?;
430
431        // Derive a validation using the same algorithm
432        let validation = self.get_validation(token_header.alg);
433
434        // Decode and verify the token
435        let token_data: TokenData<Claims> = decode(token, &decoding_key, &validation)?;
436
437        // Get the exp to cache the token - do not verify token again
438        let token_exp_data: TokenData<ExpClaim> = jsonwebtoken::dangerous::insecure_decode(token)?;
439
440        // Cache the token with its expiry
441        self.cache(token.to_owned(), token_exp_data.claims.exp);
442
443        // Parse the token to extract the expiry claim
444        Ok(token_data.claims)
445    }
446
447    /// Get cached token from the cache
448    fn get_cached_claims<Claims: serde::de::DeserializeOwned>(
449        &self,
450        token: &str,
451    ) -> Option<Claims> {
452        // Check if the token is in the cache first for cacheable claim types
453        if let Some(_cached_claims) = self.token_cache.get(token) {
454            // Tokens in the cache are already verified - skip the signature verification
455            let token_data: TokenData<Claims> =
456                jsonwebtoken::dangerous::insecure_decode(token).ok()?;
457
458            // Return the claims from the cached token
459            return Some(token_data.claims);
460        }
461
462        None
463    }
464
465    fn cache(&self, token: impl Into<String>, expiry: u64) {
466        // Store the token in the cache with its expiry
467        self.token_cache.store(token, expiry);
468    }
469
470    fn get_validation(&self, alg: Algorithm) -> Validation {
471        // Create a validation object with the configured issuer, audience, and subject
472        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        // Get issuer from claims
483        let ret = jsonwebtoken::dangerous::insecure_decode(token)?;
484
485        Ok(ret)
486    }
487
488    /// Get decoding key for verification
489    fn decoding_key(&self, token: &str) -> Result<DecodingKey, AuthError> {
490        // If the decoding key is available, return it
491        {
492            if let Some(key) = &self.decoding_key {
493                return Ok(key.read().clone());
494            }
495        }
496
497        // Try to get a cached decoding key
498        if let Some(resolver) = &self.key_resolver {
499            // Get issuer from claims
500            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                    // No cached key yet; async resolution would be required.
513                    return Err(AuthError::WouldBlockOn);
514                }
515            }
516        }
517
518        // If we don't have a decoder
519        Err(AuthError::JwtNoKeyResolver)
520    }
521
522    /// Resolve a decoding key for token verification
523    async fn resolve_decoding_key(&self, token: &str) -> Result<DecodingKey, AuthError> {
524        // First check if we already have a decoding key
525        {
526            if let Some(key) = &self.decoding_key {
527                return Ok(key.read().clone());
528            }
529        }
530
531        // As we don't have a decoding key, we need to resolve it. The resolver
532        // should be set, otherwise we can't proceed.
533        let resolver = self
534            .key_resolver
535            .as_ref()
536            .ok_or(AuthError::JwtNoKeyResolver)?;
537
538        // Parse the token header to get the key ID and algorithm
539        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        // Resolve the key
548        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        // SignerJwt has no asynchronous initialization requirements.
568        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        // StaticTokenProvider exposes a statically loaded token; nothing async to perform.
606        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(()) // no-op
633    }
634
635    async fn verify(&self, token: impl AsRef<str> + Send) -> Result<(), AuthError> {
636        // Just verify the token is valid, don't extract claims
637        self.verify_claims::<StandardClaims>(token)
638            .await
639            .map(|_| ())
640    }
641
642    fn try_verify(&self, token: impl AsRef<str>) -> Result<(), AuthError> {
643        // Just verify the token is valid, don't extract claims
644        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
662/// Helper function to extract the 'sub' claim from a JWT token without signature validation
663pub(crate) fn extract_sub_claim_unsafe(token: &str) -> Result<String, AuthError> {
664    // Decode the token without signature validation
665    let token_data = jsonwebtoken::dangerous::insecure_decode::<serde_json::Value>(token)?;
666
667    // Extract the 'sub' claim
668    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        // crate file
726        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        // create jwt builder
732        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        // Build verifier with auto_resolve (no cached key yet)
805        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        // Use test utility to generate a syntactically valid unsigned token
813        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        // crate file
831        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        // create jwt builder
837        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        // test the verifier with the first key
853        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        // test the verifier with the second key
882        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        // Try to verify with an invalid token
942        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        // Create a verifier with the wrong key
951        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        // Build the JWT with auto key resolution
1044        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        // Sign and verify the token
1065        let token = signer.sign_claims(&signer.create_claims()).unwrap();
1066        let claims: StandardClaims = verifier.get_claims(token).await.unwrap();
1067
1068        // Validate the claims
1069        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        // Set aws-lc as default crypto provider
1092        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        // Create test JWT objects
1123        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        // First verification
1151        let first_result: StandardClaims = verifier.try_get_claims(token.clone()).unwrap();
1152
1153        // Alter the decoding_key to simulate a situation where signature verification would fail
1154        // if attempted again. Since we're using the cache, it should still work.
1155        verifier.decoding_key = None;
1156
1157        // Second verification with the same token - should use the cache
1158        let second_result: StandardClaims = verifier.try_get_claims(token.clone()).unwrap();
1159
1160        // Both results should be the same
1161        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        // Now create a different token
1167        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        // Verify the new token - should fail because we removed the decoding_key
1176        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            // No subject set
1207            .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        // Create a valid JWT token with sub claim
1228        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        // Create a JWT token without sub claim
1277        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; // no-op
1317        // Verify the signer is properly configured
1318        assert!(signer.encoding_key.is_some());
1319        assert!(signer.decoding_key.is_none());
1320
1321        // Produce a token explicitly to verify signer works after initialize.
1322        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); // JWT should have 3 parts
1332    }
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        // Exercise Verifier::verify (async) — the one patch line that was uncovered
1366        verifier.verify(token).await.unwrap();
1367    }
1368}