Skip to content

fix(frontend): Fix login/logout actions; update credentials cache on state change #10017

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Jun 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 3 additions & 29 deletions autogpt_platform/frontend/src/app/(platform)/login/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,6 @@ import BackendAPI from "@/lib/autogpt-server-api";
import { loginFormSchema, LoginProvider } from "@/types/auth";
import { verifyTurnstileToken } from "@/lib/turnstile";

export async function logout() {
return await Sentry.withServerActionInstrumentation(
"logout",
{},
async () => {
const supabase = getServerSupabase();

if (!supabase) {
redirect("/error");
}

const { error } = await supabase.auth.signOut();

if (error) {
console.error("Error logging out", error);
return error.message;
}

revalidatePath("/", "layout");
redirect("/login");
},
);
}

async function shouldShowOnboarding() {
const api = new BackendAPI();
return (
Expand Down Expand Up @@ -59,23 +35,21 @@ export async function login(
}

// We are sure that the values are of the correct type because zod validates the form
const { data, error } = await supabase.auth.signInWithPassword(values);
const { error } = await supabase.auth.signInWithPassword(values);

if (error) {
console.error("Error logging in", error);
console.error("Error logging in:", error);
return error.message;
}

await api.createUser();

// Don't onboard if disabled or already onboarded
if (await shouldShowOnboarding()) {
revalidatePath("/onboarding", "layout");
redirect("/onboarding");
}

if (data.session) {
await supabase.auth.setSession(data.session);
}
revalidatePath("/", "layout");
redirect("/");
});
Expand Down
5 changes: 3 additions & 2 deletions autogpt_platform/frontend/src/app/(platform)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { useCallback, useState } from "react";
import { useRouter } from "next/navigation";
import Link from "next/link";
import useSupabase from "@/hooks/useSupabase";
import useSupabase from "@/lib/supabase/useSupabase";
import LoadingBox from "@/components/ui/loading";
import {
AuthCard,
Expand Down Expand Up @@ -80,6 +80,7 @@ export default function LoginPage() {
}

const error = await login(data, turnstile.token as string);
await supabase?.auth.refreshSession();
setIsLoading(false);
if (error) {
setFeedback(error);
Expand All @@ -89,7 +90,7 @@ export default function LoginPage() {
}
setFeedback(null);
},
[form, turnstile],
[form, turnstile, supabase],
);

if (user) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
StoreSubmissionsResponse,
StoreSubmissionRequest,
} from "@/lib/autogpt-server-api/types";
import useSupabase from "@/hooks/useSupabase";
import useSupabase from "@/lib/supabase/useSupabase";
import { useBackendAPI } from "@/lib/autogpt-server-api/context";

export default function Page({}: {}) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
import { Button } from "@/components/ui/button";
import { useRouter } from "next/navigation";
import { useCallback, useContext, useMemo, useState } from "react";
import { useCallback, useContext, useEffect, useMemo, useState } from "react";
import { useToast } from "@/components/ui/use-toast";
import { IconKey, IconUser } from "@/components/ui/icons";
import { Trash2Icon } from "lucide-react";
Expand All @@ -26,10 +26,10 @@ import {
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import useSupabase from "@/hooks/useSupabase";
import useSupabase from "@/lib/supabase/useSupabase";
import LoadingBox from "@/components/ui/loading";

export default function PrivatePage() {
export default function UserIntegrationsPage() {
const { supabase, user, isUserLoading } = useSupabase();
const router = useRouter();
const providers = useContext(CredentialsProvidersContext);
Expand Down Expand Up @@ -122,15 +122,15 @@ export default function PrivatePage() {
[],
);

useEffect(() => {
if (isUserLoading) return;
if (!user || !supabase) router.push("/login");
}, [isUserLoading, user, supabase, router]);

if (isUserLoading) {
return <LoadingBox className="h-[80vh]" />;
}

if (!user || !supabase) {
router.push("/login");
return null;
}

const allCredentials = providers
? Object.values(providers).flatMap((provider) =>
provider.savedCredentials
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import useSupabase from "@/hooks/useSupabase";
import useSupabase from "@/lib/supabase/useSupabase";
import { sendEmailFormSchema, changePasswordFormSchema } from "@/types/auth";
import { zodResolver } from "@hookform/resolvers/zod";
import { useCallback, useState } from "react";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { useCallback, useState } from "react";
import { useRouter } from "next/navigation";
import Link from "next/link";
import { Checkbox } from "@/components/ui/checkbox";
import useSupabase from "@/hooks/useSupabase";
import useSupabase from "@/lib/supabase/useSupabase";
import LoadingBox from "@/components/ui/loading";
import {
AuthCard,
Expand Down
55 changes: 0 additions & 55 deletions autogpt_platform/frontend/src/components/ProfileDropdown.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// components/RoleBasedAccess.tsx
import useSupabase from "@/hooks/useSupabase";
import useSupabase from "@/lib/supabase/useSupabase";
import React from "react";

interface RoleBasedAccessProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Button } from "./Button";
import { IconPersonFill } from "@/components/ui/icons";
import { ProfileDetails } from "@/lib/autogpt-server-api/types";
import { Separator } from "@/components/ui/separator";
import useSupabase from "@/hooks/useSupabase";
import useSupabase from "@/lib/supabase/useSupabase";
import { useBackendAPI } from "@/lib/autogpt-server-api/context";

export const ProfileInfoForm = ({ profile }: { profile: ProfileDetails }) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
"use client";

import { logout } from "@/app/(platform)/login/actions";
import useSupabase from "@/lib/supabase/useSupabase";
import { IconLogOut } from "@/components/ui/icons";

export const ProfilePopoutMenuLogoutButton = () => {
const supabase = useSupabase();
return (
<div
className="inline-flex w-full items-center justify-start gap-2.5"
onClick={() => logout()}
onClick={() => supabase.logOut()}
role="button"
tabIndex={0}
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { createContext, useCallback, useEffect, useState } from "react";
import useSupabase from "@/lib/supabase/useSupabase";
import {
APIKeyCredentials,
CredentialsDeleteNeedConfirmationResponse,
Expand All @@ -8,7 +10,6 @@ import {
UserPasswordCredentials,
} from "@/lib/autogpt-server-api";
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
import { createContext, useCallback, useEffect, useState } from "react";

// Get keys from CredentialsProviderName type
const CREDENTIALS_PROVIDER_NAMES = Object.values(
Expand Down Expand Up @@ -102,6 +103,7 @@ export default function CredentialsProvider({
}) {
const [providers, setProviders] =
useState<CredentialsProvidersContextType | null>(null);
const { isLoggedIn } = useSupabase();
const api = useBackendAPI();

const addCredentials = useCallback(
Expand Down Expand Up @@ -202,48 +204,50 @@ export default function CredentialsProvider({
);

useEffect(() => {
api.isAuthenticated().then((isAuthenticated) => {
if (!isAuthenticated) return;
if (!isLoggedIn) {
if (isLoggedIn == false) setProviders(null);
return;
}

api.listCredentials().then((response) => {
const credentialsByProvider = response.reduce(
(acc, cred) => {
if (!acc[cred.provider]) {
acc[cred.provider] = [];
}
acc[cred.provider].push(cred);
return acc;
},
{} as Record<CredentialsProviderName, CredentialsMetaResponse[]>,
);
api.listCredentials().then((response) => {
const credentialsByProvider = response.reduce(
(acc, cred) => {
if (!acc[cred.provider]) {
acc[cred.provider] = [];
}
acc[cred.provider].push(cred);
return acc;
},
{} as Record<CredentialsProviderName, CredentialsMetaResponse[]>,
);

setProviders((prev) => ({
...prev,
...Object.fromEntries(
CREDENTIALS_PROVIDER_NAMES.map((provider) => [
setProviders((prev) => ({
...prev,
...Object.fromEntries(
CREDENTIALS_PROVIDER_NAMES.map((provider) => [
provider,
{
provider,
{
provider,
providerName: providerDisplayNames[provider],
savedCredentials: credentialsByProvider[provider] ?? [],
oAuthCallback: (code: string, state_token: string) =>
oAuthCallback(provider, code, state_token),
createAPIKeyCredentials: (
credentials: APIKeyCredentialsCreatable,
) => createAPIKeyCredentials(provider, credentials),
createUserPasswordCredentials: (
credentials: UserPasswordCredentialsCreatable,
) => createUserPasswordCredentials(provider, credentials),
deleteCredentials: (id: string, force: boolean = false) =>
deleteCredentials(provider, id, force),
} satisfies CredentialsProviderData,
]),
),
}));
});
providerName: providerDisplayNames[provider],
savedCredentials: credentialsByProvider[provider] ?? [],
oAuthCallback: (code: string, state_token: string) =>
oAuthCallback(provider, code, state_token),
createAPIKeyCredentials: (
credentials: APIKeyCredentialsCreatable,
) => createAPIKeyCredentials(provider, credentials),
createUserPasswordCredentials: (
credentials: UserPasswordCredentialsCreatable,
) => createUserPasswordCredentials(provider, credentials),
deleteCredentials: (id: string, force: boolean = false) =>
deleteCredentials(provider, id, force),
} satisfies CredentialsProviderData,
]),
),
}));
});
}, [
api,
isLoggedIn,
createAPIKeyCredentials,
createUserPasswordCredentials,
deleteCredentials,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client";
import useSupabase from "@/hooks/useSupabase";
import useSupabase from "@/lib/supabase/useSupabase";
import { OnboardingStep, UserOnboarding } from "@/lib/autogpt-server-api";
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
import { usePathname, useRouter } from "next/navigation";
Expand Down
Loading
Loading