firebase Re-Authentication ios - ios

I'm trying to make User Authentication but I got the error:
Credential used before its being initialized
My code below:
if error._code == 17014 {
// required recent authentication
let credential: AuthCredential
user.reauthenticateAndRetrieveData(with: credential, completion: nil)
}
}else {
self.ShowAlert(title: "succeed", message: "mail Updated")
}
})
}
}))

You need to initialize the credential. If this is an email/password user, you should ask the user to provide the password. If this is an OAuth user, get a new OAuth credential. You would then initialize the Firebase AuthCredential with those and reauthenticate.

Related

Update email after Apple Firebase Auth

A problem I have is when a user login with Apple at first, and then he updates the Email, so it causes the refresh token to expire.
According to the document here, refresh token will expire when email address has been updated.
I tried to use reauthenticate function as below, but the credential must be created first as parameters. In order to get the credential, the user must re-login Apple again in order to get a new nonce, is there any way to avoid the prompt of the apple login window again, and also to get the credential smoothly?
let credential = OAuthProvider.credential(withProviderID: "apple.com",
idToken: idToken,
rawNonce: nonce)
let user = Auth.auth().currentUser
user?.reauthenticate(with: credential) { error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
}
}
I have seen an answer from here that I can get a new credential as below, but the key here is nil.
(error! As NSError) .userInfo [AuthErrorUserInfoUpdatedCredentialKey])

Firebase Google log in get credentials

In this example here we can re-authenticate an user by providing first the credentials which vary on the provider. However for google there is this example which says:
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
accessToken: authentication.accessToken)
How do I get user.authentication? There is no authentication property in Auth.auth().currentUser.
credential = EmailAuthProvider.credential(withEmail: email, password: password)
credential = FacebookAuthProvider.credential(withAccessToken: AccessToken.current!.tokenString)
user.reauthenticate(with: credential) { (result, error) in
//
}
The idea behind reauthentication is that for certain sensitive operations such as deleting an account, updating the primary email address, or changing the password, you want to make sure the user has recently signed in.
So what you want to do is
ask them to sign in
get the credential
perform user.reauthenticate with the fresh credential
The Swift docs have a concise example for this:
let user = Auth.auth().currentUser
var credential: AuthCredential
// 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.
}
}
For a more detailed code sample, check out FirebaseUI (FUIAccountSettingsOperation.m), which does what I outlined above.

Firebase Authentication Link Facebook to Google

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
}
}
}
}
}
}
}
}

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.

Re-using access token in Firebase 3

I have an iOS app that uses Firebase as a backend for authentication.
Once a user logs in and then closes the app, I don't want the user to have to re-enter their email and password. My approach is to save the access token after a successful login to the Keychain, and then when the user comes back to the app, use the token from the keychain to signin.
I've tried using the method FIRAuth.auth()?.signInWithCustomToken(customToken) { (user, error) in but that's not quite right as that's for when using custom tokens, which is not what I'm doing.
Is there a way for me to do this?
// login with email / password
FIRAuth.auth()?.signInWithEmail(email, password: password, completion: { (firebaseUser, error) in
if error == nil {
FIRAuth.auth()!.currentUser!.getTokenWithCompletion({ (token, error) in
if error == nil {
// save token to keychain
} else {
print(error)
}
})
} else {
print(error)
}
})
// user comes back to app
do {
// get saved token from keychain
if let myToken = try keychain.get("token") {
FIRAuth.auth()?.signInWithCustomToken(myToken, completion: { (user: FIRUser?, error: NSError?) in
if error == nil {
// show post login screen
} else {
}
})
}
} catch {
// error getting token from keychain
}
}
I was approaching this problem in the wrong way. Saving a token is appropriate when using a 3rd party authentication provider, like Facebook, Google, etc and getting an OAuth token in return from one of those services.
In my case when logging in using email and password, a token is not required and instead the password can be securely saved in the Keychain and used later for login.

Resources