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.
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
- Deleting the auth record does not delete the data
- Do the cleanup server-side, triggered by the deletion
- Deleting is not always the right operation
- A checklist for the implementation
- How this fits the rest of the stack
- FAQ
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
- Re-authenticate immediately before deleting, and handle auth/requires-recent-login as an expected branch rather than an error.
- Enumerate every store keyed on the UID: Firestore, Realtime Database, Storage, custom claims, push tokens, and any external database.
- Do the cleanup server-side on the deletion trigger, with admin credentials, written to be idempotent.
- Chunk large deletions so a single user’s data cannot exceed batch limits.
- Log that a deletion happened, without logging the personal data you just removed.
- 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:
- Firebase vs Supabase: Document Store or Postgres, and What That Costs You Later
- Cloud Engineering in 2026: What the Practice Looks Like Now
- Linux: How to Check If a User Account Name Was Changed
- Services on RunxBuild
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.