You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Supabase Auth UI+Google登录无法触发AuthProvider,前端无会话问题排查

问题:Vite + React Router v6 + Supabase Google OAuth 认证后前端无法获取会话

我正在使用Vite + React Router v6 + Supabase Auth组件结合Google OAuth提供商,已完成以下配置:

  • Supabase控制台添加了Google凭据
  • Google客户端配置中设置了回调地址 https://{my_supabase_id}.supabase.co/auth/v1/callback
  • Google授权源和Supabase站点URL均配置为Vite本地地址 http://localhost:5173

目前Supabase认证表中能看到用户已完成认证,但前端AuthProvider里的会话创建调试断点从未触发,导致页面因检测不到会话而自动跳转到登录界面。

以下是我的相关代码:

AuthProvider 代码

const AuthProvider = (props: AuthProviderProps) => {
  const [user, setUser] = useState<User | null>(null);
  const [session, setSession] = useState<Session | null>(null);
  const [loading, setLoading] = useState<boolean>(true);

  useEffect(() => {
    const { data: listener } = supabase.auth.onAuthStateChange(
      (_event, session) => {
        console.log('session onAuthStateChange: ', session);
        if (session) {
          debugger;
        }
        setSession(session);

        setUser(session?.user || null);
        setLoading(false);
      }
    );
    const setData = async () => {
      const {
        data: { session },
        error,
      } = await supabase.auth.getSession();
      console.log('session at setData:', session);
      if (session) {
        debugger;
      }

      if (error) {
        throw error;
      }
      setSession(session);
      setUser(session?.user || null);
      setLoading(false);
    };
    setData();

    return () => {
      listener?.subscription.unsubscribe();
    };
  }, [supabase.auth]);

  const value = {
    session,
    user,
  };

  return (
    <AuthContext.Provider value={value}>{props.children}</AuthContext.Provider>
  );
};

export const useAuth = () => {
  return useContext(AuthContext);
};

export default AuthProvider;

路由配置代码

const router = createBrowserRouter([
  {
    path: '/',
    element: (
      <AuthProvider>
        <Root />
      </AuthProvider>
    ),
    errorElement: <ErrorPage />,
    children: [
      {
        path: 'login',
        element: <Login />,
      },
      {
        path: '/',
        element: <ProtectedPage />,
        children: [
          {
            path: 'picks',
            element: <Picks />,
            loader: picksLoader,
          },
        ],
      },
    ],
  },
]);

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
);

ProtectedPage 组件代码

const ProtectedPage = () => {
  const { user, session } = useAuth();

  if (!session) {
    return <Navigate to='/login' replace />;
  }

  return <Layout />;
};

Login 组件代码

export const Login = () => {
  return (
    <div className='md:flex md:justify-center mb-6'>
      <div className='flex-col prose'>
        <h1>bp2</h1>
        <div className='items-center justify-center mx-12 h-screen'>
          <Auth
            supabaseClient={supabase}
            appearance={{ theme: ThemeSupa }}
            providers={['google']}
          />
        </div>
      </div>
    </div>
  );
};

简单流程说明:访问根路径/时会加载<ProtectedPage>,如果检测不到session就跳转到登录页面;登录组件使用了@supabase/auth-ui-react的<Auth />组件实现Google登录。

我遗漏了什么配置或代码逻辑?


解决方案

1. 修复会话加载时的误跳转问题

你的ProtectedPage忽略了组件初始化阶段的loading状态——此时会话还未完成获取,就被误判为未登录并跳转。

首先更新AuthProvider的context值,把loading状态传递出去:

const value = {
  session,
  user,
  loading, // 新增这行
};

然后修改ProtectedPage,先等待会话加载完成:

const ProtectedPage = () => {
  const { user, session, loading } = useAuth();

  if (loading) {
    return <div>加载中...</div>; // 可替换为骨架屏等自定义加载状态
  }

  if (!session) {
    return <Navigate to='/login' replace />;
  }

  return <Layout />;
};

2. 优化AuthProvider的状态更新逻辑

添加组件挂载状态标记,避免组件卸载后更新状态,同时处理重复设置loading的问题:

useEffect(() => {
  let isMounted = true;

  const { data: listener } = supabase.auth.onAuthStateChange(
    (_event, session) => {
      if (!isMounted) return;
      console.log('session onAuthStateChange: ', session);
      setSession(session);
      setUser(session?.user || null);
      setLoading(false);
    }
  );

  const setData = async () => {
    const { data: { session }, error } = await supabase.auth.getSession();
    if (!isMounted) return;
    console.log('session at setData:', session);
    if (error) {
      console.error('获取会话失败:', error);
      setLoading(false);
      return;
    }
    setSession(session);
    setUser(session?.user || null);
    setLoading(false);
  };

  setData();

  return () => {
    isMounted = false;
    listener?.subscription.unsubscribe();
  };
}, [supabase.auth]);

3. 检查Supabase客户端初始化配置

确保客户端初始化时正确设置auth.redirectTo,指定为当前应用的地址:

const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
  auth: {
    redirectTo: window.location.origin, // 本地开发时会自动取http://localhost:5173
  },
});

4. 处理React StrictMode的重复挂载影响

React StrictMode会触发组件两次挂载,可能干扰Supabase监听器。上面添加的isMounted标记已经处理了这个问题,若仍有异常可暂时关闭StrictMode测试。


内容的提问来源于stack exchange,提问作者AlxVallejo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 19:20:29