From 3d51aa1e6a925b7c44f4e13bde5ccdcba4d538d8 Mon Sep 17 00:00:00 2001 From: Bendik Aagaard Lynghaug Date: Tue, 1 Sep 2026 16:54:35 +0200 Subject: [PATCH] Sign-in becomes optional: KANIDM_URL unset disables auth cleanly A content-only instance (westra preview) has no review desk and no Kanidm client; booting no longer demands one. Auth routes answer 503 'sign-in is not configured on this instance'; everything public renders as usual. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Y42TyF8Zu7NGRR2893vNcZ --- src/server/oidc.rs | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/server/oidc.rs b/src/server/oidc.rs index 043429f..8dcc032 100644 --- a/src/server/oidc.rs +++ b/src/server/oidc.rs @@ -33,6 +33,13 @@ type OidcClient = CoreClient< >; pub struct Oidc { + /// `None` when `KANIDM_URL` is unset: a content-only instance with + /// sign-in disabled - auth routes answer 503, everything public + /// renders as usual. + inner: Option, +} + +struct OidcInner { client: OidcClient, http: openidconnect::reqwest::Client, } @@ -45,7 +52,13 @@ const REDIRECT_KEY: &str = "oidc_post_login_redirect"; impl Oidc { /// Discovers the provider and builds the client from environment: /// `KANIDM_URL`, `OAUTH2_CLIENT_ID`, `OAUTH2_CLIENT_SECRET`, `PUBLIC_URL`. + /// With `KANIDM_URL` unset, sign-in is disabled instead of fatal - + /// the shape of a public content instance without a review desk. pub async fn from_env() -> anyhow::Result { + if std::env::var("KANIDM_URL").is_err() { + tracing::warn!("KANIDM_URL not set - sign-in disabled on this instance"); + return Ok(Self { inner: None }); + } let kanidm_url = require_env("KANIDM_URL")?; let client_id = require_env("OAUTH2_CLIENT_ID")?; let client_secret = require_env("OAUTH2_CLIENT_SECRET")?; @@ -75,7 +88,16 @@ impl Oidc { ) .set_redirect_uri(redirect); - Ok(Self { client, http }) + Ok(Self { + inner: Some(OidcInner { client, http }), + }) + } + + fn configured(&self) -> Result<&OidcInner, HandlerError> { + self.inner.as_ref().ok_or(( + StatusCode::SERVICE_UNAVAILABLE, + "sign-in is not configured on this instance".to_string(), + )) } } @@ -118,10 +140,10 @@ pub async fn login( session.insert(REDIRECT_KEY, redirect).await.map_err(internal)?; } + let oidc = state.oidc.configured()?; let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); - let (auth_url, csrf_state, nonce) = state - .oidc + let (auth_url, csrf_state, nonce) = oidc .client .authorize_url( CoreAuthenticationFlow::AuthorizationCode, @@ -182,7 +204,7 @@ pub async fn callback( )); } - let oidc = &state.oidc; + let oidc = state.oidc.configured()?; let token_response = oidc .client .exchange_code(AuthorizationCode::new(params.code))