mcpField
F
firebase-auth

firebase-auth

github.com/evanca安全665191 次安装今天更新
代码仓库

关于 firebase-auth

firebase-auth 是面向 Flutter 应用的 Firebase Authentication 实施指南技能,涵盖登录流程、用户与会话管理、故障排查及安全实践。

主要功能

  1. 01配置 Firebase Authentication
  2. 02监听身份验证状态流
  3. 03实现邮箱、电话和社交登录
  4. 04管理个人资料、账号关联和 MFA
  5. 05处理错误并落实安全实践

适用场景

  1. 01为 Flutter 应用添加身份验证
  2. 02响应登录和退出状态变化
  3. 03关联身份提供方或更新用户资料
  4. 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);
    }
    

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.updateConfig REST 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,防止链接被拦截。
  • 配置应用以检测传入链接,并解析其底层深层链接以完成登录。

参考资料

评论

支持 Markdown