From 060e20e578cfa94c27e1de2bfd809f5ce0b9288d Mon Sep 17 00:00:00 2001 From: Stavros Date: Fri, 12 Sep 2025 17:30:30 +0300 Subject: [PATCH] feat: style oauth auto redirect screen --- cmd/root.go | 3 +- frontend/src/lib/i18n/locales/en-US.json | 3 ++ frontend/src/lib/i18n/locales/en.json | 3 ++ frontend/src/pages/continue-page.tsx | 2 +- frontend/src/pages/login-page.tsx | 40 +++++++++++++++++++++++- internal/config/config.go | 2 +- 6 files changed, 49 insertions(+), 4 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index d72cd34..aeb96a5 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -52,6 +52,7 @@ var rootCmd = &cobra.Command{ } func Execute() { + rootCmd.FParseErrWhitelist.UnknownFlags = true err := rootCmd.Execute() if err != nil { log.Fatal().Err(err).Msg("Failed to execute command") @@ -92,7 +93,7 @@ func init() { {"ldap-search-filter", "(uid=%s)", "LDAP search filter for user lookup."}, {"resources-dir", "/data/resources", "Path to a directory containing custom resources (e.g. background image)."}, {"database-path", "/data/tinyauth.db", "Path to the Sqlite database file."}, - {"trusted-proxies", "", "Comma separated list of trusted proxies (IP addresses) for correct client IP detection and for header ACLs."}, + {"trusted-proxies", "", "Comma separated list of trusted proxies (IP addresses or CIDRs) for correct client IP detection."}, } for _, opt := range configOptions { diff --git a/frontend/src/lib/i18n/locales/en-US.json b/frontend/src/lib/i18n/locales/en-US.json index 6338a88..4300428 100644 --- a/frontend/src/lib/i18n/locales/en-US.json +++ b/frontend/src/lib/i18n/locales/en-US.json @@ -14,6 +14,9 @@ "loginOauthFailSubtitle": "Failed to get OAuth URL", "loginOauthSuccessTitle": "Redirecting", "loginOauthSuccessSubtitle": "Redirecting to your OAuth provider", + "loginOauthAutoRedirectTitle": "OAuth Auto Redirect", + "loginOauthAutoRedirectSubtitle": "You will be automatically redirected to your OAuth provider to authenticate.", + "loginOauthAutoRedirectButton": "Redirect now", "continueTitle": "Continue", "continueRedirectingTitle": "Redirecting...", "continueRedirectingSubtitle": "You should be redirected to the app soon", diff --git a/frontend/src/lib/i18n/locales/en.json b/frontend/src/lib/i18n/locales/en.json index 6338a88..4300428 100644 --- a/frontend/src/lib/i18n/locales/en.json +++ b/frontend/src/lib/i18n/locales/en.json @@ -14,6 +14,9 @@ "loginOauthFailSubtitle": "Failed to get OAuth URL", "loginOauthSuccessTitle": "Redirecting", "loginOauthSuccessSubtitle": "Redirecting to your OAuth provider", + "loginOauthAutoRedirectTitle": "OAuth Auto Redirect", + "loginOauthAutoRedirectSubtitle": "You will be automatically redirected to your OAuth provider to authenticate.", + "loginOauthAutoRedirectButton": "Redirect now", "continueTitle": "Continue", "continueRedirectingTitle": "Redirecting...", "continueRedirectingSubtitle": "You should be redirected to the app soon", diff --git a/frontend/src/pages/continue-page.tsx b/frontend/src/pages/continue-page.tsx index f17bd97..dd03a4c 100644 --- a/frontend/src/pages/continue-page.tsx +++ b/frontend/src/pages/continue-page.tsx @@ -70,7 +70,7 @@ export const ContinuePage = () => { const reveal = setTimeout(() => { setLoading(false); setShowRedirectButton(true); - }, 1000); + }, 5000); return () => { clearTimeout(auto); diff --git a/frontend/src/pages/login-page.tsx b/frontend/src/pages/login-page.tsx index 3354b2d..2f3bc99 100644 --- a/frontend/src/pages/login-page.tsx +++ b/frontend/src/pages/login-page.tsx @@ -5,12 +5,14 @@ import { MicrosoftIcon } from "@/components/icons/microsoft"; import { OAuthIcon } from "@/components/icons/oauth"; import { PocketIDIcon } from "@/components/icons/pocket-id"; import { TailscaleIcon } from "@/components/icons/tailscale"; +import { Button } from "@/components/ui/button"; import { Card, CardHeader, CardTitle, CardDescription, CardContent, + CardFooter, } from "@/components/ui/card"; import { OAuthButton } from "@/components/ui/oauth-button"; import { SeperatorWithChildren } from "@/components/ui/separator"; @@ -20,7 +22,7 @@ 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"; -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { Navigate, useLocation } from "react-router"; import { toast } from "sonner"; @@ -39,8 +41,12 @@ export const LoginPage = () => { const { search } = useLocation(); const { t } = useTranslation(); const isMounted = useIsMounted(); + const [oauthAutoRedirectHandover, setOauthAutoRedirectHandover] = + useState(false); + const [showRedirectButton, setShowRedirectButton] = useState(false); const redirectTimer = useRef(null); + const redirectButtonTimer = useRef(null); const searchParams = new URLSearchParams(search); const redirectUri = searchParams.get("redirect_uri"); @@ -67,6 +73,7 @@ export const LoginPage = () => { }, 500); }, onError: () => { + setOauthAutoRedirectHandover(false); toast.error(t("loginOauthFailTitle"), { description: t("loginOauthFailSubtitle"), }); @@ -112,7 +119,11 @@ export const LoginPage = () => { !isLoggedIn && redirectUri ) { + setOauthAutoRedirectHandover(true); oauthMutation.mutate(oauthAutoRedirect); + redirectButtonTimer.current = window.setTimeout(() => { + setShowRedirectButton(true); + }, 5000); } } }, []); @@ -120,6 +131,8 @@ export const LoginPage = () => { useEffect( () => () => { if (redirectTimer.current) clearTimeout(redirectTimer.current); + if (redirectButtonTimer.current) + clearTimeout(redirectButtonTimer.current); }, [], ); @@ -137,6 +150,31 @@ export const LoginPage = () => { return ; } + if (oauthAutoRedirectHandover) { + return ( + + + + {t("loginOauthAutoRedirectTitle")} + + + {t("loginOauthAutoRedirectSubtitle")} + + + {showRedirectButton && ( + + + + )} + + ); + } return ( diff --git a/internal/config/config.go b/internal/config/config.go index 4721ffa..1ebf028 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -22,7 +22,7 @@ type Config struct { UsersFile string `mapstructure:"users-file"` SecureCookie bool `mapstructure:"secure-cookie"` OAuthWhitelist string `mapstructure:"oauth-whitelist"` - OAuthAutoRedirect string `mapstructure:"oauth-auto-redirect" validate:"oneof=none github google generic"` + OAuthAutoRedirect string `mapstructure:"oauth-auto-redirect"` SessionExpiry int `mapstructure:"session-expiry"` LogLevel string `mapstructure:"log-level" validate:"oneof=trace debug info warn error fatal panic"` Title string `mapstructure:"app-title"`