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-9532: pt2. separate vault unlock logic and fail out on error during login. #3609

Merged

Conversation

dseverns-livefront
Copy link
Collaborator

@dseverns-livefront dseverns-livefront commented Jul 23, 2024

🎟️ Tracking

PM-9532

📔 Objective

If a user is able to authenticate successfully with the server but there is an issue in the SDK when unlocking the vault (or related SDK operation like initializing crypto) they will be in a sort of limbo state where they will immediately be take to the unlock screen but unable to proceed. To prevent this and match iOS behavior we want to propagate any error from the SDK that occurs in the vault unlock process to the LoginResult and prevent a "successful" login, if they are unable to access the vault due to the issue stated above.

NOTE

The repro step which originally highlighted this issue has been fixed in the SDK according to the info on the ticket. To recreate you can force the result of VaultSdkSourceImpl.initializeCrypto to return a failure and see the difference on main vs this branch. Also testing to make sure no regression is added in the separation of some of the vault unlock logic from the storage of keys.

⏰ Reminders before review

  • Contributor guidelines followed
  • All formatters and local linters executed and passed
  • Written new unit and / or integration tests where applicable
  • Used internationalization (i18n) for all UI strings
  • CI builds passed
  • Communicated to DevOps any deployment requirements
  • Updated any necessary documentation 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

@dseverns-livefront dseverns-livefront changed the title PM-9532/pt2-separate-vault-unlock-for-result-on-login PM-9532: pt2. separate vault unlock logic and fail out on error during login. Jul 23, 2024
Copy link
Contributor

github-actions bot commented Jul 23, 2024

Logo
Checkmarx One – Scan Summary & Detailsdf21295c-b439-4523-a984-44e9dd289795

No New Or Fixed Issues Found

Copy link

codecov bot commented Jul 23, 2024

Codecov Report

Attention: Patch coverage is 88.77551% with 11 lines in your changes missing coverage. Please review.

Project coverage is 87.83%. Comparing base (bbe50ae) to head (7e72776).
Report is 17 commits behind head on main.

Files Patch % Lines
...twarden/data/auth/repository/AuthRepositoryImpl.kt 87.91% 1 Missing and 10 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3609      +/-   ##
==========================================
+ Coverage   87.81%   87.83%   +0.02%     
==========================================
  Files         368      368              
  Lines       30469    30495      +26     
  Branches     4552     4561       +9     
==========================================
+ Hits        26755    26785      +30     
+ Misses       2128     2119       -9     
- Partials     1586     1591       +5     

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

@dseverns-livefront dseverns-livefront force-pushed the PM-9532/vault-error-not-propagated-on-login branch 2 times, most recently from b5ca08e to 1e31092 Compare July 25, 2024 21:22
ssoToken = loginResponse.ssoToken,
)

// If this error was received, it also means any cached two-factor token is invalid.
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

authRequestKey = loginResponse.key,
)
}
?: AuthRequestMethod.UserKey(protectedUserKey = model.asymmetricalKey),
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we get a test for this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done!

@dseverns-livefront dseverns-livefront force-pushed the PM-9532/vault-error-not-propagated-on-login branch from 1e31092 to d8f368c Compare August 5, 2024 12:44
val userId = userStateJson.activeUserId
val deviceKey = authDiskSource.getDeviceKey(userId = userId)
if (deviceKey == null) {
// A null device key means this device is not trusted.
val pendingRequest = authDiskSource.getPendingAuthRequest(userId = userId) ?: return
val pendingRequest =
authDiskSource.getPendingAuthRequest(userId = userId) ?: return null
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we chain this:

            val pendingRequest = authDiskSource
                .getPendingAuthRequest(userId = userId)
                ?: return null

onNotYouButtonClick = {},
),
)
BitwardenTheme {
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

) {
// if possible attempt to unlock the vault with trusted device data
if ((deviceData != null) ||
(loginResponse.userDecryptionOptions?.trustedDeviceUserDecryptionOptions != null)
Copy link
Collaborator

@david-livefront david-livefront Aug 5, 2024

Choose a reason for hiding this comment

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

Can we drop the extra parenthesis?

): VaultUnlockResult? {
// Attempt to unlock the vault with password if possible.
return password?.let {
if (loginResponse.privateKey != null && loginResponse.key != null) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could we replace this with some early returns:

    ): VaultUnlockResult? {
        // Attempt to unlock the vault with password if possible.
        val masterPassword = password ?: return null
        val privateKey = loginResponse.privateKey ?: return null
        val key = loginResponse.key ?: return null
        return vaultRepository.unlockVault(
            userId = userId,
            email = userStateJson.activeAccount.profile.email,
            kdf = userStateJson.activeAccount.profile.toSdkParams(),
            userKey = key,
            privateKey = privateKey,
            masterPassword = masterPassword,
            // We can separately unlock vault for organization data after
            // receiving the sync response if this data is currently absent.
            organizationKeys = null,
        )
    }

deviceData: DeviceDataModel?,
): VaultUnlockResult? {

var vaultUnlockResult: VaultUnlockResult? = null
Copy link
Collaborator

@david-livefront david-livefront Aug 5, 2024

Choose a reason for hiding this comment

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

Do we need this?

Could we just do this:

    ): VaultUnlockResult? {
        // Attempt to unlock the vault with auth request if possible.
        // These values will only be null during the Just-in-Time provisioning flow.
        if (loginResponse.privateKey != null && loginResponse.key != null) {
            deviceData?.let { model ->
                return vaultRepository.unlockVault(
                    userId = userId,

}
}

if (vaultUnlockResult == null) {
Copy link
Collaborator

@david-livefront david-livefront Aug 5, 2024

Choose a reason for hiding this comment

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

If we return early above, we know that we should run the code inside this if if we make it this far.

So this should work, right?

        // Handle the Trusted Device Encryption flow
        return loginResponse
            .userDecryptionOptions
            ?.trustedDeviceUserDecryptionOptions
            ?.let { options ->
                loginResponse.privateKey?.let { privateKey ->
                    unlockVaultWithTrustedDeviceUserDecryptionOptionsAndStoreKeys(
                        options = options,
                        userStateJson = userStateJson,
                        privateKey = privateKey,
                    )
                }
            }

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

brilliant!

val result = block.invoke()
(result as? VaultUnlockError)?.let {
onVaultUnlockError.invoke(it)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can this be simplified?

(block() as? VaultUnlockError)?.also(onVaultUnlockError)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done!

// Attempt to unlock the vault with password if possible.
val masterPassword = password ?: return null
val privateKey = loginResponse.privateKey ?: return null
val key = loginResponse.key ?: return null
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍


// Handle the Trusted Device Encryption flow
return loginResponse
.userDecryptionOptions
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this formatted correctly?

I feels over indented here

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

weird I definitely cmd+shift+l on this several times, but yeah this a big ol \t\t

password: String?,
): VaultUnlockResult? {
// Attempt to unlock the vault with password if possible.
// Attempt to unlock the vault with password if possible.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Double comment

/**
* A helper method to handle the [TrustedDeviceUserDecryptionOptionsJson] specific to TDE.
* also store the necessary keys when appropriate.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we get the correct punctuation here:

     * A helper method to handle the [TrustedDeviceUserDecryptionOptionsJson] specific to TDE
     * and store the necessary keys when appropriate.

@dseverns-livefront dseverns-livefront merged commit 18cd66a into main Aug 6, 2024
9 checks passed
@dseverns-livefront dseverns-livefront deleted the PM-9532/vault-error-not-propagated-on-login branch August 6, 2024 18:42
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