iOS — Firebase Authentication Core / 原生登录 UI
独立模块与安装
| Pod | 用途 |
|---|---|
ROAuthFirebase | FirebaseApp/Auth、User/Session、ID Token 获取刷新、匿名、Firebase 凭证、link/reauth、状态监听/登出 |
ROAuthFirebaseUI | 选装 UIKit 缓存身份卡、Provider 方法选择、动态倒计时、错误重试、四语言资源 |
同装限制
iOS 12、FirebaseAuth/FirebaseCore 10.29.0 与 ROLoginGoogle 的 GoogleSignIn 9.2.0 当前存在官方依赖约束冲突。需要同一项目使用两者时,请先选取已验证的兼容依赖方案,不要强行覆盖官方版本。
下面是从本地源码按需集成的示例。游戏正式安装应选择实际可解析、已固定版本的 Pod 来源。UI 依赖 Firebase Core,二者需在同一消费者环境内正确解析:
platform :ios, '12.0'
target 'GameApp' do
use_frameworks! :linkage => :static
pod 'ROAuthFirebase', :path => '<本机roios-sdk>/Modules/ROAuthFirebase'
pod 'ROAuthFirebaseUI', :path => '<本机roios-sdk>/Modules/ROAuthFirebaseUI'
end初始化和 Firebase 身份取得
由宿主按 Firebase 官方步骤正确配置命名 FIRApp 或默认 FIRApp、合法 Bundle ID、GoogleService-Info.plist。资源属于消费宿主,不进入通用 SDK 的发布源码或公开文档。
ROAuthFirebase *core = [ROAuthFirebase forDefaultApp];
// 如果应用没有默认 FirebaseApp,则使用宿主明确的 FIROptions 初始化对应 App。
[core getIDTokenForceRefresh:NO completion:^(ROAuthFirebaseSession *session, NSError *error) {
if (session && session.idToken.length) {
// 交给宿主经 HTTPS 向可信游戏后端验证;不要打印 Token。
} else {
// 按 error.code 做安全错误提示或重试,不返回游戏成功。
}
}];现有 ROLoginApple 在 Firebase OAuth 流程中必须正确使用不可预测 rawNonce 与 SHA256(rawNonce),Apple identityToken 不等于 authorizationCode:
NSString *rawNonce = [ROAuthFirebase generateRawNonce];
NSString *hashedNonce = [ROAuthFirebase SHA256OfRawNonce:rawNonce];
[[ROLoginApple shared] authorizeFromWindow:window nonce:hashedNonce state:state
completion:^(ROLoginAppleResult *result, NSError *error, BOOL cancelled) {
if (!result) return; // 错误/取消留在可恢复的认证流程。
FIRAuthCredential *credential =
[ROAuthFirebase appleCredentialWithIDToken:result.identityToken
rawNonce:rawNonce
fullName:result.fullName];
[core signInWithCredential:credential completion:^(ROAuthFirebaseSession *session, NSError *error) {
// Firebase Session 成功后,游戏宿主再做 auth.Auth;并非游戏直接登录成功。
}];
}];宿主为 Apple 的 state 独立生成并校验安全随机值;需要保护首次授权的姓名/邮箱只返回一次的情况。现有 ROLoginGoogle 另提供 signInForFirebaseFromController:completion: 返回 Google ID Token + Access Token;旧 serverAuthCode 不可替代 Access Token。正式 Google9.2 + Firebase10.29 的同装阻塞解除前,不把此桥接视为生产可用组合。
可选 ROSDK 登录 UI
ROFirebaseUIOptions *options = [ROFirebaseUIOptions new];
options.loginMethods = ROFirebaseLoginMethodAnonymous; // 如需游客登录
options.showCachedAccount = YES;
options.allowSwitchAccount = YES;
options.autoContinueSeconds = 3; // 默认 3→2→1,设置 0 关闭
options.timeoutSeconds = 60; // SDK 局部等待,不是取消官方请求
ROAuthFirebaseUI *ui = [[ROAuthFirebaseUI alloc] initWithCore:core options:options];
[ui presentFromController:presenter completion:^(ROAuthFirebaseSession *session, BOOL cancelled, NSError *error) {
if (error) {
// 可重试错误,窗口仍打开;不要完成游戏 sdk.login()。
} else if (session) {
// 只代表 Firebase 身份认证成功;游戏账号依赖服务端 account/token。
} else {
// 主窗口仅允许宿主生命周期主动 dismiss;不视为游戏登录成功。
}
}];
// 宿主需持有 ui 实例,窗口/Scene 销毁时调用 [ui dismiss]。completion 的 error 参数 是非终态、可恢复错误通知,会保留登录窗口;Provider 自身取消返回登录方式列表,不能解锁必需登录。多 Provider 身份卡仅表示 Firebase 已关联方式,不代表最近登录方式;无名称或品牌素材时退回中性头像,无需远程请求。Google/Apple 等入口只在宿主实际注册可执行 Provider Handler 后显示。系统语言为 en/zh-Hans/th/id,不是 Egret 游戏语言。
选择其他认证方式时可以绕过旧缓存卡并停止倒计时,但不需要默认登出 Firebase;切换已有游客身份时需由用户确认,避免丢失尚未绑定的进度。
SDK 成功与游戏 sdk.login() 成功
Firebase currentUser 或 SDK UI 的 session 只代表 Firebase 身份。真实 Platform : SDKDelegate 使用 Firebase ID Token 发起游戏服务器 auth.Auth,服务器验签并兑换有效 account/token 后,才能按固定 JS↔Native 协议单次向游戏返回成功。业务网络错误、拒绝和空账号不能放行游戏;Firebase 缓存可保留并刷新,不需要额外的 Firebase loginFlush()。
宿主配置与错误处理
- 使用宿主自己的
GoogleService-Info.plist、Bundle ID 和安全配置初始化 Firebase,不要把真实账号或机密硬编码进模块。 completion的error参数 属于可恢复错误通知;Provider 取消返回方式选择,窗口销毁应调用dismiss。宿主应防止迟到回调和重复游戏登录完成。- 不含独立 Facebook 原生 OAuth 实现;关联身份标识并非可用登录按钮。
- UI 的四语言资源由 Pod 内的资源 Bundle 提供,系统语言为
en/zh-Hans/th/id。
API 全览:Firebase Auth Core(独立 Pod)
ROAuthFirebase 独立处理 Firebase App/User、ID Token、凭证和观察者;不包含 Egret、游戏服务器 auth.Auth、账号转换或 UI。iOS 12 候选 FirebaseAuth/FirebaseCore 10.29.0。
| Objective-C 公开接口 | 参数、结果与用途 |
|---|---|
[[ROAuthFirebase alloc] initWithApp:FIRApp] | 宿主已有 FIRApp 时创建 Core |
[ROAuthFirebase forDefaultApp] | 取得已配置的默认 FirebaseApp 对应 Core |
[ROAuthFirebase configureWithName:options:error:] | 使用 FIROptions 配置指定 Firebase App |
core.currentUser | 当前身份快照,不意味着 ID Token 或游戏 Session 有效 |
addStateListener: → handle、removeStateListener: | 订阅/解除 Firebase 身份变化 |
getIDTokenForceRefresh:completion: | 获取/强制刷新 Firebase ID Token |
signInAnonymously: | Firebase 匿名登录,与游戏后端游客账号不同 |
signInWithCredential:completion: | Provider 返回的 FIRAuthCredential 登录 |
linkWithCredential:completion: | 绑定至当前 Firebase 用户 |
reauthenticateWithCredential:completion: | 当前用户重新认证 |
signOut:error: | 清理 Firebase 当前会话,不替游戏后端注销 |
googleCredentialWithIDToken:accessToken: | Google Firebase Credential,注意 accessToken 不是 serverAuthCode |
appleCredentialWithIDToken:rawNonce:、含 fullName: 的重载 | Apple 身份凭证及首次姓名,可选全名 |
generateRawNonce、SHA256OfRawNonce: | Apple 授权请求 nonce 生成与 SHA256 编码 |
ROAuthFirebaseCompletion(session,error) | Firebase 认证成功/失败;成功仍需服务器认证 |
ROAuthFirebaseStateChanged(user) | 当前本地身份变化,不是游戏登录结果 |
ROAuthFirebaseUser 公开 uid、displayName、anonymous、providerIDs;ROAuthFirebaseSession 公开 user 与敏感 idToken。ROAuthFirebaseErrorDomain 为错误域。ProviderIDs 只代表当前已关联方式,不推断最新登录来源。
ROAuthFirebase *core = [ROAuthFirebase forDefaultApp];
// 或:core = [ROAuthFirebase configureWithName:@"game-auth" options:firebaseOptions error:&error];
id observer = [core addStateListener:^(ROAuthFirebaseUser *user) {
// user 可能为 nil;不可据此报告游戏账号登录成功。
}];
[core signInAnonymously:^(ROAuthFirebaseSession *session, NSError *error) {
if (session) {
// 将 session.idToken 安全交游戏服务器验签/兑换 account + token。
}
}];
// 任选:已有 Firebase 用户可续登刷新:
// [core getIDTokenForceRefresh:YES completion:completion];
// [core signInWithCredential:credential completion:completion];
// [core linkWithCredential:credential completion:completion];
// [core reauthenticateWithCredential:credential completion:completion];
// FIRAuthCredential *apple = [ROAuthFirebase appleCredentialWithIDToken:idToken rawNonce:rawNonce];
// NSString *nonce = [ROAuthFirebase generateRawNonce];
// NSString *sha = [ROAuthFirebase SHA256OfRawNonce:nonce];
// 用完监听:[core removeStateListener:observer];
// 退出:[core signOut:&error];API 全览:独立选装登录 UI Pod
ROAuthFirebaseUI 需要 Core,不会强制依赖 Google/Apple 登录模块。宿主保持 Core 生命周期,在每次独立游戏登录流程中创建 UI 对象,保存至宿主属性;presentFromController:completion: 是一次窗口展示入口。UI 自己管理遮罩卡、账号信息、倒计时、方式列表、Core 调用、失败重试与关闭;宿主在控制器销毁前 dismiss。不是每次按钮点击都需要重新 new 一个 UI。
| Objective-C UI 公开接口 | 用法 |
|---|---|
[[ROAuthFirebaseUI alloc] initWithCore:options:] | 创建当前宿主登录流程的 UI |
presentFromController:completion: | 在前台 ViewController 上展示一次模态卡片;获取 Firebase Session 才成功回调 |
registerMethod:handler: | 注册实际实现的 Provider,接收 FIRAuthCredential |
unregisterMethod: | 移除指定 Provider handler |
ui.state | 只读 Idle/Choosing/CachedAccount/Authorizing/FirebaseAuthenticating/RefreshingToken/Succeeded/Failed/Cancelled 阶段 |
ROFirebaseUICompletion(session,cancelled,error) | 同一回调接收 Firebase Session、主动关闭和可重试错误;错误事件不关闭 UI |
dismiss | 宿主生命周期关闭模态并使迟到结果失效,不能作为给玩家的“跳过登录”按钮 |
ROFirebaseUIProviderHandler(presenter, completion) | Provider 授权并调用 completion(credential,error,cancelled) |
ROFirebaseUICompletion(session,cancelled,error) | Firebase 成功或宿主主动取消,游戏仍须后端认证 |
ROFirebaseUIOptions 全部字段:loginMethods(方式位)、showCachedAccount、autoContinueSeconds(默认 3、0 关闭)、timeoutSeconds(默认 60)、allowSwitchAccount、showAnonymousWarning、accentColor(UIColor)、mode(SignIn/Link/Reauthenticate)、preferredOrder、methodIcons(按 NSNumber 位值注入合规 UIImage)。方式位:Google=1、Facebook=2、GitHub=4、Twitter=8、Microsoft=16、Yahoo=32、Apple=64、Anonymous=512。默认按钮按已注册 handler 与位掩码交集显示;匿名不需要外部 handler。关联 Provider 列表仅表示历史关联,不代表对应授权器已安装。
ROFirebaseUIOptions *options = [ROFirebaseUIOptions new];
options.loginMethods = ROFirebaseLoginMethodAnonymous;
options.autoContinueSeconds = 3;
options.allowSwitchAccount = YES;
options.mode = ROFirebaseLoginModeSignIn;
ROAuthFirebaseUI *ui = [[ROAuthFirebaseUI alloc] initWithCore:core options:options];
// 宿主将 ui 保存在属性中,直至当前登录流程结束/控制器销毁。
[ui presentFromController:presenter
completion:^(ROAuthFirebaseSession *session, BOOL cancelled, NSError *error) {
if (error) {
// 非终态错误:登录窗口仍打开,等待重试/选择其他登录方式。
} else if (session) {
// Firebase ID Token 交宿主游戏服务端 auth.Auth 兑换 account/token。
// 只有成功兑换后才完成原有 sdk.login()。
} else if (cancelled) {
// 只表示宿主主动销毁;游戏仍未登录。
}
}];
// 如果项目引入真实 Google 等独立授权模块,可选注册:
// [ui registerMethod:ROFirebaseLoginMethodGoogle handler:
// ^(UIViewController *vc, ROFirebaseUICredentialCompletion done) {
// ... 获取 Google ID Token + Access Token,创建 FIRAuthCredential ...;
// done(credential, nil, NO);
// }];
// 在 presenter 销毁前:[ui dismiss];**CocoaPods 依赖解析:**消费者目前必须同时提供 ROAuthFirebase 与 ROAuthFirebaseUI 两个具体来源(例如同时使用本地 :path,或经验证的两个固定 git split Tag),UI Podspec 的 s.dependency 不会自动推导 Core 的 Git URL。iOS12 + GoogleSignIn9.2 + FirebaseAuth10.29 官方依赖冲突仍 BLOCKED,不能声称真实 Google/Firebase 混装成功。
**验收状态:**UI/Core 最终代码与测试证据按当前源码 SHA 判断,XCTest build-for-testing 不等于执行;真实 OAuth/游戏服务器和固定 Egret 端到端仍需测试。