Skip to content
WebViewGold Vibe Coding to App Store
English
Get WebViewGold

ChatGPT & Codex

Account deletion for Supabase and Firebase apps, mapped and built with Codex

Both stores reject apps that let people sign up but not leave. The feature looks small, but a correct deletion touches auth, every collection or table, file storage, subscriptions and the Sign in with Apple token, which is exactly the kind of cross-cutting change Codex handles well once it has an inventory. Here is a two-pass workflow: Codex maps the data in a read-only session, then implements the purge with tests.

From $109 one-time per platform, or let our team do it all from $698.

  • Approval guarantee – or money back within 14 days
  • No coding needed
  • One-time payment, free updates
  • 24/7 human support
  • Made in Germany since 2014 · ISO 27001

What the stores require, and what a reviewer clicks

Apple (Guideline 5.1.1(v)): if the app supports account creation, people must be able to start account deletion inside the app, and deletion means the account and its data, not a deactivated flag. A confirmation step is fine. If people can sign in with Apple, Apple expects you to revoke their tokens through the Sign in with Apple REST API when they delete. Google Play: an in-app path plus a web page where people can request deletion without reinstalling the app, whose URL you enter in the Data safety section of the Play Console.

What the reviewer actually does: signs up or uses your demo account, opens settings, looks for Delete account, and sometimes goes through with it. On Google Play, the web link gets opened too; if it needs a login or returns a 404, that is a rejection (the Play rejection explained).

Why two passes with Codex: deletion fails quietly. A forgotten collection or storage folder throws no error, it simply stays. Mapping the data before writing code is what makes the result complete. The examples use React, Express, Firebase Auth, Firestore and Cloud Storage; the Supabase equivalents are in the table. The account deletion requirement guide covers the same rules for builders without a coding agent, and the isNativeApp() helper comes from the AGENTS.md template.

Pass 1: map the data in a read-only session

The read-only sandbox lets Codex read files and run harmless commands such as rg, but not change anything. That keeps the first pass honest: it can only look.

bash
# Pass 1: inventory only. Codex can read, search and answer, but not edit.
cd my-app
codex --sandbox read-only --ask-for-approval on-request

# Paste this into the session:
#   Read AGENTS.md. Find every place this codebase stores or sends data about a
#   user: auth providers, Firestore collections and subcollections (and the field
#   that links each document to the user), Cloud Storage paths, server logs,
#   third parties that receive user data (RevenueCat, Stripe, OneSignal, email,
#   analytics, error tracking) and what the web app keeps on the device
#   (cookies, localStorage, IndexedDB, values written with writevalue://).
#   Output one Markdown table: location | link to the user | delete or retain | reason.
#   Mark anything you are unsure about. Do not propose code yet.

# When the table is right, switch to write access inside the session with
# /permissions and ask Codex to save it as docs/user-data-map.md. Commit it.

What a complete deletion covers

LocationFirebase (Express API)Supabase
Auth usergetAuth().deleteUser(uid) with firebase-admin, as the last stepsupabase.auth.admin.deleteUser(id) in an Edge Function with the service role key, as the last step
DatabaserecursiveDelete() for the user document and its subcollections, plus queries on owner fieldsForeign keys to auth.users with on delete cascade, or explicit deletes before the auth call
FilesgetStorage().bucket().deleteFiles({ prefix: "users/UID/" })List the user's folder, then storage.from(bucket).remove(paths)
Sign in with ApplerevokeAccessToken(auth, token) after the user re-authenticates with Apple; Firebase stores no Apple tokensNot automatic. Apple's revocation endpoint needs a token from the sign-in; have Codex check what your flow exposes and flag the gap
Store subscriptionsYou cannot cancel them for the user. Say so in the dialog and offer cancelinapppurchase://; optionally delete the RevenueCat customer server-sideSame
Push, email, analyticsDelete the push device rows, remove the contact from your email tool, use each vendor's deletion APISame
The devicereset:// makes WebViewGold clear cookies and cache; overwrite anything stored with writevalue://, which survives cache clearsSame

WebViewGold has allowed us the shortest and most efficient time-to-market ever possible from among all the other app platforms we have ever tried during the past 5 years. I don’t know why I hadn’t discovered you sooner, but I am really, really glad I did. Currently nearing 6000 app downloads on both iOS App Store & Google Play Store without any problem!

Antonio Davila · CodeCanyon

Pass 2: the implementation prompt

Start a fresh session with codex --sandbox workspace-write --ask-for-approval on-request so the map, not the previous conversation, is the source of truth. For Supabase, replace step 3 with an Edge Function that verifies the JWT, removes storage objects, relies on cascading foreign keys and calls auth.admin.deleteUser.

Paste into Codex (workspace-write, approvals on request)
Read AGENTS.md and docs/user-data-map.md. Implement account deletion that satisfies App Store Guideline 5.1.1(v) and Google Play's account deletion policy. Stack: React front end, Express API, Firebase Auth, Firestore, Cloud Storage. This web app also runs inside native iOS and Android apps built with WebViewGold; use isNativeApp() for app-only behavior.

1. Settings > Account > Delete account. A confirmation sheet explains what is deleted, what we retain and for how long (from the map), and that store subscriptions are billed by Apple or Google until cancelled. If the user has an active store subscription and isNativeApp() is true, show a Manage subscription button that navigates to cancelinapppurchase://.
2. Recent login: if the server answers recent-login-required, re-authenticate. For users with the apple.com provider, always re-authenticate with OAuthProvider("apple.com"), read the access token with OAuthProvider.credentialFromResult, and call revokeAccessToken(auth, token) before the server call.
3. DELETE /api/account in Express: verify the ID token with checkRevoked, require auth_time within the last 5 minutes, then delete in this order: Storage files under users/{uid}/, every Firestore location from the map (recursiveDelete for subcollections, including pushDevices), the Stripe customer and, if the map says so, the RevenueCat customer (behind a feature flag, secret from the environment), and finally the Auth user. Every step must be safe to re-run if the request fails halfway.
4. After success: sign out, and when isNativeApp() is true navigate to reset:// so WebViewGold clears cookies and cache; in browsers, clear localStorage. Then show a goodbye screen.
5. Public /delete-account page that works without login: it explains the in-app steps and offers a request form (email address) for people who no longer have the app. Requests go to a deletionRequests collection and trigger an email to [support address].
6. Add a short section on deletion and retention to /privacy.
7. Tests: use the Firebase emulators if the project already uses them, otherwise mocks. Deletion removes every location in the map, a second call is a no-op, and a stale token is refused.
Report the changed files, the Play Console URL to enter, and what I must configure.

The code to look for in the diff

Two files carry the logic. The order on the server matters: data first, auth user last, so a failed request can simply be retried. The client handles the Apple revocation, because Firebase does not keep Apple tokens and the user has to re-authenticate anyway.

javascript
// server/routes/account.js (Express + firebase-admin)
import express from "express";
import { getAuth } from "firebase-admin/auth";
import { getFirestore } from "firebase-admin/firestore";
import { getStorage } from "firebase-admin/storage";

export const accountRouter = express.Router();
const OWNED = [["projects", "ownerId"], ["comments", "authorId"], ["pushDevices", "uid"]]; // from docs/user-data-map.md

accountRouter.delete("/api/account", async (req, res) => {
  const idToken = (req.get("Authorization") || "").replace(/^Bearer /, "");
  let user;
  try {
    user = await getAuth().verifyIdToken(idToken, true); // true = reject revoked tokens
  } catch {
    return res.status(401).json({ error: "unauthenticated" });
  }
  if (Date.now() / 1000 - user.auth_time > 300) {
    return res.status(403).json({ error: "recent-login-required" });
  }
  const uid = user.uid;
  const db = getFirestore();

  await getStorage().bucket().deleteFiles({ prefix: "users/" + uid + "/" });
  for (const [name, field] of OWNED) {
    const owned = await db.collection(name).where(field, "==", uid).get();
    for (const doc of owned.docs) await db.recursiveDelete(doc.ref);
  }
  await db.recursiveDelete(db.collection("users").doc(uid));
  await getAuth().deleteUser(uid); // last: everything above is safe to re-run
  res.status(204).end();
});

// src/lib/deleteAccount.js (client, Firebase JS SDK)
import { OAuthProvider, reauthenticateWithPopup, revokeAccessToken } from "firebase/auth";
import { isNativeApp } from "./platform";

export async function deleteAccount(auth) {
  const user = auth.currentUser;
  if (user.providerData.some((p) => p.providerId === "apple.com")) {
    const result = await reauthenticateWithPopup(user, new OAuthProvider("apple.com"));
    const credential = OAuthProvider.credentialFromResult(result);
    await revokeAccessToken(auth, credential.accessToken); // Apple 5.1.1(v)
  }
  const res = await fetch("/api/account", {
    method: "DELETE",
    headers: { Authorization: "Bearer " + (await user.getIdToken(true)) }
  });
  if (res.status === 403) throw new Error("recent-login-required");
  if (!res.ok) throw new Error("delete-failed");
  await auth.signOut();
  if (isNativeApp()) window.location.href = "reset://"; // WebViewGold clears cookies and cache
}

The workflow end to end

  1. Map

    Run pass 1 in the read-only sandbox, correct the table yourself (Codex cannot know which data you must keep for tax or legal reasons), and commit docs/user-data-map.md.

  2. Build

    Run pass 2 in a fresh session. Ask for /review before you accept the changes and read the server route against the map row by row.

  3. Configure

    Enter the public /delete-account URL in the Data safety section of the Play Console. Mention deletion and retention in your privacy policy (what the policy needs).

  4. Verify

    Run the checklist below with a throwaway account on a TestFlight build and an internal testing build.

  5. Protect the demo account

    Reviewers may delete it. Recreate it before every submission, and say in the review notes that they are welcome to create a fresh account to test deletion. More on the release path in the Codex workflow guide.

Verify it like a reviewer would

  • A test account with data in every location from the map (a project, a comment, an uploaded file, a push device).
  • Delete in the app: the user is gone from Firebase Authentication, and no Firestore document or Storage file of that user remains.
  • A Sign in with Apple test user no longer sees your app under the Sign in with Apple apps in their Apple account settings after deletion.
  • The app returns to a clean logged-out state; reopening it does not restore the old session.
  • The /delete-account page opens without login in a private browser window and the request form arrives.
  • Calling the delete route twice, or after a simulated failure halfway, ends in the same clean state.

Pitfalls

  • Deactivating instead of deleting. A deleted: true flag is not deletion under 5.1.1(v).
  • Client-side deletes through security rules. They are partial by design (no admin rights, no auth user, no other users' references); do it on the server.
  • Deleting the auth user first. If the request then fails, the data is orphaned and nobody can authenticate to finish the job.
  • Popups inside the app. If reauthenticateWithPopup misbehaves in the WebView, use reauthenticateWithRedirect and read the result with getRedirectResult.
  • Forgetting subscriptions. Deleting the account does not stop App Store or Google Play billing; tell the user before they confirm.
  • A /delete-account page behind login, or a service role key in client code on Supabase. Both get flagged quickly.

Frequently asked questions

Is a grace period allowed?

Yes. You can schedule permanent deletion for a short period later (for example 14 days) if you say so in the confirmation and in the privacy policy, and let the user cancel by signing in. The deletion itself must still happen.

Can I use the Firebase Delete User Data extension instead?

It is a good base: it deletes configured Firestore paths and Storage files when the auth user is deleted. You still need the in-app button, the Apple token revocation, the public request page and a check that its configuration matches your data map.

Does the web request page have to delete automatically?

No. Google Play asks for a way to request deletion outside the app. A form that creates a request you then process is acceptable, as long as you actually process it in the time you state.

Must I delete data in RevenueCat and Stripe too?

Delete or anonymize what you do not need to keep. Payment records may have legal retention periods; list what you keep and why in the privacy policy. Cancelling store subscriptions remains the user's action.

Should Codex get access to my production database for this?

No. Codex writes the code and tests against emulators or mocks; you run the deletion against production through the deployed route. Keep production credentials out of agent sessions and cloud environments.

Three ways to get your app into the stores

Same result, different amount of work on your side. All three come with the approval guarantee and 24/7 human support.

Do it yourself

$109 per platform, one-time

The WebViewGold license: a finished Xcode or Android Studio project. Paste your URL, set name and icon, switch on features, build and submit. Free updates forever.

See plans on the main page

Cloud Builder

$349 per license, one-time add-on

No Mac, no Xcode, no Android Studio: configure in the browser, we build both apps in the cloud and hand them to App Store Connect and the Play Console.

How the Cloud Builder works Try the free demo

Full Service

$698 per platform $798

Send us your URL and icon. Our team configures, builds and delivers your app within 72 hours, white-label, with source projects. Both platforms for $1,158.

Full Service details Powered by WeSetupYourWebViewApp

Winter Sale prices valid until December 31, 2026.

Ready to ship your app?

App Store & Google Play approval guarantee – or your money back within 14 days

Rather hand it over? Full Service: we configure, build and deliver within 72 hours, from $698 per platform. Questions? Chat with us 24/7.

Refunds are granted under the conditions listed in our FAQ: a non-content rejection by Apple and/or Google, claimed within 14 days after purchase.