Skip to content

Add personal data eraser - #1026

Open
faisalahammad wants to merge 8 commits into
WordPress:masterfrom
faisalahammad:feature/954-privacy-eraser
Open

faisalahammad wants to merge 8 commits into
WordPress:masterfrom
faisalahammad:feature/954-privacy-eraser

Conversation

@faisalahammad

@faisalahammad faisalahammad commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

What?

Registers the plugin with the WordPress personal data erasure tool (Tools > Erase Personal Data). Site admins can clear short-lived Two Factor records for a user.

Fixes #954

This PR is stacked on #997 (personal data exporter), as asked in review there. It builds on the provider methods from #997, so the diff shows #997's commits until that one is merged. Only the last commit is new here.

Why?

The plugin stores short-lived per-user records (login nonce, pending email code) that a personal data erasure request should remove. Without an eraser, sites that handle erasure requests must clean up user meta by hand.

How?

  • Two_Factor_Core registers the wp_privacy_personal_data_erasers filter and implements the callback.
  • Providers list the short-lived keys they want erased through an optional static privacy_eraser_user_meta_keys() method on Two_Factor_Provider, mirroring uninstall_user_meta_keys(). Email returns its token and token timestamp. TOTP returns nothing.
  • The eraser removes the login nonce and the provider keys listed above.
  • The eraser keeps these and reports them through items_retained and a message:
    • Credentials: TOTP secret, backup code hashes, enabled and primary provider. The erasure tool does not delete the account, so removing them would leave it protected by a password only.
    • The TOTP last successful login time. It still blocks reuse of the most recent code.
    • Login protection records: USER_RATE_LIMIT_KEY and USER_FAILED_LOGIN_ATTEMPTS_KEY drive the brute force backoff and the forced password reset after 30 failed attempts. USER_PASSWORD_WAS_RESET_KEY is cleared when the user sets a new password, and the CLI disable command preserves it the same way. These are cleared only by a successful Two Factor login, wp two-factor unlock, or a new password.
    • Per-session Two Factor details in session_tokens. They stay with the session.
  • All registered providers are processed, so a provider disabled in the site-wide settings is still covered.

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus
Used for: Implementation and test help. I reviewed and edited all generated code and tests.

Testing Instructions

Automated: npm test runs the new PHPUnit tests covering the removed records, the retained credentials, login protection records and session details, the retained message, and providers disabled site-wide.

Manual:

  1. Build and start the dev environment: npm install && npm run build && npm run env start.
  2. Edit a test user. Enable Authenticator App, Email, and Recovery Codes, and generate recovery codes. Log in once as that user with the Authenticator App.
  3. Start another login as the test user and choose Email on the 2FA screen, so a code is sent. Do not enter it.
  4. Set login protection data: npm run env run cli -- wp user meta update <user_id> _two_factor_failed_login_attempts 3.
  5. As admin, go to Tools > Erase Personal Data, add a request for the test user, and run erasure.
  6. The result reports retained items with a message that credentials, login protection records and session details were kept.
  7. wp user meta list <user_id>: _two_factor_nonce, _two_factor_email_token and _two_factor_email_token_timestamp are gone. The credentials, _two_factor_totp_last_successful_login and _two_factor_failed_login_attempts (still 3) remain.
  8. The old email code no longer works. Login with the Authenticator App and with a recovery code still works.

Screenshots or screencast

Before After
Two Factor is not listed in the personal data erasers. The erasure runs a "Two Factor Authentication Data" eraser and shows a retained-data message. No other admin UI change.

Changelog Entry

Added - Register a personal data eraser with the WordPress privacy tools.

Open WordPress Playground Preview

Register the plugin with the WordPress personal data export and
erasure tools.

- Two_Factor_Core registers both privacy filters and implements the
  callbacks, keeping the data provider-agnostic.
- Providers contribute via two optional base methods on
  Two_Factor_Provider: privacy_export_data() and
  privacy_eraser_user_meta_keys(), mirroring the existing
  uninstall_user_meta_keys() pattern.
- The eraser removes short-lived records (login nonce, rate limit
  counters, email tokens, TOTP replay marker) and keeps credentials
  (TOTP secret, backup codes), reporting them as retained so the
  second factor stays active on the account.
- Exports never include secret material: the TOTP key, hashed email
  tokens and backup codes stay out of the payload.
- Tests cover the export contents, the erasure behavior, and that no
  secret material appears in the export payload.

Fixes WordPress#954
- Remove the personal data eraser, it moves to a separate PR
- Show provider labels instead of class names, fall back to the key
- Export the password reset flag
- Add per-session Two Factor data to the core session tokens export

Addresses PR feedback.

Refs WordPress#997
- Register an eraser with the WordPress privacy tools
- Erase the login nonce and provider short-lived keys
- Keep credentials, login protection records and session details
- Report retained data with a message

Refs WordPress#954
@github-actions

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: faisalahammad <faisalahammad@git.wordpress.org>
Co-authored-by: dknauss <dpknauss@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

wp-env uses wordpress-develop trunk tests when the core version has an
-alpha suffix. The 7.1-branch is 7.1.4-alpha, so it got trunk tests that
need WP_Secrets_Store, which 7.1 does not have. Check out the matching
wordpress-develop branch for the branch matrix legs.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Site-disabled custom providers can be skipped, leaving their data unexported and short-lived records unerased.

1 open issue
What changed in this PR

Adds personal data erasure to WordPress’s privacy tools, building on the exporter in #997 while preserving account security.

Changes:

  • Registers export and erasure callbacks with provider-specific privacy methods.
  • Removes short-lived records and reports retained security data.
  • Adds tests for privacy output, secret exclusion, and retention.
File Description
tests/​providers/​class-two-factor-totp.php Tests TOTP export and retention.
tests/​providers/​class-two-factor-provider.php Tests default privacy methods.
tests/​providers/​class-two-factor-email.php Tests email export and erasure keys.
tests/​providers/​class-two-factor-backup-codes.php Tests code counts and secret exclusion.
tests/​class-two-factor-core.php Tests privacy registration, export, and erasure.
providers/​class-two-factor-totp.php Exports configuration and preserves replay protection.
providers/​class-two-factor-provider.php Defines optional privacy methods.
providers/​class-two-factor-email.php Exports send time and declares erasable keys.
providers/​class-two-factor-backup-codes.php Exports remaining recovery-code counts.
class-two-factor-core.php Coordinates privacy callbacks and retention reporting.

Review effort: Balanced

Comment thread class-two-factor-core.php
A custom provider registered before the site-wide setting filter was
removed when it was disabled in Settings > Two-Factor. Merging the
default providers back only restored bundled ones, so the exporter and
eraser skipped the custom provider's data.

Skip the site-wide setting filter while collecting providers for the
privacy tools, and keep every other registration filter. Add a test with
a disabled custom provider registered at priority 5.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Privacy: register a personal data exporter and eraser

3 participants