关于 firebase-auth
firebase-auth 是面向 Flutter 应用的 Firebase Authentication 实施指南技能,涵盖登录流程、用户与会话管理、故障排查及安全实践。
主要功能
- 01配置 Firebase Authentication
- 02监听身份验证状态流
- 03实现邮箱、电话和社交登录
- 04管理个人资料、账号关联和 MFA
- 05处理错误并落实安全实践
适用场景
- 01为 Flutter 应用添加身份验证
- 02响应登录和退出状态变化
- 03关联身份提供方或更新用户资料
- 04排查身份验证错误并强化流程安全
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ evanca/ flutter-ai-rules --skill firebase-auth安装后在 Claude Code 中输入/firebase-auth确认已加载。
详细介绍
Firebase Authentication 技能
此技能介绍如何在 Flutter 应用中正确使用 Firebase Authentication。
适用场景
在以下情况下使用此技能:
- 在 Flutter 项目中设置 Firebase Authentication。
- 监听身份验证状态变化。
- 实现电子邮件/密码、电话号码或社交账号登录。
- 管理用户资料、账号关联或 MFA。
- 处理身份验证错误(包括电话身份验证中的 iOS
recaptcha-sdk-not-linked)。 - 在身份验证流程中应用安全最佳实践。
1. 设置和配置
flutter pub add firebase_auth
import 'package:firebase_auth/firebase_auth.dart';
- 使用身份验证提供商之前,请先在 Firebase 控制台中启用所需的提供商。
- 使用 Firebase Authentication 的任何功能之前,请先初始化 Firebase。
用于测试的本地模拟器:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await FirebaseAuth.instance.useAuthEmulator('localhost', 9099);
// ...
}
2. 身份验证状态管理
根据要观察的内容使用相应的数据流:
| 数据流 | 触发时机 |
|---|---|
authStateChanges() | 用户登录或退出时 |
idTokenChanges() | ID 令牌变化时(包括自定义声明) |
userChanges() | 用户数据变化时(例如资料更新) |
FirebaseAuth.instance
.authStateChanges()
.listen((User? user) {
if (user == null) {
print('User is currently signed out!');
} else {
print('User is signed in!');
}
});
- 应用启动时立即监听这些数据流,以处理初始身份验证状态。
- 自定义声明仅在登录、重新进行身份验证、令牌过期或手动刷新令牌后可用。
3. 电子邮件和密码身份验证
创建新账号:
try {
final credential = await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: emailAddress,
password: password,
);
} on FirebaseAuthException catch (e) {
if (e.code == 'weak-password') {
print('The password provided is too weak.');
} else if (e.code == 'email-already-in-use') {
print('The account already exists for that email.');
}
} catch (e) {
print(e);
}
登录:
try {
final credential = await FirebaseAuth.instance.signInWithEmailAndPassword(
email: emailAddress,
password: password,
);
} on FirebaseAuthException catch (e) {
if (e.code == 'invalid-credential') {
// Email enumeration protection enabled (default since Sep 2023):
// replaces 'user-not-found' and 'wrong-password'.
print('Invalid email or password.');
} else if (e.code == 'user-not-found') {
print('No user found for that email.');
} else if (e.code == 'wrong-password') {
print('Wrong password provided for that user.');
}
}
- 创建账号后,请验证用户的电子邮件地址。
- Firebase 会限制来自同一 IP 的电子邮件/密码新账号注册,以防止滥用。
- 在 iOS/macOS 上,应用重新安装后,身份验证状态仍会通过系统钥匙串保留。
- 自 2023 年 9 月起,Firebase 默认在新项目中启用电子邮件枚举保护,并将
user-not-found和wrong-password替换为invalid-credential。可在 Firebase 控制台的 Authentication > Settings 中管理此设置。 - 启用电子邮件枚举保护后,即使电子邮件未注册,
sendPasswordResetEmail()也可能成功完成且不返回错误。这是预期行为,请勿根据密码重置响应来判断某个电子邮件是否存在。
在 Apple 应用之间共享身份验证状态:
在 Apple 平台上,可将身份验证状态存储在共享钥匙串访问组中,以便同一开发者账号下的应用共享该状态。为每个应用启用钥匙串共享功能,并使用相同的访问组,然后使用完整限定的访问组配置 Firebase Auth:
await FirebaseAuth.instance.setSettings(
userAccessGroup: 'TEAMID.com.example.group1',
);
从默认钥匙串切换到共享访问组会导致现有用户退出,除非进行迁移。传入 migrateCurrentUser: true(要求 userAccessGroup 非空)即可保留当前会话,包括匿名会话。可在应用启动时、进行任何其他 Auth 调用之前安全地调用此方法,因为它会直接从钥匙串读取现有会话,然后 Auth 才会恢复会话。迁移可能会覆盖目标访问组中已存储的用户。
4. 社交身份验证
Google Sign-In(原生平台):
Future<UserCredential> signInWithGoogle() async {
final GoogleSignInAccount? googleUser = await GoogleSignIn.instance.authenticate();
final GoogleSignInAuthentication googleAuth = googleUser.authentication;
final credential = GoogleAuthProvider.credential(idToken: googleAuth.idToken);
return await FirebaseAuth.instance.signInWithCredential(credential);
}
Google Sign-In(Web):
Future<UserCredential> signInWithGoogle() async {
GoogleAuthProvider googleProvider = GoogleAuthProvider();
googleProvider.addScope('https://www.googleapis.com/auth/contacts.readonly');
googleProvider.setCustomParameters({'login_hint': '[email protected]'});
return await FirebaseAuth.instance.signInWithPopup(googleProvider);
}
- 为每个提供方配置特定平台的设置(例如,为 Android 上的 Google Sign-In 配置 SHA1 密钥)。
- 如果用户使用某个社交提供方登录,而此前已使用相同邮箱手动注册,Firebase 的受信任提供方机制会自动更改其身份验证提供方。
- 在 Android 上,
signInWithProvider会打开 Chrome 自定义标签页。如果AndroidManifest.xml中包含android:taskAffinity=""(Flutter 的默认设置),用户切换应用(例如使用密码管理器)时标签页会关闭,从而导致web-context-already-presented错误。删除android:taskAffinity=""即可解决此问题。 - 使用 Apple 登录时,添加
email和name权限范围,以显示首次登录时的完整界面(包括“共享/隐藏邮箱”):final appleProvider = AppleAuthProvider(); appleProvider.addScope('email'); appleProvider.addScope('name'); - 登录后若要撤销 Apple 身份验证令牌,请根据平台使用相应 API:
- Apple 平台(iOS/macOS/Web):使用
revokeTokenWithAuthorizationCode(),并传入userCredential.additionalUserInfo?.authorizationCode中的授权码。 - Android:使用
revokeAccessToken(),并传入userCredential.credential?.accessToken中的访问令牌。
// Apple platforms (iOS/macOS/web) final authCode = userCredential.additionalUserInfo?.authorizationCode; if (authCode != null) { await FirebaseAuth.instance.revokeTokenWithAuthorizationCode(authCode); } // Android final accessToken = userCredential.credential?.accessToken; if (accessToken != null) { await FirebaseAuth.instance.revokeAccessToken(accessToken); } - Apple 平台(iOS/macOS/Web):使用
5. 电话号码身份验证
使用电话号码身份验证前,请确保满足特定平台的前置条件:
- Android:必须在 Firebase 控制台中配置 SHA-1 哈希值,并启用 Google Play Integrity API。
- iOS:必须在 FCM 中配置 APNs 身份验证密钥,并启用远程通知的后台模式。
- Web:在 Firebase 控制台的 OAuth 重定向域名下添加应用的域名。
仅支持在真实设备和 Web 上使用电话号码登录。不支持在设备模拟器上进行测试。
iOS:recaptcha-sdk-not-linked 错误
在 iOS 上,当 Identity Platform 需要 reCAPTCHA Enterprise,但未链接原生 SDK 时,verifyPhoneNumber 可能会抛出代码为 recaptcha-sdk-not-linked 的 FirebaseAuthException。无法通过 Dart 解决此问题——请在原生 iOS 或 GCP 层面修复:
- 推荐:按照 Google 的指南在 Xcode 中链接 reCAPTCHA Enterprise iOS SDK。
- 替代方案:通过 Identity Toolkit
projects.updateConfigREST API 禁用 reCAPTCHA 短信防护(将recaptchaConfig.phoneEnforcementState设为OFF,并将recaptchaConfig.useSmsTollFraudProtection设为false)。请参阅官方步骤。这会降低欺诈防护能力——建议优先链接 SDK。 - 如果 SDK 使用由 Safari 视图控制器承载的验证流程,请在 iOS runner 中使用
uni_links/app_links或application:openURL:处理返回 URL。
6. 错误处理
- 始终对
FirebaseAuthException使用try-catch。 - 检查
e.code以识别具体错误类型。 - 对于
account-exists-with-different-credential,通过邮箱获取登录方式,并引导用户完成正确的流程。 - 对于
too-many-requests,使用重试逻辑或向用户提供反馈。 - 对于
operation-not-allowed,请确保已在 Firebase 控制台中启用该提供方。 - 在 iOS 上,
verifyPhoneNumber期间出现的recaptcha-sdk-not-linked错误由原生 Firebase iOS Auth SDK 抛出,需要进行原生设置或更改 GCP 配置——仅靠 Dart 代码无法修复。
7. 用户管理
// Update profile
await FirebaseAuth.instance.currentUser?.updateProfile(
displayName: "Jane Q. User",
photoURL: "https://example.com/jane-q-user/profile.jpg",
);
// Update email (sends verification to new address first)
await user?.verifyBeforeUpdateEmail("[email protected]");
- 使用
verifyBeforeUpdateEmail()更改用户邮箱,不要使用updateEmail()。只有用户验证后,邮箱才会更新。 - 仅在身份验证配置文件中存储必要信息;其他用户数据请使用数据库存储。
- 使用
linkWithCredential()将多个身份验证提供方关联到同一账户。 - 关联新凭据前,请验证用户身份。
- 处理账户关联时,使用
fetchSignInMethodsForEmail()。
8. 安全最佳实践
- 切勿在客户端代码中存储敏感的身份验证凭据。
- 监控身份验证状态变化,以妥善管理会话。
- 提交身份验证请求前,请验证用户输入,以防止注入攻击。
- 用户退出应用时,调用
FirebaseAuth.instance.signOut()。 - 对于敏感操作,请使用
reauthenticateWithCredential()重新验证用户身份。 - 对邮箱/密码身份验证实施严格的密码策略。
- 在 Realtime Database 和 Cloud Storage 安全规则中,使用
auth变量获取已登录用户的 UID,以实施访问控制。 - 对于敏感应用,请使用多重身份验证。
9. 多重身份验证
**安全警告:**避免使用基于 SMS 的 MFA。SMS 安全性较差,容易遭到入侵或伪造。
**平台限制:**Windows 不支持 MFA。Flutter 不支持在多个租户中使用 MFA。
- 实施 MFA 前,请至少启用一个兼容 MFA 的提供方。
10. 邮件链接身份验证
**重要提示:**Firebase Dynamic Links 已弃用,不再用于邮件链接身份验证。现在使用 Firebase Hosting 发送登录链接。
- 在
ActionCodeSettings中设置handleCodeInApp: true— 登录必须始终在应用中完成。 - 发送登录链接时,将用户的邮箱本地存储(例如,存储在
SharedPreferences中)。 - 切勿在重定向 URL 参数中传递用户邮箱 — 这会导致会话注入攻击。
- 在生产环境中使用 HTTPS URL,防止链接被拦截。
- 配置应用以检测传入链接,并解析其底层深层链接以完成登录。