Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild

Deleting a Firebase Auth Account Properly: The Data Nobody Remembers

Sean

Platform Writer

Aug 14, 2026
9 min read

Deleting a Firebase auth account is a single call to deleteUser, and that call is the easy five percent of the job. The rest is re-authentication requirements, the documents and files that pointed at that user ID, and what your system does when the deletion fails halfway through.

Deleting a Firebase Auth Account Properly: The Data Nobody Remembers

This became a mandatory feature for a lot of teams when the app stores started requiring in-app account deletion for anything that offers in-app registration. That deadline produced a great many implementations that delete the auth record and leave everything else behind. Here is the version that holds up.

Table of contents

The call itself, and the error you will hit first

From the client SDK, deleting the currently signed-in user is straightforward.

import { getAuth, deleteUser } from "firebase/auth";

const auth = getAuth();
const user = auth.currentUser;

try {
  await deleteUser(user);
} catch (err) {
  if (err.code === "auth/requires-recent-login") {
    // Token is too old. Re-authenticate, then retry.
  }
  throw err;
}

That error code is the one everybody meets. Firebase treats deletion as a sensitive operation and refuses it when the session’s credentials are older than roughly five minutes. A user who signed in this morning and taps delete this afternoon will hit it every time.

The fix is to re-authenticate immediately before the delete, using whatever provider the account is on.

import {
  EmailAuthProvider,
  reauthenticateWithCredential,
  reauthenticateWithPopup,
  GoogleAuthProvider,
} from "firebase/auth";

// Password accounts: ask for the password again.
const cred = EmailAuthProvider.credential(user.email, password);
await reauthenticateWithCredential(user, cred);

// Federated accounts: re-run the provider flow.
await reauthenticateWithPopup(user, new GoogleAuthProvider());

await deleteUser(user);

Do not treat this as an annoyance to work around. Requiring a fresh credential before an irreversible action is correct behaviour, and it is the same reason your bank asks again before a transfer.

Deleting the auth record does not delete the data

This is the part that produces support tickets months later. Firebase Authentication holds the identity: the UID, the email, the provider links. It holds none of your application data.

Everything else keyed on that UID stays exactly where it was. Firestore documents. Realtime Database subtrees. Storage objects under a user folder. Custom claims. FCM tokens. Rows in whatever other database you run alongside Firebase.

The result is orphaned records with an owner UID that no longer resolves to anyone. They still count against your storage bill, they still appear in queries and aggregates, and if they contain personal data they are still your responsibility under whatever privacy regime applies to you.

Worse, UIDs are not reused but your own foreign keys might be. An orphaned document that a later code path renders without checking whether the user exists is a null-dereference waiting to happen in production.

So enumerate the data. Before writing any deletion code, list every place a UID is stored. That list is the specification.

Do the cleanup server-side, triggered by the deletion

The tempting approach is to have the client delete its own documents and then delete itself. Do not do that.

Client-side cleanup fails in the ways clients fail. The app is closed mid-sequence, the network drops after three of eight deletions, or the security rules stop the client from touching a collection it does not own directly. You get partial deletions with no record of how far they got.

The right shape is a server-side function that reacts to the deletion event, running with admin privileges and no rules to fight.

// Cloud Functions: fires after the auth record is removed.
exports.onUserDeleted = functions.auth.user().onDelete(async (user) => {
  const uid = user.uid;
  const db = admin.firestore();

  const batch = db.batch();
  batch.delete(db.doc(`users/${uid}`));
  batch.delete(db.doc(`profiles/${uid}`));

  const posts = await db.collection("posts").where("authorId", "==", uid).get();
  posts.forEach((doc) => batch.delete(doc.ref));

  await batch.commit();
  await admin.storage().bucket().deleteFiles({ prefix: `user-uploads/${uid}/` });
});

Two caveats. Firestore batches cap at 500 operations, so a user with more documents than that needs chunking or a recursive delete. And this function must be idempotent, because it can be retried after a partial failure and must not fall over on records that are already gone.

Deleting is not always the right operation

Immediate hard deletion is the wrong default for a lot of applications, and the alternatives are worth considering before you build.

A grace period, where the account is disabled immediately and purged after some days, handles the accidental deletion and the compromised-account case where an attacker deletes to cover tracks. The user gets a window to change their mind.

Anonymisation rather than deletion is often the right answer when the data has meaning beyond the individual. An order history that vanishes breaks your accounting. Detaching it from the person while keeping the record intact usually satisfies both the privacy obligation and the business one, but confirm that against your actual obligations rather than this paragraph.

Whichever you choose, tell the user plainly what is about to happen. A confirmation dialog that says the account will be removed and cannot be recovered, with an explicit list of what goes, is a better experience than an ambiguous one and a much better position to be in later.

A checklist for the implementation

  1. Re-authenticate immediately before deleting, and handle auth/requires-recent-login as an expected branch rather than an error.
  2. Enumerate every store keyed on the UID: Firestore, Realtime Database, Storage, custom claims, push tokens, and any external database.
  3. Do the cleanup server-side on the deletion trigger, with admin credentials, written to be idempotent.
  4. Chunk large deletions so a single user’s data cannot exceed batch limits.
  5. Log that a deletion happened, without logging the personal data you just removed.
  6. Test with a user that has data in every store, then verify each one is empty afterwards.

The sixth step is the one that catches the missed collection, and it is the only one that produces evidence rather than confidence.

How this fits the rest of the stack

Account deletion tends to be the point where a Firebase-only architecture starts touching other systems, because the data you have to clean up is rarely all in one place. If part of your stack is already a service you run yourself, that service needs somewhere to run with its own database and its own logs, and costing that separately makes the architecture easier to reason about. The RunxBuild hosting calculator puts the service, the managed database, and the storage side by side as separate numbers.

Useful related references:

FAQ

Why does Firebase say auth/requires-recent-login when deleting a user?

Deletion is a sensitive operation, so Firebase requires credentials issued within roughly the last five minutes. Re-authenticate with the user’s provider immediately before calling deleteUser and retry.

Does deleting a Firebase auth user delete their Firestore data?

No. Authentication stores only the identity record. Every Firestore document, Storage object, and Realtime Database node keyed on that UID stays until you delete it yourself, normally from a server-side function on the deletion trigger.

Can I delete a Firebase user from the server?

Yes, with the Admin SDK via deleteUser(uid), and there is a bulk variant for up to 1000 accounts per call. Admin deletion does not require recent re-authentication, since that requirement protects client sessions.

Should I soft delete or hard delete accounts?

A grace period covers accidental deletions and compromised accounts, and anonymising is often better where records have business meaning beyond the individual. Pick based on your actual obligations and tell the user clearly which one you do.

What happens to a UID after the account is deleted?

The UID is not reissued to another user, but any of your own data still referencing it becomes orphaned. Code paths that assume a stored UID resolves to a real account will fail unless they check.

#Firebase Auth#Account Deletion#User Data#GDPR#Backend