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

如何解决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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 07:45:33