PAYATHON 2026

Expo Google Sign-In 授权失败:Firebase / OAuth 如何配置?

排障赵师傅

EAS 原生应用优先使用原生 Google 登录

准备发布的 Expo Android / iOS 应用,建议通过原生 Google Sign-In 获取 Google ID token,再调用 Firebase 的 signInWithCredential() 完成登录。

问题生产环境建议
使用 Expo proxy 还是 native redirect?不再使用 Expo AuthSession proxy。原生 Google 登录由 SDK 处理认证及回调
Android 是否需要 SHA-1?使用原生 Google 登录时需要,必须对应实际安装包的签名证书
只有 Web Client ID 是否足够?Android 还需要匹配包名和 SHA-1 的 Android OAuth client;iOS 需要匹配 Bundle ID 的 iOS OAuth client
邮箱登录正常能否证明 OAuth 配置正确?不能。邮箱登录没有经过 Google OAuth 授权流程

Expo 的 AuthSession proxy 已弃用,expo-auth-session 的 Google provider 也已标记弃用。Expo 当前的 Google 登录指南提供了原生库的接入方案。Expo proxy 说明、AuthSession 文档、Expo Google 登录指南

为什么当前代码可能失败?

Google.useAuthRequest({
  clientId: WEB_CLIENT_ID,
  scopes: ['profile', 'email'],
});

这段代码只设置了 Web Client ID,无法据此判断实际运行平台、客户端类型和最终发给 Google 的 redirect_uri 是否匹配。单独调用:

makeRedirectUri({ useProxy: true })

也无法确认授权请求是否使用了生成的地址,以及 Google 是否允许该客户端使用这个回调地址。

Google 页面显示 Access Blocked,说明请求在 Google 授权阶段就被拒绝了。应先检查 OAuth 请求和客户端配置,此时 Firebase 还没有拿到可用于登录的 Google 凭据。

仅把 proxy URL 换成 myapp://callback 也未必能解决问题:

  • Web OAuth client 的回调地址必须满足 Google 的 Web redirect URI 规则,不能随意填写自定义 scheme。
  • Android OAuth client 的自定义 scheme 授权方式默认禁用,Google 建议使用原生身份认证 API。

调整时需要同时核对客户端类型、授权方式和回调机制。Web 回调地址规则、Android 自定义 scheme 限制

第一步:在同一项目中配置 Firebase 和 OAuth clients

打开 Firebase 项目对应的 Google Cloud 项目,检查已有的 OAuth clients。Firebase 可能已经创建了部分客户端,核对后再补齐缺少的配置。

Web OAuth client

保留一个类型为 Web application 的 OAuth client,后续将其 ID 配置为原生登录库的 webClientId。

在这条原生登录流程中,Web OAuth client 用于请求面向该客户端的 Google ID token。Android 应用的包名和签名仍需单独注册。

Android OAuth client

Android 客户端需要匹配以下信息:

实际 applicationId / package name
+
实际签名证书的 SHA-1

例如,Expo 配置中的包名为:

{
  "expo": {
    "android": {
      "package": "com.example.myapp"
    }
  }
}

如果开发版使用 com.example.myapp.dev,就需要为这个包名单独注册 Android 应用,并配置相应的 OAuth client。

在 Firebase 项目设置中选择对应的 Android 应用,添加 SHA-1,同时检查 Google Cloud 中是否有匹配的 Android OAuth client。启用 Google 登录并更新配置后,重新下载 google-services.json。Firebase Android Google 登录配置

iOS OAuth client

发布 iOS 版本时,还需要:

  • 与 ios.bundleIdentifier 一致的 iOS OAuth client。
  • 对应的 GoogleService-Info.plist。
  • 正确注册的 Google 回调 URL scheme,通常来自 plist 中的 REVERSED_CLIENT_ID。

Android OAuth client 的 ID 通常不需要传入 JavaScript。Google 用它识别请求登录的应用包名和签名证书,因此 JavaScript 中仍可能只出现 webClientId。客户端配置说明

第二步:登记实际安装包使用的 SHA-1

开发和发布时使用的签名证书容易混淆,需要按实际安装方式核对:

安装方式需要核对的证书
本地 debug APK本地构建实际使用的 debug 签名证书
直接安装 EAS 构建的 APK该构建实际使用的 EAS 签名证书
从 Google Play 安装Google Play 的 App signing key certificate

使用以下命令查看或管理 EAS 构建凭据:

eas credentials -p android

对于从 Google Play 安装的应用,到 Play Console 的应用完整性/应用签名页面核对 App signing key certificate。它可能与上传 AAB 时使用的 Upload key certificate 不同。

同一包名可能需要登记多张证书的 SHA-1,以覆盖开发、直接分发和商店安装场景。EAS development 和 production profile 是否使用同一张证书,也要以实际凭据为准。签名配置说明、Google 客户端身份验证

第三步:配置 Expo 原生登录库并重新构建

以 Expo 指南列出的 react-native-nitro-google-signin 为例,这个库在 Android 上使用 Credential Manager。

示例使用库文档中的 API,尚未在你的项目和签名环境中进行实机验证。安装前,先按兼容性表选择与项目 React Native 版本匹配的依赖组合。

安装依赖:

npx expo install expo-dev-client
npm install react-native-nitro-google-signin react-native-nitro-modules

将以下配置合并到现有 app.json,并替换包名和 Bundle ID:

{
  "expo": {
    "name": "MyApp",
    "slug": "my-app",
    "plugins": ["react-native-nitro-google-signin"],
    "android": {
      "package": "com.example.myapp",
      "googleServicesFile": "./google-services.json"
    },
    "ios": {
      "bundleIdentifier": "com.example.myapp",
      "googleServicesFile": "./GoogleService-Info.plist"
    }
  }
}

如果只发布 Android,可以省略 iOS 部分。确保 EAS 构建环境能够读取配置文件。

新增原生模块,或修改插件、配置文件、URL scheme 后,需要重新生成并构建原生应用。如果项目由 Expo 自动生成原生目录,可以使用:

eas build --profile development --platform android

这条命令的前提是 eas.json 已配置 development build。如果项目已有并自行维护 android/、ios/ 目录,还需要按项目流程应用 config plugin 的改动。

这些原生模块无法在 Expo Go 中运行。刷新 Metro 或发布 EAS Update,也无法补入缺失的原生代码。Expo 原生库配置

第四步:将 Google ID token 交给 Firebase

以下示例继续使用 Firebase JavaScript SDK,并假定 ./firebase 已导出现有的 auth 实例。直接复用该实例,无需再创建另一套 Firebase Auth 实例。

import {
  GoogleOneTapSignIn,
  isSuccessResponse,
} from 'react-native-nitro-google-signin';
import {
  GoogleAuthProvider,
  signInWithCredential,
} from 'firebase/auth';

import { auth } from './firebase';

// 应用初始化时执行一次。
// iOS client ID 从 GoogleService-Info.plist 读取。
GoogleOneTapSignIn.configure({
  webClientId: 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com',
});

export async function signInWithGoogle() {
  await GoogleOneTapSignIn.checkPlayServices();

  // 适合由“使用 Google 登录”按钮触发。
  const response = await GoogleOneTapSignIn.presentExplicitSignIn();

  if (!isSuccessResponse(response)) {
    return null;
  }

  const { idToken } = response.data;

  if (!idToken) {
    throw new Error('Google 登录未返回 ID token');
  }

  const credential = GoogleAuthProvider.credential(idToken);
  const result = await signInWithCredential(auth, credential);

  return result.user;
}

调用方需要捕获异常,并在请求期间禁用登录按钮,避免重复触发。原生登录 API

Google SDK 先获取 Google ID token,Firebase 再验证该 token 并建立 Firebase 会话。是否完成 Firebase 登录,应以 signInWithCredential() 成功及 Firebase Auth 状态变化为准,仅拿到 Google 用户资料还不够。Firebase 凭据登录说明

这个示例无需使用 useProxy、makeRedirectUri(),也无需在 Web OAuth client 中添加任意应用 deep link。iOS 的原生回调仍需正确配置。

“Access Blocked”应按具体错误排查

打开 Google 错误页的详细信息,记录完整错误码和说明,以及实际请求中的 client_id、redirect_uri、response_type 和 scope。仅凭页面标题无法确定原因。

错误或现象优先检查
unauthorized_client客户端是否允许当前授权方式,客户端类型是否用错
redirect_uri_mismatch实际回调地址是否与当前 Web client 注册地址完全一致,包括协议、路径和末尾 /
invalid_request,详情提到 custom URI scheme是否触发 Android 自定义 scheme 限制,或请求参数不合法
disallowed_useragent是否在受禁止的嵌入式 WebView 中打开 Google 授权页
org_internalOAuth 应用是否限制为组织内部账号
admin_policy_enforcedGoogle Workspace 管理员是否限制该客户端或请求的权限
原生登录出现 DEVELOPER_ERROR包名、实际签名 SHA-1、Android client 和 Web Client ID 所属项目是否匹配

这些错误对应不同的配置问题,需要根据具体错误处理,不能统一靠添加 redirect URI 解决。Google OAuth 错误说明

还要核对 OAuth 同意屏幕的受众、发布状态和实际请求的权限。Testing 状态通常有测试用户限制,但仅请求 openid、email、profile 等基本身份信息时存在例外,因此不能直接认定问题出在未添加测试用户。如果额外请求了 Drive、Calendar 等权限,需要重新检查测试用户和适用的验证要求。OAuth 受众与测试限制

如果必须保留 expo-auth-session

expo-auth-session 仍可用于浏览器 OAuth,但不宜继续将旧 Google provider 示例用于新项目的生产配置。

采用服务端 Web OAuth 时,应由服务端接收 Google 回调,使用合法的 HTTPS redirect URI,校验 state,并按流程使用 PKCE。服务端完成认证后,再通过受控的应用回跳和一次性交换机制建立客户端会话。不要将 Google 或 Firebase token 直接放进 deep link,也不要把 Web client secret 打包进应用。

在这类完整的 Web 服务端流程中,Google Web OAuth 本身不依赖 Android SHA-1。前面的 SHA-1 要求针对原生 Android Google 登录,两条流程的配置不能混用。

上线前,至少分别测试直接安装的构建和从 Google Play 测试轨道安装的构建。确认 Google 登录成功后,Firebase 确实建立了会话,并检查退出、取消登录和再次登录是否都能正确处理。OAuth client ID 可以出现在客户端;client secret、服务账号私钥和登录 token 不应写入应用配置或日志。

备注:内容仅供参考。