I'm having trouble resetting passwords with Parse - swift

I am making my own app in Swift, and wanted to include the feature of resetting passwords using Parse. My console eventually shows a successful notice, but when I go to the email to see if Parse sent the email to reset password, I don't receive anything. Would really appreciate it if you could help me sort this issue :D I've added a screenshot so that you can see what my console shows.
#IBAction func recoverPasswordButton(_ sender: Any) {
PFUser.requestPasswordResetForEmail(inBackground: emailTextField.text!, block: { (success, error) in
if self.emailTextField != nil {
self.displayAlert(title: "Check your email", message: "A link has been sent to recover your password. Follow the instructions.")
print("A link to recover your password has been sent")
} else {
var errorText = "Unknown error: please try again"
if let error = error {
errorText = error.localizedDescription
}
self.displayAlert(title: "Email is not valid", message: errorText)
}
})
}

For Email verification, You need to add the setting in the dashboard like below
The first thing is that Parse stops his support, you have to use Back4App.

Related

Firebase with Swift, sendSignInLinkToEmail succeeds with email not registered

I'm implementing sign-in via email link and I have it sending the email, but I'm using an email that has not been registered yet. Is there a way to detect if the email is already registered before calling sendSignInLinkToEmail? No error was reported from the call. TIA!
Nabil's answer led me to finding the function fetchSignInMethods, which was just the thing I needed. Posted here for anyone else looking for this.
Auth.auth().fetchSignInMethods(forEmail: email, completion: {
methods, error in
if methods == nil {
self.showAlert(title: "This email is not registered, please create an account")
return
}
})
Maybe this function can help you
Auth.auth().fetchProviders(forEmail: emailAddress, completion: {
error in
if let error = error {
print(error.localizedDescription)
} else {
//
}
})
So that if the email doesn't exist it'll return an error otherwise you can run the sendSignInLinkToEmail function

Swift 5 & Firebase: Creating a shared sign in and sign-out button

I thought I'd return to StackOverflow with another question because you guys helped me significantly with my last issue
Anyway, I currently have my authentication system setup so that the sign in and signup button are shared. I am looking to have firebase reference storage when an email is entered to have it checked against other accounts in the database. As of right now, a user can enter an email address for their account and then if they enter the wrong password it just sends them right to the sign up even though they currently have an account. This is a serious problem as it will cause confusion
I want it to work like so:
If the email address is taken, I want an alert to be displayed for the user says "Incorrect password"
If the email address is not taken, I want it to tell the user that they need to enter a password with at least 10 characters, 1 number, and 1 special character, which I have already figured out using
I only want it to segue to create a new user if the email is not taken and the password and email field meet the criteria fields that I have already set within my code. I just need help preventing it from switching to the create new user VC if the email is already taken, and I need to to say
func isValidPassword(_ email: String) -> Bool {
let emailRegEx = "##$%^&+=^.*(?=.{10,})(?=.*d)(?=.*[a-z])(?=.*[A-Z])(?=.*[##$%^&+=]).*$"
let emailPred = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
return emailPred.evaluate(with: email)
}
Anyway, here is the code so far attached to the IBAction
if let email = emailField.text, let password = passwordField.text {
Auth.auth().signIn(withEmail: email, password: password, completion:
{ (user,error) in
if error == nil {
if let user = user {
self.userUid = user.user.uid
self.goToFeedVC()
}
} else {
self.goToCreateUserVC()
Here is a picture of the interface
I want it to be intuitive but I have been unable to code this myself so if anyone is able to help advise me on how to finish this block it would be incredibly appreciated
Firebase gives pretty detailed error responses for their Auth call:
So you can check to see what the error is inside of your call:
Below are the two error that they give (I only added the two scenarios that you mentioned)
Description: The password is invalid or the user does not have a password.
FIRAuthErrorUserInfoNameKey: ERROR_WRONG_PASSWORD
&
There is no user record corresponding to this identifier. The user may have been deleted.
FIRAuthErrorUserInfoNameKey: ERROR_USER_NOT_FOUND
Auth.auth().signIn(withEmail: email, password: password, completion:
{ (user,error) in
if error == nil {
if let user = user {
self.userUid = user.user.uid
self.goToFeedVC()
}
} else {
guard let error = error?.localizedDescription else { return } // but actually handle this
print(error)
if error == wrong password {
// show alert for email taken/wrong password
} else if error == user doesnt exists {
// self.goToCreateUserVC()
}
}
}
Just replace the if and else if conditions with the actual errors. I'd avoid comparing the strings and use the key/code in case the strings change in the future.
Official list of error codes can be found here
And if you print the full error instead of the error?.localizedDescription you'll get the full details, as can be seen here:
Optional(Error Domain=FIRAuthErrorDomain Code=17011 "There is no user record corresponding to this identifier. The user may have been deleted." UserInfo={NSLocalizedDescription=There is no user record corresponding to this identifier. The user may have been deleted., FIRAuthErrorUserInfoNameKey=ERROR_USER_NOT_FOUND})

How To Fetch Error Code From Firebase?

Auth.auth().signIn(withEmail: emailTextField.text!, password: passwordTextField.text!)
{ (user, error) in
if error != nil {
print(error!)
self.warningLabel.isHidden = false;
self.passwordTextField.text = "";
} else {
print("Log in succesful")
self.performSegue(withIdentifier: "welcomeSeg", sender: self)
}
}
Whenever I sign in or sign up a user I just print a generic warning label instead of the actual issue. I print the error I receive and it's too verbose to show to the user.
Error Domain=FIRAuthErrorDomain Code=17009 "The password is invalid or the user does not have a password." UserInfo={NSLocalizedDescription=The password is invalid or the user does not have a password., error_name=ERROR_WRONG_PASSWORD}
Error Domain=FIRAuthErrorDomain Code=17008 "The email address is badly formatted." UserInfo={NSLocalizedDescription=The email address is badly formatted., error_name=ERROR_INVALID_EMAIL}
Is there any way to fetch the error code so I can be more specific with my error messages? I've looked through the documentation but have been unsuccessful in coming up with anything.
I would recommend creating an AuthErrorCode object (provided by the Firebase SDK) from the error you receive and using that as you see fit. If I remember correctly, AuthErrorCode is an enum with cases like .wrongPassword, .invalidEmail, etc.
A simple pseudocode example:
if error != nil {
if let error = AuthErrorCode(rawValue: error?.code) {
switch error {
case .wrongPassword:
// do some stuff with the error code
}
}
Also, I feel your pain. I've found that the Swift SDK documentation lags quite a bit when changes come along.

Guard let not distinguishing inputs

I have a SIMPLE login screen and I am protecting against missing data as follows:
#IBAction func btnLoginTapped(_ sender: Any)
{
guard let email = emailTextField.text, !email.isEmpty else
{
self.showMessage(messageToDisplay: "Email Address is required", title: "")
return
}
guard let password = passwordTextField.text, !password.isEmpty else
{
self.showMessage(messageToDisplay: "Password is required", title: "")
return
}
....
I load the app on my device and press login and get the alert "Email Address is required". I enter my email address and press login and get the alert "Email Address is required" and NOT "Password is required"
How can that be???
If there's a problem with the first guard let, we print "Email Address is required" and return.
Thus, the second guard let is never encountered and "Password is required" is never printed.
Your code will never print both messages; that would be impossible, the way you've written the code.
So we have to conclude that one of the tests in the first guard let is failing. But you have not given enough information for us to know why that is. Perhaps emailTextField is nil? Perhaps showMessage doesn't do what you think it does? You could be making a lot of wrong assumptions here. But the logic of your code is clear.

How to check if user needs to re-authenticate using Firebase Authentication

I am using Firebase to log in users into my app, but when I am adding the capability to manage their account like changing their email, password and so on. The documentation says that if the user have not recently signed in they need to re-authenticate, but my question is: How can I check if the user have signed in recently or not? According to the docs the error will return FIRAuthErrorCodeCredentialTooOld, but how can I check this?
Swift 3
I had to do this yesterday when trying to delete a user. One thing to note is FIRAuthErrorCodeCredentialTooOld is now FIRAuthErrorCode.errorCodeRequiresRecentLogin
What I did was trigger a UIView to ask for log in details if that error is thrown. Since I was using email and password, that's what I collected from the user in my example.
private func deleteUser() {
//get the current user
guard let currentUser = FIRAuth.auth()?.currentUser else { return }
currentUser.delete { (error) in
if error == nil {
//currentUser is deleted
} else {
//this gets the error code
guard let errorCode = FIRAuthErrorCode(rawValue: error!._code) else { return }
if errorCode == FIRAuthErrorCode.errorCodeRequiresRecentLogin {
//create UIView to get user login information
let loginView = [yourLoginUIViewController]
self.present(loginView, animated: true, completion: nil)
}
}
}
Once I had the login information I ran this function to reauthenticate the user. In my case I ran it the loginView in the above code if the login in was successful:
func reauthenticateUserWith(email: String, password: String) {
FIRAuth.auth()?.signIn(withEmail: email, password: password) { (user, error) in
if error == nil {
//display UIView to delete user again
let deleteUserView = deleteUserView()
present(deleteUserView, animated: true, completion: nil)
} else {
//handle error
print(error!.localizedDescription)
}
}
}
The deleteUserView in my case calls deleteUser() on a button tap from the user. You can also use UIAlertController in place of the custom UIViews, but that's up to you.
Hope this helps.
Update for current Swift 5
let user = Auth.auth().currentUser
user?.delete { error in
if let error = error {
let authErr = AuthErrorCode(rawValue: error.code)
if authErr == .requiresRecentLogin {
// reauthenticate
}
// other error
} else {
// delete success
}
}
According to the documents, there is currently no way to check FIRAuthErrorCodeCredentialTooOld other than going through the deleting of the account or the other sensitive cases mentioned.
If you are like me and ended up here because you are trying to figure out how to handle removing someone from Auth and removing other user data in Cloud Firestore, Realtime Database, and/or Cloud Storage, then there is a better solution.
Check out the Delete User Data Extension from Firebase to handle this. In short, when a user profile is deleted from Auth, you can use this also to delete data associated with the uid from those other Firebase data storage tools.