首页 > 解决方案 > 使用 Firebase 身份验证链接多个身份验证提供程序

问题描述

当已存在具有相同电子邮件地址的帐户时,我在尝试使用 FB 登录凭据登录 Firebase 身份验证帐户时遇到困难。最终,我想将这两个身份验证提供程序链接到同一个 Firebase 用户帐户,但目前我无法获取 FB 凭据以登录 Firebase,如果已经存在的话,使用我认为完全遵循 Firebase 文档的下面的代码。我已经根据身份验证提供者限制了单个用户(电子邮件)拥有多个帐户的能力。当我删除“原始”Firebase 用户帐户时,Facebook 登录能够按预期登录并创建用户帐户,因此问题似乎仅在已有来自不同身份验证提供商的具有相同电子邮件地址的 Firebase 身份验证帐户时出现

设想

在我正在测试的场景中,我已经创建了一个电子邮件/密码帐户(原始),并尝试通过 FB 登录(使用与电子邮件/密码帐户相同的电子邮件地址)将我的 FB 帐户添加到该原始帐户。我能够获得 FB AccessToken 和 FB 凭据,但是当我将其传递给 Auth.auth().sign(with: credential ..... 它总是出错并出现以下错误:

错误

错误域 = FIRAuthErrorDomain 代码 = 17012 “已存在具有相同电子邮件地址但登录凭据不同的帐户。使用与此电子邮件地址关联的提供商登录。” UserInfo={FIRAuthErrorUserInfoNameKey=ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL, FIRAuthErrorUserInfoEmailKey=akash11x@gmail.com, FIRAuthErrorUserInfoUpdatedCredentialKey=, NSLocalizedDescription=一个帐户已经存在,具有相同的电子邮件地址但不同的登录凭据。使用与此电子邮件地址关联的提供商登录。

代码

@IBAction func fbLoginButton(_ sender: FBButton) {

    let loginManager = LoginManager()
    loginManager.logIn(permissions: ["public_profile", "email"], from: self) { (result, error) in
        if error != nil {
            return
        }

        print(result)
        guard let accessToken = AccessToken.current else {
            print("Failed to get access token")
            return
        }
        print(accessToken)

        let credential = FacebookAuthProvider.credential(withAccessToken: accessToken.tokenString)
        print(credential)

        Auth.auth().signIn(with: credential, completion: { (firebaseUser, error) in //Can't get passed this point when another account with the same email address already exists
            if error != nil {
                print("Could not login into Firebase using FB credentials")
                return
            }

            print("This is the FirebaseUser after FB Login: \(firebaseUser)")

            firebaseUser?.user.link(with: credential, completion: { (authResult, error) in
                if error != nil {
                    print("Firebase Auth Providers not linked")
                    return
                }

                //                let prevUser = Auth.auth().currentUser
                //                Auth.auth().signIn(with: credential) { (authResult, error) in
                //                  if let error = error {
                //                    // ...
                //                    return
                //                  }
                //                  // User is signed in
                //                  // ...
                //                }
                //                            // Merge prevUser and currentUser accounts and data
                //
                // ...

                //This user has a profile, go to tab controller
                let tabBarVC = self.storyboard?.instantiateViewController(withIdentifier: Constants.Storyboard.tabBarController)

                self.view.window?.rootViewController = tabBarVC
                self.view.window?.makeKeyAndVisible()

            })
        })
    }
}

标签: swiftfacebookfirebasegoogle-cloud-platformfirebase-authentication

解决方案


以防其他人遇到同样的问题。

似乎无法通过登录流程让新的身份验证提供商与现有的 Firebase 身份验证帐户具有相同的电子邮件地址,从而“即时”链接 Firebase 身份验证提供商。因此,我的解决方案是在他们使用电子邮件/密码登录后,在个人资料中创建一个“将 Facebook 链接到您的个人资料”按钮。然后,该按钮在 Firebase 中创建链接,以将 facebook 识别为该帐户的辅助身份验证提供程序。为了进行测试,我注销并使用了“使用 facebook 登录”按钮,一切都按预期工作。

我认为这个 UX 大大降低了该功能的实用性,但经过数周的研究,这是我能想到的最好的。

个人资料上的代码将 facebook 链接到电子邮件/密码原始帐户

    @IBAction func fbLoginButton(_ sender: FBButton) {

    let loginManager = LoginManager()
    loginManager.logIn(permissions: ["public_profile", "email"], from: self) { (result, error) in
        if error != nil {
            return
        }

        print(result)

        guard let accessToken = AccessToken.current else {
            print("Failed to get access token")
            return
        }
        print(accessToken)

        let credential = FacebookAuthProvider.credential(withAccessToken: accessToken.tokenString)
        print(credential)

        if let user = Auth.auth().currentUser {
            print("This is the user:  \(user)")
            // [START link_credential]
            user.link(with: credential) { _, error in
                // [START_EXCLUDE]
                if let error = error {
                    print("FB did not link to account.  \(error)")
                    return
                }
            }
        }
    }
}

登录页面上的代码(下)电子邮件/密码登录

不要忘记将 LoginButtonDelegate 添加到 View Controller

@IBAction func fbLoginButton(_ sender: FBButton) {

    let loginManager = LoginManager()
    loginManager.logIn(permissions: ["public_profile", "email"], from: self) { (result, error) in
        if error != nil {
            return
        }

        guard let accessToken = AccessToken.current else {
            print("Failed to get access token")
            return
        }

        let credential = FacebookAuthProvider.credential(withAccessToken: accessToken.tokenString)

        Auth.auth().signIn(with: credential) { (authResult, error) in

            if let error = error {
                // ...
                return
            }

            let user = authResult?.user

            //Check if user is nill
            if user != nil {

                //This means that we have a user, now check if they have a User document
                UserService.getUserProfile() { (u) in

                    if u == nil {

                        //No profile, go to Profile Controller
                        self.performSegue(withIdentifier: Constants.Segue.profileViewController, sender: self)
                    }
                    else {

                        //Save the logged in user to local storage
                        LocalStorageService.saveCurrentUser(user: u!)

                        //This user has a profile, go to tab controller
                        let tabBarVC = self.storyboard?.instantiateViewController(withIdentifier: Constants.Storyboard.tabBarController)

                        self.view.window?.rootViewController = tabBarVC
                        self.view.window?.makeKeyAndVisible()

                    }

                }

            }

        }

    }
}

对于按钮本身,我使用情节提要向视图添加了一个按钮,并使用了客户类“FBSDKButton”并相应地对其进行了样式设置:

    let fbButtonText = NSAttributedString(string: "Link facebook to Profile")
    fbLoginButton.setAttributedTitle(fbButtonText, for: .normal)

    let fbButtonText = NSAttributedString(string: "Login with facebook")
    fbLoginButton.setAttributedTitle(fbButtonText, for: .normal)

最后,确保导入适当的库;我使用: 导入 FirebaseAuth 导入 FBSDKCoreKit 导入 FacebookCore 导入 FacebookLogin 导入 FBSDKLoginKit 导入 Firebase


推荐阅读