Delete Parse PFUser Account - swift

I have an app(written in Swift) that uses Parse and allows a User to create an account. I want the user to have the option to delete their account but can't seem to figure it out and not sure what I'm doing wrong. I want the user to have to verify their email and password for this to occur. Once they've deleted their account, an alert pops up and then once the user dismisses the alert it takes them back to the log in screen and out of the app.
This is what I've tried:
#IBAction func deleteAccount(sender: AnyObject) {
if PFUser.currentUser()?.username == emailAddressTextField.text && PFUser.currentUser()?.password == passwordTextFieldOne.text && PFUser.currentUser()?.password == passwordTextFieldTwo.text {
PFUser.currentUser()?.deleteInBackgroundWithBlock({ (success: Bool, error: NSError?) -> Void in
if success {
let alertController = UIAlertController(title: "Success!", message: "Now Please Login", preferredStyle: .Alert)
let enterAppAction = UIAlertAction(title: "OK", style: .Default, handler: { (UIAlertAction) -> Void in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let viewController:UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("Login")
self.presentViewController(viewController, animated: true, completion: nil)
})
})
alertController.addAction(enterAppAction)
self.presentViewController(alertController, animated: true, completion: nil)
} else {
print(error)
self.displayAlert("Account deletion failed", message: "Please try again", actionTitle: "OK")
}
})
}
}

You can make a cloud function to verify that the user correctly entered their current password and have it return success/error to iOS, at which point you can delete the account (provided the successful completion of the password check). Here's a relevant Parse forum post.
From the referenced post:
Parse.Cloud.define(FUNCTION_PASSWORD_CHECK, function(request, response)
{
var password = request.params.password;
Parse.User.logIn(request.user.getUsername(), password, {
success: function(results)
{
response.success(true);
},
error: function() {
response.success(false);
}
});
});
Note: you can do a similar login function on iOS, but that may mess with the PFUser.currentUser() if the user incorrectly enters their password (I'm not sure on that). Also, the referenced function uses the request.user, which is advised (i.e., not passing the user as a param as well), so that it only works for users with sessions.

Your initial if-statement:
if PFUser.currentUser()?.username == emailAddressTextField.text && PFUser.currentUser()?.password == passwordTextFieldOne.text && PFUser.currentUser()?.password == passwordTextFieldTwo.text
The statement PFUser.currentUser()?.password will return nil as parse does not make a users password accessible to you. Therefore, you won't be able to compare the user's password with the text entered into the textfield.
To check if the user has in fact entered a password that matches an existing password:
PFUser.logInWithUsernameInBackground(PFUser.currentUser(), password:passwordTextFieldOne.text!) {
(user: PFUser?, error: NSError?) -> Void in
if user != nil {
//continue with delete function
} else {
//Display error
}
}

Related

Firebase email verification not working as expected, without verification the user is able to login

I'm having an issue with Firebase and it's email verification flow. I'm able to create a new user, email with a link to verify email address is delivered with no issues. Now, just for testing purposes I'm not clicking on the link to verify the email, but, if I open the app, I'm able to access and do anything. I'm not sure what I'm missing or what I'm doing wrong. I've been stuck with this for the past couple days. Any help is greatly appreciated.
my code
#IBAction func loginBtnTapped(_ sender: Any) {
SVProgressHUD.show()
guard let email = emailTxt.text,
let password = passwordTxt.text else { return }
Auth.auth().signIn(withEmail: email, password: password) {
(user, error) in
if error != nil {
let alert = UIAlertController(title: "Login Error",
message:"Incorrect Email and/or Password", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style:
.default) { _ in })
self.present(alert, animated: true){}
if let error = error {
print("error: \(error.localizedDescription)")
}
if Auth.auth().currentUser?.isEmailVerified == false {
let alert = UIAlertController(title: "Unable to
login", message:"Pending: email verification", preferredStyle:
.alert)
alert.addAction(UIAlertAction(title: "OK", style:
.default) { _ in })
self.present(alert, animated: true){}
print("")
SVProgressHUD.dismiss()
}
}
self.dismiss(animated: true, completion: nil)
SVProgressHUD.dismiss()
}
}
Expected results
Newly created user should not be able to login and open the app unless email is verified.
Firebase Authentication is about authenticating users. If you type (say) the correct email address and password, we trust that you are you.
If you only want to allow data access to users who have verified their email address, that is possible (and know as authorization). You'll check this in the backend that you're trying to protect though, for example in security rules of your Firestore database, as shown here (Firebase) Firestore security rules - allow if email verified without custom tokens?
Also see
How to stop users from signing in without verifying e-mail I.D. first in Firebase?
Prevent user account creation with sign in by email in firestore
Only let pre-verified users log into Firebase
Is it possible to enable Firebase email authentication but disable sign in?
you need to check on the firebase databasae for the field that says "is email verified" and then if that BOOL value is TRUE, then let them in the app. the bool value will turn to TRUE automatically after they click the link in their email. so instead of doing it like your'e doing, query the user table for that user and check the boolean value for whether they are verified, if they are not, then don't let them in. good luck and have a fabulous day
You should keep the user account disabled until the email address is verified. That seems to be the only way to securely forbid login.
Typically, you may use sendSignInLinkToEmail() to send an email address validation message with specific URL. The user will be automatically redirected to this url after the email validation process.
In our case, we invite user to create a password and then activate their account before redirecting them to the login screen.
I was able to get this working as expected. The user needs to verify the email, if not, they cannot access the app. I did not have to modify the rules in Firebase.
Hope this helps anyone.
loginVC
private var authUser : User? {
return Auth.auth().currentUser
}
public func verifyEmail() {
authUser?.reload(completion: { (err) in
if err == nil {
if self.authUser!.isEmailVerified == true {
self.dismiss(animated: true, completion: nil)
} else {
let alert = UIAlertController(title: "Confirm your email
address.", message: "A confirmation email has been sent to" + " " +
((self.emailTxt.text)!) + " . " + "Click on the confirmation link to activate
your account. ", preferredStyle: .alert)
let actionTaken = UIAlertAction(title: "OK", style:
.default, handler: nil)
alert.addAction(actionTaken)
self.present(alert, animated: true, completion: nil)
}
}
})
}
#IBAction func loginBtnTapped(_ sender: Any) {
SVProgressHUD.show()
guard let email = emailTxt.text,
let password = passwordTxt.text else { return }
Auth.auth().signIn(withEmail: email, password: password) { (user,
error) in
self.verifyEmail()
if error != nil {
let alert = UIAlertController(title: "Login Error",
message:"Error: \(error!.localizedDescription)", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default) { _
in })
self.present(alert, animated: true){}
if let error = error {
print("error: \(error.localizedDescription)")
}
}
SVProgressHUD.dismiss()
}
}

How to Delete Current User with UIAlert using Swift with Firebase

quick question I am trying to give the user the option to delete their account from firebase using swift on Xcode.
I currently have a "Delete" button that leads to a UIAlert that ask if the user is sure they want to delete their account (just in case) like this
and my code for this action is below
//Handles delete current user account (not fnished yet)
#IBAction func deleteAccount(_ sender: Any) {
createAlert2(title: "Delete Account", message: "Are you sure you want to delete your account? This will permanently erase your account.")
}
to this function
func createAlert2 (title:String, message:String){
let alert2 = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert2.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: { (action) in
alert2.dismiss(animated: true, completion: nil)
}))
alert2.addAction(UIAlertAction(title: "Delete", style: UIAlertActionStyle.destructive, handler: { (action) in
let user = Auth.auth().currentUser
user?.delete { error in
if error != nil {
// An error happened.
} else {
// Account deleted.
print("user deleted")
}
}
let controller2 = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MenuViewController") as! MenuViewController
self.present(controller2, animated: true, completion: nil)
}))
self.present(alert2, animated: true, completion: nil)
My goal is to delete the user and send them back to that MenuViewController. But all it seems to do is send the user to that menu view and not actually delete their account on firebase. Any help is appreciated and I always like and check mark answers. Thank you:)
If you print errors or just put few breakpoints after user?.delete, most probably you going to see something like this:
Optional
- some : Error Domain=FIRAuthErrorDomain Code=17014 "This operation is sensitive and requires recent authentication. Log in again before retrying this request." UserInfo={NSLocalizedDescription=This operation is sensitive and requires recent authentication. Log in again before retrying this request., error_name=ERROR_REQUIRES_RECENT_LOGIN}
It means that you need to sign up or re-authenticate again with credentials (email, password) to have the ability to delete your user. You can user code bellow with some improvements(choose your way how to get credentials)
// Latest firebase + swift 4
let user = Auth.auth().currentUser
let password = "mypassword from stream - ask for a password from your user"
let email = user?.email
let credential = EmailAuthProvider.credential(withEmail: email, password: password)
user?.reauthenticateAndRetrieveData(with: credential, completion: { (result, error) in
if let error = error {
// handle error here
print(error)
}else{
user?.delete { error in
if let error = error {
// handle your error here
print(error)
}else{
do {
// handle your signout smth like:
try Auth.auth().signOut()
self.dismiss(animated: true, completion: nil)
} catch let logoutError {
// handle your error here
print(logoutError)
}
}
}
}
})
Unfortunately this issue documentation is very poor(especially latest version). Hope you find my answer useful.
Note***: giving your user ability to delete his profile directly it is a very BAD idea (IMHO), better if you send email notification and disable user and invalidate his tokens or UID, it will give you more flexibility in the future.

Swift 4: Azure DB with Facebook Authentication Callback method

app in swift4+xcode9 we are connection with DataBase in Azure with authentication through facebook.
in the App when launch the viewController, the login window of facebook appears.
I enter the user and the password but when the control will be return to the app will appears this window and stay there (nor return to the app control:
window callback
this is the code of viewDidAppear:
override func viewDidAppear(_ animated: Bool) {
let delegate = UIApplication.shared.delegate as! AppDelegate
let cliente = delegate.cliente
cliente?.login(withProvider: "facebook", urlScheme: "portfoliodispositivos", controller: self, animated: true) {
(usuario, error) -> Void in
if (error != nil) {
let alerta = UIAlertController(title: "Error", message: "Ocurrió un error al iniciar sesión en Facebook Error: \(String(describing: error?.localizedDescription))", preferredStyle: .alert)
alerta.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
self.present(alerta, animated: true, completion: nil)
} else {
print("sin error en login")
let tablaUbicaciones = cliente?.table(withName: "Ubicaciones")
tablaUbicaciones?.read(){
(resultado, error) in
if error != nil {
print("Error: \(error.debugDescription)")
} else {
self.ubicaciones.removeAll()
for item in (resultado?.items)! {
let ubicacion = item as NSDictionary
self.ubicaciones.append(ubicacion)
}
}
}
}
}
self.tableView.reloadData()
}
In previous versions of swift the login not included urlScheme and I do not understand what scheme it refers to.
If push 'Done' in the window the control return with error. I d not understand if the message is 'You have successfully signed in' why does not it come back without error automatically?

AWS Cognito doesn't call confirmSignUp when confirming user identity in Swift with Xcode

I finally managed to get the SignUp to work, but when trying to confirm the signup, I'm reaching a problem. Here is my code:
var user: AWSCognitoIdentityUser?
#IBAction func submitButton(_ sender: Any) {
guard let confirmationCodeValue = self.codeTextField.text, !confirmationCodeValue.isEmpty else {
let confirmationAlert = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)
confirmationAlert.addAction(UIAlertAction(title: "好", style: .default, handler: {action in
print("Try again!")
}))
self.present(confirmationAlert, animated: true, completion: nil)
return
}
self.user?.confirmSignUp(self.codeTextField.text!, forceAliasCreation: true).continue({[weak self] (task: AWSTask) -> Any? in
guard let strongSelf = self else { return nil }
DispatchQueue.main.async(execute: {
print("At least this is working...")
if let error = task.error {
let confirmationFailAlert = UIAlertController(title: (error as NSError).userInfo["__type"] as? String,
message: (error as NSError).userInfo["__type"] as? String,
preferredStyle: .alert)
confirmationFailAlert.addAction(UIAlertAction(title: "好",
style: .default,
handler: {action in
}))
self?.present(confirmationFailAlert, animated: true, completion: nil)
} else {
let confirmationSuccessAlert = UIAlertController(title: self?.alertTitleConfirmationComplete,
message:self?.alertMessageConfirmationComplete,
preferredStyle: .alert)
confirmationSuccessAlert.addAction(UIAlertAction(title: "好",
style: .default,
handler: {action in
self?.dismiss(animated: true, completion: nil)
}))
self?.present(confirmationSuccessAlert, animated: true, completion: nil)
}
})
return nil
})
}
The first part of this code works fine. If I type nothing in the space, I get an alertView telling me so. However, if I type anything in the space, nothing happens. The print statement "At least this is working..." never gets called. I've been staring at this code for a couple hours now trying to figure out what's wrong, and I feel like it's probably something simple, but as of now, I could use some help!
Thanks in advance!
I assume the code block above is not the full source, but be sure the optional, user, is "unencapsulated" and equal to an actual instance of AWSCognitoIdentityUser.
If it isn't, which I am assuming it is not, confirmSignUp won't know the username, sub, or have any information on the user it is "confirming".
I would recommend logging user and be sure that username is in fact a value within user.
I believe you set it equal to that instance type in the response to your AWSCognitoIdentityUserPool class signUp:password:userAttributes:validationData: call.
Check those values that are returned in AWSCognitoIdentityUserPoolSignUpResponse.

Invalid Session Token (Code: 209 Version: 1.7.4)

Every time I go to run my app the app runs fine but the console prints an error. The error is "Invalid Session Token (Code: 209 Version: 1.7.4)"
I checked Parse.com and they told me to handle this error, writing a global utility function that is called by all of my parse request error callbacks. They said i can handle the "invalid session token" error in this global function and I should prompt the user to login again so that they can obtain a new session token. However when I try to input the code in my app I get the error that I am using unresolved identifiers.
Does anyone know how to fix an Invalid Session Token error. Or how I can use the code "kPFErrorInvalidSessionToken" in my app. Any help would be greatly appreciated. (the language I am writing in is swift)
I have tried to call ParseErrorHandler.handleParseError(err) not just only from AppDelegate, but also from other view controllers and it was not worked properly. Here is solution which works from every VC:
class ParseErrorHandler {
class func handleParseError(error: NSError) {
if error.domain != PFParseErrorDomain {
return
}
switch (error.code) {
// error code 209 handling
case PFErrorCode.ErrorInvalidSessionToken.rawValue:
invalidSessionTokenHandler()
default:
break
}
}
// NOTE: User has no other option but to log out in this implementation
private class func invalidSessionTokenHandler() {
let message: String = "Session is no longer valid! Please login again!"
let alert = UIAlertController(title: nil, message: message, preferredStyle: .Alert)
var vc = UIApplication.sharedApplication().keyWindow?.rootViewController
while (vc!.presentedViewController != nil)
{
vc = vc!.presentedViewController
}
vc?.presentViewController(alert, animated: true, completion: nil)
let logoutAction = UIAlertAction(title: "OK", style: .Default, handler: {
(UIAlertAction) -> Void in
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let exampleViewController: SignUpViewController = mainStoryboard.instantiateViewControllerWithIdentifier("SignUpViewController") as! SignUpViewController
vc?.presentViewController(exampleViewController, animated: true, completion: nil)
PFUser.logOut()
})
alert.addAction(logoutAction)
}
}
Hopefully I will save somebody's time!
If by saying "unresolved identifiers" you mean to "kPFErrorInvalidSessionToken" then you should use PFErrorCode.ErrorInvalidSessionToken.rawValue instead.
please tell me if you succeded in catching the error because all i get back is a nil NSErrorPointer.
Here is my solution, largely based on code in the Parse.com iOS developers guide.
class ParseErrorHandler {
class func handleParseError(error: NSError) {
if error.domain != PFParseErrorDomain {
return
}
switch (error.code) {
// error code 209 handling
case PFErrorCode.ErrorInvalidSessionToken.rawValue:
invalidSessionTokenHandler()
default:
break
}
}
// NOTE: User has no other option but to log out in this implementation
private class func invalidSessionTokenHandler() {
let message: String = "Session is no longer valid, you are no longer logged in"
let alert = UIAlertController(title: nil, message: message, preferredStyle: .Alert)
let presentingViewController = UIApplication.sharedApplication().keyWindow?.rootViewController
presentingViewController?.presentViewController(alert, animated: true, completion: nil)
let logoutAction = UIAlertAction(title: "OK", style: .Default, handler: {
(UIAlertAction) -> Void in
let loginViewController:UIViewController = UIStoryboard(name: "Main", bundle:
nil).instantiateViewControllerWithIdentifier("Login_2.0")
presentingViewController?.presentViewController(loginViewController, animated: true, completion: nil)
PFUser.logOut()
})
alert.addAction(logoutAction)
}
}
Above is the errorHandler class that should be used to catch all errors according to the Parse documentation (in this case I'm only showing error 209)
Here is how I catch this error in AppDelegate.swift within application function:
PFAnalytics.trackAppOpenedWithLaunchOptionsInBackground(launchOptions, block: {
(suceeded: Bool?, error: NSError?) -> Void in
if let err = error {
ParseErrorHandler.handleParseError(err)
}
})
Note that the user is technically still logged in hence why logout is called