简体   繁体   English

使用 Firebase 身份验证链接多个身份验证提供程序

[英]Linking multiple Auth Providers using Firebase Auth

I'm having difficulty trying to use FB Login credentials to log into a Firebase Auth account when an account with the same email address already exists.当具有相同电子邮件地址的帐户已存在时,我无法尝试使用 FB 登录凭据登录 Firebase Auth 帐户。 Ultimately I want to link the two auth providers to the same Firebase user account but presently I can't get the FB credential to login to Firebase, if one already exists, using the code below which I think follows the Firebase documentation exactly.最终,我想将两个身份验证提供程序链接到同一个 Firebase 用户帐户,但目前我无法获得登录 Firebase 的 FB 凭据(如果已经存在),请使用下面我认为完全遵循 Firebase 文档的代码。 I have already restricted the ability for a single user (email) to have multiple accounts based on Auth Provider.我已经限制了单个用户(电子邮件)拥有基于 Auth Provider 的多个帐户的能力。 When I delete the 'original' Firebase user account, Facebook login is able to login and create a user account as expected so the issues appears to only occur when there's already a Firebase auth account with the same email address from a different auth provider当我删除“原始”Firebase 用户帐户时,Facebook 登录能够按预期登录并创建用户帐户,因此问题似乎仅在已有来自不同身份验证提供商的具有相同电子邮件地址的 Firebase 身份验证帐户时发生

Scenario设想

In the scenario I'm testing I've already created an email/password account (original) and am trying to add my FB account via FB Login (that uses the same email address as the email/password account) to that original account.在我正在测试的场景中,我已经创建了一个电子邮件/密码帐户(原始),并尝试通过 FB 登录(使用与电子邮件/密码帐户相同的电子邮件地址)将我的 FB 帐户添加到该原始帐户。 I'm able to get the FB AccessToken and FB credential but when I pass it into Auth.auth().sign(with: credential..... it always errors out with the following error:我能够获得 FB AccessToken 和 FB 凭证,但是当我将它传递给 Auth.auth().sign(with: credential..... 它总是出错并显示以下错误:

Error错误

Error Domain=FIRAuthErrorDomain Code=17012 "An account already exists with the same email address but different sign-in credentials. Sign in using a provider associated with this email address."错误域=FIRAuthErrorDomain 代码=17012 “一个帐户已存在,其电子邮件地址相同但登录凭据不同。使用与此电子邮件地址关联的提供商登录。” UserInfo={FIRAuthErrorUserInfoNameKey=ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL, FIRAuthErrorUserInfoEmailKey=akash11x@gmail.com, FIRAuthErrorUserInfoUpdatedCredentialKey=, NSLocalizedDescription=An account already exists with the same email address but different sign-in credentials. UserInfo={FIRAuthErrorUserInfoNameKey=ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL, FIRAuthErrorUserInfoEmailKey=akash11x@gmail.com, FIRAuthErrorUserInfoUpdatedCredentialKey=, NSLocalizedDescription=一个帐户已存在,其电子邮件地址相同但登录凭据不同。 Sign in using a provider associated with this email address.使用与此电子邮件地址关联的提供商登录。

Code代码

@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()

            })
        })
    }
}

In case anyone else runs into the same issue.万一其他人遇到同样的问题。

It appears there's no way to link Firebase Auth providers 'on the fly' by having the new auth provider that has the same email address as an existing Firebase Auth account via the login flow.似乎无法通过登录流程让新的身份验证提供程序与现有 Firebase 身份验证帐户具有相同的电子邮件地址来“即时”链接 Firebase 身份验证提供程序。 Therefore, my solution was to create a 'link facebook to your profile' button in the profile after they have logged in using their email/password.因此,我的解决方案是在他们使用电子邮件/密码登录后在个人资料中创建一个“将 Facebook 链接到您的个人资料”按钮。 That button then creates the linkage in Firebase to recognize facebook as secondary auth provider for that account.然后该按钮会在 Firebase 中创建链接以将 facebook 识别为该帐户的辅助身份验证提供者。 For testing I logged out and used the 'Login with facebook' button and all works as expected.为了进行测试,我注销并使用了“使用 Facebook 登录”按钮,一切都按预期工作。

I think this UX greatly diminishes the utility of the feature but after weeks of research its the best I could come up with.我认为这种 UX 大大降低了该功能的实用性,但经过数周的研究,这是我能想到的最好的方法。

Code on the Profile to link facebook to email/password original account个人资料上的代码将 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
                }
            }
        }
    }
}

Code on Login page (below) email/password login登录页面上的代码(下方)电子邮件/密码登录

Don't forget to add the LoginButtonDelegate to the View Controller不要忘记将 LoginButtonDelegate 添加到视图控制器

@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()

                    }

                }

            }

        }

    }
}

For the button itself I added a button using storyboard to the View and used the customer class 'FBSDKButton' and styled it accordingly:对于按钮本身,我使用故事板向视图添加了一个按钮,并使用了客户类“FBSDKButton”并对其进行了相应的样式设置:

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

and

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

Lastly, be sure you import the appropriate libraries;最后,确保您导入了适当的库; I used: import FirebaseAuth import FBSDKCoreKit import FacebookCore import FacebookLogin import FBSDKLoginKit import Firebase我用过: import FirebaseAuth import FBSDKCoreKit import FacebookCore import FacebookLogin import FBSDKLoginKit import Firebase

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM