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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 04:47:10