Skip to content

feat(mfa): add idp:reset-2fa command - #171

Open
romanetar wants to merge 1 commit into
feat/mfa-disable-two-factor-servicefrom
feat/mfa-reset-2fa-command
Open

romanetar wants to merge 1 commit into
feat/mfa-disable-two-factor-servicefrom
feat/mfa-reset-2fa-command

Conversation

@romanetar

Copy link
Copy Markdown
Contributor

Stacked on #169 (base: feat/mfa-disable-two-factor-service), which is stacked on #166 and #125. Merge bottom-up: #125, #166, #169, then this one.

Summary

Second half of ClickUp ticket 12 (Admin Enforcement and Reset Commands): idp:reset-2fa {email} --reason="...", for operators with server access who need to recover a user that lost their second factor and recovery codes.

  • --reason is required: without it (or blank) the command exits non-zero before touching anything.
  • Fails with a clear message if the email does not exist.
  • Calls IRecoveryCodeService::disableTwoFactor($user, null, null) from feat(mfa): add IRecoveryCodeService::disableTwoFactor #169, which clears two_factor_enabled and two_factor_enforced_at, deletes the recovery codes and revokes the trusted devices in one transaction. The command does not reimplement any of that.
  • Emits the settings_changed audit event with {"reason": "<--reason>", "actor": "console", "operator": "<os user>@<hostname>"} and ip_address 127.0.0.1.
  • Prints a summary: unused recovery codes deleted and active trusted devices revoked (counted before the reset).
  • Group-enforced users stay challenged at the next login, since enforcement is derived from group membership. The output says so explicitly, and tells the operator the user has no codes and must regenerate them from the profile Security section after logging in with the email OTP.

Things to know

  • If the audit event cannot be recorded, the reset is already committed, so the command reports it and exits with a failure instead of hiding the missing trail.
  • "The next password login of a group-enforced admin still lands on the 2FA challenge; a self-enrolled user's does not" is checked through User::shouldRequire2FA() on the reloaded user, which is what the login uses, not through an HTTP login.
  • Together with feat(mfa): add idp:enforce-admin-2fa command #166 this covers the ticket's two commands. Ticket 14 can reuse disableTwoFactor as is.

Test plan

  • tests/unit/Console/Reset2FACommandTest.php: 6 tests (missing and blank reason, unknown user, success with audit metadata, enforced-group warning, audit failure).
  • tests/Reset2FACommandIntegrationTest.php: 3 tests against the DB (no reason changes nothing; self-enrolled user fully cleared with 1 settings_changed row; group-enforced admin still requires 2FA).
  • The four new test files of this stack run together: 25 tests, 135 assertions, 0 failures.
  • Full phpunit suite not run. tests/RecoveryCodeRegenerationTest.php already fails on the base branch (Class "Strategies\MFA\MFAChallengeStrategyFactory" not found), unrelated to this change.

Resets 2FA for one user, for operators with server access: requires
--reason (aborts without it), finds the user by email and calls
IRecoveryCodeService::disableTwoFactor, which clears the enrollment, deletes
the recovery codes and revokes the trusted devices in one transaction.

Emits a settings_changed audit event with {reason, actor: "console",
operator} and prints a summary with the number of codes deleted and devices
revoked. Group-enforced users stay challenged at login because enforcement is
derived from group membership; the output says so explicitly.
@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2d16d703-b41c-4f0f-a4a7-4fa663107480

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@romanetar
romanetar added this pull request to stack #170 October 7, 2026 17:51
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

➡️ https://OpenStackweb.github.io/openstackid/openapi/pr-171/

This page is automatically updated on each push to this PR.

This branch has not been deployed

No deployments
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