You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

5.0 KiB

Guide: FCM Token Registration & Sync for Flutter

This document details how the Flutter mobile app should manage, retrieve, and send the device's Firebase Cloud Messaging (FCM) token to the backend server. Syncing the FCM token is required to ensure users receive push notifications on their devices.


1. API Endpoint Details

The backend provides a dedicated endpoint to update the FCM token of the currently authenticated user.

  • URL: /api/account/update-fcm/
  • Method: POST
  • Headers:
    • Content-Type: application/json
    • Authorization: Token <USER_AUTH_TOKEN> (Token Authentication required)
  • Request Body:
    {
      "fcm": "YOUR_FIREBASE_FCM_TOKEN"
    }
    
  • Success Response (200 OK):
    {
      "detail": "FCM token updated successfully."
    }
    
  • Error Response (401 Unauthorized):
    • Returned if the authorization header is missing, incorrect, or expired.

2. When to Send the FCM Token

To prevent notification delivery failures, the Flutter application must send the FCM token to the backend in the following three scenarios:

  1. On Successful Login / Account Verification: As soon as the user logs in (/api/account/login/) or verifies their verification code (/api/account/verify/) and obtains their authentication token, retrieve the FCM token and upload it.
  2. On App Start (If Logged In): If the user has an active session saved locally, retrieve the current FCM token on app launch and sync it to the backend. This handles cases where the user's token might have refreshed while the app was closed.
  3. On Token Refresh Event: Firebase periodically refreshes device tokens. Listen to Firebase's token refresh stream and upload the new token immediately if the user is authenticated.

3. Flutter (Dart) Implementation Example

Here is a complete integration example using firebase_messaging and the dio (or http) package:

import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';

class FcmTokenManager {
  final Dio _dio = Dio(BaseOptions(baseUrl: 'https://api.yourdomain.com'));
  
  // Replace with your local storage lookup (e.g. FlutterSecureStorage or SharedPreferences)
  Future<String?> getAuthToken() async {
    return "SAVED_USER_AUTH_TOKEN";
  }

  /// Retrieves the current FCM token and sends it to the backend.
  Future<void> syncTokenToBackend() async {
    try {
      final String? authToken = await getAuthToken();
      if (authToken == null) {
        debugPrint("User is not authenticated. Skipping FCM sync.");
        return;
      }

      // 1. Get token from Firebase
      String? fcmToken = await FirebaseMessaging.instance.getToken();
      if (fcmToken == null) {
        debugPrint("Failed to retrieve FCM token from Firebase.");
        return;
      }

      debugPrint("FCM Token retrieved: $fcmToken");

      // 2. Send token to backend
      final response = await _dio.post(
        '/api/account/update-fcm/',
        data: {'fcm': fcmToken},
        options: Options(
          headers: {
            'Authorization': 'Token $authToken',
            'Content-Type': 'application/json',
          },
        ),
      );

      if (response.statusCode == 200) {
        debugPrint("FCM token synced successfully: ${response.data}");
      }
    } catch (e) {
      debugPrint("Error syncing FCM token: $e");
    }
  }

  /// Setup listeners for token refreshes.
  void setupTokenRefreshListener() {
    FirebaseMessaging.instance.onTokenRefresh.listen((newFcmToken) async {
      debugPrint("FCM Token refreshed: $newFcmToken");
      final String? authToken = await getAuthToken();
      if (authToken != null) {
        // Sync token immediately since user is authenticated
        await _dio.post(
          '/api/account/update-fcm/',
          data: {'fcm': newFcmToken},
          options: Options(
            headers: {
              'Authorization': 'Token $authToken',
            },
          ),
        );
      }
    });
  }
}

4. Best Practices for Mobile Developers

  1. Clear Token on Logout: When the user logs out, it is critical to call /api/account/update-fcm/ passing null or an empty string "" before deleting the auth token locally. This prevents the device from continuing to receive push notifications destined for the logged-out user:
    {
      "fcm": ""
    }
    
  2. Handle Token Request Permissions: On iOS, you must request permission before fetching the token:
    NotificationSettings settings = await FirebaseMessaging.instance.requestPermission(
      alert: true,
      badge: true,
      sound: true,
    );
    if (settings.authorizationStatus == AuthorizationStatus.authorized) {
      syncTokenToBackend();
    }
    
  3. Avoid Redundant Calls: You can cache the last successfully synced FCM token in local storage (e.g. SharedPreferences). Compare the current token with the cached one on app start, and only perform the network request if they differ.