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

Flutter/Firebase iOS端手机号验证失败(token mismatch)求助

iOS端Firebase Phone Auth触发'verificationFailed'并报'token mismatch'错误

问题概述

Firebase的手机号验证功能在Android端完全正常,但iOS端始终触发verificationFailed回调,错误信息为**'token mismatch'**,已排查多日仍未解决。

代码片段

Firebase初始化代码

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize Firebase
  await Firebase.initializeApp(
    name: '<app name>',
    options: DefaultFirebaseOptions.currentPlatform,
  ).then((_) {
    print("Firebase initialized successfully.");
  }).catchError((error) {
    print("Error initializing Firebase: $error");
  });

// Clear Firestore persistence here
  try {
    await FirebaseFirestore.instance.clearPersistence();
    print('Firestore persistence cleared.');
  } catch (e) {
    print('Error clearing Firestore persistence: $e');
  }

  // Set Firestore settings
  try {
    FirebaseFirestore.instance.settings = const Settings(
      persistenceEnabled: false,
      sslEnabled: false,
      cacheSizeBytes: Settings.CACHE_SIZE_UNLIMITED,
    );
    print('Firestore settings updated.');
  } catch (e) {
    print('Error setting up Firestore: $e');
  }

  globals.fbdb = FirebaseFirestore.instance;
  globals.fbauth = FirebaseAuth.instance;
  globals.fbs = FirebaseStorage.instance;

  // Run the app
  runApp(const <appname>());
}

手机号验证代码

static Future<void> verifyPhoneNumber(String phoneNumber) async {
    await FirebaseMessaging.instance.getToken();

    print("+++ phoneNumber = ${phoneNumber}");
    await globals.fbauth.verifyPhoneNumber(
      phoneNumber: phoneNumber,
      verificationCompleted: (PhoneAuthCredential credential) async {
        // Auto-retrieve verification code
        print("Verification Complete has been triggered for some reason!");
      },
      verificationFailed: (FirebaseAuthException e) {
        // Verification failed
        print('+++ Verification failed. Error = ${e}');
      },
      codeSent: (String verificationId, int? resendToken) async {
        // Save the verification ID for future use
        print('got through to codeSent section');
      },
      codeAutoRetrievalTimeout: (String verificationId) {},
      timeout: Duration(seconds: 60),
    );
  }
}

AppDelegate.swift代码

import UIKit
import Firebase
import Flutter
import FirebaseMessaging
import FirebaseAuth

@main
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // Firebase initialization
        GeneratedPluginRegistrant.register(with: self)
        Messaging.messaging().delegate = self
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }

    override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        if Auth.auth().canHandleNotification(userInfo) {
            print(userInfo)
            return
        }
        completionHandler(.newData)
    }

    override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        Auth.auth().setAPNSToken(deviceToken, type: AuthAPNSTokenType.unknown)
    }
}

extension AppDelegate: MessagingDelegate {
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
        print("Firebase registration token: \(String(describing: fcmToken))")
    }
}

环境信息

  • Flutter版本:3.24.3(稳定版)
  • Dart版本:3.5.3
  • Devtools版本:2.37.3
  • Xcode版本:15.4(15F31d)
  • Android Studio版本:Koala Feature Drop | 2024.1.2
  • 测试设备:iPhone 13(真机)

已排查项

  • 无编译错误
  • 所有插件均为最新版本
  • firebase-options.dart配置正确
  • 包含REVERSE_CLIENT_ID的URL Type配置正确
  • Apple证书及.p8密钥已多次重新生成并确认无误
  • Firebase控制台已启用手机号登录等认证方式
  • Bundle名称及Firebase应用名称一致
  • Google Cloud Platform中iOS自动生成密钥已锁定至iOS应用
  • 已拥有Apple开发者付费账号
  • 配置了正确的描述文件
  • GoogleService-Info.plist位置及内容正确
  • 已开启Background Fetch和Remote Notifications后台模式

解决方案建议

  1. 修正APNS Token类型设置
    当前AppDelegate中设置APNS Token类型为AuthAPNSTokenType.unknown,这可能导致Token不匹配。根据测试环境明确指定类型:

    // 开发环境用.sandbox,生产环境用.prod
    Auth.auth().setAPNSToken(deviceToken, type: .sandbox) 
    

    若使用Xcode自动签名,可保留自动适配,但手动指定能避免环境识别偏差。

  2. 调整Firebase初始化顺序
    原生端需优先完成Firebase初始化,再注册Flutter插件,修改AppDelegate的didFinishLaunchingWithOptions方法:

    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        FirebaseApp.configure() // 新增此行,确保Firebase优先初始化
        GeneratedPluginRegistrant.register(with: self)
        Messaging.messaging().delegate = self
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
    
  3. 同步FCM Token与Auth状态
    在获取FCM Token时,确保APNS Token已同步到Firebase Auth,修改MessagingDelegate的实现:

    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
        guard let token = fcmToken else { return }
        print("Firebase registration token: \(token)")
        // 重新同步APNS Token到Auth
        if let deviceToken = Messaging.messaging().apnsToken {
            Auth.auth().setAPNSToken(deviceToken, type: .sandbox)
        }
    }
    
  4. 清理缓存并重新构建

    • 执行flutter clean清除Flutter缓存
    • 删除Xcode的Derived Data(Xcode > 设置 > 位置 > Derived Data > 删除对应项目文件夹)
    • 卸载设备上的App,重新执行flutter run --release测试生产环境逻辑
  5. 检查Google Cloud API密钥限制
    登录Google Cloud控制台,确认对应API密钥的应用限制中已正确添加iOS应用的Bundle ID,且未限制Firebase Phone Authentication API的调用权限。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 08:32:33