-
Notifications
You must be signed in to change notification settings - Fork 815
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
PM-9532: pt2. separate vault unlock logic and fail out on error during login. #3609
Conversation
No New Or Fixed Issues Found |
Codecov ReportAttention: Patch coverage is
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. |
app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceImpl.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt
Outdated
Show resolved
Hide resolved
b5ca08e
to
1e31092
Compare
ssoToken = loginResponse.ssoToken, | ||
) | ||
|
||
// If this error was received, it also means any cached two-factor token is invalid. |
There was a problem hiding this comment.
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), |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceImpl.kt
Outdated
Show resolved
Hide resolved
1e31092
to
d8f368c
Compare
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 |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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,
)
}
}
There was a problem hiding this comment.
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) | ||
} |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
🎟️ 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 onmain
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
🦮 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 confirmedissue 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