diff --git a/src/application/ports/auth_ports.rs b/src/application/ports/auth_ports.rs index 50fd049b..3ba14799 100644 --- a/src/application/ports/auth_ports.rs +++ b/src/application/ports/auth_ports.rs @@ -138,7 +138,8 @@ pub struct OidcIdClaims { pub trait OidcServicePort: Send + Sync + 'static { /// Get the authorization URL for redirecting the user to the IdP. /// Includes PKCE code_challenge (S256) and nonce for ID token binding. - fn get_authorize_url(&self, state: &str, nonce: &str, pkce_challenge: &str) -> Result; + /// This is async because it may need to fetch the OIDC discovery document. + async fn get_authorize_url(&self, state: &str, nonce: &str, pkce_challenge: &str) -> Result; /// Exchange an authorization code for tokens, providing PKCE code_verifier. async fn exchange_code(&self, code: &str, pkce_verifier: &str) -> Result; diff --git a/src/application/services/auth_application_service.rs b/src/application/services/auth_application_service.rs index 5d08c0c1..798a180a 100644 --- a/src/application/services/auth_application_service.rs +++ b/src/application/services/auth_application_service.rs @@ -785,7 +785,7 @@ impl AuthApplicationService { /// Prepare the OIDC authorization flow: generates CSRF state, PKCE pair, /// nonce, stores them in pending_oidc_flows, and returns the authorize URL. - pub fn prepare_oidc_authorize(&self) -> Result { + pub async fn prepare_oidc_authorize(&self) -> Result { let oidc = self.oidc_service().ok_or_else(|| DomainError::new( ErrorKind::InternalError, "OIDC", "OIDC service not configured", ))?; @@ -826,7 +826,7 @@ impl AuthApplicationService { } // Build authorization URL with state, nonce, and PKCE challenge - let authorize_url = oidc.get_authorize_url(&state_token, &nonce, &pkce_challenge)?; + let authorize_url = oidc.get_authorize_url(&state_token, &nonce, &pkce_challenge).await?; tracing::info!("OIDC authorize flow prepared (state={}...)", &state_token[..8]); diff --git a/src/infrastructure/services/oidc_service.rs b/src/infrastructure/services/oidc_service.rs index 5408b324..4f645168 100644 --- a/src/infrastructure/services/oidc_service.rs +++ b/src/infrastructure/services/oidc_service.rs @@ -238,22 +238,10 @@ impl OidcService { #[async_trait] impl OidcServicePort for OidcService { - fn get_authorize_url(&self, state: &str, nonce: &str, pkce_challenge: &str) -> Result { - // We need the authorization_endpoint. If not cached, we'll construct it from issuer. - // In practice, the discovery should be pre-fetched during startup. - let auth_endpoint = { - let cache = self.discovery.read().map_err(|_| DomainError::new( - ErrorKind::InternalError, "OIDC", "Lock poisoned", - ))?; - match &*cache { - Some(disc) => disc.authorization_endpoint.clone(), - None => { - // Fallback: construct typical endpoint - let issuer = self.config.issuer_url.trim_end_matches('/'); - format!("{}/authorize", issuer) - } - } - }; + async fn get_authorize_url(&self, state: &str, nonce: &str, pkce_challenge: &str) -> Result { + // Fetch or use cached discovery to get the correct authorization_endpoint + let discovery = self.get_discovery().await?; + let auth_endpoint = discovery.authorization_endpoint; let scopes = self.config.scopes.replace(',', " "); let url = format!( diff --git a/src/interfaces/api/handlers/auth_handler.rs b/src/interfaces/api/handlers/auth_handler.rs index 3782a8a3..70c1c0bc 100644 --- a/src/interfaces/api/handlers/auth_handler.rs +++ b/src/interfaces/api/handlers/auth_handler.rs @@ -334,7 +334,7 @@ async fn oidc_authorize( } // Prepare OIDC authorization flow (generates CSRF state, PKCE pair, nonce) - let authorize_url = auth_app.prepare_oidc_authorize()?; + let authorize_url = auth_app.prepare_oidc_authorize().await?; tracing::info!("OIDC authorize redirect generated");