From 6c900463430ea4af2eb071fd4e4eb2fa3195fd88 Mon Sep 17 00:00:00 2001 From: Stavros Date: Fri, 21 Nov 2025 17:37:01 +0200 Subject: [PATCH] feat: add option to disable ui warnings --- cmd/root.go | 1 + frontend/package.json | 2 +- frontend/src/components/layout/layout.tsx | 4 +- frontend/src/lib/hooks/use-is-mounted.ts | 15 ------- frontend/src/pages/continue-page.tsx | 29 +++++++------- frontend/src/pages/login-page.tsx | 39 +++++++------------ frontend/src/schemas/app-context-schema.ts | 1 + internal/bootstrap/app_bootstrap.go | 1 + internal/config/config.go | 1 + internal/controller/context_controller.go | 7 ++++ .../controller/context_controller_test.go | 3 ++ 11 files changed, 43 insertions(+), 60 deletions(-) delete mode 100644 frontend/src/lib/hooks/use-is-mounted.ts diff --git a/cmd/root.go b/cmd/root.go index aa1ce50..4399ef0 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -71,6 +71,7 @@ func (c *rootCmd) Register() { {"disable-analytics", false, "Disable anonymous version collection."}, {"disable-resources", false, "Disable the resources server."}, {"socket-path", "", "Path to the Unix socket to bind the server to."}, + {"disable-ui-warnings", false, "Disable UI warnings about insecure configurations."}, } for _, opt := range configOptions { diff --git a/frontend/package.json b/frontend/package.json index 64120c6..eee5688 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,5 +1,5 @@ { - "name": "tinyauth-shadcn", + "name": "tinyauth", "private": true, "version": "0.0.0", "type": "module", diff --git a/frontend/src/components/layout/layout.tsx b/frontend/src/components/layout/layout.tsx index 286d47c..5c29e1f 100644 --- a/frontend/src/components/layout/layout.tsx +++ b/frontend/src/components/layout/layout.tsx @@ -31,7 +31,7 @@ const BaseLayout = ({ children }: { children: React.ReactNode }) => { }; export const Layout = () => { - const { appUrl } = useAppContext(); + const { appUrl, disableUiWarnings } = useAppContext(); const [ignoreDomainWarning, setIgnoreDomainWarning] = useState(() => { return window.sessionStorage.getItem("ignoreDomainWarning") === "true"; }); @@ -42,7 +42,7 @@ export const Layout = () => { setIgnoreDomainWarning(true); }, [setIgnoreDomainWarning]); - if (!ignoreDomainWarning && appUrl !== currentUrl) { + if (!ignoreDomainWarning && !disableUiWarnings && appUrl !== currentUrl) { return ( boolean { - const isMounted = useRef(false); - - useEffect(() => { - isMounted.current = true; - - return () => { - isMounted.current = false; - }; - }, []); - - return useCallback(() => isMounted.current, []); -} diff --git a/frontend/src/pages/continue-page.tsx b/frontend/src/pages/continue-page.tsx index b19205f..b6c8b00 100644 --- a/frontend/src/pages/continue-page.tsx +++ b/frontend/src/pages/continue-page.tsx @@ -14,7 +14,7 @@ import { Navigate, useLocation, useNavigate } from "react-router"; import { useEffect, useState } from "react"; export const ContinuePage = () => { - const { cookieDomain } = useAppContext(); + const { cookieDomain, disableUiWarnings } = useAppContext(); const { isLoggedIn } = useUserContext(); const { search } = useLocation(); const { t } = useTranslation(); @@ -53,12 +53,16 @@ export const ContinuePage = () => { }; useEffect(() => { + if (!isLoggedIn) { + return; + } + if ( - !isLoggedIn || - !isValidRedirectUri || - !isTrustedRedirectUri || - !isAllowedRedirectProto || - isHttpsDowngrade + (!isValidRedirectUri || + !isAllowedRedirectProto || + !isTrustedRedirectUri || + isHttpsDowngrade) && + !disableUiWarnings ) { return; } @@ -76,14 +80,7 @@ export const ContinuePage = () => { clearTimeout(auto); clearTimeout(reveal); }; - }, [ - handleRedirect, - isAllowedRedirectProto, - isHttpsDowngrade, - isLoggedIn, - isTrustedRedirectUri, - isValidRedirectUri, - ]); + }, []); if (!isLoggedIn) { return ( @@ -98,7 +95,7 @@ export const ContinuePage = () => { return ; } - if (!isTrustedRedirectUri) { + if (!isTrustedRedirectUri && !disableUiWarnings) { return ( @@ -136,7 +133,7 @@ export const ContinuePage = () => { ); } - if (isHttpsDowngrade) { + if (isHttpsDowngrade && !disableUiWarnings) { return ( diff --git a/frontend/src/pages/login-page.tsx b/frontend/src/pages/login-page.tsx index b39c580..e2efdd6 100644 --- a/frontend/src/pages/login-page.tsx +++ b/frontend/src/pages/login-page.tsx @@ -18,7 +18,6 @@ import { OAuthButton } from "@/components/ui/oauth-button"; import { SeperatorWithChildren } from "@/components/ui/separator"; import { useAppContext } from "@/context/app-context"; import { useUserContext } from "@/context/user-context"; -import { useIsMounted } from "@/lib/hooks/use-is-mounted"; import { LoginSchema } from "@/schemas/login-schema"; import { useMutation } from "@tanstack/react-query"; import axios, { AxiosError } from "axios"; @@ -40,7 +39,6 @@ export const LoginPage = () => { const { providers, title, oauthAutoRedirect } = useAppContext(); const { search } = useLocation(); const { t } = useTranslation(); - const isMounted = useIsMounted(); const [oauthAutoRedirectHandover, setOauthAutoRedirectHandover] = useState(false); const [showRedirectButton, setShowRedirectButton] = useState(false); @@ -112,31 +110,20 @@ export const LoginPage = () => { }); useEffect(() => { - if (isMounted()) { - if ( - oauthProviders.length !== 0 && - providers.find((provider) => provider.id === oauthAutoRedirect) && - !isLoggedIn && - redirectUri - ) { - // Not sure of a better way to do this - // eslint-disable-next-line react-hooks/set-state-in-effect - setOauthAutoRedirectHandover(true); - oauthMutation.mutate(oauthAutoRedirect); - redirectButtonTimer.current = window.setTimeout(() => { - setShowRedirectButton(true); - }, 5000); - } + if ( + providers.find((provider) => provider.id === oauthAutoRedirect) && + !isLoggedIn && + redirectUri + ) { + // Not sure of a better way to do this + // eslint-disable-next-line react-hooks/set-state-in-effect + setOauthAutoRedirectHandover(true); + oauthMutation.mutate(oauthAutoRedirect); + redirectButtonTimer.current = window.setTimeout(() => { + setShowRedirectButton(true); + }, 5000); } - }, [ - isMounted, - oauthProviders.length, - providers, - isLoggedIn, - redirectUri, - oauthAutoRedirect, - oauthMutation, - ]); + }, []); useEffect( () => () => { diff --git a/frontend/src/schemas/app-context-schema.ts b/frontend/src/schemas/app-context-schema.ts index ec766ee..6935ca4 100644 --- a/frontend/src/schemas/app-context-schema.ts +++ b/frontend/src/schemas/app-context-schema.ts @@ -14,6 +14,7 @@ export const appContextSchema = z.object({ forgotPasswordMessage: z.string(), backgroundImage: z.string(), oauthAutoRedirect: z.string(), + disableUiWarnings: z.boolean(), }); export type AppContextSchema = z.infer; diff --git a/internal/bootstrap/app_bootstrap.go b/internal/bootstrap/app_bootstrap.go index f717b7b..01b60f6 100644 --- a/internal/bootstrap/app_bootstrap.go +++ b/internal/bootstrap/app_bootstrap.go @@ -236,6 +236,7 @@ func (app *BootstrapApp) Setup() error { ForgotPasswordMessage: app.config.ForgotPasswordMessage, BackgroundImage: app.config.BackgroundImage, OAuthAutoRedirect: app.config.OAuthAutoRedirect, + DisableUIWarnings: app.config.DisableUIWarnings, }, apiRouter) oauthController := controller.NewOAuthController(controller.OAuthControllerConfig{ diff --git a/internal/config/config.go b/internal/config/config.go index 753f971..f13d97e 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -41,6 +41,7 @@ type Config struct { TrustedProxies string `mapstructure:"trusted-proxies"` DisableAnalytics bool `mapstructure:"disable-analytics"` DisableResources bool `mapstructure:"disable-resources"` + DisableUIWarnings bool `mapstructure:"disable-ui-warnings"` SocketPath string `mapstructure:"socket-path"` } diff --git a/internal/controller/context_controller.go b/internal/controller/context_controller.go index 592aa3a..5b3b30d 100644 --- a/internal/controller/context_controller.go +++ b/internal/controller/context_controller.go @@ -32,6 +32,7 @@ type AppContextResponse struct { ForgotPasswordMessage string `json:"forgotPasswordMessage"` BackgroundImage string `json:"backgroundImage"` OAuthAutoRedirect string `json:"oauthAutoRedirect"` + DisableUIWarnings bool `json:"disableUiWarnings"` } type Provider struct { @@ -48,6 +49,7 @@ type ContextControllerConfig struct { ForgotPasswordMessage string BackgroundImage string OAuthAutoRedirect string + DisableUIWarnings bool } type ContextController struct { @@ -56,6 +58,10 @@ type ContextController struct { } func NewContextController(config ContextControllerConfig, router *gin.RouterGroup) *ContextController { + if config.DisableUIWarnings { + log.Warn().Msg("UI warnings are disabled. This may expose users to security risks. Proceed with caution.") + } + return &ContextController{ config: config, router: router, @@ -117,5 +123,6 @@ func (controller *ContextController) appContextHandler(c *gin.Context) { ForgotPasswordMessage: controller.config.ForgotPasswordMessage, BackgroundImage: controller.config.BackgroundImage, OAuthAutoRedirect: controller.config.OAuthAutoRedirect, + DisableUIWarnings: controller.config.DisableUIWarnings, }) } diff --git a/internal/controller/context_controller_test.go b/internal/controller/context_controller_test.go index 85be0b5..bfefd13 100644 --- a/internal/controller/context_controller_test.go +++ b/internal/controller/context_controller_test.go @@ -30,6 +30,7 @@ var controllerCfg = controller.ContextControllerConfig{ ForgotPasswordMessage: "Contact admin to reset your password.", BackgroundImage: "/assets/bg.jpg", OAuthAutoRedirect: "google", + DisableUIWarnings: false, } var userContext = config.UserContext{ @@ -75,6 +76,7 @@ func TestAppContextHandler(t *testing.T) { ForgotPasswordMessage: controllerCfg.ForgotPasswordMessage, BackgroundImage: controllerCfg.BackgroundImage, OAuthAutoRedirect: controllerCfg.OAuthAutoRedirect, + DisableUIWarnings: controllerCfg.DisableUIWarnings, } router, recorder := setupContextController(nil) @@ -102,6 +104,7 @@ func TestUserContextHandler(t *testing.T) { Provider: userContext.Provider, OAuth: userContext.OAuth, TotpPending: userContext.TotpPending, + OAuthName: userContext.OAuthName, } // Test with context