Deleting PFUser objects from database in swift - swift

I just started working with servers in swift and I'm using parse server to store a database of users when they create an account for my app. I have this function called sign up that is linked to a sign up button which works fine and properly stores user's info into my parse database.:
#IBAction func signUp(_ sender: AnyObject) {
//I first check to see if the users left any of the fields blank
if firstName.text == "" || lastName.text == "" || email.text == "" || userName.text == "" || password.text == "" {
createAlert(title: "Error in form", message: "Please fill in all text fields")
//If everything is filled in
}else{
let user = PFUser()
user.username = userName.text
user["firstname"] = firstName.text
user["lastname"] = lastName.text
user.email = email.text
user.password = password.text
user.signUpInBackground(block: { (success, error) in
if error != nil {
var displayErrorMessage = "Please try again later."
if let errorMessage = (error! as NSError).userInfo["error"] as? String {
displayErrorMessage = errorMessage
}
self.createAlert(title: "Signup error", message: displayErrorMessage)
}else{
print("User signed up")
}
})
}
}
Can anyone help me write a function that deletes a specified user or many loops through all users and deletes them.
Thanks

Don't put that code on a client. Yuck.
Deleting a user is something you want to safeguard very carefully. Put that in cloud code if you really need it, and do validation to ensure it's used properly.
I.e., a user should only be able to make the request to delete their own user. Make a cloud code function that has the parameter userId, and check that param against request.user.id. If they don't match, return an error. Otherwise you can call the destroy method on that User.
You should also put a CLP in place to only allow the master key to delete a User object.

Related

Document ID retrieval

this is supposed to take the user ID from the result!.user.uid and store it a variable or function in order for me to use it again.
the problem is that I dont know how to get it to store the value outside of this function.
Ive tried to make it store to a variable outside of the initial button function, and Ive also tried to return it outside of the function by removing a part of the code which made it become a void. Im not sure where i need to go/what else I can try and do in order to fix this problem.
If anybody know how do I retrieve my document ID from this code your help would be greaty appreciated
#IBAction func NextButtonTapped(_ sender: Any) {
//validate the fileds
let Error = validateFields()
if Error != nil {
// there is somthing wrong with the fields show error message
showError(Error!)
}
else {
// create cleaned versions of the data
let Password = PasswordTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let Email = EmailTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let Firstname = FirstnameTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let Lastname = LastnameTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let Age = AgeTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
// create the user
Auth.auth().createUser(withEmail: Email, password: Password) { (results, Err) in
// check for errors
if Err != nil {
// there was an error creating the user
self.showError("Error creating user")
}
else {
// user was created succesfully store first and last name
let db = Firestore.firestore()
db.collection("users").document(results!.user.uid).setData(["first name":Firstname, "last name":Lastname, "age":Age, "uid":results!.user.uid]) { (Error) in
if Error != nil {
// show error message
self.showError("error saving user data")
}
//showing users document id
}
//transition to the home screen
self.transitionToHome()
}
}
}
}
I have no idea what to do any help would be amazing,
thank you very much!!!!
Define a uid variable outside of the IBAction function like so.
var uid: String? = nil
Then, within the createUser function
self.uid = results!.user.uid

swift 3 store code on the cloud

I just started working with servers in swift and I'm using parse server hosted by AWS to store a database of users when they create an account for my app. I have this function called sign up that is linked to a sign up button which works fine and properly stores user's info into my parse database.:
#IBAction func signUp(_ sender: AnyObject) {
//I first check to see if the users left any of the fields blank
if firstName.text == "" || lastName.text == "" || email.text == "" || userName.text == "" || password.text == "" {
createAlert(title: "Error in form", message: "Please fill in all text fields")
//If everything is filled in
}else{
let user = PFUser()
user.username = userName.text
user["firstname"] = firstName.text
user["lastname"] = lastName.text
user.email = email.text
user.password = password.text
user.signUpInBackground(block: { (success, error) in
if error != nil {
var displayErrorMessage = "Please try again later."
if let errorMessage = (error! as NSError).userInfo["error"] as? String {
displayErrorMessage = errorMessage
}
self.createAlert(title: "Signup error", message: displayErrorMessage)
}else{
print("User signed up")
}
})
}
}
Now i need help storing this code on the cloud.
The reason why I ask this is because I had previously posted a question a little different on stack overflow: Deleting PFUser objects from database in swift
And in the response, I was told to store this exact function in cloud code (rather than locally) and I don't know how to do that.
Can anyone show me how it's done?
Thanks a lot in advance!
I'm assuming what it means when you want to "store your code in the cloud" is that all personal info (name, email, password) should be handled from the backend, not that you actually store the code in the cloud. You should handle sensitive personal data from the server-side and not the client-side. This involves server-side encryption and handling requests with SSL exchanges. This is beyond the scope of my expertise and this question, and I highly encourage that you do more research before you try to tackle this problem in your app. Here are some links I found:
http://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html
https://www.raywenderlich.com/92667/securing-ios-data-keychain-touch-id-1password
What they told you is to implement that code in the server, the server should be the one to process the function you ask for in the past question, this one, I think you have already done it because you said the info is stored successfully, but what the answer above this says is that to implement that code in an actual app you should investigate more about security and encryption to protect users information.

Unique usernames in Firebase

I have been trying to implement Chris’ answer here: Can I make Firebase use a username login process? for the Facebook login but I can’t seem to get my head around it.
So far I’ve tried to set conditions on the textField but as Firebase observer works asynchronously, the conditions to check if the username exists in the database won’t work.
let usernameString = usernameTextField.text
let uid = FIRAuth.auth()?.currentUser?.uid
ref.runTransactionBlock({ (currentData: FIRMutableData) -> FIRTransactionResult in
if var post = currentData.value as? [String : AnyObject], let uid = FIRAuth.auth()?.currentUser?.uid {
let usernamesDictionary = post["usernames"] as! NSDictionary
for (key, _) in usernamesDictionary {
if key as? String == usernameString {
print("username not available: \(key)")
}
else if usernameString == "" {
print("Uh oh! Looks like you haven't set a username yet.")
}
else if key as? String != usernameString {
print("username available: \(key)")
print("All set to go!")
let setValue: NSDictionary = [usernameString!: uid]
post["usernames"] = setValue
currentData.value = post
}
}
return FIRTransactionResult.successWithValue(currentData)
}
return FIRTransactionResult.successWithValue(currentData)
}
Then I tried creating /usernames/ node in the database and set up rules as:
{
"rules": {
"usernames": {
".read": "auth != null",
".write": "newData.val() === auth.uid && !data.exists()"
}
}
}
Now that won’t let me set any username to the database. I get confused in creating rules but my whole point is that I need a sign up flow with the username data that’s unique for each user in the database.
While trying every answer I found in related posts, what worked for me the easy way i.e. without making Firebase rules play a part in it or creating a separate usernames node in the database was to not put an if/else condition inside the Firebase observer but instead to use the exists() method of FIRDataSnapshot.
Now here’s the trick, while I did try only the exists() method with a simple observer but that did not help me. What I did was first query usernames in order, then match the username with queryEqualToValue to filter the query:
refUsers.queryOrderedByChild("username").queryEqualToValue(usernameString).observeSingleEventOfType(.Value , withBlock: {
snapshot in
if !snapshot.exists() {
if usernameString == "" {
self.signupErrorAlert("Uh oh!", message: "Looks like you haven't set a username yet.")
}
else {
// Update database with a unique username.
}
}
else {
self.signupErrorAlert("Uh oh!", message: "\(usernameString!) is not available. Try another username.")
}
}) { error in
print(error.localizedDescription)
}
}
This is the first time out of most of the answers here that worked for me. But for now, I don’t know if this would scale. Post your experiences and best practices. They’ll be appreciated.

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.

Firebase How to verify an email to log in belongs to which category in database?

I am working on new Firebase Log in. There are two categories in database. "Cooks" and "Customer". "Cooks" has a CookViewController and "Customers" has a customerViewController. I need to know which category this email belongs to so after logging in, segue to CookViewController or CustomerViewController. Below is code for sign in, but I have no idea how to tell which category the email belongs to. Thanks!
#IBAction func loginPressed(sender: AnyObject) {
if let email = emailField.text where email != "", let password = passwordField.text where password != "" {
FIRAuth.auth()?.signInWithEmail(email, password: password, completion: { (user, err) in
if err != nil {
print(err)
} else {
// Here I need to know whcih view Controller to Segue to:
self.performSegueWithIdentifier(identifier: String, sender: self)
}
}
Data Structure as below:
How about making your JSON like :-
yourApp:{
cooks:{
email1:true,
email3:true,
email4:true,
email7:true,
email8:true},
customers:{
email2:true,
email12:true,
email13:true,
email4:true,
email8:true},
users:{
uid1:{
email : blah#blah.com,
isCook: true
},
uid2:{
email : burp#blah.com,
isCook: false
}....
}
}
So you can either check in the cooks section or the customer section or the uid under users section itself and then relating to the isCook node, if he is ->Go to cookViewController, else go to customerViewController
Easiest way would be to check in the uid:-
FIRDatabase.database().reference().child("users/\(FIRAuth.auth()!.currentUser!.uid)/isCook").observeSingleEventOfType(.Value,withBlock:{(snap) in
if let isThisUserCook = snap.value as? Bool //or String{
if isThisUserCook == true{
//segue to cookVC
}else{
//segue to customerVC
}
}
})