From bacb070e6d32b503345722cd86136e6793d43608 Mon Sep 17 00:00:00 2001 From: Hayden Hargreaves Date: Thu, 12 Mar 2026 20:16:45 -0700 Subject: [PATCH] fix: Using UI to set the cookie instead of server. --- internal/app/server/auth_handler_v2.go | 12 +++++------ web/src/App.tsx | 4 ++++ web/src/pages/AuthCallback.tsx | 29 ++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 web/src/pages/AuthCallback.tsx diff --git a/internal/app/server/auth_handler_v2.go b/internal/app/server/auth_handler_v2.go index 5ab70f3..d3c9b17 100644 --- a/internal/app/server/auth_handler_v2.go +++ b/internal/app/server/auth_handler_v2.go @@ -4,7 +4,6 @@ import ( "fmt" "net/http" "net/url" - "time" "github.com/gin-gonic/gin" ) @@ -32,12 +31,13 @@ func (s *Server) GoogleCallbackHandlerV2(ctx *gin.Context) { domain := s.deps.EnvironmentConfig.FrontendDomain if jwt, err := s.deps.AuthService.GoogleAuthSuccess(state, code); err != nil { - url := fmt.Sprintf("%s/v2/web/login?error=%s", domain, url.QueryEscape(err.Error())) - ctx.Redirect(http.StatusSeeOther, url) + redirectUrl := fmt.Sprintf("%s/v2/web/login?error=%s", domain, url.QueryEscape(err.Error())) + ctx.Redirect(http.StatusSeeOther, redirectUrl) } else { - url := fmt.Sprintf("%s/v2/web/home", domain) - s.SetCookie(ctx, "jwt_token", jwt, time.Hour*24*7) - ctx.Redirect(http.StatusSeeOther, url) + // Pass JWT via query param - frontend will set the cookie + // This bypasses cross-origin cookie issues with Cloudflare/proxies + redirectUrl := fmt.Sprintf("%s/v2/web/auth/callback?token=%s", domain, url.QueryEscape(jwt)) + ctx.Redirect(http.StatusSeeOther, redirectUrl) } } diff --git a/web/src/App.tsx b/web/src/App.tsx index a4ef3b0..8e46a3d 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -13,6 +13,7 @@ import { use, type ReactNode } from 'react'; import { AuthContext } from './context/AuthContext'; import RecipePage from './pages/Recipe'; import SearchPage from './pages/Search'; +import AuthCallback from './pages/AuthCallback'; function ProtectedRoute({ children }: { children: ReactNode }) { const { isLoggedIn } = use(AuthContext) @@ -37,6 +38,9 @@ function App() { {/* Login page does not inherit WebLayout */} } /> + {/* Auth callback - handles token from OAuth redirect */} + } /> + }> } /> } /> diff --git a/web/src/pages/AuthCallback.tsx b/web/src/pages/AuthCallback.tsx new file mode 100644 index 0000000..545b9ff --- /dev/null +++ b/web/src/pages/AuthCallback.tsx @@ -0,0 +1,29 @@ +import { useEffect } from "react"; +import { useSearchParams, useNavigate } from "react-router-dom"; +import { useCookies } from "react-cookie"; + +export default function AuthCallback() { + const [searchParams] = useSearchParams(); + const navigate = useNavigate(); + const [, setCookie] = useCookies(["jwt_token"]); + + useEffect(() => { + const token = searchParams.get("token"); + + if (token) { + // Set cookie with 7 day expiration, accessible across all subdomains + setCookie("jwt_token", token, { + path: "/", + maxAge: 60 * 60 * 24 * 7, // 7 days in seconds + secure: true, + sameSite: "lax", + }); + void navigate("/v2/web/home", { replace: true }); + } else { + // No token provided, redirect to login + void navigate("/v2/web/login", { replace: true }); + } + }, [searchParams, setCookie, navigate]); + + return null; +} -- 2.47.2