如何通过Cloud Functions验证FCM Token的有效性?
Hey there! Let's tackle your two questions about FCM token validity checks in Cloud Functions—first, how to verify if a token is still valid, and second, how to validate tokens before sending notifications. I'll walk you through practical code examples and best practices below.
FCM doesn't offer a dedicated API to check token validity directly, but we can leverage the fact that sending a minimal, low-priority test message will fail if the token is invalid or unregistered. Here's how to build a Cloud Function to do this:
Step-by-Step Implementation
- First, make sure you've initialized the Firebase Admin SDK in your Cloud Functions project.
- Create a callable function that accepts an FCM token, sends a silent test message, and checks for errors.
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); exports.checkFCMTokenValidity = functions.https.onCall(async (data, context) => { const token = data.token; // Validate input if (!token) { throw new functions.https.HttpsError("invalid-argument", "FCM token is required"); } try { // Send a silent, low-priority test message to avoid interrupting users const response = await admin.messaging().send({ token: token, data: { "validation-test": "true" }, // Minimal payload android: { priority: "low" }, // Won't wake up the device apns: { payload: { aps: { contentAvailable: true } }, // Silent notification for iOS headers: { "apns-priority": "5" } // Low priority } }); // If we get a message ID, the token is valid return { valid: true, messageId: response }; } catch (error) { // Catch token-specific errors if (error.code === "messaging/invalid-registration-token" || error.code === "messaging/registration-token-not-registered") { return { valid: false, reason: "Token is invalid or no longer registered" }; } // Re-throw other errors as internal issues throw new functions.https.HttpsError("internal", "Failed to check token validity", error.message); } });
Key Notes
- Using low-priority/silent messages ensures users won't be bothered during validation.
- The error codes
messaging/invalid-registration-tokenandmessaging/registration-token-not-registeredare definitive signs that the token is no longer valid.
When sending notifications to multiple tokens, it's efficient to validate them in bulk during the send process. The sendMulticast method from the Admin SDK returns individual results for each token, letting you filter out invalid ones and update your database.
Bulk Validation & Notification Implementation
exports.sendNotificationsWithTokenValidation = functions.https.onCall(async (data, context) => { const targetTokens = data.tokens; const notificationContent = data.notification; // Validate input if (!targetTokens || !Array.isArray(targetTokens) || targetTokens.length === 0) { throw new functions.https.HttpsError("invalid-argument", "A non-empty array of tokens is required"); } try { // Send multicast notification to all target tokens const response = await admin.messaging().sendMulticast({ tokens: targetTokens, notification: notificationContent, // Optional: Use normal priority to balance delivery speed and user experience android: { priority: "normal" }, apns: { headers: { "apns-priority": "5" } } }); // Separate valid and invalid tokens from the results const invalidTokens = []; const successfulTokens = []; response.responses.forEach((result, index) => { if (!result.success) { // Check if failure is due to invalid token if (result.error.code === "messaging/invalid-registration-token" || result.error.code === "messaging/registration-token-not-registered") { invalidTokens.push(targetTokens[index]); } } else { successfulTokens.push(targetTokens[index]); } }); // Optional: Clean up invalid tokens from your database (example using Firestore) if (invalidTokens.length > 0) { const batch = admin.firestore().batch(); // Assuming you have a "userTokens" collection storing tokens for (const token of invalidTokens) { const tokenDoc = await admin.firestore().collection("userTokens").where("token", "==", token).limit(1).get(); if (!tokenDoc.empty) { batch.delete(tokenDoc.docs[0].ref); } } await batch.commit(); functions.logger.info(`Removed ${invalidTokens.length} invalid tokens from database`); } // Return summary results return { successfulCount: response.successCount, failureCount: response.failureCount, invalidTokens: invalidTokens, successfulTokens: successfulTokens }; } catch (error) { throw new functions.https.HttpsError("internal", "Failed to send notifications", error.message); } });
Best Practices
- Avoid redundant checks: Instead of validating tokens separately before every send, combine validation with the send process to save FCM quota.
- Handle temporary errors: Some failures (like network issues) are temporary—you can add retry logic for those, but invalid tokens should be removed immediately.
- Regular cleanup: Even with on-send validation, periodically scan your token database to remove stale entries (e.g., tokens older than 6 months).
内容的提问来源于stack exchange,提问作者Manatea Vagner




