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

使用expo-auth-session谷歌登录后,Firebase未获取到凭证的问题

问题排查与修复方案

你的代码存在几个关键问题导致凭证无法同步到Firebase,以下是具体问题和修复步骤:

1. idToken赋值错误

你错误地将accessToken赋值给了idToken,Firebase的GoogleAuthProvider.credential()需要的是谷歌返回的idToken,而非accessToken。

错误代码:

const idToken = response.authentication.accessToken;

修复:

const idToken = response.authentication.idToken; // 正确获取idToken

2. 未处理异步登录操作的结果与错误

signInWithCredential是异步函数,你没有处理它的Promise结果,也未捕获错误,导致登录失败时无法排查问题,同时也没有更新本地用户状态。

修复:使用async/await处理异步操作,并更新user状态:

React.useEffect(() => {
  const handleLogin = async () => {
    if (response?.type === "success") {
      try {
        const { accessToken, idToken } = response.authentication;
        const credential = GoogleAuthProvider.credential(idToken, accessToken);
        const userCredential = await signInWithCredential(auth, credential);
        setUser(userCredential.user); // 更新本地用户状态
      } catch (error) {
        console.error("Firebase登录失败:", error); // 打印错误便于排查
      }
    }
  };

  handleLogin();
}, [response]);

3. AuthContext中的user固定为null

你在Context Provider中硬编码了user: null,导致组件无法获取到实际登录后的用户信息。

修复:将本地state中的user传入Context:

<AuthContext.Provider value={{
  user, // 使用本地state的user
  promptAsync,
}}>
  {children}
</AuthContext.Provider>

4. 未监听Firebase用户状态变化

应该通过onAuthStateChanged监听Firebase的用户状态,确保页面刷新或其他登录方式下,本地user状态能同步更新。

修复:添加监听逻辑:

React.useEffect(() => {
  const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
    setUser(currentUser); // 同步Firebase用户状态到本地
  });

  return unsubscribe; // 组件卸载时取消监听
}, []);

5. 无效的scopes配置

expo-auth-session/providers/google的配置中,permissions参数是多余的,且scopes需要包含openid才能获取到idToken(这是谷歌OAuth2获取idToken的必要条件)。

修复调整scopes:

const [request, response, promptAsync] = google.useAuthRequest({
  androidClientId: '***',
  iosClientId: '***',
  clientId: '***',
  scopes: ["openid", "email", "profile"], // 正确的scopes,openid必须
});

修正后的完整useAuth.js代码

import { createContext, useContext } from "react";
import { View, StyleSheet } from "react-native";
import * as React from 'react';
import * as google from 'expo-auth-session/providers/google';
import * as WebBrowser from 'expo-web-browser';
import { GoogleAuthProvider, onAuthStateChanged, signInWithCredential } from "@firebase/auth";
import { auth } from "../firebase";

WebBrowser.maybeCompleteAuthSession();
const AuthContext = createContext({});

export const AuthProvider = ({ children }) => {
  const [user, setUser] = React.useState(null);
  const [request, response, promptAsync] = google.useAuthRequest({
    androidClientId: '***',
    iosClientId: '***',
    clientId: '***',
    scopes: ["openid", "email", "profile"],
  });

  // 监听Firebase用户状态变化
  React.useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
      setUser(currentUser);
    });
    return unsubscribe;
  }, []);

  // 处理谷歌登录回调
  React.useEffect(() => {
    const handleLogin = async () => {
      if (response?.type === "success") {
        try {
          const { accessToken, idToken } = response.authentication;
          const credential = GoogleAuthProvider.credential(idToken, accessToken);
          await signInWithCredential(auth, credential);
        } catch (error) {
          console.error("Firebase登录失败:", error);
        }
      }
    };
    handleLogin();
  }, [response]);

  return (
    <AuthContext.Provider value={{
      user,
      promptAsync,
    }}>
      {children}
    </AuthContext.Provider>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

export default function useAuth() {
  return useContext(AuthContext);
}

额外优化:LoginScreen中的冗余代码

你的LoginScreen中存在冗余的解构赋值,可以简化:

import React from "react";
import { View, Text, Button } from "react-native";
import useAuth from "../hooks/useAuth";

const LoginScreen = () => {
  const { promptAsync, user } = useAuth();
  console.log(user);

  return (
    <View>
      <Text>Login to the app</Text>
      <Button title="Login" onPress={() => promptAsync()} />
    </View>
  )
}

export default LoginScreen

内容的提问来源于stack exchange,提问作者Abdiaziz Abdullahi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 22:51:52