From c7b0a317d032e06d222306f9d795975fde232b2e Mon Sep 17 00:00:00 2001 From: Sasank Talasila Date: Mon, 24 Aug 2026 19:06:55 +0000 Subject: [PATCH] feat: surface Google SSO on the login page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Google Workspace SSO is fully implemented server-side — /auth/google/start and /auth/google/callback exist, are permitAll in SecurityConfig, and are gated by a google_workspace_domain allowlist that verifies the OIDC `hd` claim. The client helper authAPI.getGoogleStartUrl() exists too. Nothing ever called it. Login.jsx had no Google reference at all, so on an install with SECURITY_GOOGLE_ENABLED=true the only way to sign in via SSO was to type /api/auth/google/start into the address bar. The login page is unauthenticated, so it cannot read security.google.enabled to decide whether to offer the button. Rather than add an endpoint, this extends GET /setup/status — already public, and already fetched by Login.jsx on mount — with a googleEnabled flag. Installs that never configured Google see no button and are unaffected. - SetupController: expose googleEnabled on the public status response. Field is non-final by necessity; @RequiredArgsConstructor would otherwise pull it into the constructor, which Spring cannot satisfy. - Login.jsx: render a "Sign in with Google" button below the password form when the flag is set. Plain , not a submit button — the endpoint 302s to Google, so it is a full-page navigation and must not post the form. --- .../dbaagent/controller/SetupController.java | 19 ++++++++- src/pages/Login.jsx | 39 +++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/com/dbaagent/controller/SetupController.java b/backend/src/main/java/com/dbaagent/controller/SetupController.java index 0c87351..fbceab9 100644 --- a/backend/src/main/java/com/dbaagent/controller/SetupController.java +++ b/backend/src/main/java/com/dbaagent/controller/SetupController.java @@ -6,6 +6,7 @@ import com.dbaagent.service.SystemConfigService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestClient; @@ -34,6 +35,17 @@ public class SetupController { private final CredentialRepository credentialRepository; private final LlmConfigResolver llmConfigResolver; + /** + * Mirrors {@code security.google.enabled}. Surfaced on the public status + * endpoint so the login page can decide whether to offer Google sign-in — + * it is otherwise unauthenticated and has no way to know the server was + * configured for SSO. Deliberately NOT final: {@code @RequiredArgsConstructor} + * would pull a final field into the constructor and Spring has no bean to + * satisfy it. + */ + @Value("${security.google.enabled:false}") + private boolean googleEnabled; + // ── GET /setup/status ───────────────────────────────────────────────────── /** Returns setup completion state. Public endpoint — no auth required. */ @@ -53,7 +65,8 @@ public SetupStatusResponse getStatus() { setupComplete, hasOrgInfo, hasConnections, - hasLlmConfig + hasLlmConfig, + googleEnabled ); } @@ -278,7 +291,9 @@ public record SetupStatusResponse( boolean setupComplete, boolean hasOrganizationInfo, boolean hasConnections, - boolean hasLlmConfig + boolean hasLlmConfig, + /** Whether Google Workspace SSO is configured; drives the login page's SSO button. */ + boolean googleEnabled ) {} public record InitializeRequest(String orgName, String adminUsername, String adminEmail, String adminPassword) {} diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx index 80e0d41..308ec5f 100644 --- a/src/pages/Login.jsx +++ b/src/pages/Login.jsx @@ -99,6 +99,7 @@ export default function Login() { const renderLoginStep = () => ( + <>
, not a button: /api/auth/google/start issues a 302 to Google, + so this is a full-page navigation and must not submit the form above. + */} + {setupStatus?.googleEnabled && ( +
+
+ + + + + Sign in with Google + +
+ )} + ) const renderOtpStep = () => (