Skip to content

feat(mfa): two-step verification account panel and routes - #89

Open
bordoni wants to merge 1 commit into
feat/mfa-plugin-enforcementfrom
feat/mfa-self-service
Open

bordoni wants to merge 1 commit into
feat/mfa-plugin-enforcementfrom
feat/mfa-self-service

Conversation

@bordoni

@bordoni bordoni commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

Users can turn two-step verification on and off themselves. Stacked on #88. Part of #28.

Changes

  • Account panel (React):
    • A "Two-step verification" section on profile.php, and a [workos:mfa] shortcode for front-end account pages.
    • Turning MFA on shows a QR code and setup key. The factor only counts once the first code checks out.
    • Removing a factor needs a current code from it, so a borrowed session can't quietly turn MFA off.
  • Routes (cookie auth with the wp_rest nonce, so any UI can use them):
    • GET /users/{id}/mfa: the owner, or anyone with edit_user.
    • POST /users/{id}/mfa/enroll and POST /users/{id}/mfa/confirm.
    • DELETE /users/{id}/mfa/factors/{factor_id}, with a current code.
    • Per-user rate limits. Filters workos_mfa_can_manage and workos_mfa_can_manage_user.
  • The older profile-scoped routes (/auth/mfa/factors, /totp/enroll, /factor/delete) now go through the same service. Enrollment needs the confirm step, and delete needs code. Nothing in the plugin called them, and they couldn't have worked against WorkOS before fix(mfa): read WorkOS MFA errors, call the real MFA endpoints, stop the password fallback skipping MFA #85.

Screenshots

Verified

  • New MfaAccountRestTest (10 tests):
    • Permissions: visitor, owner, other user, admin, and the filter.
    • Enroll then confirm.
    • Removing a factor needs a code; someone else's factor returns 404.
    • The shortcode renders only for linked users.
  • AuthKitRestMfaTest updated for the delete contract, plus a new test that delete without a code is refused.
  • Local WPUnit: 855 tests, same 11 environment-only failures. PHPCS, PHPStan, tsc and build pass.

- Mfa\RestApi: GET /users/{id}/mfa (owner or edit_user), POST
  /mfa/enroll and /mfa/confirm, DELETE /mfa/factors/{id} with a current
  code from the factor; wp_rest cookie auth; per-user rate limits;
  filters workos_mfa_can_manage and workos_mfa_can_manage_user
- Account panel (React, wp-element): profile.php section and the
  [workos:mfa] shortcode
- The profile-scoped /auth/mfa/factors|totp/enroll|factor/delete routes
  go through FactorService (confirm step, code to delete)
@bordoni
bordoni force-pushed the feat/mfa-self-service branch from 9adff18 to f7fa19e Compare October 18, 2026 17:24
@bordoni
bordoni marked this pull request as ready for review October 19, 2026 05:28
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.

1 participant