Repository navigation
Add personal data exporter - #997
faisalahammad wants to merge 7 commits into
Conversation
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
|
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 If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Before we start reviewing. @faisalahammad would you mind changing all versions to 0.18.0 as 0.17.0 is already finalized? |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Address the incomplete export of disabled providers and clarify the email timestamp’s accuracy.
Review effort: Lite
Findings: 1
Open (1)
What changed in this PR
Adds WordPress personal-data export and erasure support for Two-Factor authentication data.
Changes:
- Registers privacy exporter and eraser callbacks.
- Adds provider-specific privacy methods.
- Exports non-secret metadata and removes short-lived records.
- Adds comprehensive PHPUnit coverage.
| File | Reviewed changes |
|---|---|
tests/providers/class-two-factor-totp.php |
Tests TOTP privacy behavior. |
tests/providers/class-two-factor-provider.php |
Tests base provider privacy methods. |
tests/providers/class-two-factor-email.php |
Tests email privacy behavior. |
tests/providers/class-two-factor-backup-codes.php |
Tests recovery-code export behavior. |
tests/class-two-factor-core.php |
Tests core privacy behavior and secret exclusion. |
providers/class-two-factor-totp.php |
Handles TOTP export and replay-marker erasure. |
providers/class-two-factor-provider.php |
Adds provider privacy extension methods. |
providers/class-two-factor-email.php |
Handles email metadata export and token erasure. |
providers/class-two-factor-backup-codes.php |
Exports remaining recovery-code counts. |
class-two-factor-core.php |
Implements privacy registration, export, and erasure. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Review comment, requested by @masteradhoc:
|
|
@masteradhoc Done. All new @SInCE tags in this PR now say 0.18.0. The older 0.17.0 tags from the released version stay untouched. |
|
@dknauss Thanks for the review. Both red points are fixed in commit 18ff905.
|
masteradhoc
left a comment
There was a problem hiding this comment.
Thank you @faisalahammad for the work on this PR!
Could we split the exporter and the eraser into two separate PRs? That would help speed up review and testing :) The exporter could go first, and the eraser could build on its provider methods.
Early feedback on what I'd do differently:
Eraser
- Please don't delete
USER_RATE_LIMIT_KEYandUSER_FAILED_LOGIN_ATTEMPTS_KEY. They drive the brute-force backoff and the automatic password reset after 30 failed attempts, and today only a successful 2FA login orwp two-factor unlockclears them. An erasure request shouldn't reset them; report them as retained instead. - I'd also keep
USER_PASSWORD_WAS_RESET_KEYrather than erase it. The CLIdisablecommand preserves it on purpose, and it's cleared automatically once the user sets a new password. Since it stays, please add it to the export.
Exporter
- The export shows PHP class names (
Two_Factor_Totp, Two_Factor_Email, Two_Factor_Backup_Codes) for both the enabled and the primary method. Could you use each provider'sget_label()("Authenticator App", "Email", "Recovery Codes") and fall back to the raw key for providers that no longer exist? - Per-session 2FA data is missing.
two-factor-loginandtwo-factor-providerare stored insession_tokens, and core's exporter drops them. If you emit items withgroup_id => 'session-tokens'and core's item IDsession-tokens-{$user_id}-{$token_key}(the array key of thesession_tokensmeta), core merges them into the matching session, so each session shows when 2FA was verified and with which method.
- 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
|
@masteradhoc Thanks for the review. I updated the PR in aa621db. Split
Exporter
Eraser (next PR)
I also updated the PR description and added the AI disclosure section. |
…cy-exporter-eraser
- Use the matching wordpress-develop PHPUnit library for -branch matrix legs in .github/workflows/test.yml - Return "Unknown" for non-string provider keys in the exporter label helper in class-two-factor-core.php - Label setup-only sessions "Two Factor enabled" in the session export - Cast the failed attempts count to int in the export - Rename the TOTP export row to "Last successful TOTP verification" in providers/class-two-factor-totp.php - Add a test for malformed provider meta and update label assertions Errors fixed: - Interface "WP_Secrets_Store" not found: wp-env loaded the trunk PHPUnit library for 7.1.4-alpha, which needs the Secrets API. - TypeError from isset() with array provider meta in the exporter. PHP 7.4+ compatible. PHPCS, PHPStan and PHPUnit (single site and multisite) pass locally. Refs WordPress#997
faisalahammad
left a comment
There was a problem hiding this comment.
CI fix summary: 5 failures fixed
All 5 failing jobs were the wordpress/wordpress#7.1-branch legs. They failed with Interface "WP_Secrets_Store" not found in the PHPUnit bootstrap. The same error also shows on master. The core version on that branch is 7.1.4-alpha, so wp-env loads the PHPUnit library from wordpress-develop trunk. Trunk needs the Secrets API, and 7.1 does not have it.
| # | File | Error | Fix |
|---|---|---|---|
| 1 | .github/workflows/test.yml | WP_Secrets_Store not found |
For -branch legs, check out the matching wordpress-develop branch for the PHPUnit library |
| 2 | class-two-factor-core.php | TypeError from isset() when provider meta is an array |
Return "Unknown" for provider keys that are not strings |
| 3 | class-two-factor-core.php | Sessions set up without a code were labeled "verified" | Label them "Two Factor enabled" |
| 4 | class-two-factor-core.php | Raw failed attempts meta in the export | Cast to int |
| 5 | providers/class-two-factor-totp.php | Label said "login" but the time also updates on revalidation | Rename to "Last successful TOTP verification" |
Fix 1 is the same change as in #1026. Items 2 to 5 came from a CodeRabbit review.
PHPCS, PHPStan and PHPUnit pass locally for single site and multisite (324 tests each).
|
Thank you @faisalahammad ! Can you change the subject of this PR to "exporter" only? |
The workflow step that matches the PHPUnit library to the WordPress branch now lives in its own PR, as asked in review. Refs WordPress#997
|
@masteradhoc Thanks. Both done.
|
- Report recovery codes with 0 left after all codes are used. - Skip the site-wide provider filter for the exporter, so disabled third-party providers are still exported. - Skip provider export data that is not an array. Addresses PR feedback. Refs WordPress#997
faisalahammad
left a comment
There was a problem hiding this comment.
Updated in 2b90abc after the Copilot review.
- Recovery codes: a user who used all codes now exports "0 unused codes". Before, the row was missing.
- Third-party providers that are off in Settings > Two-Factor are now in the export. The site-wide setting still hides them on login and profile.
- Provider export data that is not an array is skipped, so one bad provider does not break the export.
- The test option comment needs no change. The WP test case rolls back the DB and flushes the cache after each test.
Two new tests. PHPCS, PHPStan, and PHPUnit pass on single site and multisite (326 tests each).


What?
Registers the plugin with the WordPress personal data export tool (Tools > Export Personal Data). Users can request a copy of their two-factor data.
Fixes #954
The eraser was split out of this PR, as asked in review. It will follow in a separate PR that builds on the provider methods added here.
Why?
The plugin stores per-user authentication data (enabled methods, failed login counters, one-time tokens, TOTP replay markers, per-session 2FA details) but does not hook into the export tool. None of it shows up in a personal data export.
How?
Two_Factor_Coreregisters thewp_privacy_personal_data_exportersfilter and implements the callback. The "Two Factor Authentication" group lists the enabled methods, primary method, failed login attempts, last failed login time, and the password reset flag when it is set.get_label()("Authenticator App", "Email", "Recovery Codes"). The raw key is used for providers that no longer exist.session_tokens(two-factor-login,two-factor-provider) is exported with core'ssession-tokensgroup ID andsession-tokens-{$user_id}-{$token_key}item ID, so core merges it into the matching session. Each session shows when 2FA was verified and with which method.privacy_export_data( $user )method onTwo_Factor_Provider, mirroring theuninstall_user_meta_keys()pattern: TOTP shows "Configured" plus the last successful login, Email shows when a code was last sent, Backup Codes shows the remaining code count.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 testruns the new PHPUnit tests covering the export contents, provider labels and the fallback, per-session data, and that no secret material appears in the export payload.Manual:
npm install && npm run build && npm run env start.Authenticator App, Email, Recovery Codesas enabled methods andAuthenticator Appas primary. NoTwo_Factor_*class names appear.Screenshots or screencast
Changelog Entry
Added - Register a personal data exporter with the WordPress privacy tools.