When calling the code below I get the following error message
"There is no user record corresponding to this identifier. The user may have been deleted."
Isn't the user created by the code above at that point?
I am trying to validate the new user's email using a verification email after its creation.
Thanks
let saveAction = UIAlertAction(title: "Create",
style: .default) { action in
let emailField = alert.textFields![0]
let passwordField = alert.textFields![1]
Auth.auth().createUser(withEmail: emailField.text!,
password: passwordField.text!) { user, error in
if error == nil {
Auth.auth().signIn(withEmail: self.textFieldLoginEmail.text!,
password: self.textFieldLoginPassword.text!)
}
}
Auth.auth().currentUser?.sendEmailVerification { (error) in
if let error = error
{print("Error when sending Email verification is \(error)")}
}
}
When you create a user, they're automatically signed in. So you can remove the sign-in call and move the sending of the verification email into the completion handler:
Auth.auth().createUser(withEmail: emailField.text!,
password: passwordField.text!) { user, error in
if error == nil {
Auth.auth().currentUser?.sendEmailVerification { (error) in
if let error = error
....
In cases where that won't work, the sign-in method also has a completion handler, so:
Auth.auth().signIn(withEmail: email, password: password) { (user, error) in
if error == nil {
Auth.auth().currentUser?.sendEmailVerification { (error) in
if let error = error
// ...
}
Related
I'm currently adding Firebase Authentication to my iOS application. I can sign up and sign in users, however, I'm struggling to find where I can add a segue to move on to the next screen.
func signInUser(email: String, password: String){
// creates user with the firebase autenthication platform
Auth.auth().signIn(withEmail: email, password: password) { [weak self] authResult, error in
guard let strongSelf = self else { return }
}
// would the segue go here?
}
You should execute your code inside the signIn function completion handler:
func signInUser(email: String, password: String){
//creates user with the firebase autenthication platform
Auth.auth().signIn(withEmail: email, password: password) { [weak self] authResult, error in
guard let strongSelf = self else { return }
if let error = error as? NSError {
// Handle sign in error
switch AuthErrorCode(error.code) {
...
}
} else {
// No errors: Perform segue here...
}
}
}
I have issue, even when i put wrong password, my app showing me info about Success login, but in reality im not. How to make work it properly?
Auth.auth().fetchSignInMethods(forEmail: userEmail, completion: {
(providers, error) in
if error != nil {
self.displayAlertMessage(alertTitle: "Unhandled error", alertMessage: "Undefined error #SignUpViewController_0001");
return;
} else if providers == nil {
self.displayAlertMessage(alertTitle: "Error", alertMessage: "This account is not exist.");
return;
}
})
// Login
Auth.auth().signIn(withEmail: userEmail, password: userPassword) { [weak self] authResult, error in
guard self != nil else {
self?.displayAlertMessage(alertTitle: "Alert", alertMessage: "Wrong password.");
return }
}
self.displayAlertMessage(alertTitle: "Success", alertMessage: "You are successfuly sign in.", dismiss: true);
// Return to initial view
Auth.auth().signIn() is asynchronous and returns immediately. The callback you pass will be invoked some time later with the results of the sign in. What your code is doing is immediately calling self.displayAlertMessage(alertTitle: "Success",...) before the sign in is complete. You should only expect sign in results inside the callback, not on the next line of code after you call signIn().
This function I wrote for my current application.
func loginButtonTapped() {
indicator.setupIndicatorView(view, containerColor: .white, indicatorColor: .CustomGreen())
view.alpha = 0.7
let email = mainView.userEmail
let password = mainView.userPassword
Auth.auth().signIn(withEmail: email, password: password) { (user, error) in
if error == nil {
if Auth.auth().currentUser?.isEmailVerified == true {
self.view.alpha = 1.0
self.indicator.hideIndicatorView()
print("Logined")
} else {
Auth.auth().currentUser?.sendEmailVerification(completion: { (error) in
if error == nil {
self.view.alpha = 1.0
self.indicator.hideIndicatorView()
Alert.showAlert(title: "Warning", subtitle: "You have not activate your account yet. We have sent you an email to activate it.", leftView: UIImageView(image: #imageLiteral(resourceName: "isWarningIcon")), style: .warning)
} else {
self.view.alpha = 1.0
self.indicator.hideIndicatorView()
Alert.showAlert(title: "Error", subtitle: "Incorrect email.", leftView: UIImageView(image: #imageLiteral(resourceName: "isErrorIcon")), style: .danger)
}
})
}
} else {
self.view.alpha = 1.0
self.indicator.hideIndicatorView()
Alert.showAlert(title: "Error", subtitle: "Incorrect email or password.", leftView: UIImageView(assetIdentifier: AssetIdentifier.error)!, style: .danger)
}
}
}
As Doug mentioned in his answer, Firebase is asynchronous and data is only valid within the closure following the function call. It takes time for that data to become valid so any code outside the function call following the closure will be called before the code inside the closure.
So what that means for your code is
Auth.auth().signIn(withEmail: userEmail, password: userPassword) { [weak self] authResult, error in
//this code will execute *after* the code that displays success
}
//this code will execute *before* the code within the closure following the signIn
self.displayAlertMessage(alertTitle: "Success"
Here's some sample code that handles errors and provides the proper sequence for code flow.
Auth.auth().signIn(withEmail: user, password: pw, completion: { (auth, error) in
if let x = error {
let err = x as NSError
switch err.code {
case AuthErrorCode.wrongPassword.rawValue:
print("wrong password")
case AuthErrorCode.invalidEmail.rawValue:
print("invalued email")
case AuthErrorCode.accountExistsWithDifferentCredential.rawValue:
print("accountExistsWithDifferentCredential")
default:
print("unknown error: \(err.localizedDescription)")
}
} else {
if let _ = auth?.user {
print("authd") //user is auth'd proceed to next step
} else {
print("authentication failed - no auth'd user")
}
}
})
I implemented a button in my app that allows the user to change their email using Firebase.
#IBAction func resetEmail(_ sender: Any) {
let alertController = UIAlertController(title: "Change Email", message: "", preferredStyle: .alert)
alertController.addTextField { (textField : UITextField!) -> Void in
textField.placeholder = "Enter New Email Address"
let saveAction = UIAlertAction(title: "Save", style: .default, handler: { (action : UIAlertAction!) -> Void in
//Reset Email
let currentUser = Auth.auth().currentUser
if Auth.auth().currentUser != nil{
currentUser?.updateEmail(to: textField.text!) { error in
if let error = error {
print(error)
} else {
print("CHANGED")
let user = Auth.auth().currentUser
let name = user?.displayName!
let ref = Database.database().reference().child("main").child("users_sen").child(name!).child("email")
ref.setValue(textField.text!)
}
}
}
})
alertController.addAction(saveAction)
}
self.present(alertController, animated: true, completion: {
alertController.view.superview?.isUserInteractionEnabled = true
alertController.view.superview?.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.alertClose(gesture:))))
})
}
However, when I run it and I try to change the email it gives me this error:
UserInfo={NSLocalizedDescription=This operation is sensitive and requires
recent authentication. Log in again before retrying this request.
and tells me to re-sign in order to change the email. How do I avoid this? How do I change the email without re-signing in?
This is how I change the password:
// Password updated.
let currentUser = Auth.auth().currentUser
currentUser?.updatePassword(to: textField.text!) { error in
if let error = error {
} else {
// Password updated.
print("success")
}
}
let userEmail = Auth.auth().currentUser?.email
self.currentPassword = textField.text!
let credential = EmailAuthProvider.credential(withEmail: userEmail!, password: textField.text!)
currentUser?.reauthenticate(with: credential) { error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
}
}
Base on Firebase's documentation, you need to re-authenticate the user when performing this type of action.
Re-authenticate a 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 an error. When this happens, re-authenticate the user by
getting new sign-in credentials from the user and passing the
credentials to reauthenticateWithCredential.
let user = Auth.auth().currentUser
let credential = EmailAuthProvider.credential(withEmail: "email", password: "password")
user?.reauthenticate(with: credential)
{ error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
user?.updateEmail(to: "newemail")
{ error in
}
}
}
To change user email without re-authentication you can also leverage Cloud Functions. An example course of action could be:
Create a function that accepts user access token and new email address as parameters
In the function, verify access token and get the user ID from it
In the function, call
admin.auth().updateUser(userId, { email: newEmail })
Call the new function from the client
Note: This solution is less secure because the user intent is not verified by additional authentication. Therefore anyone getting hold of the user's device could change their email address.
If you use email and password to authenticate a user you should to do something like this.
You have to re-authenticate user using credential
Re-authenticate user
Update email
Before don't forget to get current user in your class and import Firebase like this :
...
import Firebase
class Blabla {
...
var currentUser: User? {
return Auth.auth().currentUser
}
Then :
func updateUserEmail(newEmail: String, password: String) {
// 1. Get the credential
guard let currentEmail = currentUser?.email else {return}
var credential = EmailAuthProvider.credential(withEmail: currentEmail, password: password)
You can't get directly password, so you must ask to the user his password by a textfield or other.
// 2. Re-authenticate the user
//(To change mail or password, the user must to be authentificate a short time ago !!!)
self.currentUser?.reauthenticate(with: credential, completion: { (result, error) in
if error != nil {
print("ERROR: ", error?.localizedDescription)
return
}
//3. Update email
self.currentUser?.updateEmail(to: newEmail, completion: { (error) in
if error != nil {
print("ERROR: ", error?.localizedDescription)
}else {
//Do something, for example present an alert of confirmation..
}
})
})
All of the code in the same function from the step 1.
I am trying to login to my user after updating firebase, and after tracing the error, I get the following error:
Error Domain=FIRAuthErrorDomain Code=17007 "The email address is
already in use by another account."
UserInfo={NSLocalizedDescription=The email address is already in use
by another account., error_name=ERROR_EMAIL_ALREADY_IN_USE}
After looking it seems to be a because that firebase user is already in use, I am not sure how to fix this. I believe it is because I never signed out the user before closing app, but not am unable to login as any of my users.
Below is my code:
#IBAction func Login(sender: AnyObject) {
let email = self._Email.text!
let password = self._Password.text!
Auth.auth().createUser(withEmail: email, password: password) { (user, error) in
if error == nil {
//successfull login
print("Successful login****************************************")
//performs a segue to the next view controller
if user!.isEmailVerified{
//if the email is verified
let vc = self.storyboard!.instantiateViewController(withIdentifier: "ProfileView") as! ProfileView
self.present(vc, animated: true, completion: nil)
}
else {
print("email is not verified")
}
} else {
print("Some login error")
}
}
}
As ZassX pointed out, you're indeed using the signUp method of the Firebase iOS SDK, which is the createUserWithEmail. Use this method instead to signIn using email and password:
Auth.auth().signIn(withEmail: email, password: password) { (user, error) in
// ...
}
More info: https://firebase.google.com/docs/auth/ios/password-auth
You can check the list of your registered users in your Firebase Authentication Dashboard (https://console.firebase.google.com)
Also, it is good to print out the error description if you're having an error object. Like so:
print(error.localizedDescription).
I tried running the following and it does not work. What am I doing wrong to check the Firebase server if the user has already used the email?
FIRAuth.auth()!.createUser(withEmail: self.userEmailTextField.text!, password: self.userPasswordTextField.text!) {(createUser, error) in
if error != nil {
FIRAuth.auth()!.signIn(withEmail: self.userEmailTextField.text!, password: self.userPasswordTextField.text!)
//Display logged in
let viewController = self.storyboard!.instantiateViewController(withIdentifier: "TabBarController") as UIViewController
self.present(viewController, animated: true, completion: nil)
}else {
//Display an alert message
self.displayMyAlertMessage(userMessage: "Email already in use. Please see the login page.")
return
}
}
Please refer below code.
Code:
FIRAuth.auth()?.createUser(withEmail: email, password: password) {
(user, error) in
if (error) {
// Email is already in use.
} else {
// Create new user successfully
}
}
Handle Firebase iOS Auth Errors
Firebase already does this for you. Erase the sign in method and just run the create user method using an email that already exists and FIRAuthErrorCodeEmailAlreadyInUse will be returned. Check this out for more info