登录10分钟后supabase.auth.onAuthStateChange()自发触发原因
问题分析与解决
问题背景
使用Next.js搭配Supabase数据库,通过Supabase魔法链接实现身份认证,在_app.tsx中编写了以下认证监听逻辑:
useEffect(() => { const {data: authListener} = supabase.auth.onAuthStateChange((event, session) => { handleAuthChange(event, session) if (event === 'SIGNED_IN') { setIsAuthenticated(true) console.log('SIGNED_IN') console.log(`router.asPath: ${router.asPath}`) if(router.asPath.includes('/signin')) { console.log('redirect from signin') router.push('/') } } if (event === 'SIGNED_OUT') { setIsAuthenticated(false) console.log('SIGNED_OUT') router.push('/signin') } }) checkUser() return () => { authListener.unsubscribe() } }, [])
登录约10分钟后,SIGNED_IN事件会自动触发,导致页面被重定向到首页,此时router.asPath显示为带魔法链接token参数的/signin路径。
原因拆解
- Supabase会话自动刷新机制:Supabase的access token默认有效期为1小时,客户端SDK会在token过期前主动发起刷新请求。刷新成功时,无论用户是否已经处于登录状态,都会触发
SIGNED_IN事件,用于更新本地会话信息——这就是你看到的“自发触发登录事件”的核心原因。 - 路由状态残留:从日志的
router.asPath可以看出,当前路由仍保留着/signin路径和魔法链接的hash参数。这是因为登录跳转后,Next.js的客户端路由状态没有正确同步,或者URL中的hash参数未被清理,导致你的代码误判用户仍停留在登录页,进而执行了重定向逻辑。
解决方案
1. 优化SIGNED_IN事件的判断逻辑
不要仅通过router.asPath判断是否需要重定向,结合会话是否为新创建、当前页面的实际路径(不带hash)来做判断:
// 在_app.tsx组件中新增状态保存会话ID const [prevSessionId, setPrevSessionId] = useState<string | null>(null); useEffect(() => { const {data: authListener} = supabase.auth.onAuthStateChange((event, session) => { handleAuthChange(event, session); if (event === 'SIGNED_IN') { setIsAuthenticated(true); // 仅当会话是全新创建,且当前确实在登录页时才执行重定向 const isNewSession = session?.id !== prevSessionId; const isOnSigninPage = router.pathname === '/signin'; if (isNewSession && isOnSigninPage) { router.push('/'); } // 更新会话ID记录 setPrevSessionId(session?.id || null); } if (event === 'SIGNED_OUT') { setIsAuthenticated(false); router.push('/signin'); } }); // 初始化时获取当前会话ID checkUser().then(currentSession => setPrevSessionId(currentSession?.id || null)); return () => authListener.unsubscribe(); }, [router.pathname, handleAuthChange]); // 补充完整依赖,避免闭包问题
2. 清理登录页的hash参数
在/signin页面的组件中添加逻辑,登录成功后自动清理URL中的token参数,避免路由状态残留:
// 在signin页面组件内 useEffect(() => { // 检测URL中是否包含魔法链接的token参数 if (window.location.hash.includes('access_token')) { // 保留路径,移除hash参数 window.history.replaceState({}, document.title, window.location.pathname); } }, []);
3. 修正useEffect依赖
原代码的useEffect依赖为空数组,可能导致闭包问题(比如引用旧的router或handleAuthChange)。将依赖补充完整,确保逻辑使用的是最新的状态和方法。
内容的提问来源于stack exchange,提问作者Neil Girardi
相关产品推荐
相关产品推荐

