5

I want to delete my current user from Firebase. The authenticated user gets deleted however, I am unable to delete the data for that user in the database. What am i doing wrong?

This is my delete user method....

FIRAuth.auth()?.signIn(withEmail: (emailTextField?.text)! , password: (passwordTextField?.text)!, completion: { (user, error) in
            if error == nil {
                print("User Authenticate!!!")
                let user = FIRAuth.auth()?.currentUser

                user?.delete(completion: { (error) in
                    if error != nil {
                        print("Error unable to delete user")

                    } else {

                        DataService.ds.deleteCurrentFirebaseDBUser()
                        KeychainWrapper.standard.removeObject(forKey: KEY_UID)
                        self.performSegue(withIdentifier: "goToLogin", sender: nil)
                    }
                })

            } else {
                //Password was wrong, unable to authenicate user. Data is not updated
                print("!!!ALERT!!! Unable to authenticate user")
                let alert = UIAlertController(title: "Incorrect Password", message: "Please re-enter your password", preferredStyle: UIAlertControllerStyle.alert)
                alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
                self.present(alert, animated: true, completion: nil)
            }

        })

Firebase Rules:

{
"rules": {
    "users": {
      "$uid": {
        ".read": "$uid === auth.uid",
        ".write": "$uid === auth.uid"
      }
    }
  }
}

Database:

App
   -> users
           ->
             4erkjkl543jfe46
                            ->name
                            ->email

ERRORS:

2017-01-21 21:33:10.321704 APP[11582:4102711] [FirebaseDatabase] setValue: or removeValue: at /users/4erkjkl543jfe46 failed: permission_denied

Optional(Error Domain=com.firebase Code=1 "Permission denied" UserInfo={NSLocalizedDescription=Permission denied})

ThunderStruct
  • 1,504
  • 6
  • 23
  • 32
Nick
  • 213
  • 3
  • 6

4 Answers4

5

I'm having the same issue. You are not able to make use of your function deleteCurrentFirebaseDBUser() because the Firebase delete function (if successful) removes the user auth object.

As a result user is not authenticated anymore at the time you want to delete user's data in database with deleteCurrentFirebaseDBUser().

Currently I delete user's data in database before Firebase delete function which is not the ideal solution.

maxwell
  • 3,788
  • 6
  • 26
  • 40
SvshX
  • 146
  • 1
  • 5
0

We can delete user from both side authentication and database.But before that we need to reauthenticate user first then we get latest token to delete the user.

Here is the pretty code:

  let user = Auth.auth().currentUser
            user?.reauthenticate(with:credential) { error in
                if let error = error {
                    // An error happened.
                    showAlertWithErrorMessage(message: error.localizedDescription)
                } else {
                    // User re-authenticated.
                    user?.delete { error in
                        if let error = error {
                            // An error happened.
                            showAlertWithErrorMessage(message: error.localizedDescription)
                        } else {
                            // Account deleted.
                            let userID = HelperFunction.helper.FetchFromUserDefault(name: kUID)
                            Database.database().reference(fromURL: kFirebaseLink).child(kUser).child(userID).removeValue()

                            try!  Auth.auth().signOut()
                             showAlertWithErrorMessage(message: "Your account deleted successfully...")
                           return
                        }
                    }

                }
            }

100% working in my project and well tested

Mr.Javed Multani
  • 12,549
  • 4
  • 53
  • 52
  • It shouldn't work if you have correctly set rules in Firebase Console (e.g. `$uid === auth.uid`) since the data has been deleted by the user that is no longer exists and cannot be authorized to perform this API call for `removeValue()`. You should delete all data that related to the user before deleting the actual user. – Ido Jan 22 '22 at 19:13
0

for just to delete a child from Firebase use "removeValue()"

 var db: DatabaseReference!
  override func viewDidLoad() {
        super.viewDidLoad()
        db = Database.database().reference()        
        deleteByID()
    }
 func deleteByID(){
        db.child("YOURID").removeValue()
    }
Osman
  • 1,496
  • 18
  • 22
0

Swift 5 | Firebase 8.11.0

As @SvshX said, deleting the user data before deleting the actual user is the only available solution.

The problem with this method is that deleting the user might give an error like AuthErrorCode.requiresRecentLogin, then the data will be deleted but the user will not.

This error is given when the last authentication of the user was more than 5 minuets ago (from Firebase Docs)

So, fixing both of the issues can be achieved by using DispatchGroup and checking the lastSignInDate.

This is my final solution (just call deleteUserProcess()):

let deleteDataGroup = DispatchGroup()

func deleteUserProcess() {
    guard let currentUser = Auth.auth().currentUser else { return }
    deleteUserData(user: currentUser)
    // Call deleteUser only when all data has been deleted
    deleteDataGroup.notify(queue: .main) {
        self.deleteUser(user: currentUser)
    }
}
/// Remove data from Database & Storage
func deleteUserData(user currentUser: User) {
    // Check if `currentUser.delete()` won't require re-authentication
    if let lastSignInDate = currentUser.metadata.lastSignInDate,
        lastSignInDate.minutes(from: Date()) >= -5 {
        deleteDataGroup.enter()
        Database.database().reference().child(userId).removeValue { error, _ in
            if let error = error { print(error) }
            self.deleteDataGroup.leave()
        }
        // Delete folders from Storage isn't possible,
        // so list and run over all files to delete each one independently
        deleteDataGroup.enter()
        Storage.storage().reference().child(userId).listAll { list, error in
            if let error = error { print(error) }
            list.items.forEach({ file in
                self.deleteDataGroup.enter()
                file.delete { error in
                    if let error = error { print(error) }
                    self.deleteDataGroup.leave()
                }
            })
            deleteDataGroup.leave()
        }
    }
}

/// Delete user
func deleteUser(user currentUser: User) {
    currentUser.delete { error in
        if let error = error {
            if AuthErrorCode(rawValue: error._code) == .requiresRecentLogin {
                reauthenticate()
            } else {
                // Another error occurred
            }
            return
        }

        // Logout properly
        try? Auth.auth().signOut()
        GIDSignIn.sharedInstance.signOut()
        LoginManager().logOut()

        // The user has been deleted successfully
        // TODO: Redirect to the login UI
    }
}

func reauthenticate() {
    // TODO: Display some UI to get credential from the user
    let credential = ... // Complete from https://stackoverflow.com/a/38253448/8157190
    Auth.auth().currentUser?.reauthenticate(with: credential) { _, error in
        if let error = error {
            print(error)
            return
        }

        // Reload user (to update metadata.lastSignInDate)
        Auth.auth().currentUser?.reload { error in
            if let error = error {
                print(error)
                return
            }
            // TODO: Dismiss UI
            // Call `deleteUserProcess()` again, this time it will delete the user
            deleteUserProcess()
        }
    }
}

The minuets function can be added in an extension to Date (thanks to Leo Dabus):

extension Date {
    /// Returns the amount of minutes from another date
    func minutes(from date: Date) -> Int {
        return Calendar.current.dateComponents([.minute], from: date, to: self).minute ?? 0
    }
}
Ido
  • 473
  • 4
  • 16