Expo开发客户端Supabase魔法链接auth setSession静默挂起问题
Expo SDK 54 + Supabase Auth 魔法链接登录静默挂起问题
基于Expo SDK 54开发的应用,采用Supabase Auth隐式流程实现魔法链接登录。用户点击魔法链接后,深度链接回调可正常接收URL并解析片段,但调用supabase.auth.setSession()时会静默挂起,既不返回结果也不抛出错误,应用持续停留在“设置您的账户”加载界面。此前使用PKCE流程时,exchangeCodeForSession也出现过相同挂起问题,切换至隐式流程后问题依旧。
技术栈
- Expo SDK 54
- React Native(Hermes引擎)
@supabase/supabase-jsv2.x(最新版)expo-router用于导航- EAS Build构建的iOS开发客户端(自定义URL Scheme)
- 会话存储采用AsyncStorage适配器
- 已安装并优先导入
react-native-get-random-valuespolyfill
认证流程
Gate → 邮箱输入 → 发送链接 → 接收魔法链接 → 点击链接 → 深度链接打开应用 → 回调解析#access_token → setSession() → 挂起
已验证正常的环节
- Supabase应用表验证查询(双向通信正常)
signInWithOtp魔法链接生成- 深度链接正常打开应用,URL格式为
myapp://auth/callback#access_token=...&refresh_token=... - URL片段解析可正确提取令牌
- 错误流程完全正常:令牌无效(
otp_expired)时,回调可解析错误并导航回邮箱输入页,日志正常输出
挂起场景
- 传入有效未使用令牌调用
setSession({ access_token, refresh_token })时挂起 - PKCE流程的
exchangeCodeForSession曾出现相同挂起模式 - 两个认证函数传入有效令牌时均静默挂起
诊断日志
点击未使用的全新魔法链接(成功流程):
[callback] run() entered, url: myapp://auth/callback#access_token=eyJ...&refresh_token=...&token_type=bearer&expires_in=3600 [callback] parsed fragment, has tokens: true [callback] setSession start
后续无输出,
setSession done日志从未打印。
点击已使用的魔法链接(Gmail预获取):
[callback] run() entered, url: myapp://auth/callback#error=access_denied&error_code=otp_expired&... [callback] parsed fragment, has tokens: false errorCode: access_denied [callback] error in fragment: access_denied Email link is invalid or has expired [callback] routing to: /(auth)/email (error path)
错误流程完全正常,仅成功流程挂起。
已尝试的方案
- 带
react-native-get-random-valuespolyfill的PKCE流程(仅提供crypto.getRandomValues,无crypto.subtle) - 切换至隐式流程(
flowType: 'implicit')避开PKCE - 替换SecureStore适配器为AsyncStorage
- 多次重新构建EAS开发客户端
- 卸载重装应用清空AsyncStorage
- 使用多种邮件方式确保令牌未被预使用
代码配置
Supabase客户端配置(lib/supabase.ts)
import 'react-native-get-random-values'; import 'react-native-url-polyfill/auto'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { createClient } from '@supabase/supabase-js'; export const supabase = createClient(supabaseUrl, supabaseAnonKey, { auth: { storage: AsyncStorage, autoRefreshToken: true, persistSession: true, detectSessionInUrl: false, // 自行处理深度链接回调 flowType: 'implicit', }, });
回调处理器(app/auth/callback.tsx 相关片段)
const url = Linking.useURL(); useEffect(() => { if (!url) return; if (processedRef.current === url) return; processedRef.current = url; const run = async () => { console.log('[callback] run() entered, url:', url); const fragment = parseAuthFragment(url); console.log('[callback] parsed fragment, has tokens:', !!fragment?.accessToken); if (fragment?.error) { // ... 错误流程正常执行 ... return; } if (!fragment?.accessToken || !fragment?.refreshToken) { // ... 令牌缺失处理 ... return; } console.log('[callback] setSession start'); const { error } = await supabase.auth.setSession({ access_token: fragment.accessToken, refresh_token: fragment.refreshToken, }); console.log('[callback] setSession done:', error); // ← 从未打印 // ... 后续流程未执行 ... }; run(); }, [url]);
相关Metro警告
supabase-js初始化时会触发警告:
WARN WebCrypto API is not supported. Code challenge method will default to use plain instead of sha256.
切换至隐式流程后警告仍存在,推测supabase-js在隐式流程中仍调用crypto.subtle,因API缺失导致静默挂起。
疑问
是否有人遇到React Native + Hermes环境中Supabase魔法链接认证setSession/exchangeCodeForSession静默挂起的问题?
怀疑因缺少crypto.subtle(react-native-get-random-values未polyfill该API)导致。若已解决:
- 添加完整WebCrypto polyfill是否有效?
- 降级
@supabase/supabase-js到特定版本是否可行? - 是否有兼容的Expo SDK+supabase-js+认证流程组合?
- 是否需切换至邮箱密码认证作为临时方案?
应用架构其他环节均正常,仅成功流程因该挂起受阻。
内容的提问来源于stack exchange,提问作者Ana Smith
相关产品推荐
相关产品推荐

