React Expo移动端应用Firebase手机号认证及OTP接口开发问题求助
React Expo移动端应用Firebase手机号认证及OTP接口开发问题求助
嘿,看你在开发React Expo移动端的手机号OTP认证功能,还写了Express后端接口但代码没写完对吧?我来帮你补全代码、梳理开发流程,再结合Firebase的手机号认证给你一些实用建议~
首先先把你没写完的后端Express OTP接口补全并优化下,这块是用户获取和验证验证码的核心:
import { Router, Request, Response } from 'express'; import { deleteOtp, generateOtp, verifyOtp } from '../services/otp.services'; import { sendOtp } from '../utils/sendOtp'; const router = Router(); // 发送OTP验证码接口 router.post('/send-otp', async (req: Request, res: Response) => { try { const { phone } = req.body; // 先校验手机号是否传入 if (!phone) return res.status(400).json({ message: '手机号为必填项,请检查输入' }); // 生成OTP并存储(建议用Redis带过期时间,或者数据库加过期字段,比如5分钟过期) const otp = await generateOtp(phone); // 调用短信服务发送OTP到用户手机 await sendOtp(phone, otp); res.status(200).json({ message: '验证码已发送,请注意查收短信' }); } catch (error) { console.error('发送验证码出错:', error); res.status(500).json({ message: '发送验证码失败,请稍后重试' }); } }); // 验证OTP验证码接口 router.post('/verify-otp', async (req: Request, res: Response) => { try { const { phone, otp } = req.body; // 校验必填参数 if (!phone || !otp) return res.status(400).json({ message: '手机号和验证码均为必填项' }); // 验证OTP是否有效(检查是否存在、是否过期) const isVerified = await verifyOtp(phone, otp); if (!isVerified) return res.status(401).json({ message: '验证码无效或已过期,请重新获取' }); // 验证成功后删除已使用的OTP,防止重复验证 await deleteOtp(phone); // 这里可以结合Firebase Admin SDK生成自定义登录token,返回给Expo端用于Firebase认证 // 如果你用Firebase原生的手机号认证,这步也可以省略,直接让Expo端和Firebase交互 // const customToken = await admin.auth().createCustomToken(phone); res.status(200).json({ message: '验证码验证成功', // customToken: customToken // 需要的话就返回这个 }); } catch (error) { console.error('验证验证码出错:', error); res.status(500).json({ message: '验证验证码失败,请稍后重试' }); } }); export default router;
接下来是Expo移动端的代码,结合Firebase的手机号认证,给你结合后端接口的实现示例:
- 先在Expo项目里安装Firebase依赖:
npx expo install firebase
- 配置Firebase(从Firebase控制台复制你的项目配置):
// src/firebaseConfig.js import { initializeApp } from 'firebase/app'; import { getAuth } from 'firebase/auth'; const firebaseConfig = { apiKey: "你的API_KEY", authDomain: "你的AUTH_DOMAIN", projectId: "你的PROJECT_ID", storageBucket: "你的STORAGE_BUCKET", messagingSenderId: "你的MESSAGING_SENDER_ID", appId: "你的APP_ID" }; const app = initializeApp(firebaseConfig); export const auth = getAuth(app);
- 编写手机号认证的页面组件:
import { useState } from 'react'; import { View, TextInput, Button, Alert, StyleSheet } from 'react-native'; import { auth } from '../firebaseConfig'; import { signInWithCustomToken } from 'firebase/auth'; const PhoneAuthScreen = () => { const [phone, setPhone] = useState(''); const [otp, setOtp] = useState(''); // 调用后端接口发送验证码 const handleSendOtp = async () => { if (!phone.trim()) { Alert.alert('提示', '请输入手机号'); return; } try { const response = await fetch('https://你的后端域名/send-otp', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ phone: `+86${phone}` }), // 统一格式为带国际区号的手机号 }); const data = await response.json(); if (response.ok) { Alert.alert('成功', data.message); } else { Alert.alert('错误', data.message); } } catch (err) { Alert.alert('错误', '发送验证码失败,请检查网络后重试'); } }; // 验证验证码并完成Firebase登录 const handleVerifyOtp = async () => { if (!phone.trim() || !otp.trim()) { Alert.alert('提示', '请填写手机号和验证码'); return; } try { const response = await fetch('https://你的后端域名/verify-otp', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ phone: `+86${phone}`, otp }), }); const data = await response.json(); if (response.ok) { // 如果后端返回了Firebase自定义token,用这个完成登录 if (data.customToken) { await signInWithCustomToken(auth, data.customToken); Alert.alert('成功', '手机号认证完成!'); // 这里可以跳转到首页或者其他页面 } else { Alert.alert('成功', '验证码验证通过'); } } else { Alert.alert('错误', data.message); } } catch (err) { Alert.alert('错误', '验证验证码失败,请检查网络后重试'); } }; return ( <View style={styles.container}> <TextInput style={styles.input} placeholder="请输入手机号(如138xxxx1234)" value={phone} onChangeText={setPhone} keyboardType="phone-pad" maxLength={11} /> <Button title="发送验证码" onPress={handleSendOtp} /> <TextInput style={styles.input} placeholder="请输入6位验证码" value={otp} onChangeText={setOtp} keyboardType="number-pad" maxLength={6} /> <Button title="验证并登录" onPress={handleVerifyOtp} /> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, padding: 20, justifyContent: 'center', gap: 15, }, input: { borderWidth: 1, borderColor: '#ccc', padding: 12, borderRadius: 8, }, }); export default PhoneAuthScreen;
最后给你几个踩坑提醒,都是我之前做这类功能遇到的:
- 手机号格式一定要统一!不管是后端存储还是前端传入,都用带国际区号的格式(比如+86开头),避免不同格式导致验证失败
- OTP的过期时间别设太长,5分钟左右刚好,存储的时候一定要加过期逻辑,防止旧验证码被重复使用
- Expo端要确保网络权限正常,iOS和Android的配置里都要开启网络访问权限
- 如果选择用Firebase原生的手机号认证,其实Firebase已经帮你处理了OTP的发送和验证,你可以直接用
signInWithPhoneNumber方法,不用自己搭后端OTP服务,看你需求来选
内容来源于stack exchange




