如何使用expo-apple-authentication对接React Native Firebase Auth?
解决Expo Apple Authentication + Firebase Auth的invalid-credential错误
问题背景
用expo-apple-authentication对接Firebase Auth时,已在Firebase控制台配置好Apple登录提供商,但调用signInAsync后仅拿到identityToken和授权码。手动生成nonce配合identityToken传给Firebase时,触发[auth/invalid-credential] The supplied auth credential is malformed or has expired错误。
核心问题
你当前代码的问题是拿到Apple返回的token后才生成nonce,但Firebase要求的nonce必须满足三个关键条件:
- 必须在请求Apple登录前生成
- 要将哈希后的nonce传给Apple,让Apple用它签名
identityToken - 传给Firebase的是原始未哈希的nonce,用于验证Apple签名的有效性
前后nonce不匹配的话,Firebase会直接判定凭证无效。
解决步骤
- 提前生成原始nonce:在调用
signInAsync前生成随机字符串作为nonce - 对nonce做SHA-256哈希:Apple要求传入的nonce是哈希后的值
- 将哈希后的nonce传入Apple登录请求:让Apple用这个哈希值签名
identityToken - 用原始nonce和Apple返回的
identityToken生成Firebase凭证
修正后的完整代码
import * as AppleAuthentication from 'expo-apple-authentication'; import { auth } from '../path/to/your/firebase/config'; // 替换成你的Firebase配置路径 import * as Crypto from 'expo-crypto'; // 需先安装:npx expo install expo-crypto // 生成随机nonce,移除Apple不允许的特殊字符 const generateRandomNonce = () => { const randomBytes = Crypto.getRandomBytes(32); return Crypto.bytesToBase64(randomBytes).replace(/[+/=]/g, ''); }; // 对nonce做SHA-256哈希 const hashNonce = async (nonce) => { const utf8 = new TextEncoder().encode(nonce); const hashBuffer = await Crypto.digestStringAsync( Crypto.CryptoDigestAlgorithm.SHA256, utf8 ); return Array.from(new Uint8Array(hashBuffer)) .map((b) => b.toString(16).padStart(2, '0')) .join(''); }; const signInWithApple = async () => { try { // 1. 生成原始nonce const rawNonce = generateRandomNonce(); // 2. 生成哈希后的nonce const hashedNonce = await hashNonce(rawNonce); // 3. 调用Apple登录,传入哈希后的nonce const credential = await AppleAuthentication.signInAsync({ requestedScopes: [ AppleAuthentication.AppleAuthenticationScope.FULL_NAME, AppleAuthentication.AppleAuthenticationScope.EMAIL, ], nonce: hashedNonce, }); if (!credential.identityToken) { throw new Error("Apple登录失败:未返回identity token"); } // 4. 用原始nonce和identityToken生成Firebase凭证 const appleCredential = auth.AppleAuthProvider.credential( credential.identityToken, rawNonce ); // 完成Firebase登录 await auth().signInWithCredential(appleCredential); console.log("Apple登录成功"); } catch (error) { console.error("Apple登录出错:", error); } };
额外注意事项
- 必须用真实iOS设备测试,Apple Sign-In在模拟器上可能无法正常返回
identityToken - 确保Firebase控制台的Apple提供商配置正确:
- 填写正确的团队ID、服务ID、密钥ID和私钥
- 回调URL要和你的App配置一致(比如Expo Go的回调URL是
exp://localhost:19000,生产环境用App的bundle ID对应的URL)
- 若使用Expo,务必安装
expo-crypto,原生React Native环境可使用crypto模块,但需额外配置
内容的提问来源于stack exchange,提问作者Victor
相关产品推荐
相关产品推荐

