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

Flutter FCM定时通知集成:首次登录云函数认证失败问题

Flutter FCM定时通知初始化时云函数认证失败问题

我开发了一款集成食谱与用户周计划功能的Flutter应用,需求是用户完成三餐规划后,系统按设定时间(如9:00早餐、14:00午餐、20:00晚餐)推送FCM定时提醒通知。

问题详情

用户首次登录/注册时,调用初始化通知流程中的Firebase云函数registerForMealNotifications时,持续抛出firebase_functions/unauthenticated错误,提示「该函数必须在认证状态下调用」。

已尝试的处理逻辑

我在代码中添加了ID Token强制刷新与延迟等待逻辑:

  • 在AuthService的authStateChanges监听中,延迟5秒后调用initNotifications
  • 在initNotifications内部,对当前用户执行getIdToken(true)强制刷新并延迟2秒

但日志显示云函数调用发生在「✅ ID token refreshed inside initNotifications」日志之前,导致认证失败。


Flutter端初始化通知代码

Future<void> initNotifications() async {
  debugPrint('🚀 [FirebaseApi] initNotifications() start');

  await _requestPermission();
  debugPrint('🚀 [FirebaseApi] permission granted');

  // 强制刷新ID Token
  final user = FirebaseAuth.instance.currentUser;
  if (user != null) {
    await user.getIdToken(true);
    await Future.delayed(Duration(seconds: 2));//延迟2秒
    debugPrint('✅ ID token refreshed inside initNotifications');
  }

  final token = await _firebaseMessaging.getToken();
  debugPrint('🚀 [FirebaseApi] FCM token fetched: $token');
  if (token != null) await _saveTokenToDatabase(token);

  debugPrint('🚀 [FirebaseApi] registering onTokenRefresh listener');
  _firebaseMessaging.onTokenRefresh.listen((newToken) {
    debugPrint('🔄 [FirebaseApi] onTokenRefresh → $newToken');
    _saveTokenToDatabase(newToken);
  });

  _setupFCMHandlers();
  debugPrint('🚀 [FirebaseApi] calling registerForMealNotifications');

  await _registerForNotifications(); // 此处调用云函数
  debugPrint('🚀 [FirebaseApi] calling checkInactivity');

  await checkInactivity();
  debugPrint('🚀 [FirebaseApi] initNotifications() end');
}    

// 请求通知权限
Future<void> _requestPermission() async {
  NotificationSettings settings = await _firebaseMessaging.requestPermission(
    alert: true,
    badge: true,
    sound: true,
  );

  debugPrint('User granted permission: ${settings.authorizationStatus}');
} 

Node.js云函数代码

exports.registerForMealNotifications = functions.https.onCall(
async (data, context) => {
  // 验证请求是否已认证
  console.log("Function called:", data, context.auth);
  if (!context.auth) {
    throw new functions.https.HttpsError(
        "unauthenticated",
        "The function must be called while authenticated.",
    );
  }

  const userId = data.userId;
  const mealTimes = data.mealTimes || [];

  try {
    // 将用户用餐时间保存到Firestore
    await admin.firestore().collection("User").doc(userId).update({
      "mealNotificationTimes": mealTimes,
      "notificationsEnabled": true,
    });

    // 通过Pub/Sub主题调度通知
    for (const meal of mealTimes) {
      const topicName = `meal-${meal.mealType.toLowerCase()}-${userId}`;

      // 获取用户的FCM Token
      const userDoc = await admin
          .firestore()
          .collection("User")
          .doc(userId)
          .get();
      const fcmToken = userDoc.data().fcmToken;

      if (fcmToken) {
        // 订阅用户到主题
        await admin.messaging().subscribeToTopic(fcmToken, topicName);
      }
    }

    return {success: true};
  } catch (error) {
    console.error("Error registering for notifications:", error);
    throw new functions.https.HttpsError(
        "internal",
        "An error occurred while registering for notifications.",
    );
  }
},
 ); 

AuthService监听代码

static late AppRepository _repo;
static void configure(AppRepository repository) {

  _repo = repository;

  FirebaseAuth.instance.authStateChanges().listen((user) async {
    debugPrint('🔔 [AuthService] authStateChanges → user = $user');
    if (user != null) {
      debugPrint('🔑 [AuthService] got signed-in user ${user.uid}, forcing ID token refresh');

      // 确保ID Token已刷新
      await user.getIdToken(true);
      debugPrint('🔑 [AuthService] ID token refreshed');

      // 添加延迟确保认证状态同步到Firebase服务
      await Future.delayed(Duration(seconds: 5));

      // 初始化通知
      if (!_didInitNotifications) {
        await FirebaseApi().initNotifications();
        debugPrint('🔔 [AuthService] initNotifications() complete');
        _didInitNotifications = true;
      }
    }
  });
}

错误日志

注册通知时出错:[firebase_functions/unauthenticated] 该函数必须在认证状态下调用。

E/libEGL (31183): called unimplemented OpenGL ES API

D/EGL_emulation(31183): app_time_stats: avg=180.22ms min=130.11ms max=216.15ms count=6

I/flutter (31183): 🚀 [FirebaseApi] FCM token fetched: ****

I/flutter (31183): 🚀 [FirebaseApi] registering onTokenRefresh listener

I/flutter (31183): 🚀 [FirebaseApi] calling registerForMealNotifications

随后再次出现错误:注册通知时出错:[firebase_functions/unauthenticated] 该函数必须在认证状态下调用。


内容的提问来源于stack exchange,提问作者Γιωργος Ρετσ

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 06:52:09