8

I'm using Firebase Auth for my Swift iOS app. Google recommends using Firebase Auth UI as a "drop in" authentication system, but it only handles the initial login. I'm now working on allowing the user to make profile changes, such as email and password.

The documentation for making these changes mentions in several places that certain changes require the user to have logged in recently (see https://firebase.google.com/docs/auth/ios/manage-users#get_the_currently_signed-in_user):

Some security-sensitive actions—such as deleting an account, setting a primary email address, and changing a password—require that the user has recently signed in. If you perform one of these actions, and the user signed in too long ago, the action fails with the FIRAuthErrorCodeCredentialTooOld error.

First of all, there doesn't appear to be a FIRAuthErrorCodeCredentialTooOld error anywhere in the API.

Second, the documentation suggests using reauthenticate(with:) to solve this problem, with this code sample:

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

The problem is, because I used Firebase Auth UI, I have no custom UI for getting the user's credentials.

My current thinking is I could reauthenticate by presenting the same Firebase Auth UI used for logging in when this error occurs. However, I don't know if this is the sanctioned way to do this, or if it will work at all, or if it will continue to work in the future. I checked the Firebase Auth UI code base and there is no call to reauthenticate() anywhere. The documentation makes a big deal of calling this method specifically in case of this error, so I'm confused.

If I need to build an entire UI to perform reauthentication, including multiple providers, what's the point of using Firebase Auth UI?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Andy Chou
  • 854
  • 6
  • 5
  • 2
    This question was asked 2 year ago. Do we have a valid solution for this already in 2019? – Nik Nov 29 '19 at 03:12

1 Answers1

1

Regarding the error code, the documentation just needs updating. The error code is now called FIRAuthErrorCode.errorCodeRequiresRecentLogin.

Now, as of the UI problem you're facing, why not just present a UIAlertController with a text field that the users can use to enter their passwords for re-authentication? It's certainly much simpler (and user-friendlier) than creating an entire view controller.

Here's a pretty straightforward sample of how you can re-authenticate your users without going through so much trouble:

// initialize the UIAlertController for password confirmation
let alert = UIAlertController(title: "", message: "Please, enter your password:", preferredStyle: UIAlertControllerStyle.alert)
// add text field to the alert controller
alert.addTextField(configurationHandler: { (textField) in
    textField.placeholder = "Password"
    textField.autocapitalizationType = .none
    textField.autocorrectionType = .no
    textField.isSecureTextEntry = true
})
// delete button action
alert.addAction(UIAlertAction(title: "Delete account", style: UIAlertActionStyle.destructive, handler: {action in
    // retrieve textfield
    let txtFld = alert.textFields![0]
    // init the credentials (assuming you're using email/password authentication
    let credential = FIREmailPasswordAuthProvider.credential(withEmail: (FIRAuth.auth()?.currentUser?.email)!, password: txtFld.text!)
    FIRAuth.auth()?.currentUser?.reauthenticate(with: credential, completion: { (error) in
        if error != nil {
            // handle error - incorrect password entered is a possibility
            return
        }

        // reauthentication succeeded!
    })
}))
// cancel reauthentication
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: {action in }))

// finally, present the alert controller
self.present(alert, animated: true, completion: nil)

Whenever you need to change the user's email or delete their account (password reset doesn't require a login at all), use the snippet above to just pop up an alert controller where they can re-enter their password.

EDIT: Please note that the code provided above force-unwraps the current user's email, so make sure you have a user logged-in at that stage or your app will crash.

ThunderStruct
  • 1,504
  • 6
  • 23
  • 32
  • Sure, but what if the user logged in with a provider like Facebook or Google? There's no indication that this error only occurs when using email login. – Andy Chou Jan 18 '17 at 19:04
  • In that case you'll need to prompt the user for a login method first. You'll probably need a proper view controller for that not just an alert controller – ThunderStruct Jan 18 '17 at 21:04
  • 1
    Thanks, that is pretty much what I expected. So, my original question remains: can I just use Firebase Auth UI again for this reauthentication? It does not call reauthenticate anywhere. But if it's ok to just log in again, that should also work? Some clarification from the Firebase team would help. – Andy Chou Jan 18 '17 at 23:21
  • My bad! I seem to have missed that part of your question. Re-logging in entirely using Firebase Auth UI *should* work as you expect it to since it generates a new access token. I agree though, they really should document the authentication/reauthentication process a bit further! – ThunderStruct Jan 19 '17 at 00:23
  • I have a similar question, I am using Phone Auth UI. When not using the UI one can get the verificationID and persist it to disk, Can this verificationID be reused later, eliminating the need to do a complete re-authenication with a SMS token? My question is located here: https://stackoverflow.com/questions/47311502/how-to-deregister-firebase-user-when-registered-using-fuiphoneauth – Wayne Nov 16 '17 at 10:18