Android端Expo AuthSession接收redirectUri后无法关闭跳转的问题
Expo AuthSession Android端无法从自定义Chrome标签页跳转回App的解决方案
问题描述
开发电商App时,iOS端使用Expo AuthSession的认证流程完全正常,但Android端触发认证后会打开自定义Chrome标签页,导致App被关闭,认证完成后无法跳转回App。尝试切换到expo-web-browser的WebBrowser.openAuthSessionAsync()方法、修改Android Intent、更换多台设备测试,问题均未解决。
可能原因
- Android端Deep Link配置不完整,系统无法识别
redirectUri并唤起App - Expo的
AuthSession在Android端对自定义Chrome标签页的跳转处理存在兼容性问题 - 应用的AndroidManifest.xml未正确配置intent-filter捕获Deep Link
解决方案
1. 确认Deep Link配置正确性
确保redirect_uri格式符合Android Deep Link规范,并在app.json中正确设置scheme:
{ "expo": { "android": { "scheme": "shop.${storeID}.app" // 替换为你的redirect_uri的scheme部分 } } }
2. 使用AuthSession.makeRedirectUri生成适配链接
不要硬编码redirect_uri,改用Expo提供的工具方法自动适配iOS和Android差异:
const redirect_uri = AuthSession.makeRedirectUri({ scheme: `shop.${storeID}.app`, path: 'authorize' });
3. 配置AndroidManifest.xml的intent-filter(裸机/预构建项目)
如果使用EAS Build或裸机项目,需在AndroidManifest.xml中添加intent-filter,并设置launchMode="singleTask"避免重复打开App:
<activity android:name=".MainActivity" android:launchMode="singleTask"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="shop.${storeID}.app" android:path="/authorize" /> </intent-filter> </activity>
4. 强制使用系统浏览器或代理处理跳转
调用promptAsync时启用useProxy,或指定浏览器包名,避免自定义Chrome标签页的问题:
let response = await authRequest.promptAsync({ authorizationEndpoint: authorizationRequestUrl.toString(), useProxy: true // 使用Expo代理处理跳转,兼容Android });
5. 更新Expo相关依赖
确保expo-auth-session、expo-web-browser及Expo SDK为最新版本,旧版本可能存在Android端兼容性bug。
调整后的完整代码示例
import { AuthSession, AuthSessionResult, AuthRequest, generateCodeVerifier, generateCodeChallenge, CodeChallengeMethod, generateState, generateNonce } from 'expo-auth-session'; import { useState, useEffect } from 'react'; import { ThemedView, ThemedText, Pressable, ActivityIndicator, Suspense } from 'react-native'; import { router } from 'expo-router'; export default function Login() { const [result, setResult] = useState<AuthSessionResult | null>(null) const [authRequest, setAuthRequest] = useState<AuthRequest | null>(null) const storeID = process.env.EXPO_PUBLIC_STORE_ID const clientID = process.env.EXPO_PUBLIC_CLIENT_ID const authorizationRequestUrl = new URL(`https://shopify.com/${storeID}/auth/oauth/authorize`) // 生成适配两端的跳转链接 const redirect_uri = AuthSession.makeRedirectUri({ scheme: `shop.${storeID}.app`, path: 'authorize' }); const handleAuthSession = async () => { try { if (authRequest) { console.log('Making Auth Request') let response = await authRequest.promptAsync({ authorizationEndpoint: authorizationRequestUrl.toString(), useProxy: true }) setResult(response) } else { console.log("Could not make request"); } } catch (error) { console.error(error) } } useEffect(() => { const getAuthSessionInfo= async () => { const verifier = await generateCodeVerifier() const challenge = await generateCodeChallenge(verifier!) const state = await generateState() const nonce = await generateNonce(10) setAuthRequest( new AuthRequest({ redirectUri: redirect_uri, state: state, responseType: 'code', clientId: clientID!, codeChallenge: challenge, codeChallengeMethod: CodeChallengeMethod.S256, scopes: [ 'openid', 'email', 'https://api.customers.com/auth/customer.graphql' ], extraParams: { nonce: nonce } })) } getAuthSessionInfo() }, []) useEffect(() => { console.log(result) }, [result]) return ( <ThemedView> <Suspense fallback={<ActivityIndicator/>}> <Pressable onPress={() => router.canGoBack() && router.back()}><ThemedText>Go Back</ThemedText></Pressable> <Pressable onPress={handleAuthSession} > <ThemedText>Sign In</ThemedText> </Pressable> <Pressable onPress={() => router.push('/(drawer)/')}><ThemedText>Navigate to Home</ThemedText></Pressable> <ThemedText>{JSON.stringify(result) ?? ''}</ThemedText> </Suspense> </ThemedView> ) }
内容的提问来源于stack exchange,提问作者Abraham Guerrero
相关产品推荐
相关产品推荐

