Skip to content

Admin Portal - Fix GetTwoFactorProviders wrong exception type handling on Organization and User - #8234

Open
JaredSnider-Bitwarden wants to merge 3 commits into
mainfrom
admin-portal/organizations-2fa-deserialization-error-catch-fix
Open

Admin Portal - Fix GetTwoFactorProviders wrong exception type handling on Organization and User#8234
JaredSnider-Bitwarden wants to merge 3 commits into
mainfrom
admin-portal/organizations-2fa-deserialization-error-catch-fix

Conversation

@JaredSnider-Bitwarden

@JaredSnider-Bitwarden JaredSnider-Bitwarden commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-42460

📔 Objective

Organization.GetTwoFactorProviders and User.GetTwoFactorProviders both catch JsonException intending to return null on malformed JSON (per their doc comments). But Organization.cs and User.cs each import System.Text.Json, so the catch binds to System.Text.Json.JsonException — while JsonHelpers.LegacyDeserialize uses Newtonsoft under the hood and throws Newtonsoft.Json.JsonException (or its JsonReaderException / JsonSerializationException subclasses), an unrelated type. The catch never matched, so any row with an invalid TwoFactorProviders value took down the admin console Organizations view (via Admin/Controllers/OrganizationsController) and would take down the Users view the same way (via TwoFactorIsEnabledQuery fanned out by Admin/Controllers/UsersController).

Observed locally: integration-test rows seeded via AutoFixture left "TwoFactorProviders" + Guid.NewGuid() in the column (AutoFixture's default string generator prefixes with the property name), which is not valid JSON.

Fix: catch Newtonsoft.Json.JsonException (base of both reader and serialization exceptions) on both entities so the methods degrade to null as intended, drop the now-unused System.Text.Json using, and add matching theories on OrganizationTests and UserTests covering pure garbage, the AutoFixture pattern, malformed JSON, and a schema mismatch.

📸 Screenshots

Before:

Before

After:

After

GetTwoFactorProviders catches JsonException intending to return null when
TwoFactorProviders contains invalid JSON (as its doc comment promises).
Organization.cs imports System.Text.Json, so that catch binds to
System.Text.Json.JsonException — but JsonHelpers.LegacyDeserialize uses
Newtonsoft.Json under the hood and throws Newtonsoft.Json.JsonException
(or its JsonReaderException / JsonSerializationException subclasses),
which is an unrelated type. The catch never matched and the exception
bubbled up through the admin console Organizations view any time a row
had a malformed TwoFactorProviders value, taking down the whole page.

Trigger observed locally: integration-test orgs seeded via AutoFixture
had "TwoFactorProviders" + Guid.NewGuid() left in the column (the default
AutoFixture string generator uses the property name as a prefix), which
is not valid JSON.

Fix: catch Newtonsoft.Json.JsonException (base of both reader and
serialization exceptions) so the method degrades to null as intended.
Drop the now-unused System.Text.Json using. Add a theory covering pure
garbage, the AutoFixture pattern, malformed JSON, and a schema mismatch.
@JaredSnider-Bitwarden
JaredSnider-Bitwarden marked this pull request as ready for review August 19, 2026 20:18
@JaredSnider-Bitwarden
JaredSnider-Bitwarden requested a review from a team as a code owner August 19, 2026 20:18
@JaredSnider-Bitwarden JaredSnider-Bitwarden added t:bugfix Change Type - Bugfix ai-review Request a Claude code review labels Aug 19, 2026
@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

The core fix is correct: JsonHelpers.LegacyDeserialize delegates to Newtonsoft.Json.JsonConvert.DeserializeObject, which throws JsonReaderException / JsonSerializationException — both derived from Newtonsoft.Json.JsonException — so the new catch clauses on User and Organization now match, and this aligns both entities with the pre-existing OrganizationUserUserDetails.GetTwoFactorProviders. Neither entity retains any other System.Text.Json reference after the using removals, and every existing caller of GetTwoFactorProviders() already handles null because the empty-column path always returned it. The new ILogger<TwoFactorIsEnabledQuery> parameter resolves through the existing AddScoped registration, no call site constructs the query manually, all its tests use SutProvider, and the warn log records only the user id — never the raw column — so the zero-knowledge and logging rules hold.

Code Review Details
  • ❓ : Unparseable Organization.TwoFactorProviders now degrades org-level 2FA silently, with no equivalent to the user-side warn log
    • src/Core/AdminConsole/Entities/Organization.cs:482

Previously raised and closed by the author: the login-path fail-open on User (providers == null treated as "no providers"). That was the pre-existing contract at the GetEnabledTwoFactorProviders layer, the column is not user-writable, and corrupt rows are now observable via the new log — deferring to that decision.

Comment thread src/Core/AdminConsole/Entities/Organization.cs
@JaredSnider-Bitwarden
JaredSnider-Bitwarden marked this pull request as draft August 19, 2026 20:24
User.cs has the identical defect Organization.cs did: it imports
System.Text.Json, so its catch (JsonException) binds to
System.Text.Json.JsonException — but JsonHelpers.LegacyDeserialize
uses Newtonsoft.Json and throws Newtonsoft.Json.JsonException. The
catch never matched, so a User row with malformed TwoFactorProviders
would bubble up through any caller (e.g. TwoFactorIsEnabledQuery,
fanned out by Admin/Controllers/UsersController), taking down the
admin console Users view the same way orgs did.

Fix mirrors the Organization commit: catch Newtonsoft.Json.JsonException,
drop the now-unused System.Text.Json using, add the same four-case
invalid-JSON theory to UserTests.
@JaredSnider-Bitwarden JaredSnider-Bitwarden changed the title Admin Portal - Fix Organization.GetTwoFactorProviders swallowing wrong exception type Admin Portal - Fix GetTwoFactorProviders swallowing wrong exception type on Organization and User Aug 19, 2026
Comment thread src/Core/Entities/User.cs
@codecov

codecov Bot commented Aug 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 60.00000% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.56%. Comparing base (2e3db8d) to head (b622daf).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
...torAuth/Implementations/TwoFactorIsEnabledQuery.cs 53.84% 5 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8234      +/-   ##
==========================================
+ Coverage   63.29%   68.56%   +5.27%     
==========================================
  Files        2383     2399      +16     
  Lines      103921   103955      +34     
  Branches     9403     9409       +6     
==========================================
+ Hits        65778    71280    +5502     
+ Misses      35899    30325    -5574     
- Partials     2244     2350     +106     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Now that Newtonsoft parse failures degrade to null instead of bubbling
up as a 500, corruption of the TwoFactorProviders column is silent on
the login and admin Users paths. Warn-level log inside
TwoFactorIsEnabledQuery.GetEnabledTwoFactorProviders records the user
id (never the raw column, which carries 2FA secrets and metadata) when
providers is null but the column is non-empty — distinguishing a
parse failure from a normal "no 2FA configured" user.
@JaredSnider-Bitwarden
JaredSnider-Bitwarden marked this pull request as ready for review August 19, 2026 21:02
@JaredSnider-Bitwarden
JaredSnider-Bitwarden requested a review from a team as a code owner August 19, 2026 21:02
@JaredSnider-Bitwarden
JaredSnider-Bitwarden requested review from enmande and ike-kottlowski and removed request for enmande August 19, 2026 21:02
@JaredSnider-Bitwarden JaredSnider-Bitwarden changed the title Admin Portal - Fix GetTwoFactorProviders swallowing wrong exception type on Organization and User Admin Portal - Fix GetTwoFactorProviders wrong exception type handling on Organization and User Aug 19, 2026
Comment thread src/Core/AdminConsole/Entities/Organization.cs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review Request a Claude code review t:bugfix Change Type - Bugfix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants