如何限制Firebase Phone Authentication仅允许已注册用户登录?
解决Firebase手机号登录自动注册问题:先检查用户是否已注册
Firebase默认会为未注册的手机号自动创建账号,要实现"先检查用户是否已注册,未注册则引导注册"的需求,可通过以下两种方案实现:
方案一:验证OTP后判断是否为新用户(前端快速实现)
利用Firebase用户元数据判断是否为首次登录:新用户的metadata.creationTime和metadata.lastSignInTime时间完全一致,老用户则存在差异。
修改你的verifyOTP函数,添加判断逻辑:
const verifyOTP = (e) => { let otp = e.target.value; setOtp(otp); if(otp.length === 6) { let confirmationResult = window.confirmationResult; confirmationResult.confirm(otp).then((result) => { const user = result.user; if(user != null){ // 判断是否为首次登录(新用户) const isNewUser = user.metadata.creationTime === user.metadata.lastSignInTime; if(isNewUser) { // 新用户引导至注册完善页面 console.log('请完善注册信息'); router.push('/register-profile'); } else { // 老用户直接进入首页 console.log('登录成功'); router.push('/home'); } } }).catch((err) => { console.log(err); }) } }
更严谨的扩展方案(结合Firestore)
若担心短时间内重复登录导致时间判断失效,可在Firestore中为用户添加isRegistered标记:
- 新用户完善资料后,在Firestore的
users集合中写入{ isRegistered: true } - 验证OTP后先查询该标记,再决定跳转逻辑:
import { doc, getDoc } from "firebase/firestore"; import { db } from './firebase-config'; // 你的Firestore配置文件 const verifyOTP = async (e) => { let otp = e.target.value; setOtp(otp); if(otp.length === 6) { let confirmationResult = window.confirmationResult; try { const result = await confirmationResult.confirm(otp); const user = result.user; if(user) { // 查询用户是否已完成注册 const userDocRef = doc(db, 'users', user.uid); const userDoc = await getDoc(userDocRef); if(userDoc.exists() && userDoc.data().isRegistered) { router.push('/home'); } else { router.push('/register-profile'); } } } catch(err) { console.log(err); } } }
方案二:发送OTP前检查手机号是否已注册(需Cloud Function)
若希望在发送OTP前就拦截未注册用户,可借助Firebase云函数实现(前端无法直接查询手机号对应的用户):
步骤1:编写Cloud Function
在Firebase项目中创建Callable函数:
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); exports.checkPhoneExists = functions.https.onCall(async (data, context) => { const phoneNumber = data.phoneNumber; try { await admin.auth().getUserByPhoneNumber(phoneNumber); return { exists: true }; } catch (error) { if (error.code === 'auth/user-not-found') { return { exists: false }; } throw new functions.https.HttpsError('unknown', error.message); } });
步骤2:前端调用云函数修改handleSubmit
发送OTP前先调用云函数检查手机号状态:
import { getFunctions, httpsCallable } from "firebase/functions"; const functions = getFunctions(); const handleSubmit = async (event) => { event.preventDefault(); if(phone) { // 先检查手机号是否已注册 const checkPhoneExists = httpsCallable(functions, 'checkPhoneExists'); try { const result = await checkPhoneExists({ phoneNumber: phone }); if(result.data.exists) { // 已注册用户,发送OTP setExpanded(true); generateRecaptcha(); let appVerifier = window.recaptchaVerifier; // 注意修正原代码中的拼写错误(recapture→recaptcha) const confirmationResult = await signInWithPhoneNumber(auth, phone, appVerifier); window.confirmationResult = confirmationResult; } else { // 未注册用户,引导至注册页面 console.log('该手机号未注册,请先完成注册'); router.push('/register'); } } catch(err) { console.log(err); } } }
注意:原代码中
window.recaptureVerifier存在拼写错误,正确应为window.recaptchaVerifier,请及时修正避免报错。
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

