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

React Native Expo Google登录授权失败:重定向URI配置问题求助

React Native Expo Google登录集成问题:重定向URI配置错误导致授权失败

我正在为React Native(Expo)应用集成Google登录功能,用户可通过Google账号直接登录,无需注册新账号。尝试过多种方案后仍未解决,推测问题出在Google开发者控制台的授权重定向URI配置上。

实现细节

GoogleLogin组件代码

import React, { useState, useEffect } from "react";
import { Button, View, Text } from "react-native";
import * as Google from "expo-auth-session/providers/google";
import * as WebBrowser from "expo-web-browser";
import {
  GOOGLE_WEB_CLIENT_ID,
  GOOGLE_ANDROID_CLIENT_ID,
  GOOGLE_IOS_CLIENT_ID,
} from "@env";
import * as Linking from "expo-linking";

console.log("GOOGLE_WEB_CLIENT_ID:", GOOGLE_WEB_CLIENT_ID);
console.log("GOOGLE_ANDROID_CLIENT_ID:", GOOGLE_ANDROID_CLIENT_ID);
console.log("GOOGLE_IOS_CLIENT_ID:", GOOGLE_IOS_CLIENT_ID);

WebBrowser.maybeCompleteAuthSession();

const GoogleLogin = ({ onLoginSuccess }) => {
  const [request, response, promptAsync] = Google.useAuthRequest({
    expoClientId: GOOGLE_WEB_CLIENT_ID,
    iosClientId: GOOGLE_IOS_CLIENT_ID,
    androidClientId: GOOGLE_ANDROID_CLIENT_ID,
    webClientId: GOOGLE_WEB_CLIENT_ID,
    redirectUri: Linking.createURL("/auth/callback"),
  });

  const [userInfo, setUserInfo] = useState(null);

  useEffect(() => {
    console.log("Google Auth Request:", request);
    console.log("Google Auth Response:", response);

    if (response) {
      if (response.type === "success") {
        const { authentication } = response;
        console.log("Authentication successful:", authentication);
        if (authentication) {
          fetchUserInfo(authentication.accessToken);
        }
      } else if (response.type === "error") {
        console.error("Google Auth Error:", response.params);
      }
    }
  }, [response]);

  const fetchUserInfo = async (token) => {
    try {
      const response = await fetch(
        "https://www.googleapis.com/userinfo/v2/me",
        {
          headers: { Authorization: `Bearer ${token}` },
        }
      );

      if (!response.ok) {
        throw new Error("Failed to fetch user info");
      }

      const user = await response.json();
      console.log("Fetched user info:", user);
      setUserInfo(user);
      onLoginSuccess(user);
    } catch (error) {
      console.error("Error fetching user info:", error);
    }
  };

  return (
    <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
      <Button
        disabled={!request}
        title="Login with Google"
        onPress={() => {
          console.log("Prompting Google Sign-In");
          promptAsync();
        }}
      />
      {userInfo && (
        <Text style={{ marginTop: 20 }}>Welcome, {userInfo.name}!</Text>
      )}
    </View>
  );
};

export default GoogleLogin;

app.json配置

{
  "expo": {
    "name": "towntaste",
    "slug": "towntaste",
    "version": "1.0.0",
    "orientation": "portrait",
    "icon": "./assets/icon.png",
    "userInterfaceStyle": "light",
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },
    "assetBundlePatterns": ["**/*"],
    "plugins": [
      [
        "@react-native-google-signin/google-signin",
        {
          "iosUrlScheme": "com.googleusercontent.apps.468953728495-5cdcfubfhvrn76omjuldpdh66t8h66rf"
        }
      ]
    ],
    "ios": {
      "supportsTablet": true,
      "bundleIdentifier": "com.townTaste.app"
    },
    "android": {
      "package": "com.townTaste",
      "versionCode": 1,
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#ffffff"
      }
    },
    "web": {
      "favicon": "./assets/favicon.png"
    },
    "extra": {
      "eas": {
        "projectId": "567ad302-63d0-4b12-a457-52484b479a04"
      }
    },
    "owner": "caiolagreca",
    "scheme": "towntaste"
  }
}

日志与错误信息

  • Android端:点击Google登录后跳转空白页,返回登录页时日志:
LOG  Google Auth Request: {"clientId": "468953728495-vekml2qel4cdjunjp27t9jokvqn7k5i2.apps.googleusercontent.com", "clientSecret": undefined, "codeChallenge": "JoP1_YjhxpNXNwzX5JqfeEFb16Y7mblT2Pykau17JXY", "codeChallengeMethod": "S256", "codeVerifier": "Hz651y7b1p5HmPF60mP2DPNQItdvrPuj6mdG3H8e8d8JMKX1AnScb9tu8HDGwhETpKnNY49AWVACATx31eD8stee6llB2LgJQyT1IZBUhNJABAYQW2BGr8mJd9T0BPTZ", "extraParams": {}, "prompt": undefined, "redirectUri": "exp://192.168.1.7:8081/--/auth/callback", "responseType": "code", "scopes": ["openid", "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email"], "state": "wRFZ8nmYxv", "url": "https://accounts.google.com/o/oauth2/v2/auth?code_challenge=JoP1_YjhxpNXNwzX5JqfeEFb16Y7mblT2Pykau17JXY&code_challenge_method=S256&redirect_uri=exp%3A%2F%2F192.168.1.7%3A8081%2F--%2Fauth%2Fcallback&client_id=468953728495-vekml2qel4cdjunjp27t9jokvqn7k5i2.apps.googleusercontent.com&response_type=code&state=wRFZ8nmYxv&scope=openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email", "usePKCE": true}
LOG  Google Auth Response: {"type": "dismiss"}
  • iOS端:点击登录后提示Access blocked: authorization error. Error 400: invalid_request; redirect_uri=exp://192.168.1.7:8081/auth/callback flowName=GeneralOAuthFlow,返回后日志同Android端的{"type": "dismiss"}

Google Cloud重定向URI配置

重定向URI配置截图

解决步骤

1. 修正重定向URI匹配问题

从日志可见,Android端实际使用的redirectUri是exp://192.168.1.7:8081/--/auth/callback(带--路径),iOS端是exp://192.168.1.7:8081/auth/callback(不带--),但Google控制台配置的URI与这两个均不匹配。

需在Google开发者控制台的OAuth 2.0 客户端ID中,为对应客户端添加以下重定向URI:

  • 开发环境:
    • exp://<你的本地IP>:8081/--/auth/callback(Android开发用)
    • exp://<你的本地IP>:8081/auth/callback(iOS开发用)
  • 生产环境(打包后):
    • Android:com.townTaste:/oauth2redirect/google
    • iOS:com.googleusercontent.apps.468953728495-5cdcfubfhvrn76omjuldpdh66t8h66rf:/oauth2redirect/google(对应app.json中的iosUrlScheme)

2. 统一redirectUri配置

在Google.useAuthRequest中,避免动态生成带IP的URI,改用Expo通用格式:

import { Platform } from 'react-native';

// ...

const [request, response, promptAsync] = Google.useAuthRequest({
  expoClientId: GOOGLE_WEB_CLIENT_ID,
  iosClientId: GOOGLE_IOS_CLIENT_ID,
  androidClientId: GOOGLE_ANDROID_CLIENT_ID,
  webClientId: GOOGLE_WEB_CLIENT_ID,
  redirectUri: Platform.select({
    ios: Linking.createURL('/auth/callback'),
    android: Linking.createURL('/auth/callback'),
    web: 'https://towntaste.expo.dev/auth/callback'
  }),
});

3. 检查客户端ID匹配

确保GOOGLE_ANDROID_CLIENT_ID、GOOGLE_IOS_CLIENT_ID分别对应Google控制台中Android客户端和iOS客户端的ID,GOOGLE_WEB_CLIENT_ID对应Web客户端ID,不要混淆。

4. 移除冲突插件配置

当前使用expo-auth-session方案,无需@react-native-google-signin/google-signin原生插件,两者可能冲突。移除app.json中的以下配置:

"plugins": [
  [
    "@react-native-google-signin/google-signin",
    {
      "iosUrlScheme": "com.googleusercontent.apps.468953728495-5cdcfubfhvrn76omjuldpdh66t8h66rf"
    }
  ]
],

5. 验证PKCE配置

Expo的Google.useAuthRequest默认启用PKCE,确保Google控制台的客户端ID配置中未禁用PKCE(Web、Android、iOS客户端默认支持)。

内容的提问来源于stack exchange,提问作者Caio Lagreca

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 18:30:54