FirebaseIdentity
简介
此库的目的是为了使那些不想使用 FirebaseUI 库的人们在 iOS 上更轻松地构建围绕 Firebase 身份验证服务的自定义前端 UI。它通过实现标准的身份验证工作流和错误处理来实现这一点(例如账户链接、个人资料更新、设置/更新密码、重新认证、启用/禁用第三方提供商、账户删除、自动重试等)。这是通过将大量的错误处理逻辑抽象化到一个单例状态机('AuthManager')中,该状态机能够处理大多数 Firebase 身份验证用例来完成的。
使用方式
AuthManager 单例
您需要主要与之交互的类是 AuthManager
。
设置应在应用程序的生命周期早期完成,但在初始化 Firebase 库之后。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { ... // firebase FirebaseApp.configure() // facebook ApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions) // configure other auth providers as needed ... // auth manager AuthManager.configure() // observer auth state // the auth manager state changes as the Firebase authentication state changes; by listening to this state change // you can appropriately update your UI authenticationStateObserver = NotificationCenter.default.addObserver(forName: AuthManager.authenticationStateChangedNotification, object: nil, queue: .main, using: { (note) in guard let manager = note.object as? AuthManager else { return } switch manager.authenticationState { case .authenticated: AppController.login() case .notDetermined, .notAuthenticated: AppController.logout() } }) ... return true }
身份提供者
身份提供者是通过 IdentityProvider
类来建模的。
目前仅实现了 EmailIdentityProvider
和 FacebookIdentityProvider
,但您很轻松地可以通过继承 IdentityProvider
来实现其他提供者。如果您实现了其他的,请随时提交一个拉取请求 :)。
使用电子邮件身份验证注册新用户
let provider = EmailIdentityProvider(email: user.email, password: user.password)
AuthManager.shared.signUp(with: provider) { (result) in
switch result {
case .success(let value):
print(value)
case .failure(let error):
self.showAuthenticationErrorAlert(for: error)
}
}
使用 Facebook 身份验证注册/登录用户
let requestedPermissions: [String] = ["email"]
self.fbLoginManager.logIn(permissions: requestedPermissions, from: self) { (result, error) in
guard let result = result, !result.isCancelled else {
if let error = error {
print(error.localizedDescription)
self.showAlert(for: error)
}
return
}
let token = AccessToken.current!.tokenString
let provider = FaceboookIdentityProvider(accessToken: token)
AuthManager.shared.signUp(with: provider) { (result) in
switch result {
case .success(let value):
print(value)
case .failure(let error):
self.showAuthenticationErrorAlert(for: error)
}
}
}
示例应用程序中包含了许多其他示例,您应该查看这些示例。
重新认证
Firebase 中有一些用户资料变更需要最近的一次登录。尤其是,Firebase 在这种情况下会触发 FIRAuthErrorCodeRequiresRecentLogin = 17014,
错误。
此库通过 AuthManagerReauthenticating
协议来处理此错误。
但作为额外的奖励,此库还跟踪最后一次用户登录时间,如果在最后一次登录之后超过了5分钟(5分钟相当于当前记录的 Firebase 最近登录阈值),则会乐观地请求重新认证。在本地跟踪此信息的益处在于,我们可以避免生成此错误所需的额外网络请求。
重新认证只在用户已经登录时生效,因此实现此协议的一个好地方是在提供账户资料变更 UI 的视图控制器上。例如,
extension AccountViewController: AuthManagerReauthenticating {
func authManager(_ manager: AuthManager, reauthenticateUsing providers: [IdentityProviderUserInfo], challenge: ProfileChangeReauthenticationChallenge) {
// ask for reauthentication from the highest priority auth provider
guard let provider = providers.first else {
return
}
switch provider.providerID {
case .email:
// an email provider will always have an email associated with it, therefore it should be safe to force unwrap this value here;
// what if there is some kind of error that causes the email to be non-existant in this scenario? Force the user to log-out, then back in?
// it seems like it would be impossible for the email to not exist on an email auth provider
let email = provider.email!
// present UI for user to provider their current password
let alert = UIAlertController(title: "Confirm Password", message: "Your current password is required to change your email address.\n\nCurrent Email: \(email)\nTarget Email:\(challenge.context.profileChangeType.attemptedValue)", preferredStyle: .actionSheet)
for password in Set(AuthManager.debugEmailProviderUsers.map({ $0.password })) {
alert.addAction(UIAlertAction(title: password, style: .default, handler: { (action) in
let provider = EmailIdentityProvider(email: email, password: password)
manager.reauthenticate(with: provider, for: challenge) { (error) in
guard let error = error else {
return
}
self.showAuthenticationErrorAlert(for: error)
}
}))
}
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
case .facebook:
fetchFacebookAccessTokenForReauthentication { (token) in
guard let token = token else {
return
}
let provider = FaceboookIdentityProvider(accessToken: token)
manager.reauthenticate(with: provider, for: challenge) { (error) in
guard let error = error else {
return
}
self.showAuthenticationErrorAlert(for: error)
}
}
default:
print("undefined provider")
}
}
}
这已在包含的示例应用程序中的 SignedInViewController
中实现。
账户删除
您可能想让用户有能力删除自己的账户。
此库通过提供 DeleteAccountOperation
来简化这一过程。
删除用户的账户
...
// optionally provide database refs to delete (i.e. user scoped refs); these will be deleted before the account is deleted
let userRefs: [DatabaseReference]? = [userRef1, userRef2, ...]
// create a new op instance
let deleteAccountOp = DeleteAccountOperation(refs: userRefs)
deleteAccountOp.deleteAccountCompletionBlock = { [unowned self] (result) in
DispatchQueue.main.async {
switch result {
case .success(let user):
// post account deletion (i.e. show login screen, track event, etc.)
case .failure(let error):
switch error {
case .cancelledByUser:
break
case .other(let msg):
// show alert
}
}
}
}
// pass the op to the AuthManager
AuthManager.shared.deleteAccount(with: deleteAccountOp)
...
示例
包含的示例项目展示了此库的功能。
先决条件
要运行示例项目,请首先克隆存储库,然后从示例目录运行pod install
。
要运行示例项目,您还需要一个Firebase账户。一旦创建账户,请按照说明创建一个演示项目。在Firebase账户中创建演示项目后,您需要下载项目相应的GoogleService-Info.plist
并将其添加到演示项目中。在应用启动时,Firebase框架会自动检测此文件并根据环境进行配置。
需求
- iOS 11.4+
- Swift 5+
- 一个Firebase账户
安装
FirebaseIdentity 可通过 CocoaPods 获取。要安装它,只需将以下行添加到您的 Podfile 中
pod 'FirebaseIdentity'
作者
cgossain, [email protected]
许可证
FirebaseIdentity 是在MIT许可证下可用的。有关更多信息,请参阅 LICENSE 文件。