Skip to content

[PM-27278] add AccountKeysRequestModel to RegisterFinishRequestModel for account encryption v2 support#6798

Open
eligrubb wants to merge 51 commits intomainfrom
km/pm-27278/register-password-based-account-v2
Open

[PM-27278] add AccountKeysRequestModel to RegisterFinishRequestModel for account encryption v2 support#6798
eligrubb wants to merge 51 commits intomainfrom
km/pm-27278/register-password-based-account-v2

Conversation

@eligrubb
Copy link
Member

@eligrubb eligrubb commented Jan 5, 2026

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-27278
Stacks on top of #6855.

📔 Objective

Deprecating V1 User Asymmetric Key information in favor of new V2 User Asymmetric Account Keys structure.

This PR adds support for the new AccountKeys structure while maintaining support for the legacy UserAsymmetricKey-based flow. Validation is updated to check either AccountKeys or UserAsymmetricKeys are updated. Tests include modeling for both scenarios.

📸 Screenshots

⏰ 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

…d more comments and fixed up some long lines.
…tModel.cs

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
…t additions to make sure properties that were once required are still required.
…ted tests and added comments for the future.
…tModel.cs

Co-authored-by: Maciej Zieniuk <167752252+mzieniukbw@users.noreply.github.com>
…d new checks for master password authentication data.
…fled around validation checks to the request model instead of the controller.
…Added comments to clarify how these data types should be used.
…Fixed error in register finish model validation.
Base automatically changed from auth/pm-27084/register-accepts-new-data-types to main January 15, 2026 20:55
@eligrubb eligrubb force-pushed the km/pm-27278/register-password-based-account-v2 branch from 64585b7 to 2a7a753 Compare February 2, 2026 09:54
@eligrubb eligrubb changed the base branch from main to auth/pm-27084/register-accepts-new-data-types-repush February 2, 2026 09:57
@eligrubb eligrubb force-pushed the km/pm-27278/register-password-based-account-v2 branch from 2a7a753 to c026649 Compare February 4, 2026 05:55
@eligrubb eligrubb added the ai-review Request a Claude code review label Feb 4, 2026
@claude
Copy link
Contributor

claude bot commented Feb 4, 2026

Claude finished @eligrubb's task in 2m 13s —— View job


Code Review Summary: PR #6798

PM-27278 add AccountKeysRequestModel to RegisterFinishRequestModel for account encryption v2 support

Overview

This PR introduces V2 account encryption support for user registration. The changes allow the RegisterFinish endpoint to handle both legacy (V1) and new (V2) encryption schemes during the registration process.

Key Changes Reviewed

  • New data models: RegisterFinishData, modifications to UserAccountKeysData, MasterPasswordUnlockData, MasterPasswordAuthenticationData
  • Modified RegisterFinishRequestModel with new properties and comprehensive validation
  • Updated IRegisterUserCommand interface to pass RegisterFinishData instead of string masterPasswordHash
  • New repository method SetRegisterFinishUserData with corresponding SQL stored procedure
  • Flow changes in UserService.CreateUserAsync to handle V2 encryption

Review Result

APPROVED - The implementation is well-structured and maintains backward compatibility.

Positive Observations

  1. Backward Compatibility: The code gracefully handles both V1 and V2 encryption schemes through the IsV2Encryption() check
  2. Validation: Comprehensive input validation in RegisterFinishRequestModel.Validate() with clear error messages
  3. Transaction Safety: The V2 encryption flow properly uses transactions via SetV2AccountCryptographicStateAsync
  4. Security: No sensitive data exposed in logs or error messages; zero-knowledge principles maintained
  5. Established Patterns: Uses existing patterns like UpdateUserData delegate for transactional operations

Test Coverage Note

While codecov reports low coverage, the PR includes unit tests in:

  • RegisterFinishRequestModelTests.cs - validation and data conversion tests
  • AccountsControllerTests.cs - integration tests for the registration flow

Reviewed with Claude Code

@eligrubb eligrubb removed the ai-review Request a Claude code review label Feb 4, 2026
@eligrubb eligrubb marked this pull request as ready for review February 4, 2026 07:10
@eligrubb eligrubb requested review from a team as code owners February 4, 2026 07:10
@eligrubb eligrubb requested review from quexten and rr-bw and removed request for a team February 4, 2026 07:10
Base automatically changed from auth/pm-27084/register-accepts-new-data-types-repush to main February 4, 2026 15:04
@rr-bw rr-bw requested a review from enmande February 4, 2026 17:33
var result = await CreateAsync(user, registerFinishData.MasterPasswordAuthenticationData.MasterPasswordAuthenticationHash);
if (result.Succeeded)
{
var setRegisterFinishUserDataTask = _userRepository.SetRegisterFinishUserData(user.Id, registerFinishData);
Copy link
Contributor

Choose a reason for hiding this comment

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

Question: SetRegisterFinishUserData sets:
{

  • KDF Settings
  • MasterKey Protected Userkey
    } // masterpassword unlock
    and
    {
    public key
    private key
    } // keypair

But SetV2AccountCryptographicState already writes the keypair

PublicKey = accountKeysData.PublicKeyEncryptionKeyPairData.PublicKey,

So essentially we are writing the keypair twice here. Could we remove the key-pair from the registerFinishUserData task / SQL sproc?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

@KdfMemory INT,
@KdfParallelism INT,
@Key VARCHAR(MAX),
@PublicKey VARCHAR(MAX),
Copy link
Contributor

Choose a reason for hiding this comment

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

As per the other comment, we can probably remove this. Maybe it makes sense to just rename to "SetMasterPasswordUnlockData"?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done. I went with SetMasterPasswordUnlockUserData

// Arrange
// V1 model and fields to be removed with
// https://bitwarden.atlassian.net/browse/PM-27326
var legacyModel = new RegisterFinishRequestModel
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit/quesion (don't have to act on it): I wonder whether these would be better served as separate tests, one legacy one not.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm open to either, whichever is seen as more maintainable. I put the testing the legacy & new models in one test because I wanted to match what Auth had already been doing in the file. So if Auth feels strongly one way or the other I'd like to go with that.

// all should be default/unset, with the exception of email and masterPasswordHint
Assert.Equal(email, newResult.Email);
Assert.Equal(masterPasswordHint, newResult.MasterPasswordHint);
Assert.Equal(KdfType.PBKDF2_SHA256, newResult.Kdf);
Copy link
Contributor

Choose a reason for hiding this comment

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

Question: Don't e expect argon2 for v2? Subsequently memory and praallelism should also be set?

Copy link
Member Author

Choose a reason for hiding this comment

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

This test in particular is checking the return of the ToUser function, which I changed for v2 users to only set the email and MasterPasswordHint. All others values in the User entity will be the default value, which for Kdf settings means PBKDF2 for type & iterations, and null for memory & parallelism

var kdfModel = new KdfRequestModel
{
KdfType = KdfType.Argon2id,
Iterations = AuthConstants.ARGON2_ITERATIONS.Default
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing memory / parallelism?

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

var kdfModel = new KdfRequestModel
{
KdfType = KdfType.Argon2id,
Iterations = AuthConstants.ARGON2_ITERATIONS.Default
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing Memory / parallelism

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

var user = model.ToUser();
var kdfModel = new KdfRequestModel
{
KdfType = KdfType.Argon2id,
Copy link
Contributor

Choose a reason for hiding this comment

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

Memory / parallelism?

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

KdfMemory = kdfData.Memory,
KdfParallelism = kdfData.Parallelism,
UserSymmetricKey = masterKeyWrappedUserKey,
UserAsymmetricKeys = new KeysRequestModel
Copy link
Contributor

Choose a reason for hiding this comment

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

Which user is this in the test? A V1 user should use PBKDF2, a V2 User Argon2 but a v2 user should also have the other required values, not just public key / encrypted private key?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch, this should be a v1 user. Changed the values to PBKDF2.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Feb 6, 2026

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.

4 participants