[PM-28519] Remove Emergency Access Contacts for AutoConfirm Org Flows#7123
[PM-28519] Remove Emergency Access Contacts for AutoConfirm Org Flows#7123sven-bitwarden merged 6 commits intomainfrom
Conversation
… or when accepted/restored
|
Great job! No new security vulnerabilities introduced in this pull request |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #7123 +/- ##
==========================================
- Coverage 60.65% 56.61% -4.04%
==========================================
Files 2016 2016
Lines 88416 88458 +42
Branches 7882 7888 +6
==========================================
- Hits 53625 50083 -3542
- Misses 32887 36554 +3667
+ Partials 1904 1821 -83 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Claude finished @sven-bitwarden's task in 4m 46s —— View job Review SummaryPR: PM-28519 Remove Emergency Access Contacts for AutoConfirm Org Flows OverviewThis PR removes emergency access (EA) contacts from users in organizations with the Automatic User Confirmation policy. EA records are deleted in four flows: when the policy is first enabled (bulk deletion for all org users), and when individual users are accepted, confirmed, or restored. The implementation also refactors Previous Review ItemsAll previously flagged items have been addressed:
Findings
What Looks Good
|
...rganizationFeatures/Policies/PolicyValidators/AutomaticUserConfirmationPolicyEventHandler.cs
Show resolved
Hide resolved
...rganizationFeatures/Policies/PolicyValidators/AutomaticUserConfirmationPolicyEventHandler.cs
Show resolved
Hide resolved
...sole/OrganizationFeatures/OrganizationUsers/RestoreUser/v1/RestoreOrganizationUserCommand.cs
Show resolved
Hide resolved
src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/AcceptOrgUserCommand.cs
Show resolved
Hide resolved
| await deleteEmergencyAccessCommand.DeleteAllByUserIdsAsync(orgUserIds); | ||
| } | ||
|
|
||
| public Task OnSaveSideEffectsAsync(PolicyUpdate policyUpdate, Policy? currentPolicy) => Task.CompletedTask; |
There was a problem hiding this comment.
Just want to preface this by saying that we need documentation around this, and we already have a ticket for it. I’ll move it higher up on my priority list.
The review: You're adding ExecutePreUpsertSideEffectAsync, so you need to have OnSaveSideEffectsAsync call ExecutePreUpsertSideEffectAsync so that this handler gets triggered for both SavePolicyCommand and VNextSavePolicyCommand. Here’s an example.
There was a problem hiding this comment.
makes sense/thanks for the example - updated.
JimmyVo16
left a comment
There was a problem hiding this comment.
Everything looks good. Just one small change request to ensure the handler works for both SavePolicyCommand and VNextSavePolicyCommand.
|
|
||
| if (policyRequirement.IsEnabled(orgUser.OrganizationId)) | ||
| { | ||
| await _deleteEmergencyAccessCommand.DeleteAllByUserIdAsync(user.Id); |
There was a problem hiding this comment.
In AcceptOrgUserAsync, HandleAutomaticUserConfirmationPolicyAsync runs at line 178 -- before ValidateTwoFactorAuthenticationPolicyAsync at line 200. If the AutoConfirm policy is enabled and the user passes its compliance check, their emergency access contacts are deleted here at line 279. But if ValidateTwoFactorAuthenticationPolicyAsync subsequently throws (user lacks 2FA, org requires it), the accept operation fails while the EA records are already permanently deleted.
This does not affect the Confirm or Restore flows because those check 2FA before the AutoConfirm block.
Scenario:
- Org has both AutoConfirm and TwoFactor policies enabled
- User without 2FA accepts invite
- AutoConfirm compliance passes (no other orgs, not a provider) -- EA deleted
- TwoFactor check fails --
BadRequestExceptionthrown - User cannot accept, but EA contacts are gone
Suggested fix: Move the EA deletion out of HandleAutomaticUserConfirmationPolicyAsync and into AcceptOrgUserAsync after all validation passes (e.g., just before line 202 where status is updated). The validation-only check can remain where it is. For example:
if (_featureService.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers))
{
await HandleAutomaticUserConfirmationPolicyAsync(orgUser, allOrgUsers, user);
}
// ... SingleOrg checks ...
// ... TwoFactor check ...
// Delete EA only after all validation has passed
if (_featureService.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers))
{
var policyRequirement = await _policyRequirementQuery
.GetAsync<AutomaticUserConfirmationPolicyRequirement>(user.Id);
if (policyRequirement.IsEnabled(orgUser.OrganizationId))
{
await _deleteEmergencyAccessCommand.DeleteAllByUserIdAsync(user.Id);
}
}This would require removing the deletion from HandleAutomaticUserConfirmationPolicyAsync and having that method only perform validation. This matches the approach already taken in the Confirm and Restore flows where non-recoverable checks complete before the EA deletion runs.
|
…#7123) * Remove emergency access from all organization users on policy enable, or when accepted/restored * Use correct policy save system * Add additional tests * Implement both PreUpsert and OnSave side effects




🎟️ Tracking
PM-28519
📔 Objective
To prevent unintended access of org keys from users' emergency access contacts, we are eliminating all these contacts from accepted/confirmed users in the organization as they go through user flows, and when the policy is enabled.
Technical Implementation
We already have extensive policy validation setup through PolicyRequirements, but the entire process is self-contained (between loading && validating data). We needed an extension, at the time a policy is enforced (User is being accepted/confirmed/restored), to know that a policy is enabled and validated.
This was accomplished by exposing an overload to automatic confirmation policy validation that takes a PolicyRequirement, which avoids duplicitous data loading. We can use that requirement to both validate and check enabled status.
📸 Screenshots
EA Removed On Policy Save
Screen.Recording.2026-03-02.at.3.46.29.PM.mov
EA Removed On Invite Accept
Screen.Recording.2026-03-02.at.3.54.32.PM.mov
EA Removed On Confirm User
Note
When confirmed, users do not have access to the EA page anymore, so I removed the user from the auto-confirm org before demonstrating their EA contacts were removed.
Screen.Recording.2026-03-02.at.3.56.23.PM.mov
EA Removed on Restore Accepted User
Screen.Recording.2026-03-02.at.4.06.46.PM.mov