The brief for OSS-4 (Story, Medium, compliance): add an admin-only `DELETE /api/admin/users/:identifier/data` that erases all of a user's app-side data (documents, chats, projects, tabular reviews, API keys, profile, and storage blobs) keyed by their Entra `oid`, while never touching the Entra identity itself. Summarizes why (the app is the data controller and the Entra-mode 403 currently leaves right-to-erasure with no execution path), the scope (admin-group auth, oid-or-email resolution, a shared erasure helper to prevent admin/self-service drift, audit logging), and acceptance criteria including no-op success for a user with no `user_profiles` row and an explicit ban on any Graph identity-deletion call. Flags a required policy/DPA sign-off as a non-code dependency. For the quick OSS-4 scope, acceptance criteria, and the compliance sign-off dependency; read spec.md for the full design.
OSS-4 — Admin-initiated erasure of an Entra user's app data (GDPR)
- Key: OSS-4
- Type: Story
- Priority: Medium (compliance / right-to-erasure)
- Components: backend, auth/Entra, admin, privacy
- Labels: gdpr, entra, admin, privacy
- Provenance: surfaced while reviewing
3a10943— in Entra mode,DELETE /user/accountreturns 403 ("contact your tenant administrator"), but no administrator tool exists to actually erase the user's app data.
Summary
Add an admin-only endpoint that erases all of a given user's app-side data
(documents, chats, projects, tabular reviews, API keys, profile, and storage
blobs), keyed by their Entra oid. The Entra identity itself is never
touched — that's removed separately by the tenant admin in Azure AD.
Why
The app is the data controller for everything it persisted against a user's
oid. Self-service deletion is (correctly) disabled for Entra tenants, so
right-to-erasure currently has no execution path: the 403 tells users to contact
their admin, but the admin has no tool. This closes that gap.
Scope
- Admin-gated
DELETE /api/admin/users/:identifier/data(admin-group auth). - Resolve
:identifieras anoid, or an email →oidviauser_profiles. - Reuse the proven erasure cascade (
deleteUserAccountData+ the identity-table cleanup) behind a shared helper so the admin and self-service paths can't drift. - Audit-log every erasure (admin oid, target oid, timestamp, counts).
Acceptance criteria
- An admin (in the configured admin group) can erase a target user's data by oid or email; a non-admin gets 403.
- Erases the same set as self-service deletion (DB rows + storage objects) — no orphans (see spec.md for the table-by-table guarantee).
- Never calls any Entra/Graph identity-deletion API.
- A user who has no
user_profilesrow → no-op success ("nothing to erase"), not a 500/404. - Erasure is recorded in an audit log.
Out of scope
- Deleting the Entra identity / Azure AD object (tenant-admin action).
- Self-service deletion changes (the 403 path stays).
- A bulk / scheduled-retention erasure job (separate concern).
Dependencies / risk
- Reuses existing primitives:
isInAdminGroup(installAuth.ts), the/api/admin/*namespace (diagnosticsRouterprecedent), anddeleteUserAccountData(lib/userDataCleanup.ts). - Policy sign-off required: confirm admin-mediated erasure (+ the self-service 403) satisfies the org's DPA/GDPR data-subject-request process. Code can't settle this.
See spec.md for the full design.