如何解决Expo Go中Google OAuth的无效请求错误400?
Expo Go下Google OAuth 400无效请求问题排查方案
核心问题原因
Expo Go是共享运行环境,和你打包的APK/网页端不同,它使用自身的应用签名、包名/Bundle ID,导致你在Google Console中配置的自有凭证无法匹配,从而触发400错误。
具体修复步骤
1. 为Expo Go配置Google Console凭证
- Android端:
在Google Console对应androidClientId的Android客户端中,添加Expo Go的包名和签名指纹:- 包名:
host.exp.exponent - SHA-256指纹:
94:10:1E:71:12:45:6D:E7:56:28:6B:8D:73:F8:BB:BA:8C:9A:3D:3E:87:2F:25:14:B3:0E:4E:8E:0A:FC:63
- 包名:
- iOS端:
在Google Console对应iosClientId的iOS客户端中,添加Expo Go的Bundle ID:host.exp.exponent
2. 调整useProxy参数
在Google.useAuthRequest中将useProxy设为true,适配Expo Go的重定向流程:
const [request, response, promptAsync] = Google.useAuthRequest({ webClientId: "871816637044-ejvm***", expoClientId: "871816637044-ta7mh***", iosClientId: "871816637044-esek***", androidClientId: "871816637044-taf44***", useProxy: true // 改为true });
3. 验证Expo Client ID的重定向URI
确保Google Console中对应expoClientId的Web应用已添加以下重定向URI:https://auth.expo.io/@你的Expo用户名/google-sign-in
(替换@你的Expo用户名为你实际的Expo账户用户名)
4. 清理缓存并重启
- 清除Expo Go应用的缓存
- 重启Expo开发服务器(执行命令
expo start -c)
附:原始代码参考
app.json
{"expo": {"plugins": [["@react-native-google-signin/google-signin", {"iosUrlScheme": "com.googleusercontent.apps.871816637044-***"}]],"name": "google-sign-in","slug": "google-sign-in","version": "1.0.0","orientation": "portrait","icon": "./assets/icon.png","userInterfaceStyle": "light","splash": {"image": "./assets/splash.png","resizeMode": "contain","backgroundColor": "#ffffff"},"assetBundlePatterns": ["**/*"],"ios": {"supportsTablet": true,"bundleIdentifier": "com.alexx.googlesignin","infoPlist": {"CFBundleURLTypes": [{"CFBundleURLSchemes": ["com.googleusercontent.apps.871816637044-***"]}]}},"android": {"adaptiveIcon": {"foregroundImage": "./assets/adaptive-icon.png","backgroundColor": "#ffffff"},"package": "com.alexx.googlesignin"},"web": {"favicon": "./assets/favicon.png"},"scheme": "com.alexx.googlesignin","extra": {"eas": {"projectId": "0c0362e2-936e-4029-afbb-ee123b9a93f0"}},"runtimeVersion": {"policy": "appVersion"},"updates": {"url": "https://u.expo.dev/0c0362e2***"}}}
app.js
WebBrowser.maybeCompleteAuthSession(); export default function App() { const [token, setToken] = useState(""); const [userInfo, setUserInfo] = useState(null); const [request, response, promptAsync] = Google.useAuthRequest({ webClientId: "871816637044-ejvm***", expoClientId: "871816637044-ta7mh***", iosClientId: "871816637044-esek***", androidClientId: "871816637044-taf44***", useProxy: false }); useEffect(() => { handleEffect(); }, [response, token]); async function handleEffect() { const user = await getLocalUser(); console.log("user", user); if (!user) { if (response?.type === "success") { getUserInfo(response.authentication.accessToken); } } else { setUserInfo(user); console.log("loaded locally"); } } const getLocalUser = async () => { const data = await AsyncStorage.getItem("@user"); if (!data) return null; return JSON.parse(data); }; const getUserInfo = async (token) => { if (!token) return; try { const response = await fetch( "https://www.googleapis.com/userinfo/v2/me", { headers: { Authorization: `Bearer ${token}` }, } ); const user = await response.json(); await AsyncStorage.setItem("@user", JSON.stringify(user)); setUserInfo(user); } catch (error) { // Add your own error handler here } }; return ( <View style={styles.container}> {!userInfo ? ( <Button title="Sign in with Google" disabled={!request} onPress={() => { promptAsync(); }} /> ) : ( <View style={styles.card}> {userInfo?.picture && ( <Image source={{ uri: userInfo?.picture }} style={styles.image} /> )} <Text style={styles.text}>Email: {userInfo.email}</Text> <Text style={styles.text}> Verified: {userInfo.verified_email ? "yes" : "no"} </Text> <Text style={styles.text}>Name: {userInfo.name}</Text> </View> )} <Button title="remove local store" onPress={async () => await AsyncStorage.removeItem("@user")} /> </View> ); }
内容的提问来源于stack exchange,提问作者Alex M
相关产品推荐
相关产品推荐

