Firebase Authentication: Linking Multiple Accounts in Swift

Linking multiple Auth Providers using Firebase Auth

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. 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. That button then creates the linkage in Firebase to recognize facebook as secondary auth provider for that account. For testing I logged out and used the 'Login with facebook' button and all works as expected.

I think this UX greatly diminishes the utility of the feature but after weeks of research its the best I could come up with.

Code on the Profile to link facebook to email/password original account

    @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

@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:

    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

Firebase authentication: linking multiple accounts in Swift

So your code follows the first 2 steps in this link. But the documentation explicity says not to call signInWithCredential but instead call

FIRAuth.auth()?.currentUser.linkWithCredential(credential) { (user, error) in
// ...
}

After getting your credential from Facebook's SDK.

Quote from link: "If the call to linkWithCredential:completion: succeeds, the user's new account can access the anonymous account's Firebase data."

Account linking with different emails

You don't really know they are the same users if their emails differ (anyone can create an email that is similar to another email). So you shouldn't want to build functionality that automatically links the 2 users.

That said, you can manually use the linkWithCredential API to link the Apple credential to the email/password user provided that user is already signed in, but I think you shouldn't require this unless the email/password user wants to do so (via some button to link their Apple account).

Managing multiple iOS authentication Firebase - Swift - Programmatically

Try this:

if GIDSignIn.sharedInstance()?.handle(url) {
return true
}
else if ApplicationDelegate.shared.application(app, open: url, options: options) {
return true
}
return false

FirebaseUI follows a similar approach - it loops through all available / activated authentication providers, checking for each of them if it is able to handle the URL, and returning true if that's the case. Check out their implementation.

How to correctly link different Auth accounts in Firebase IOS

When you get the credential already exists error, you already have the email at that point, you then call fetchProvidersForEmail with that email which will lookup the provider IDs associated with that email. You then sign in the user with one of those providers. After you finish sign-in with the existing account, you call linkWithCredential:completion: with the original credential that caused the error to occur. This causes the accounts to link. The next time the user tries to sign in, they will be able to sign in to the same user with either provider.

Check FirebaseUI-iOS which already takes care of the whole flow for you. You can also check there source code to see how they handle such situations: https://github.com/firebase/FirebaseUI-iOS



Related Topics



Leave a reply



Submit