diff --git a/cda-gui/src/pages/swagger-ui/index.jsx b/cda-gui/src/pages/swagger-ui/index.jsx index d1a40ed56..31612584b 100644 --- a/cda-gui/src/pages/swagger-ui/index.jsx +++ b/cda-gui/src/pages/swagger-ui/index.jsx @@ -4,9 +4,10 @@ import "../../css/swagger.css"; import { createCwmsLoginAuthMethod, - createKeycloakAuthMethod, + useAuth, } from "@usace-watermanagement/groundwork-water"; import { useEffect, useMemo, useRef, useState } from "react"; +import { useAuthConfiguration } from "../../components/auth-configuration-context"; import { getBasePath } from "../../utils/base"; import { getKeycloakConfig, @@ -26,16 +27,15 @@ function isExternalOpenIdOnLocalhost(keycloakConfig) { } export default function SwaggerUI() { + const appAuth = useAuth(); + const { error: appAuthError } = useAuthConfiguration(); const [authStatus, setAuthStatus] = useState("checking"); const [customAuthType, setCustomAuthType] = useState(null); const [authUiMode, setAuthUiMode] = useState("hidden"); - const [isOpenIdAuthReady, setIsOpenIdAuthReady] = useState(false); const [isLocalOpenIdAuth, setIsLocalOpenIdAuth] = useState(false); const [authError, setAuthError] = useState(null); const [swaggerError, setSwaggerError] = useState(null); const autoLoginAttemptedRef = useRef(false); - const openIdAuthMethodRef = useRef(null); - const openIdAuthConfigSignatureRef = useRef(null); const cwmsAuthMethod = useMemo(() => { const basePath = getBasePath(); return createCwmsLoginAuthMethod({ @@ -43,21 +43,20 @@ export default function SwaggerUI() { authCheckUrl: `${basePath}/auth/keys`, }); }, []); - const openIdAuthMethod = isOpenIdAuthReady ? openIdAuthMethodRef.current : null; - const authMethod = - customAuthType === "cwms" - ? cwmsAuthMethod - : customAuthType === "openid" - ? openIdAuthMethod - : null; + const authMethod = customAuthType === "cwms" ? cwmsAuthMethod : null; + const hasAuthMethod = + customAuthType === "openid" ? !appAuthError : Boolean(authMethod); const authLabel = authUiMode === "cwms-login" ? "CWMS Login" : "CWBI Login"; const getUnavailableMessage = () => { - if (authMethod) { + if (hasAuthMethod) { return null; } if (customAuthType === "openid") { + if (appAuthError) { + return appAuthError; + } return isLoopbackHost(window.location.hostname) ? "Local sign-in is not available for this OpenID configuration. Start the local CDA/Keycloak stack, or use Swagger's Authorize controls with an API key." : "Sign-in is not available because this Swagger spec does not advertise a usable OpenID client."; @@ -109,7 +108,6 @@ export default function SwaggerUI() { setCustomAuthType(null); setAuthUiMode("hidden"); setAuthStatus("anonymous"); - setIsOpenIdAuthReady(false); setIsLocalOpenIdAuth(false); document.querySelector("#swagger-ui").innerHTML = ""; } @@ -135,46 +133,24 @@ export default function SwaggerUI() { if (nextCustomAuthType === "cwms") { setAuthUiMode("cwms-login"); } else if (nextCustomAuthType === "openid" && keycloakConfig) { - const keycloakConfigSignature = JSON.stringify(keycloakConfig); if (isExternalOpenIdOnLocalhost(keycloakConfig)) { - openIdAuthMethodRef.current = null; - openIdAuthConfigSignatureRef.current = null; setAuthUiMode("hidden"); setAuthError(null); setAuthStatus("anonymous"); setIsLocalOpenIdAuth(false); - setIsOpenIdAuthReady(false); } else { - try { - if (openIdAuthConfigSignatureRef.current !== keycloakConfigSignature) { - openIdAuthMethodRef.current = createKeycloakAuthMethod(keycloakConfig); - openIdAuthConfigSignatureRef.current = keycloakConfigSignature; - } - setAuthError(null); - setIsLocalOpenIdAuth(keycloakConfig.flow === "direct-grant"); - setAuthUiMode( - keycloakConfig.flow === "direct-grant" - ? "local-keycloak" - : "external-openid", - ); - setIsOpenIdAuthReady(true); - } catch (error) { - openIdAuthMethodRef.current = null; - openIdAuthConfigSignatureRef.current = null; - setAuthUiMode("external-openid"); - setAuthError(error?.message ?? "Unable to configure OpenID sign-in."); - setAuthStatus("anonymous"); - setIsLocalOpenIdAuth(false); - setIsOpenIdAuthReady(false); - } + setAuthError(null); + setIsLocalOpenIdAuth(keycloakConfig.flow === "direct-grant"); + setAuthUiMode( + keycloakConfig.flow === "direct-grant" + ? "local-keycloak" + : "external-openid", + ); } } else { - openIdAuthMethodRef.current = null; - openIdAuthConfigSignatureRef.current = null; setAuthUiMode("hidden"); setAuthError(null); setIsLocalOpenIdAuth(false); - setIsOpenIdAuthReady(false); } if (cancelled) { @@ -204,8 +180,10 @@ export default function SwaggerUI() { req.url = requestUrl.toString(); req.headers = req.headers ?? {}; - if (authMethod?.token) { - req.headers.Authorization = `Bearer ${authMethod.token}`; + const token = + customAuthType === "openid" ? appAuth.token : authMethod?.token; + if (token) { + req.headers.Authorization = `Bearer ${token}`; } req.cache = "no-store"; @@ -247,10 +225,10 @@ export default function SwaggerUI() { cancelled = true; document.querySelector("#swagger-ui").innerHTML = ""; }; - }, [authMethod, customAuthType]); + }, [appAuth.token, authMethod, customAuthType]); useEffect(() => { - if (!authMethod) { + if (customAuthType !== "cwms" || !authMethod) { setAuthStatus("anonymous"); return; } @@ -271,12 +249,12 @@ export default function SwaggerUI() { return () => { mounted = false; }; - }, [authMethod]); + }, [authMethod, customAuthType]); useEffect(() => { if ( customAuthType !== "openid" || - !authMethod || + !hasAuthMethod || !isLocalOpenIdAuth || autoLoginAttemptedRef.current ) { @@ -287,14 +265,8 @@ export default function SwaggerUI() { let mounted = true; setAuthStatus("checking"); setAuthError(null); - authMethod + appAuth .login() - .then(() => authMethod.isAuth()) - .then((isAuth) => { - if (mounted) { - setAuthStatus(isAuth ? "authenticated" : "anonymous"); - } - }) .catch((error) => { if (mounted) { setAuthError(error?.message ?? "Automatic local sign-in failed."); @@ -305,10 +277,10 @@ export default function SwaggerUI() { return () => { mounted = false; }; - }, [authMethod, customAuthType, isLocalOpenIdAuth]); + }, [appAuth, customAuthType, hasAuthMethod, isLocalOpenIdAuth]); const startLogin = async () => { - if (!authMethod) { + if (!hasAuthMethod) { setAuthError(getUnavailableMessage()); return; } @@ -316,8 +288,12 @@ export default function SwaggerUI() { setAuthError(null); setAuthStatus("checking"); try { - await authMethod.login(); - await checkAuth(); + if (customAuthType === "openid") { + await appAuth.login(); + } else { + await authMethod.login(); + await checkAuth(); + } } catch (error) { setAuthError(error?.message ?? "Sign-in failed."); setAuthStatus("anonymous"); @@ -325,27 +301,33 @@ export default function SwaggerUI() { }; const signOut = async () => { - if (!authMethod) { + if (!hasAuthMethod) { return; } setAuthError(null); setAuthStatus("checking"); try { - await authMethod.logout(); - await checkAuth(); + if (customAuthType === "openid") { + await appAuth.logout(); + } else { + await authMethod.logout(); + await checkAuth(); + } } catch (error) { setAuthError(error?.message ?? "Sign-out failed."); setAuthStatus("anonymous"); } }; - const isAuthenticated = authStatus === "authenticated"; - const isCheckingAuth = authStatus === "checking"; + const isAuthenticated = + customAuthType === "openid" ? appAuth.isAuth : authStatus === "authenticated"; + const isCheckingAuth = + customAuthType === "openid" ? appAuth.isLoading : authStatus === "checking"; const unavailableMessage = getUnavailableMessage(); const showAuthBar = authUiMode !== "hidden"; const showUnavailableMessage = - showAuthBar && !authMethod && authStatus !== "checking"; + showAuthBar && !hasAuthMethod && !isCheckingAuth; return ( <> @@ -374,7 +356,7 @@ export default function SwaggerUI() { )}