Firebase Auth手机号ID认证及Flutter+RevenueCat订阅防刷免费额度方案
1. 如何通过手机号ID(Phone-ID)实现Firebase Auth用户认证?
这里的Phone-ID通常指设备的唯一标识(如Android的Android ID、iOS的IDFV),Firebase Auth无原生支持该方式的认证,需通过自定义Token实现,步骤如下:
步骤1:获取设备唯一Phone-ID
使用Flutter的device_info_plus包获取设备标识:import 'package:device_info_plus/device_info_plus.dart'; import 'dart:io'; Future<String?> getDeviceId() async { final deviceInfo = DeviceInfoPlugin(); if (Platform.isAndroid) { final androidInfo = await deviceInfo.androidInfo; return androidInfo.id; // Android ID } else if (Platform.isIOS) { final iosInfo = await deviceInfo.iosInfo; return iosInfo.identifierForVendor; // IDFV } return null; }注意:Android 10+无法获取IMEI(需特殊权限),推荐用Android ID;iOS的IDFV在重置设备后会变更,但适合作为普通设备标识。
步骤2:后端生成Firebase自定义Token
用Firebase Admin SDK(比如Cloud Functions)生成绑定该Phone-ID的自定义Token,将Phone-ID作为用户UID:const admin = require('firebase-admin'); exports.createCustomToken = functions.https.onCall(async (data, context) => { const deviceId = data.deviceId; if (!deviceId) throw new functions.https.HttpsError('invalid-argument', 'Device ID missing'); // 可在此处添加设备ID合法性校验逻辑 const token = await admin.auth().createCustomToken(deviceId); return { token }; });步骤3:Flutter端用自定义Token登录
获取设备ID后调用云函数拿到Token,再完成Firebase Auth登录:final deviceId = await getDeviceId(); final HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('createCustomToken'); final result = await callable.call({'deviceId': deviceId}); final token = result.data['token']; await FirebaseAuth.instance.signInWithCustomToken(token);
2. 解决更换Firebase账号重复领取免费额度的问题
核心思路是将免费额度的领取状态与非Firebase账号的唯一标识绑定,以下是可行方案:
方案一:绑定设备唯一标识(最简方案)
- 用Firestore或实时数据库存储已领取免费额度的设备ID列表
- 用户申请免费额度时,先查询数据库:
final deviceId = await getDeviceId(); final doc = FirebaseFirestore.instance.collection('free_tier_claims').doc(deviceId); final snapshot = await doc.get(); if (!snapshot.exists) { // 发放免费额度 await doc.set({'claimedAt': FieldValue.serverTimestamp()}); // 执行额度发放逻辑(比如更新用户本地或云端的额度数据) } else { // 提示用户已领取过免费额度 } - 优势:实现简单;劣势:设备重置后用户可再次领取,适合风险较低的场景
方案二:绑定RevenueCat用户ID(推荐结合订阅场景)
- 将设备ID作为RevenueCat的用户ID,而非Firebase Auth的UID,确保订阅/免费额度状态与设备绑定:
final deviceId = await getDeviceId(); await Purchases.configure( PurchasesConfiguration('your_revenuecat_api_key') ..appUserID = deviceId, ); // 检查该RevenueCat用户是否已领取免费额度 final purchaserInfo = await Purchases.getPurchaserInfo(); if (!purchaserInfo.entitlements.all.containsKey('free_tier')) { // 发放免费额度(可通过RevenueCat的Promotional Offers或自定义字段记录) await Purchases.logIn(deviceId); // 确保用户身份固定 } - 优势:直接与订阅系统联动,状态统一;劣势:同样受设备重置影响,但可配合后端校验加强
方案三:后端统一校验(最高安全性)
- 搭建独立后端服务,维护设备ID/手机号(若已收集)与免费额度的绑定关系
- 发放免费额度前必须调用后端接口校验,接口需验证请求合法性(如通过Firebase App Check):
import 'package:http/http.dart' as http; import 'dart:convert'; // Flutter端调用后端接口 final response = await http.post( Uri.parse('https://your-backend.com/check-free-tier'), body: {'deviceId': deviceId, 'firebaseUid': FirebaseAuth.instance.currentUser?.uid}, headers: {'Authorization': 'Bearer ${await FirebaseAuth.instance.currentUser?.getIdToken()}'}, ); if (response.statusCode == 200) { final data = jsonDecode(response.body); if (data['canClaim']) { // 发放免费额度 } else { // 提示已领取 } } - 优势:可灵活添加风控规则(如IP限制、手机号绑定);劣势:需要额外维护后端
补充安全措施
- 启用Firebase App Check,防止恶意伪造设备ID请求
- 对设备ID进行哈希处理后存储,避免明文泄露
内容的提问来源于stack exchange,提问作者matti peterbull
相关产品推荐
相关产品推荐

