Skip to content

[PM-38796] Create link confirmation endpoint#7907

Draft
JimmyVo16 wants to merge 10 commits into
mainfrom
ac/pm-38796/add-link-confirmation-endpoint
Draft

[PM-38796] Create link confirmation endpoint#7907
JimmyVo16 wants to merge 10 commits into
mainfrom
ac/pm-38796/add-link-confirmation-endpoint

Conversation

@JimmyVo16

@JimmyVo16 JimmyVo16 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

@JimmyVo16 JimmyVo16 self-assigned this Jul 1, 2026
@JimmyVo16 JimmyVo16 changed the title [PM-38796] wip [PM-38796] Create link confirmation endpoint Jul 1, 2026
@sonarqubecloud

sonarqubecloud Bot commented Jul 1, 2026

Copy link
Copy Markdown

Comment on lines +194 to +197
catch (Exception ex)
{
logger.LogError(ex, "Failed to create default collection for user confirmed via invite link.");
}
@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.79275% with 39 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.40%. Comparing base (0352b1f) to head (92afcf8).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
...InviteLinks/ConfirmOrganizationInviteLinkErrors.cs 29.54% 31 Missing ⚠️
...nviteLinks/ConfirmOrganizationInviteLinkCommand.cs 95.28% 4 Missing and 1 partial ⚠️
...iteLinks/ConfirmOrganizationInviteLinkValidator.cs 78.57% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7907      +/-   ##
==========================================
+ Coverage   61.34%   61.40%   +0.05%     
==========================================
  Files        2244     2248       +4     
  Lines       98682    98960     +278     
  Branches     8915     8939      +24     
==========================================
+ Hits        60537    60763     +226     
- Misses      36008    36050      +42     
- Partials     2137     2147      +10     

☔ 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.

@JimmyVo16 JimmyVo16 added ai-review Request a Claude code review t:feature Change Type - Feature Development labels Jul 7, 2026
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: REQUEST CHANGES

Reviewed the new invite-link confirmation endpoint: the ConfirmInviteLink controller action, ConfirmOrganizationInviteLinkCommand, its RFC 7807 error mapping, validator changes, DI registration, feature flag, and the unit/integration tests. The command logic and its unit-test coverage look sound. One correctness issue in the integration tests plus previously raised threads remain to address.

Code Review Details
  • ⚠️ : Integration tests POST to /organizations/users/invite-link/link-confirm, but the controller route is /organizations/users/invite-link/confirm; the valid-request test will fail and the not-found test passes for the wrong reason
    • test/Api.IntegrationTest/AdminConsole/Controllers/OrganizationUsersControllerConfirmInviteLinkTests.cs:80

Previously raised threads on this PR still apply (not re-posted): missing audit-log event on confirm, doc comment claiming emergency-access removal the implementation does not perform, leftover TODO in the validator, and the question about the omitted device push-sync.

  • QUESTION: The PR description Objective section is still the empty template placeholder; a one-line summary of the endpoint's purpose would help reviewers.

Comment on lines +173 to +181
/// <summary>
/// Creates the user's default collection when the Organization Data Ownership policy applies. Failures
/// are logged but not surfaced: the user is already confirmed and the collection can be recreated.
/// </summary>
private async Task CreateDefaultCollectionAsync(Organization organization, OrganizationUser organizationUser, string defaultUserCollectionName)
{
try
{
if (!await ShouldCreateDefaultCollectionAsync(organization, organizationUser, defaultUserCollectionName))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ IMPORTANT: Confirming a user into an organization is not recorded in the audit log.

Details and fix

ConfirmAsync moves a user to Confirmed (a security-sensitive membership change) but logs no event. Both comparable flows do:

  • ConfirmOrganizationUserCommand logs EventType.OrganizationUser_Confirmed.
  • AcceptOrganizationInviteLinkCommand logs EventType.OrganizationUser_InviteLinkAccepted.

Without an event, organization admins have no audit trail of link-confirmed memberships, which is relevant to Bitwarden's SOC 2 / HIPAA obligations. Consider injecting IEventService and logging OrganizationUser_Confirmed for organizationUser after the membership is persisted.

Comment on lines +6 to +10
/// Confirms a user into an organization through an invite link. Runs the shared read-only precheck
/// (<see cref="IConfirmOrganizationInviteLinkValidator"/>), creates the membership when the user is not
/// yet a member, confirms it with the supplied organization key, and performs the policy-driven side
/// effects: creating a default collection for Organization Data Ownership, enrolling the user in account
/// recovery when required, and removing the user's emergency access.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ IMPORTANT: Doc comment claims emergency-access removal that the implementation does not perform.

Details and fix

This summary (and the matching remarks in ConfirmOrganizationInviteLinkCommand.cs and ConfirmOrganizationInviteLinkRequest.cs) states the command performs "removing the user's emergency access." ConfirmAsync never calls IDeleteEmergencyAccessCommand — the dependency is not even injected.

The sibling AcceptOrganizationInviteLinkCommand calls deleteEmergencyAccessCommand.DeleteAllByUserIdAsync(user.Id) when the automatic-user-confirmation policy is enabled, and ConfirmOrganizationUserCommand does the same. Since confirmation is the higher-privilege step, this looks like a missing side effect rather than only a stale comment.

Either implement the emergency-access removal (gated on the automatic-user-confirmation policy, mirroring the accept flow) or, if intentionally omitted, remove the claim from all three doc comments so maintainers aren't misled.

{
return singleOrgError;

// TODO: Jimmy reassess this

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎨 SUGGESTED: Remove the // TODO: Jimmy reassess this before merge — it's a personal-name TODO left in production code with no actionable context.

Comment on lines +97 to +113
/// <summary>
/// Confirms an existing membership (a pending email invitation or an accepted membership) by linking
/// it to the user, releasing the org key, and moving it straight to <see cref="OrganizationUserStatusType.Confirmed"/>.
/// Persisting via <c>ReplaceAsync</c> bumps the user's account revision date so their other devices sync.
/// </summary>
private async Task<CommandResult<OrganizationUser>> ConfirmExistingMembershipAsync(
OrganizationUser existingOrganizationUser, User user, string orgUserKey)
{
existingOrganizationUser.Status = OrganizationUserStatusType.Confirmed;
existingOrganizationUser.UserId = user.Id;
existingOrganizationUser.Email = null;
existingOrganizationUser.Key = orgUserKey;

await organizationUserRepository.ReplaceAsync(existingOrganizationUser);

return existingOrganizationUser;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

QUESTION: Is the omission of a device push-sync intentional here?

Details

The other confirm-to-Confirmed flows push an org-keys sync so the user's already-signed-in devices pick up the new org key immediately:

  • ConfirmOrganizationUserCommandPushSyncOrgKeysAsync (plus device-registration cleanup)
  • AutomaticallyConfirmOrganizationUserCommandPushSyncOrganizationKeysAsync

This command releases the org key (Key = orgUserKey) and moves the membership straight to Confirmed, but injects no IPushNotificationService. The doc comment notes the ReplaceAsync revision-date bump handles other-device sync, but that relies on the next poll rather than an immediate push, and CreateConfirmedMembershipAsync (new membership via CreateAsync) has no equivalent note. Was relying solely on the revision-date bump — with no PushSyncOrgKeysAsync — a deliberate choice for this endpoint?


// Act
var response = await joinerClient.PostAsJsonAsync(
"/organizations/users/invite-link/link-confirm", BuildConfirmRequest(code));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ IMPORTANT: Integration tests POST to a route the controller does not expose.

Details and fix

The controller declares the endpoint as:

[HttpPost("/organizations/users/invite-link/confirm")]
public async Task<IResult> ConfirmInviteLink(...)

but all three requests in this file POST to /organizations/users/invite-link/link-confirm (lines 80, 104, 118). There is no link-confirm route anywhere in src, so these requests never reach ConfirmInviteLink.

ConfirmInviteLink_WithValidRequest_ReturnsOkAndConfirmsMembership asserts HttpStatusCode.OK and will fail (the missing route returns 404/405). ConfirmInviteLink_WithUnknownCode_ReturnsNotFound passes for the wrong reason — 404 comes from the missing route, not from InviteLinkNotFound.

Change the three request paths to /organizations/users/invite-link/confirm to match the controller.

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:feature Change Type - Feature Development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant