Firebase Authentication Link Facebook to Google - ios

After many tests I decided to create a new xCode project to better understand Firebase authentication with multiple providers.
I set up in Firebase -> SignIn Methods -> An account per email address
An account per email address
Prevents users from creating multiple
accounts using the same email address with different authentication
providers
At this point I have implemented, carefully following the Firebase guide, the login with Facebook and with Google .. Everything seems to work perfectly but I always find myself with the same error that I can't manage:
When my user creates a Firebase account via Google he is no longer able to log in if he decides to use Facebook.
Facebook returns its error when it completes its authentication flow with Firebase:
Firebase Error With Facebook Provider: An account already exists with the same email address but different sign-in credentials. Sign in using a provider associated with this email address.
Continuing to follow the documentation step by step I stopped here (firebase explains how to handle this error)
I have also implemented error handling but after calling Auth.auth().fetchSignInMethods Firebase says I should authenticate the user with the existing provider, at this point how do I get the credentials for authentication with the existing provider?
I wouldn't want to reopen the existing provider controller to get new credentials
Am I obliged to ask the user to log in with the existing provider and show another access controller again (in this case that of Google)?
How should I handle this situation?
override func viewDidLoad() {
super.viewDidLoad()
facebookSetup()
}
func facebookSetup() {
let loginButton = FBLoginButton(permissions: [ .publicProfile, .email ])
loginButton.center = view.center
loginButton.delegate = self
view.addSubview(loginButton)
}
//MARK: - FACEBOOK Delegate
func loginButton(_ loginButton: FBLoginButton, didCompleteWith result: LoginManagerLoginResult?, error: Error?) {
if let error = error {
print(error.localizedDescription)
return
}
let credential = FacebookAuthProvider.credential(withAccessToken: AccessToken.current!.tokenString)
Auth.auth().signIn(with: credential) { (authResult, error) in
if let error = error {
print("\n FIREBASE: ",error.localizedDescription)
// An account with the same email already exists.
if (error as NSError?)?.code == AuthErrorCode.accountExistsWithDifferentCredential.rawValue {
// Get pending credential and email of existing account.
let existingAcctEmail = (error as NSError).userInfo[AuthErrorUserInfoEmailKey] as! String
let pendingCred = (error as NSError).userInfo[AuthErrorUserInfoUpdatedCredentialKey] as! AuthCredential
// Lookup existing account identifier by the email.
Auth.auth().fetchSignInMethods(forEmail: existingAcctEmail) { providers, error in
if (providers?.contains(GoogleAuthProviderID))! {
// Sign in with existing account.
Auth.auth().signIn(with: "? ? ? ?") { user, error in
// Successfully signed in.
if user != nil {
// Link pending credential to account.
Auth.auth().currentUser?.link(with: pendingCred) { result, error in
// Link Facebook to Google Account
}
}
}
}
}
}
}
}

Related

Unable to link Facebook and Google in Firebase Authentication

I am trying to link Facebook and Google. So, the scenario is this:
I have already authenticated with Google. So, now I am logging in Facebook, having same email id which was used earlier with Google. So, I get the error of account Exists with a different credential. And, I did this:
func fetchUserInfo()
{
Auth.auth().signInAndRetrieveData(with:FacebookAuthProvider.credential(withAccessToken: (FBSDKAccessToken.current().tokenString)!), completion: { (result, error) in
if let error = AuthErrorCode.init(rawValue: error!._code)
{
switch error
{
case .accountExistsWithDifferentCredential :
let credential = FacebookAuthProvider.credential(withAccessToken: (FBSDKAccessToken.current()?.tokenString)!)
Auth.auth().currentUser?.linkAndRetrieveData(with: credential, completion: { (result, error) in
if let error = error
{
print("Unable to link Facebook Account", error.localizedDescription)
}
else
{
NavigationHelper.shared.moveToHome(fromVC: self)
}
})
default: break
}
}
else
{
GeneralHelper.shared.keepLoggedIn()
if let currentUser = Auth.auth().currentUser
{
print(currentUser.email!)
}
NavigationHelper.shared.moveToHome(fromVC: self)
}
})
}
Here Firebase Documentation says that we need to just link the currentUser and retrieve data. But, the issue I am facing is that the currentUser is always nil. So, how can I get the current user? I have already tried this months ago and then I was able to link Facebook, Google and Email. Do, I need to signInAndRetrieve the data from Google in order to get the currentUser?
The Error "account Exists with a different credential" is because, by default, Firebase do not allow to use the same email address for two (or more) different Sing In methods. You need to enable this option.
1 - Go to Authentication > Sign-in method
2 - Scroll down to Advanced: Multiple accounts per email address
3 - Change the option to Allow creation of multiple accounts with the same email address
FYI: You need to do whole login process for each Sign In method in your app. Each method has is own credentials.
Hope this helps.

Log Out Facebook User When Authenticated With Firebase Auth

I have the following code that authenticates a user with Facebook and then with Firebase:
func authenticateWithFacebook() {
FBSDKLoginManager().logIn(withReadPermissions: ["public_profile"], from: self) { (user, error) in // Signs up user with Facebook
if let error = error {
print(error.localizedDescription)
} else if (user!.isCancelled) { // User cancels sign up
print("User Cancelled")
} else {
self.authenticateFacebookUserWithFirebase(credential: FacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString))
}
}
}
func authenticateFacebookUserWithFirebase(credential: AuthCredential) {
Auth.auth().signInAndRetrieveData(with: credential) { (user, error) in
if let error = error {
print(error.localizedDescription)
} else {
print("Success")
}
}
}
This code works as expected. Once the user is authenticated with Firebase, what do I do with the Facebook user that has been "created" in the app? Do I need to keep track of the currentAccessToken and alert Firebase auth when the token expires? If not, do I just leave the code as is or should I log the Facebook user out of my app using the FBSDK? I just don't want a Facebook token floating around in my app.
The user is not logged in to Firebase with Facebook as such. Your app does not get the user's facebook email and password credentials in order to log them into your app's Firebase. Instead it gets the access token for that user and then that token is used to authenticate the user with Firebase. Therefore you cannot log out your user from Facebook but what you can do is invalidate the access token.

Auth.auth().signIn(withCustomToken: ) { (user, error) received callback is nil

I am trying to authenticate LinkedIn with Firebase but I am unable to understand the step needed to get the custom token to access Firebase authentications.
Here is my function to sign the user in:
func signInWithCustomToken() {
let customToken = user.id!
Auth.auth().signIn(withCustomToken: customToken) { (user, error) in
print(user)
}
}
But this is printing out
nil
I have looked at these questions: LinkedIn login with custom token ( ERROR_INVALID_CUSTOM_TOKEN ) and Firebase Auth (with Custom Token, for Linkedin) returns a user with no email and no data (only uid). But still unable to get this to work.

Firebase Not Capturing Email/Phone from Facebook

When a user logs into the app using Facebook, I am able to capture and display their full name; however, neither email nor phone number is coming across. I have tried both the "One account per email address" as well as "Multiple accounts per email". I have tested it with an account whose email address is definitely not already registered in Firebase. What am I missing such that email/phone are not being captured? This all does work with Google accounts.
let name = Auth.auth().currentUser?.displayName // works!
let email = Auth.auth().currentUser?.email // nil- why?
let phone = Auth.auth().currentUser?.phoneNumber // nil -why?
The login process, which is standard Firebase w/ Facebook, looks like this:
func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!)
{
if let error = error
{
print(error.localizedDescription)
}
else
{
if FBSDKAccessToken.current() != nil
{
let credential = FacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString)
Auth.auth().signIn(with: credential) { (user, error) in
if let error = error
{
print (error.localizedDescription)
}
}
}
}
}
To update based on comments below. The following also produces a nil email and phone when inspecting the contents of userInfo. I understand the phone might just be that way, but it seems the email was supposed to work.
let userInfo = Auth.auth().currentUser?.providerData
The top level phoneNumber currentUser.phoneNumber is only for Firebase verified phone numbers. If you have that Facebook phone number, you can use the currentUser.updatePhoneNumber API to verify that number (you will need to go through the whole flow). Otherwise, you can wait for the upcoming Admin node.js API to set phone numbers with Admin privileges on existing users: https://github.com/firebase/firebase-admin-node/commit/68563c4b2c8128fbc45fc65bad3f6730d320b539
As for the email, in the case of "multiple accounts per email" you need to set it yourself via currentUser.updateEmail. You can get the Facebook email from currentUser.providerData which contains the Facebook provider data.

How to confirm credentials while the user is logged in? (on Firebase)

I am using Firebase in an iOS app to provide server support to the users.
When someone wants to change his credentials (email/password) or his display name, I request to confirm the current credentials. Is there a way (an API) to check that the provided credentials are the ones matching the currently logged in user, other than perform a log out and trying to log in again, hoping the provided credentials are correct?
Use the below method to Re-authenticate a user using firebase
let user = FIRAuth.auth()?.currentUser
var credential: FIRAuthCredential
// Prompt the user to re-provide their sign-in credentials
user?.reauthenticate(with: credential) { error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
}
}
Email:
let credential = FIREmailPasswordAuthProvider.credentialWithEmail(email,` password: password)
Facebook:
let credential = FIRFacebookAuthProvider.credentialWithAccessToken(FBSDKAccessToken.currentAccessToken().tokenString)
Twitter:
let credential = FIRTwitterAuthProvider.credentialWithToken(session.authToken, secret: session.authTokenSecret)
Google:
let authentication = user.authentication
let credential = FIRGoogleAuthProvider.credentialWithIDToken(authentication.idToken, accessToken: authentication.accessToken)
heres the firebase link describing the user management in ios
ios user management in firebase

Resources