diff --git a/src/auth.rs b/src/auth.rs index 8dd7dff..8453fd3 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -273,6 +273,14 @@ async fn refresh_token(cached: &AuthTokens) -> Result { /// Try to read the client_id from K8s secret `oidc-sunbeam-cli` in `ory` namespace. /// Falls back to the default client ID. async fn resolve_client_id() -> String { + // Try reading from K8s secret — silently fall back if cluster is unreachable. + // The tracing ERROR from kube client init is noisy; suppress by not even trying + // when we know the cluster isn't configured. + let host = crate::config::get_production_host(); + if host.is_empty() && crate::kube::ssh_host().is_empty() { + // No cluster configured, skip K8s lookup + return DEFAULT_CLIENT_ID.to_string(); + } match crate::kube::kube_get_secret_field("ory", "oidc-sunbeam-cli", "client_id").await { Ok(id) if !id.is_empty() => id, _ => DEFAULT_CLIENT_ID.to_string(), @@ -340,7 +348,15 @@ async fn wait_for_callback( ) -> Result { use tokio::io::{AsyncReadExt, AsyncWriteExt}; - let (mut stream, _) = listener.accept().await.ctx("Failed to accept callback connection")?; + // Wait up to 5 minutes for the callback, or until Ctrl+C + let accept_result = tokio::time::timeout( + std::time::Duration::from_secs(300), + listener.accept(), + ) + .await + .map_err(|_| SunbeamError::identity("Login timed out (5 min). Try again with `sunbeam auth login`."))?; + + let (mut stream, _) = accept_result.ctx("Failed to accept callback connection")?; let mut buf = vec![0u8; 4096]; let n = stream @@ -389,10 +405,15 @@ async fn wait_for_callback( // Send success response let html = concat!( - "", + "
", "

Authentication successful

", "

You can close this tab and return to the terminal.

", - "" + "
" ); let response = format!( "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",