Reactivate deactivated users signing in with 2FA enforced
What does this MR do and why?
Skips the check_two_factor_requirement authorization gate on the sign-in action (POST /users/sign_in), in addition to the sign-out action. One line in app/controllers/sessions_controller.rb:
- skip_before_action :check_two_factor_requirement, only: [:destroy]
+ skip_before_action :check_two_factor_requirement, only: [:create, :destroy]Why
The gate is a post-authentication check, but it ran as a before_action on sign-in itself. It reads current_user, which authenticates via params as a side effect, then redirects users without 2FA to the enrollment page — halting the chain before Devise's create body runs.
Reactivation lives in that body, so a deactivated user never got reactivated, and the enrollment page's active_user_check signed them straight back out. A correct password returned them to the sign-in page with "Your account has been deactivated by your administrator", every time.
Enforcement is unchanged: the redirect happens on the next request instead, and every other controller still runs the gate.
Behaviour change
create's body now runs for 2FA-enforced users who haven't enrolled, so these stop being skipped:
- sign-in audit events and
AuthenticationEventrows - unknown sign-in emails
- pending invitation acceptance
- password-reset token invalidation
user_session_logins_total
In my opinion, these should always have fired - those users were already authenticated, with sign_in_count and current_sign_in_at recorded. Self-managed admins will see new audit events, and some users will get unknown sign-in emails they weren't getting before.
References
- Closes #486718
Screenshots or screen recordings
| Before | After |
|---|---|
![]() |
![]() |
How to set up and validate locally
-
On
master, open the Rails console and create a deactivated user in a group that enforces 2FA:password = 'quiet-harbor-92-Kx' user = User.new( username: 'repro486718', name: 'Repro 486718', email: 'repro486718@example.com', password: password, password_confirmation: password, organization: Organizations::Organization.default_organization ) user.assign_personal_namespace(Organizations::Organization.default_organization) user.skip_confirmation! user.save! group = Group.new( name: 'Repro 486718 Group', path: 'repro486718-group', require_two_factor_authentication: true, two_factor_grace_period: 0, organization: Organizations::Organization.default_organization ) group.save! group.add_developer(user) user.update_columns( require_two_factor_authentication_from_group: true, otp_grace_period_started_at: 1.year.ago ) user.deactivate!Enforcement is on a group rather than instance-wide so it doesn't drag your own admin account into 2FA enrollment. The grace period is zeroed and backdated so there's no "Configure it later" link.
require_two_factor_authentication_from_groupis set directly because the worker that maintains it skips deactivated members. -
In a private window, sign in at
/users/sign_inasrepro486718/quiet-harbor-92-Kx. You land back on the sign-in page with the deactivation message. Try again — same result. -
Confirm the account was never reactivated:
User.find_by_username('repro486718').state # => "deactivated" -
Check out this branch and restart Rails:
git checkout fix/reactivate-deactivated-users-with-2fa-enforced gdk restart rails-web -
Sign in again in a fresh private window. You now land on
/-/profile/two_factor_authwith the enrollment form. -
Confirm the account was reactivated:
User.find_by_username('repro486718').state # => "active" -
Clean up:
User.find_by_username('repro486718').destroy Group.find_by_path('repro486718-group').destroy
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

