如何在Supabase中实现认证而不混用Server Actions与Client Actions?
问题:Supabase + Next.js 混和认证导致状态追踪失效
我用Supabase和NextJS实现认证功能,大部分操作(登录、注册等)采用服务端实现,但登出操作按照Supabase文档建议用客户端实现。这种服务端与客户端操作的混用,导致无法可靠追踪认证状态变化——因为每种操作仅会向客户端或服务端触发事件。我尝试实现一个根据用户登录状态切换登录/登出按钮的Header,但目前完全无法正常工作。
客户端登出代码(src/app/auth/sign-out/actions.ts)
'use client'; // src/app/auth/sign-out/actions.ts import { createClient } from '@/lib/supabase/client'; import { revalidatePath } from 'next/cache'; import { redirect } from 'next/navigation'; export async function signout() { const supabase = await createClient(); console.log("its working") const { error } = await supabase.auth.signOut({ scope: "local" }); if (error) { return { success: false, error: error.message }; } return { success: true }; }
服务端认证操作代码(src/lib/auth-actions.ts)
// src/lib/auth-actions.ts 'use server' import { revalidatePath } from 'next/cache' import { redirect } from 'next/navigation' import { createClient } from '@/lib/supabase/server' export async function login(formData: FormData) { const supabase = await createClient() const data = { email: formData.get('email') as string, password: formData.get('password') as string, } const { error } = await supabase.auth.signInWithPassword(data) if (error) { return { success: false, error: error.message } } revalidatePath('/', 'layout') redirect('/') return { success: true, error: "" } } export async function signup(formData: FormData) { const supabase = await createClient() const data = { email: formData.get('email') as string, password: formData.get('password') as string, options: { data: { fname: formData.get('fname') as string, lname: formData.get('lname') as string, } } } const { error } = await supabase.auth.signUp(data) console.log(error) if (error) { return { success: false, error: error.message } } revalidatePath('/', 'layout') return { success: true, error: null } } export async function loginWithGoogle() { const supabase = await createClient(); const { data, error } = await supabase.auth.signInWithOAuth({ provider: 'google', options: { queryParams: { access_type: "offline", prompt: "consent", } } }); if (data.url) { redirect(data.url) // use the redirect API for your server framework } redirect("/") return { success: !error, error: error ? error.message : null }; } export async function sendPasswordResetEmail(email: string) { const supabase = await createClient(); const { data, error } = await supabase.auth.resetPasswordForEmail(email); if (error) { return { success: false, error: error.message }; } return { success: true, error: null }; } export async function updatePassword(newPassword: string) { const supabase = await createClient(); const { error } = await supabase.auth.updateUser({ password: newPassword }); if (error) { return { success: false, error: error.message }; } return { success: true, error: null }; }
Navbar组件代码(src/components/Navbar.tsx)
// src/components/Navbar.tsx 'use server'; import AuthBtn from "./AuthBtn"; import { createClient } from "@/lib/supabase/server"; export default async function Navbar() { const supabase = await createClient(); const { data: { user }, } = await supabase.auth.getUser(); return ( <header className="bg-teal-500 w-full relative z-50 shadow-md"> <nav className="container mx-auto px-6 py-4 flex justify-between items-center"> {/* Title */} <h1 className="text-white font-bold text-2xl">Newsly</h1> <AuthBtn isLoggedIn={!!user} /> </nav> </header> ); }
已尝试的解决方案
- 使用Context和AuthProvider组件统一管理认证状态
- 在layout.tsx中使用服务端Supabase实例,Navbar.tsx中使用客户端实例
以上方法均未解决问题。
内容的提问来源于stack exchange,提问作者frzzled
相关产品推荐
相关产品推荐

