Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PM-3726] prevent legacy user login #2769

Merged
merged 11 commits into from
Sep 20, 2023
Merged

Conversation

jlf0dev
Copy link
Member

@jlf0dev jlf0dev commented Sep 13, 2023

Type of change

  • Bug fix
  • New feature development
  • Tech debt (refactoring, code cleanup, dependency upgrades, etc)
  • Build/deploy pipeline (DevOps)
  • Other

Objective

Prevent legacy users from logging in and direct them to the web vault for migration.

Code changes

Legacy users are detected early in the AuthService.LoginHelper by checking for the existence of a local hashed password (meaning MP was used to login) and the absence of a IdentityToken.Key. We can then return early before the authentication process and prevent any state from being set.

  • LockPageViewModel.cs: Prevents unlocks for legacy users. Most of these changes are formatting. I split out the SubmitAsync method into UnlockWithPinAsync and UnlockWithMasterPasswordAsync. I also am catching any LegacyUserException errors now in the SubmitAsync method in order to process them.
  • BaseLockPasswordViewController.cs: Duplicate changes as LockPageViewModel
  • LoginPageViewModel.cs: Prevent logins for legacy users. Note: Instead of using the LegacyUserException like on the LockPage, I have added AuthResult.RequiresEncryptionKeyMigration. The clients repo couldn't use an exception in the same way so I decided to keep the models similar.
  • AuthService.cs: If a user is logging in with their MP, we can determine if they are a legacy user and return early.
  • CryptoService.cs: Add ValidateUserKeyAsync and IsLegacyUserAsync for legacy user detection. Throw exceptions from migration methods if a legacy user is detected.
  • VaultTimeoutService.cs: If a legacy user has an auto key set, we need to catch the exception and log them out before they are migrated.

Screenshots

image

Before you submit

  • Please check for formatting errors (dotnet format --verify-no-changes) (required)
  • Please add unit tests where it makes sense to do so (encouraged but not required)
  • If this change requires a documentation update - notify the documentation team
  • If this change has particular deployment requirements - notify the DevOps team

@bitwarden-bot
Copy link

bitwarden-bot commented Sep 13, 2023

Logo
Checkmarx One – Scan Summary & Details9d53fcac-8c0a-4f2f-b63b-17e1a1cc85b7

No New Or Fixed Issues Found

mpbw2
mpbw2 previously approved these changes Sep 13, 2023
@jlf0dev jlf0dev requested a review from mpbw2 September 14, 2023 20:00
mpbw2
mpbw2 previously approved these changes Sep 15, 2023
Copy link
Member

@fedemkr fedemkr left a comment

Choose a reason for hiding this comment

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

Great work!
I see this repeated several times:

if (await _cryptoService.IsLegacyUserAsync(masterKey))
{
    throw new LegacyUserException();
}

If every time checking legacy should throw that exception, couldn't that be just done inside the method like:

public async Task CheckIfUserIsLegacyAsync(MasterKey masterKey = null, string userId = null)
{
    if (await ValidateUserKeyAsync(new UserKey((masterKey ?? await GetMasterKeyAsync(userId)).Key))) // Also here see other comment to check the possibility of null getting the master key
    {
        throw new LegacyUserException();
    }
}

So we avoid checking and throwing it everywhere. Thoughts?

src/App/Pages/Accounts/LockPageViewModel.cs Outdated Show resolved Hide resolved
Comment on lines 366 to 374
var masterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, _email, kdfConfig);
var storedKeyHash = await _cryptoService.GetMasterKeyHashAsync();
var passwordValid = false;
MasterPasswordPolicyOptions enforcedMasterPasswordOptions = null;

if (await _cryptoService.IsLegacyUserAsync(masterKey))
{
throw new LegacyUserException();
}
Copy link
Member

Choose a reason for hiding this comment

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

⛏️ Can the check be moved right after masterKey initialization, because there's no need to await for getting the master key hash if it's a legacy user. Unless it depends on some internal computation on that latter task to be able to say whether it's legacy or not.
🤔 Could the check be done directly in MakeMasterKeyAsync()? Or is it not done that way to prevent potentially breaking other places?

Copy link
Member Author

Choose a reason for hiding this comment

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

We won't have all the pieces yet to determine if they are legacy when simply logging in. I also didn't know if we wanted to run that code every time so left it out for now.

src/App/Pages/Accounts/LockPageViewModel.cs Outdated Show resolved Hide resolved
src/App/Resources/AppResources.resx Outdated Show resolved Hide resolved
Comment on lines 65 to 68
public async Task<bool> IsLegacyUserAsync(MasterKey masterKey = null, string userId = null)
{
return await ValidateUserKeyAsync(new UserKey((masterKey ?? await GetMasterKeyAsync(userId)).Key));
}
Copy link
Member

Choose a reason for hiding this comment

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

⚠️ GetMasterKeyAsync(...) could potentially return null. Is it ok if that happens to throw a null reference exception here (when accessing .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, added a check

src/Core/Services/CryptoService.cs Outdated Show resolved Hide resolved
src/iOS.Core/Controllers/BaseLockPasswordViewController.cs Outdated Show resolved Hide resolved
@jlf0dev
Copy link
Member Author

jlf0dev commented Sep 17, 2023

So we avoid checking and throwing it everywhere. Thoughts?

I actually had to add that check to the login process, where we don't want it to throw. Would definitely clean up the code but I didn't want to use exceptions for the login process.

@jlf0dev jlf0dev requested a review from fedemkr September 17, 2023 22:37
fedemkr
fedemkr previously approved these changes Sep 18, 2023
# Conflicts:
#	src/App/Pages/Accounts/LockPageViewModel.cs
#	src/App/Resources/AppResources.Designer.cs
#	src/iOS.Core/Controllers/BaseLockPasswordViewController.cs
@trmartin4 trmartin4 requested a review from fedemkr September 20, 2023 19:50
@trmartin4 trmartin4 merged commit c4f6ae9 into master Sep 20, 2023
@trmartin4 trmartin4 deleted the auth/pm-3726/migrate-legacy-users branch September 20, 2023 19:56
jlf0dev added a commit that referenced this pull request Sep 21, 2023
* [PM-3726] prevent legacy user login

* [PM-3726] prevent unlock or auto key migration if legacy user

* [PM-3726] add legacy checks to lock page and refactor

* [PM-3726] rethrow exception from pin

* formatting

* [PM-3726] add changes to LockViewController, consolidate logout calls

* formatting

* [PM-3726] pr feedback

* generate resx

* formatting

(cherry picked from commit c4f6ae9)
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.

5 participants