Skip to content

Conversation

@ike-kottlowski
Copy link
Contributor

@ike-kottlowski ike-kottlowski commented Nov 18, 2025

🎟️ Tracking

PM-1632

📔 Objective

We need to return the Organization Identifier so that we can consume it to shuttle the user to their IdP automagically. This will speed up the SSO login flow by reducing the number of clicks it takes the user to authenticate with their Organizations IdP.

If a user is an Owner or an Admin they cannot be shuttled to the IdP since they have different policies controlling them.

If a user is a member of more than one organization then they cannot be shuttled to their IdP since there is no way to know which IdP they wish to authenticate with. There will be no functional change for these users.

📸 Screenshots

SSO Required failure with ssoOrganizationIdentifier in response body

firefox_mwtNomqZV5.mp4

A user tries to authenticate with a password, but it fails because the SSO Authentication Required policy applies. Dev tools show the ssoOrganizationIdentifier is present in the response.

SSO not Required User is allowed to login (Regression smoke test)

firefox_JuewqLLFW2.mp4

A user authenticates with password flow and is allowed to login to their vault.

⏰ Reminders before review

  • Contributor guidelines followed
  • All formatters and local linters executed and passed
  • Written new unit and / or integration tests where applicable
  • Protected functional changes with optionality (feature flags)
  • Used internationalization (i18n) for all UI strings
  • CI builds passed
  • Communicated to DevOps any deployment requirements
  • Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team

🦮 Reviewer guidelines

  • 👍 (:+1:) or similar for great changes
  • 📝 (:memo:) or ℹ️ (:information_source:) for notes or general info
  • ❓ (:question:) for questions
  • 🤔 (:thinking:) or 💭 (:thought_balloon:) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion
  • 🎨 (:art:) for suggestions / improvements
  • ❌ (:x:) or ⚠️ (:warning:) for more significant problems or concerns needing attention
  • 🌱 (:seedling:) or ♻️ (:recycle:) for future improvements or indications of technical debt
  • ⛏ (:pick:) for minor or nitpick changes

@github-actions
Copy link
Contributor

github-actions bot commented Nov 18, 2025

Logo
Checkmarx One – Scan Summary & Details26324219-49c9-409c-b760-05f903802106

Great job! No new security vulnerabilities introduced in this pull request

@ike-kottlowski ike-kottlowski changed the title Auth/pm 1632/redirect on sso required [PM-1632] Redirect on SsoRequired - return SsoOrganizationIdentifier Nov 19, 2025
@codecov
Copy link

codecov bot commented Nov 19, 2025

Codecov Report

❌ Patch coverage is 99.18699% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 53.31%. Comparing base (2b926ef) to head (3d31f7a).
⚠️ Report is 36 commits behind head on main.

Files with missing lines Patch % Lines
...tityServer/Constants/RequestValidationConstants.cs 80.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6597      +/-   ##
==========================================
- Coverage   56.95%   53.31%   -3.64%     
==========================================
  Files        1913     1909       -4     
  Lines       85276    85072     -204     
  Branches     7635     7651      +16     
==========================================
- Hits        48568    45360    -3208     
- Misses      34884    37958    +3074     
+ Partials     1824     1754      -70     

☔ View full report in Codecov by Sentry.
📢 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.

@ike-kottlowski ike-kottlowski marked this pull request as ready for review November 20, 2025 16:02
@ike-kottlowski ike-kottlowski requested a review from a team as a code owner November 20, 2025 16:02
@ike-kottlowski ike-kottlowski requested a review from rr-bw November 20, 2025 16:02
@claude
Copy link
Contributor

claude bot commented Nov 20, 2025

Claude finished @ike-kottlowski's task —— View job


Code Review Complete

Task List:

  • Get PR diff and understand scope of changes
  • Review core implementation files
  • Review test coverage and quality
  • Check for security implications (zero-knowledge, encryption)
  • Verify alignment with architectural patterns (ADRs)
  • Review previous feedback resolution
  • Post comprehensive review

Summary

This PR implements functionality to return the SSO organization identifier when SSO authentication is required, enabling automatic client-side redirection to the correct IdP. The implementation is well-structured with proper separation of concerns, comprehensive test coverage (99.18%), and appropriate feature flag protection.

Previous Review Status: Patrick's non-blocking comments have been addressed. The author chose to structure the legacy code removal intentionally for easier cleanup during feature flag removal, which is a reasonable approach.


Findings

Finding 1: Property naming inconsistency - FeatureService_featureService

💭 In BaseRequestValidator.cs:47 and BaseRequestValidator.cs:81, the property FeatureService was changed from a protected property to a private field _featureService. However, the property is still declared on line 47, suggesting incomplete refactoring. This creates inconsistency.

Context
// Line 47 - Still declared as property
protected IFeatureService _featureService { get; }

// But used as private field throughout
_featureService.IsEnabled(FeatureFlagKeys.RecoveryCodeSupportForSsoRequiredUsers)

Recommendation: Either keep it as protected IFeatureService FeatureService { get; } (property) or change to private readonly IFeatureService _featureService; (field). The { get; } syntax indicates a property, not a field.

Finding 2: Organization identifier exposure requires security validation

💭 The new functionality exposes organization identifiers in authentication error responses (SsoRequestValidator.cs:106-123). While this appears intentional for UX improvement, it's worth confirming this doesn't leak sensitive organizational information to unauthorized parties.

Analysis

The identifier is only returned when:

  1. User is already confirmed member of exactly one organization
  2. SSO authentication failed (user already proved partial authentication)

This seems reasonable, but confirm that organization identifiers are not considered sensitive. The logic correctly returns null for:

  • Users in multiple organizations (no way to determine intent)
  • Users in zero confirmed organizations
  • Admins/Owners (different policy controls)

Finding 3: Duplicate SSO validation logic during feature flag transition

♻️ Both BaseRequestValidator.cs:125-148 and BaseRequestValidator.cs:375-418 contain similar feature flag conditional logic, creating duplication. This is technical debt for the feature flag cleanup (PM-28281).

Context

The dual validation paths exist in:

  1. Legacy flow (lines 112-248) - when RecoveryCodeSupportForSsoRequiredUsers is disabled
  2. New validator pattern (lines 267-298) - when the feature flag is enabled

Both paths have nested feature flags for RedirectOnSsoRequired, creating complexity. This is acknowledged in TODO comments but worth tracking.

Recommendation: Ensure PM-28281 cleanup is prioritized after feature flag stabilization to remove this duplication.

Finding 4: Missing null check for organization in query

⚠️ In UserSsoOrganizationIdentifierQuery.cs:29-34, there's a null check for the organization, but it's after calling GetByIdAsync. Consider if this could throw or return unexpected results.

Code
var organization = await _organizationRepository.GetByIdAsync(confirmedOrgUser.OrganizationId);

if (organization == null)
{
    return null;
}

This is likely fine if GetByIdAsync returns null for non-existent IDs. Just confirming this is the expected repository behavior rather than throwing an exception.

Finding 5: Incomplete inline documentation

🎨 RequestValidationConstants.cs:15 uses This Key (capitalized) which is inconsistent with typical XML comment style.

Suggestion
// Current
/// This Key is used when a user is in a single organization...

// Suggested
/// This key is used when a user is in a single organization...

Minor style nitpick for consistency.


Good Practices Observed

  • Comprehensive test coverage with multiple scenarios (469 lines in SsoRequestValidatorTests.cs)
  • Proper dependency injection pattern with ISsoRequestValidator interface
  • Feature flag protection for gradual rollout
  • Clear separation of concerns with dedicated SsoRequestValidator class
  • Appropriate use of constants to avoid magic strings
  • Well-documented XML comments on public interfaces

Security Assessment

Zero-knowledge principles: Not compromised - only organization identifiers (non-sensitive) are exposed, not vault data
Authentication flow: Properly validates SSO requirements before allowing authentication
Information disclosure: Limited to users who are already confirmed members - minimal risk
Feature flag protection: Changes are properly guarded for safe rollout


Action Items

  1. Fix property/field naming inconsistency (FeatureService vs _featureService) - see Finding 1
  2. Confirm organization identifier exposure is acceptable from security perspective - see Finding 2
  3. Track feature flag cleanup (PM-28281) to remove duplicate validation logic - see Finding 3
  4. Verify repository null behavior for GetByIdAsync - see Finding 4
  5. Consider fixing documentation style in RequestValidationConstants.cs - see Finding 5 (optional)

@rr-bw rr-bw requested review from Patrick-Pimentel-Bitwarden and removed request for rr-bw November 20, 2025 20:00
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Non blocking comments! Thinks look really good. Thank you for all the tests!

validatorContext.SsoRequired = await RequireSsoLoginAsync(user, request.GrantType);
if (validatorContext.SsoRequired)
// TODO: Clean up Feature Flag: Remove this if block: PM-28281
if (!_featureService.IsEnabled(FeatureFlagKeys.RedirectOnSsoRequired))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⛏️ I feel like we normally check the positive first so that the new work sits above the old, small nit pick.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do but I wanted to make sure the legacy was removed with the logic. This way we can remove the whole without changing logic.

It felt like less mental load over all to be able to just remove the code block rather than figure out what all needed to come out when the feature flag is removed.

validatorContext.SsoRequired = await RequireSsoLoginAsync(validatorContext.User, request.GrantType);
if (!validatorContext.SsoRequired)
// TODO: Clean up Feature Flag: Remove this if block: PM-28281
if (!_featureService.IsEnabled(FeatureFlagKeys.RedirectOnSsoRequired))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⛏️ I feel like we normally check the positive first so that the new work sits above the old, small nit pick.

Comment on lines 146 to 151
// context.GrantResult.Error = requestContext.ValidationErrorResult.Error;
// context.GrantResult.IsError = requestContext.ValidationErrorResult.IsError;
// context.GrantResult.ErrorDescription = requestContext.ValidationErrorResult.ErrorDescription;
// context.GrantResult.CustomResponse = requestContext.CustomResponse;
context.GrantResult.IsError = true;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed
3d31f7a

Removed commented-out code for clarity.
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.

3 participants