Fetch JWKS from a provider's endpoint and update the cache. Returns the number of keys successfully parsed. On HTTP or parse failure, logs a warning and returns 0 (cache unchanged).
(
provider_name: &str,
jwks_url: &str,
cache: &JwksCache,
policy: &JwksPolicy,
)
| 26 | /// Returns the number of keys successfully parsed. |
| 27 | /// On HTTP or parse failure, logs a warning and returns 0 (cache unchanged). |
| 28 | pub async fn fetch_and_cache( |
| 29 | provider_name: &str, |
| 30 | jwks_url: &str, |
| 31 | cache: &JwksCache, |
| 32 | policy: &JwksPolicy, |
| 33 | ) -> usize { |
| 34 | match fetch_jwks(jwks_url, policy).await { |
| 35 | Ok(keys) => { |
| 36 | let count = keys.len(); |
| 37 | if count > 0 { |
| 38 | info!( |
| 39 | provider = %provider_name, |
| 40 | url = %jwks_url, |
| 41 | keys = count, |
| 42 | "JWKS fetched successfully" |
| 43 | ); |
| 44 | cache.update_provider(provider_name, keys); |
| 45 | } else { |
| 46 | warn!( |
| 47 | provider = %provider_name, |
| 48 | url = %jwks_url, |
| 49 | "JWKS response contained no usable signature keys" |
| 50 | ); |
| 51 | } |
| 52 | count |
| 53 | } |
| 54 | Err(e) => { |
| 55 | // Log only the variant (no embedded body bytes) — error bodies |
| 56 | // could carry cloud-metadata contents if SSRF guards ever fail |
| 57 | // open. We never want that material in the log pipeline. |
| 58 | warn!( |
| 59 | provider = %provider_name, |
| 60 | url = %jwks_url, |
| 61 | error = e.category(), |
| 62 | "JWKS fetch failed — using cached keys if available" |
| 63 | ); |
| 64 | 0 |
| 65 | } |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | /// Fetch and parse JWKS from a URL, with full SSRF defense. |
| 70 | async fn fetch_jwks( |