From 1ab66eaed451dd41ddf6522e7bef6b2f230020ee Mon Sep 17 00:00:00 2001 From: Ubbe Date: Tue, 25 Nov 2025 15:47:29 +0700 Subject: [PATCH] fix(frontend): login/signup pages away redirects (#11430) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Changes 🏗️ When the user is logged in and tries to navigate to `/login` or `/signup` manually, redirect them away to `/marketplace`. ### Checklist 📋 #### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: - [x] Login - [x] Go to `/login` or `/signup` - [x] You are redirected back into `/marketplace` --- .../src/app/(platform)/login/useLoginPage.ts | 19 +++++++++--- .../app/(platform)/signup/useSignupPage.ts | 20 +++++++++++-- .../frontend/src/tests/signin.spec.ts | 30 +++++++++++++++++-- 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/autogpt_platform/frontend/src/app/(platform)/login/useLoginPage.ts b/autogpt_platform/frontend/src/app/(platform)/login/useLoginPage.ts index 14999954be..8eea6752a5 100644 --- a/autogpt_platform/frontend/src/app/(platform)/login/useLoginPage.ts +++ b/autogpt_platform/frontend/src/app/(platform)/login/useLoginPage.ts @@ -4,21 +4,28 @@ import { environment } from "@/services/environment"; import { loginFormSchema, LoginProvider } from "@/types/auth"; import { zodResolver } from "@hookform/resolvers/zod"; import { useRouter } from "next/navigation"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import z from "zod"; import { login as loginAction } from "./actions"; export function useLoginPage() { - const { supabase, user, isUserLoading } = useSupabase(); + const { supabase, user, isUserLoading, isLoggedIn } = useSupabase(); const [feedback, setFeedback] = useState(null); const router = useRouter(); const { toast } = useToast(); const [isLoading, setIsLoading] = useState(false); + const [isLoggingIn, setIsLoggingIn] = useState(false); const [isGoogleLoading, setIsGoogleLoading] = useState(false); const [showNotAllowedModal, setShowNotAllowedModal] = useState(false); const isCloudEnv = environment.isCloud(); + useEffect(() => { + if (isLoggedIn && !isLoggingIn) { + router.push("/marketplace"); + } + }, [isLoggedIn, isLoggingIn]); + const form = useForm>({ resolver: zodResolver(loginFormSchema), defaultValues: { @@ -29,6 +36,7 @@ export function useLoginPage() { async function handleProviderLogin(provider: LoginProvider) { setIsGoogleLoading(true); + setIsLoggingIn(true); try { const response = await fetch("/api/auth/provider", { @@ -46,6 +54,7 @@ export function useLoginPage() { if (url) window.location.href = url as string; } catch (error) { setIsGoogleLoading(false); + setIsLoggingIn(false); setFeedback( error instanceof Error ? error.message : "Failed to start OAuth flow", ); @@ -53,8 +62,6 @@ export function useLoginPage() { } async function handleLogin(data: z.infer) { - setIsLoading(true); - if (data.email.includes("@agpt.co")) { toast({ title: "Please use Google SSO to login using an AutoGPT email.", @@ -62,9 +69,12 @@ export function useLoginPage() { }); setIsLoading(false); + setIsLoggingIn(false); return; } + setIsLoggingIn(true); + try { const result = await loginAction(data.email, data.password); @@ -86,6 +96,7 @@ export function useLoginPage() { variant: "destructive", }); setIsLoading(false); + setIsLoggingIn(false); } } diff --git a/autogpt_platform/frontend/src/app/(platform)/signup/useSignupPage.ts b/autogpt_platform/frontend/src/app/(platform)/signup/useSignupPage.ts index 7c4cf19bf1..23ee8fb57c 100644 --- a/autogpt_platform/frontend/src/app/(platform)/signup/useSignupPage.ts +++ b/autogpt_platform/frontend/src/app/(platform)/signup/useSignupPage.ts @@ -4,21 +4,28 @@ import { environment } from "@/services/environment"; import { LoginProvider, signupFormSchema } from "@/types/auth"; import { zodResolver } from "@hookform/resolvers/zod"; import { useRouter } from "next/navigation"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import z from "zod"; import { signup as signupAction } from "./actions"; export function useSignupPage() { - const { supabase, user, isUserLoading } = useSupabase(); + const { supabase, user, isUserLoading, isLoggedIn } = useSupabase(); const [feedback, setFeedback] = useState(null); const { toast } = useToast(); const router = useRouter(); const [isLoading, setIsLoading] = useState(false); + const [isSigningUp, setIsSigningUp] = useState(false); const [isGoogleLoading, setIsGoogleLoading] = useState(false); const [showNotAllowedModal, setShowNotAllowedModal] = useState(false); const isCloudEnv = environment.isCloud(); + useEffect(() => { + if (isLoggedIn && !isSigningUp) { + router.push("/marketplace"); + } + }, [isLoggedIn, isSigningUp]); + const form = useForm>({ resolver: zodResolver(signupFormSchema), defaultValues: { @@ -31,6 +38,7 @@ export function useSignupPage() { async function handleProviderSignup(provider: LoginProvider) { setIsGoogleLoading(true); + setIsSigningUp(true); try { const response = await fetch("/api/auth/provider", { @@ -44,6 +52,7 @@ export function useSignupPage() { if (error === "not_allowed") { setShowNotAllowedModal(true); + setIsSigningUp(false); return; } @@ -54,6 +63,7 @@ export function useSignupPage() { if (url) window.location.href = url as string; } catch (error) { setIsGoogleLoading(false); + setIsSigningUp(false); toast({ title: error instanceof Error ? error.message : "Failed to start OAuth flow", @@ -76,6 +86,8 @@ export function useSignupPage() { return; } + setIsSigningUp(true); + try { const result = await signupAction( data.email, @@ -89,10 +101,12 @@ export function useSignupPage() { if (!result.success) { if (result.error === "user_already_exists") { setFeedback("User with this email already exists"); + setIsSigningUp(false); return; } if (result.error === "not_allowed") { setShowNotAllowedModal(true); + setIsSigningUp(false); return; } @@ -100,6 +114,7 @@ export function useSignupPage() { title: result.error || "Signup failed", variant: "destructive", }); + setIsSigningUp(false); return; } @@ -107,6 +122,7 @@ export function useSignupPage() { if (next) router.replace(next); } catch (error) { setIsLoading(false); + setIsSigningUp(false); toast({ title: error instanceof Error diff --git a/autogpt_platform/frontend/src/tests/signin.spec.ts b/autogpt_platform/frontend/src/tests/signin.spec.ts index 8fdc6bceb4..6e53855a8e 100644 --- a/autogpt_platform/frontend/src/tests/signin.spec.ts +++ b/autogpt_platform/frontend/src/tests/signin.spec.ts @@ -1,11 +1,11 @@ // auth.spec.ts import test from "@playwright/test"; -import { getTestUser } from "./utils/auth"; +import { BuildPage } from "./pages/build.page"; import { LoginPage } from "./pages/login.page"; import { hasUrl, isHidden, isVisible } from "./utils/assertion"; +import { getTestUser } from "./utils/auth"; import { getSelectors } from "./utils/selectors"; -import { BuildPage } from "./pages/build.page"; test.beforeEach(async ({ page }) => { await page.goto("/login"); @@ -171,3 +171,29 @@ test("multi-tab logout with WebSocket cleanup", async ({ context }) => { await page1.close(); await page2.close(); }); + +test("logged in user is redirected from /login to /marketplace", async ({ + page, +}) => { + const testUser = await getTestUser(); + const loginPage = new LoginPage(page); + + await loginPage.login(testUser.email, testUser.password); + await hasUrl(page, "/marketplace"); + + await page.goto("/login"); + await hasUrl(page, "/marketplace"); +}); + +test("logged in user is redirected from /signup to /marketplace", async ({ + page, +}) => { + const testUser = await getTestUser(); + const loginPage = new LoginPage(page); + + await loginPage.login(testUser.email, testUser.password); + await hasUrl(page, "/marketplace"); + + await page.goto("/signup"); + await hasUrl(page, "/marketplace"); +});