forked from
smokesignal.events/smokesignal
Fork i18n + search + filtering- v0.2
1use anyhow::Result;
2use axum::{
3 extract::State,
4 http::{header, HeaderValue},
5 response::{IntoResponse, Response},
6};
7use std::sync::Arc;
8
9use crate::http::{context::WebContext, errors::WebError};
10use crate::jose::jwk::{WrappedJsonWebKey, WrappedJsonWebKeySet};
11
12// Function to compute JWKS data and serialize to JSON string
13fn compute_jwks_json(web_context: &WebContext) -> Result<String, serde_json::Error> {
14 let mut keys = vec![];
15 let signing_keys = web_context.config.signing_keys.as_ref();
16
17 for available_signing_key in web_context.config.oauth_active_keys.as_ref() {
18 let available_signing_key = available_signing_key.clone();
19
20 let signing_key = match signing_keys.get(&available_signing_key) {
21 Some(key) => key.clone(),
22 None => continue,
23 };
24 let public_key = signing_key.public_key();
25
26 let wrapped_json_web_key = WrappedJsonWebKey {
27 jwk: public_key.to_jwk(),
28 kid: Some(available_signing_key.clone()),
29 alg: Some("ES256".to_string()),
30 };
31
32 keys.push(wrapped_json_web_key);
33 }
34
35 let jwks = WrappedJsonWebKeySet { keys };
36 serde_json::to_string(&jwks)
37}
38
39// Global cache for the pre-serialized JSON string
40static JWKS_JSON_CACHE: once_cell::sync::OnceCell<Arc<String>> = once_cell::sync::OnceCell::new();
41
42#[tracing::instrument(skip_all, err)]
43pub async fn handle_oauth_jwks(
44 State(web_context): State<WebContext>,
45) -> Result<impl IntoResponse, WebError> {
46 tracing::debug!("handle_oauth_jwks");
47
48 // Initialize the cache if needed
49 if JWKS_JSON_CACHE.get().is_none() {
50 // Compute and serialize the JWKS data
51 let jwks_json = compute_jwks_json(&web_context)
52 .map_err(|e| anyhow::anyhow!("error-oauth-jwks-1 Failed to serialize JWKS: {}", e))?;
53
54 // Store in cache - don't worry if another thread beat us to it
55 let _ = JWKS_JSON_CACHE.set(Arc::new(jwks_json));
56 }
57
58 // By this point, the cache should be initialized - either by us or another thread
59 // In the extremely unlikely event it's still not initialized, we'll create it one more time
60 let jwks_json = if let Some(json) = JWKS_JSON_CACHE.get() {
61 json
62 } else {
63 // Final attempt to compute and cache
64 let jwks_json = compute_jwks_json(&web_context)
65 .map_err(|e| anyhow::anyhow!("error-oauth-jwks-1 Failed to serialize JWKS: {}", e))?;
66
67 // Create a new Arc and set it in the cache
68 let json_arc = Arc::new(jwks_json);
69
70 // This will either succeed in setting it, or another thread beat us to it
71 if JWKS_JSON_CACHE.set(json_arc.clone()).is_err() {
72 // Another thread set it first, so use that value
73 JWKS_JSON_CACHE.get().ok_or_else(|| {
74 anyhow::anyhow!("error-oauth-jwks-2 Failed to initialize JWKS cache")
75 })?
76 } else {
77 // We set it, so we can use our local copy
78 JWKS_JSON_CACHE.get().ok_or_else(|| {
79 anyhow::anyhow!("error-oauth-jwks-2 Failed to initialize JWKS cache")
80 })?
81 }
82 };
83
84 // Create response with proper content type
85 let mut response = Response::new((**jwks_json).clone());
86
87 // Set content type to application/json
88 response.headers_mut().insert(
89 header::CONTENT_TYPE,
90 HeaderValue::from_static("application/json"),
91 );
92
93 Ok(response)
94}