Guideline 5.1.1(v)

Guideline 5.1.1(v): adding in-app account deletion to an Expo and Supabase app

Last verified by Designated Dev

Short answer

If your app lets people create an account, guideline 5.1.1(v) requires a way to delete it from inside the app. Deleting means removing the account and its data, not deactivating it. In an Expo app using Supabase, the deletion must run on a server, because the admin call needs the service role key: an Edge Function verifies the signed-in user, deletes their files, then calls auth.admin.deleteUser. Google Play also requires a web link for deletion.

If your app supports account creation, you must also offer account deletion within the app.

App Review Guideline 5.1.1(v), Account Sign-In

At a glance

What you seeWhy it happensThe fix
Rejected: “does not include an option to initiate account deletion”Sign-up exists but there is no delete option in the appA Delete account action in Settings that deletes the account and its data
Rejected even though deletion existsIt only deactivates the account, or asks the user to email supportDelete the account record and personal data; no support flow required
deleteUser fails in the Edge FunctionThe user still owns Storage files, or a table references auth.users without ON DELETE CASCADEDelete the user's files first; add ON DELETE CASCADE to the foreign keys
Service role key found in the app bundledeleteUser was called from the clientMove it into an Edge Function and rotate the leaked key

What Apple actually checks

Apple’s support page, Offering account deletion in your app, spells out what passes review:

  • Delete, don’t deactivate. “Offer to delete the entire account record, along with associated personal data… only offering to temporarily deactivate or disable an account is insufficient.”
  • Easy to find. Typically in the app’s account settings.
  • No support flows. Apps outside highly regulated industries “should not require people to make a phone call, send an email, or go through other support flows.”
  • Confirming is fine. You can re-authenticate or ask for confirmation, but making deletion “unnecessarily difficult” fails review.
  • Sign in with Apple. Apps that support it “should use the Sign in with Apple REST API to revoke user tokens.”
  • Subscriptions. Tell people billing continues through Apple and ask them to cancel before deleting.
  • It can take time. A manual or slow process is acceptable if you say how long it will take and confirm when it’s done.

The rejection usually reads: “The app supports account creation but does not include an option to initiate account deletion.”

Why AI-built apps miss it

Builders generate the sign-up screen because it’s part of the preview. Deletion needs a server-side step, because the client isn’t allowed to delete users. So it’s usually missing, or it’s done the dangerous way, with the service role key shipped inside the app.

The fix: an Edge Function that deletes the signed-in user

Supabase’s auth.admin.deleteUser “requires a service_role key” and “should only be called on a server.” Put it in an Edge Function. The pattern below follows Supabase’s current Edge Function auth docs: the function only runs for a signed-in user, and uses the admin client to delete that user and nobody else.

// supabase/functions/delete-account/index.ts
import { withSupabase } from "npm:@supabase/server";

export default {
  fetch: withSupabase({ auth: "user" }, async (_req, ctx) => {
    const userId = ctx.userClaims?.id;
    if (!userId) return Response.json({ error: "unauthorized" }, { status: 401 });
    const admin = ctx.supabaseAdmin;

    // 1. Files block deletion. This assumes each user's files live under `${userId}/`.
    const { data: files } = await admin.storage.from("avatars").list(userId);
    if (files?.length) {
      await admin.storage.from("avatars").remove(files.map((f) => `${userId}/${f.name}`));
    }

    // 2. If the app supports Sign in with Apple, revoke the user's Apple token here.

    // 3. Hard delete. Tables that reference auth.users with ON DELETE CASCADE go with it.
    const { error } = await admin.auth.admin.deleteUser(userId);
    if (error) return Response.json({ error: error.message }, { status: 500 });
    return Response.json({ ok: true });
  }),
};

In the app, call it from a Delete account screen after a confirmation step, then sign out:

const { error } = await supabase.functions.invoke("delete-account");
if (!error) await supabase.auth.signOut();

functions.invoke sends the signed-in user’s session token automatically, which is how the function knows whose account to delete.

Make the database follow

Supabase’s docs create user tables with references auth.users on delete cascade, so deleting the user removes their rows. If your tables reference auth.users without it, the delete fails or leaves orphaned personal data. Also note that Storage list calls are not recursive: if users have nested folders, remove those too.

Deleting the user ends their sessions and refresh tokens. An access token already issued stays valid until it expires, so sign the user out in the app straight away.

Google Play asks for two paths

Google Play requires an in-app path to delete the account and its data, and a web link where people can request deletion, entered in Play Console. It also says temporarily deactivating or “freezing” an account “does not qualify as account deletion.” A small web page that uses the same Edge Function after the user signs in covers the web link.

How we handle it

We add the Delete account screen with a clear confirmation, move deletion into an Edge Function, check every table and bucket that holds the user’s data, revoke Sign in with Apple tokens where the app uses them, and rotate the service role key if it was ever shipped in the client.

Questions

Can I just link to a web page to delete the account?

Deletion has to start in the app. Apple allows the in-app option to link directly to the page on your website where the deletion is completed, but not a general page that makes people search for it.

Is deactivating or soft-deleting the account enough?

No. Apple calls offering only temporary deactivation insufficient, and Google Play says freezing an account does not qualify as deletion. In Supabase, keep deleteUser's soft-delete option off.

Can I call supabase.auth.admin.deleteUser from my React Native app?

No. It requires the service role key, which Supabase says must never be exposed in a client. Run it in an Edge Function that checks who is calling.

What about users with an active subscription?

Apple asks you to tell them billing continues through Apple and to ask them to cancel first. You may schedule deletion for when the subscription ends, as long as immediate deletion is also offered.

Does deleting the Supabase user revoke Sign in with Apple?

Not that Supabase documents. Apple asks apps that support Sign in with Apple to revoke the user's tokens through its REST API, so do that yourself during deletion.

Does Google Play require the same thing?

Yes, plus a little more: an in-app path to delete the account and its data, and a web link where people can request deletion, entered in Play Console's Data safety section.

Sources

  1. Apple: App Review Guidelines, 5.1.1(v)
  2. Apple: Offering account deletion in your app
  3. Apple: Sign in with Apple REST API, revoke tokens
  4. Google Play: Understanding Google Play's app account deletion requirements
  5. Google Play: User Data policy
  6. Supabase: auth.admin.deleteUser
  7. Supabase: Managing user data
  8. Supabase: Authentication in Edge Functions
  9. Supabase: functions.invoke

Stuck on this in your own app?

We fix it, test it on real devices, and resubmit.

30 minutes with the engineer who'd do the work. Bring the repo link or the rejection email. Fixed price once scoped, in writing.