使用Pages目录搭建Supabase Auth + Next.js遇到的问题
解决方案:Next.js Pages 目录集成 Supabase Auth 并保留 _app.tsx
1. 修复 _app.tsx 的 Supabase 状态配置
问题核心是 _app.tsx 中 Supabase 客户端初始化或状态同步逻辑冲突,导致 Auth 状态异常。按以下方式配置全局 Provider:
import { createContext, useContext, useEffect, useState } from 'react' import { Session, createClientComponentClient } from '@supabase/auth-helpers-nextjs' import type { AppProps } from 'next/app' const SupabaseContext = createContext<{ supabase: ReturnType<typeof createClientComponentClient> session: Session | null } | null>(null) export const useSupabase = () => { const context = useContext(SupabaseContext) if (!context) throw new Error('useSupabase 必须在 SupabaseProvider 内部使用') return context } export default function App({ Component, pageProps }: AppProps<{ session: Session }>) { const supabase = createClientComponentClient() const [session, setSession] = useState<Session | null>(pageProps.session) useEffect(() => { const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => { setSession(session) }) return () => subscription.unsubscribe() }, [supabase]) return ( <SupabaseContext.Provider value={{ supabase, session }}> <Component {...pageProps} /> </SupabaseContext.Provider> ) }
2. 实现邮箱/密码 + OAuth 登录页面
基于 @supabase/auth-ui-react 快速构建登录界面,直接复用全局 Supabase 实例:
import { Auth } from '@supabase/auth-ui-react' import { ThemeSupa } from '@supabase/auth-ui-shared' import { useSupabase } from '../_app' export default function LoginPage() { const { supabase } = useSupabase() return ( <div style={{ maxWidth: '400px', margin: '2rem auto' }}> <Auth supabaseClient={supabase} appearance={{ theme: ThemeSupa }} providers={['google', 'github']} // 按需添加OAuth服务商 redirectTo="/dashboard" signUp={true} /> </div> ) }
3. 配置 Middleware 保护页面
通过 middleware.ts 拦截未认证用户访问受保护路由:
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs' import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' export async function middleware(req: NextRequest) { const res = NextResponse.next() const supabase = createMiddlewareClient({ req, res }) const { data: { session } } = await supabase.auth.getSession() // 未登录访问受保护路由时重定向到登录页 if (!session && req.nextUrl.pathname.startsWith('/dashboard')) { return NextResponse.redirect(new URL('/login', req.url)) } return res } // 指定需要保护的路由规则 export const config = { matcher: ['/dashboard/:path*'], }
4. 同步服务端与客户端状态
在受保护页面中通过 getServerSideProps 传递 session,避免状态不一致:
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs' import { cookies } from 'next/headers' import type { GetServerSidePropsContext } from 'next' import { useSupabase } from '../_app' export default function DashboardPage() { const { session } = useSupabase() return <div>欢迎,{session?.user.email}</div> } export async function getServerSideProps(context: GetServerSidePropsContext) { const supabase = createServerComponentClient({ cookies: () => context.req.cookies }) const { data: { session } } = await supabase.auth.getSession() if (!session) { return { redirect: { destination: '/login', permanent: false, }, } } return { props: { session }, } }
关键注意事项
- 全局仅保留一个 Supabase 客户端实例,避免重复初始化
- 区分环境使用对应客户端:Middleware 用
createMiddlewareClient,客户端组件用createClientComponentClient,服务端组件用createServerComponentClient - 确保
@supabase/auth-helpers-nextjs与 Next.js 版本兼容(推荐使用最新稳定版)
内容的提问来源于stack exchange,提问作者user20951184
相关产品推荐
相关产品推荐

