diff --git a/app/src/main/java/com/x8bit/bitwarden/MainViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/MainViewModel.kt index 36855feb2c5..e8516ef3558 100644 --- a/app/src/main/java/com/x8bit/bitwarden/MainViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/MainViewModel.kt @@ -13,7 +13,7 @@ import com.x8bit.bitwarden.data.auth.util.getPasswordlessRequestDataIntentOrNull import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilitySelectionManager import com.x8bit.bitwarden.data.autofill.fido2.manager.Fido2CredentialManager import com.x8bit.bitwarden.data.autofill.fido2.util.getFido2AssertionRequestOrNull -import com.x8bit.bitwarden.data.autofill.fido2.util.getFido2CredentialRequestOrNull +import com.x8bit.bitwarden.data.autofill.fido2.util.getFido2CreateCredentialRequestOrNull import com.x8bit.bitwarden.data.autofill.fido2.util.getFido2GetCredentialsRequestOrNull import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManager import com.x8bit.bitwarden.data.autofill.util.getAutofillSaveItemOrNull @@ -257,7 +257,7 @@ class MainViewModel @Inject constructor( val hasGeneratorShortcut = intent.isPasswordGeneratorShortcut val hasVaultShortcut = intent.isMyVaultShortcut val hasAccountSecurityShortcut = intent.isAccountSecurityShortcut - val fido2CredentialRequestData = intent.getFido2CredentialRequestOrNull() + val fido2CreateCredentialRequestData = intent.getFido2CreateCredentialRequestOrNull() val completeRegistrationData = intent.getCompleteRegistrationDataIntentOrNull() val fido2CredentialAssertionRequest = intent.getFido2AssertionRequestOrNull() val fido2GetCredentialsRequest = intent.getFido2GetCredentialsRequestOrNull() @@ -318,25 +318,30 @@ class MainViewModel @Inject constructor( ) } - fido2CredentialRequestData != null -> { + fido2CreateCredentialRequestData != null -> { // Set the user's verification status when a new FIDO 2 request is received to force // explicit verification if the user's vault is unlocked when the request is // received. - fido2CredentialManager.isUserVerified = false + fido2CredentialManager.isUserVerified = + fido2CreateCredentialRequestData.isUserVerified + ?: fido2CredentialManager.isUserVerified specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( - fido2CreateCredentialRequest = fido2CredentialRequestData, + fido2CreateCredentialRequest = fido2CreateCredentialRequestData, ) // Switch accounts if the selected user is not the active user. if (authRepository.activeUserId != null && - authRepository.activeUserId != fido2CredentialRequestData.userId + authRepository.activeUserId != fido2CreateCredentialRequestData.userId ) { - authRepository.switchAccount(fido2CredentialRequestData.userId) + authRepository.switchAccount(fido2CreateCredentialRequestData.userId) } } fido2CredentialAssertionRequest != null -> { + fido2CredentialManager.isUserVerified = + fido2CredentialAssertionRequest.isUserVerified + ?: false specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Assertion( fido2AssertionRequest = fido2CredentialAssertionRequest, diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/di/Fido2ProviderModule.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/di/Fido2ProviderModule.kt index c6b52ce30cb..aa146911316 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/di/Fido2ProviderModule.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/di/Fido2ProviderModule.kt @@ -13,6 +13,8 @@ import com.x8bit.bitwarden.data.autofill.fido2.manager.Fido2OriginManagerImpl import com.x8bit.bitwarden.data.autofill.fido2.processor.Fido2ProviderProcessor import com.x8bit.bitwarden.data.autofill.fido2.processor.Fido2ProviderProcessorImpl import com.x8bit.bitwarden.data.platform.manager.AssetManager +import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager +import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource import com.x8bit.bitwarden.data.vault.repository.VaultRepository @@ -44,6 +46,8 @@ object Fido2ProviderModule { fido2CredentialManager: Fido2CredentialManager, dispatcherManager: DispatcherManager, intentManager: IntentManager, + biometricsEncryptionManager: BiometricsEncryptionManager, + featureFlagManager: FeatureFlagManager, clock: Clock, ): Fido2ProviderProcessor = Fido2ProviderProcessorImpl( @@ -54,6 +58,8 @@ object Fido2ProviderModule { fido2CredentialManager, intentManager, clock, + biometricsEncryptionManager, + featureFlagManager, dispatcherManager, ) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/model/Fido2CreateCredentialRequest.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/model/Fido2CreateCredentialRequest.kt index 05f98f756eb..14586c3b887 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/model/Fido2CreateCredentialRequest.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/model/Fido2CreateCredentialRequest.kt @@ -20,6 +20,7 @@ data class Fido2CreateCredentialRequest( val packageName: String, val signingInfo: SigningInfo, val origin: String?, + val isUserVerified: Boolean?, ) : Parcelable { val callingAppInfo: CallingAppInfo get() = CallingAppInfo( diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/model/Fido2CredentialAssertionRequest.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/model/Fido2CredentialAssertionRequest.kt index 3c0e840a7ed..1f9b8a5d1f5 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/model/Fido2CredentialAssertionRequest.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/model/Fido2CredentialAssertionRequest.kt @@ -18,6 +18,7 @@ data class Fido2CredentialAssertionRequest( val packageName: String, val signingInfo: SigningInfo, val origin: String?, + val isUserVerified: Boolean?, ) : Parcelable { val callingAppInfo: CallingAppInfo get() = CallingAppInfo(packageName, signingInfo, origin) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/processor/Fido2ProviderProcessorImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/processor/Fido2ProviderProcessorImpl.kt index 531f1d67f8c..4ab3657e6e0 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/processor/Fido2ProviderProcessorImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/processor/Fido2ProviderProcessorImpl.kt @@ -6,6 +6,8 @@ import android.os.Build import android.os.CancellationSignal import android.os.OutcomeReceiver import androidx.annotation.RequiresApi +import androidx.biometric.BiometricManager +import androidx.biometric.BiometricPrompt import androidx.credentials.exceptions.ClearCredentialException import androidx.credentials.exceptions.ClearCredentialUnsupportedException import androidx.credentials.exceptions.CreateCredentialCancellationException @@ -22,6 +24,7 @@ import androidx.credentials.provider.BeginCreatePublicKeyCredentialRequest import androidx.credentials.provider.BeginGetCredentialRequest import androidx.credentials.provider.BeginGetCredentialResponse import androidx.credentials.provider.BeginGetPublicKeyCredentialOption +import androidx.credentials.provider.BiometricPromptData import androidx.credentials.provider.CreateEntry import androidx.credentials.provider.CredentialEntry import androidx.credentials.provider.ProviderClearCredentialStateRequest @@ -34,9 +37,13 @@ import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.autofill.fido2.manager.Fido2CredentialManager import com.x8bit.bitwarden.data.autofill.util.isActiveWithFido2Credentials +import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager +import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager +import com.x8bit.bitwarden.data.platform.manager.model.FlagKey import com.x8bit.bitwarden.data.platform.repository.model.DataState import com.x8bit.bitwarden.data.platform.repository.util.takeUntilLoaded +import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.model.DecryptFido2CredentialAutofillViewResult import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager @@ -45,6 +52,7 @@ import kotlinx.coroutines.flow.fold import kotlinx.coroutines.launch import java.time.Clock import java.util.concurrent.atomic.AtomicInteger +import javax.crypto.Cipher private const val CREATE_PASSKEY_INTENT = "com.x8bit.bitwarden.fido2.ACTION_CREATE_PASSKEY" const val GET_PASSKEY_INTENT = "com.x8bit.bitwarden.fido2.ACTION_GET_PASSKEY" @@ -54,7 +62,7 @@ const val UNLOCK_ACCOUNT_INTENT = "com.x8bit.bitwarden.fido2.ACTION_UNLOCK_ACCOU * The default implementation of [Fido2ProviderProcessor]. Its purpose is to handle FIDO2 related * processing. */ -@Suppress("LongParameterList") +@Suppress("LongParameterList", "TooManyFunctions") @RequiresApi(Build.VERSION_CODES.S) class Fido2ProviderProcessorImpl( private val context: Context, @@ -64,6 +72,8 @@ class Fido2ProviderProcessorImpl( private val fido2CredentialManager: Fido2CredentialManager, private val intentManager: IntentManager, private val clock: Clock, + private val biometricsEncryptionManager: BiometricsEncryptionManager, + private val featureFlagManager: FeatureFlagManager, dispatcherManager: DispatcherManager, ) : Fido2ProviderProcessor { @@ -127,7 +137,7 @@ class Fido2ProviderProcessorImpl( private fun UserState.Account.toCreateEntry(isActive: Boolean): CreateEntry { val accountName = name ?: email - return CreateEntry + val entryBuilder = CreateEntry .Builder( accountName = accountName, pendingIntent = intentManager.createFido2CreationPendingIntent( @@ -145,7 +155,20 @@ class Fido2ProviderProcessorImpl( // Set the last used time to "now" so the active account is the default option in the // system prompt. .setLastUsedTime(if (isActive) clock.instant() else null) - .build() + .setAutoSelectAllowed(true) + + return if ( + !isVaultUnlocked || + featureFlagManager.getFeatureFlag(FlagKey.SingleTapPasskeyCreation) == false + ) { + entryBuilder.build() + } else { + entryBuilder + .setBiometricPromptDataIfSupported( + cipher = biometricsEncryptionManager.getOrCreateCipher(userId), + ) + .build() + } } override fun processGetCredentialRequest( @@ -258,32 +281,81 @@ class Fido2ProviderProcessorImpl( private fun List.toCredentialEntries( userId: String, option: BeginGetPublicKeyCredentialOption, - ): List = - this + ): List { + return this .map { - PublicKeyCredentialEntry + val publicKeyEntryBuilder = PublicKeyCredentialEntry .Builder( context = context, username = it.userNameForUi ?: context.getString(R.string.no_username), - pendingIntent = intentManager - .createFido2GetCredentialPendingIntent( - action = GET_PASSKEY_INTENT, - userId = userId, - credentialId = it.credentialId.toString(), - cipherId = it.cipherId, - requestCode = requestCode.getAndIncrement(), - ), + pendingIntent = intentManager.createFido2GetCredentialPendingIntent( + action = GET_PASSKEY_INTENT, + userId = userId, + credentialId = it.credentialId.toString(), + cipherId = it.cipherId, + requestCode = requestCode.getAndIncrement(), + ), beginGetPublicKeyCredentialOption = option, ) .setIcon( - Icon - .createWithResource( - context, - R.drawable.ic_bw_passkey, - ), + Icon.createWithResource( + context, + R.drawable.ic_bw_passkey, + ), + ) + + if (!featureFlagManager.getFeatureFlag(FlagKey.SingleTapPasskeyAuthentication)) { + publicKeyEntryBuilder.build() + } else { + publicKeyEntryBuilder + .setBiometricPromptDataIfSupported( + cipher = biometricsEncryptionManager.getOrCreateCipher(userId), + ) + .build() + } + } + } + + private fun PublicKeyCredentialEntry.Builder.setBiometricPromptDataIfSupported( + cipher: Cipher?, + ): PublicKeyCredentialEntry.Builder { + if (isBuildVersionBelow(Build.VERSION_CODES.VANILLA_ICE_CREAM)) return this + return BiometricPromptData.Builder().buildPromptDataOrNull(cipher) + ?.let { setBiometricPromptData(it) } + ?: this + } + + private fun CreateEntry.Builder.setBiometricPromptDataIfSupported( + cipher: Cipher?, + ): CreateEntry.Builder { + if (isBuildVersionBelow(Build.VERSION_CODES.VANILLA_ICE_CREAM)) return this + return BiometricPromptData.Builder().buildPromptDataOrNull(cipher) + ?.let { setBiometricPromptData(it) } + ?: this + } + + @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) + private fun BiometricPromptData.Builder.buildPromptDataOrNull( + cipher: Cipher?, + ): BiometricPromptData? { + val promptBuilder = BiometricPromptData.Builder() + when { + cipher == null -> { + promptBuilder.setAllowedAuthenticators( + BiometricManager.Authenticators.BIOMETRIC_WEAK, + ) + } + + else -> { + promptBuilder + .setAllowedAuthenticators( + BiometricManager.Authenticators.BIOMETRIC_STRONG, ) - .build() + .setCryptoObject(BiometricPrompt.CryptoObject(cipher)) } + } + return promptBuilder.build() + } override fun processClearCredentialStateRequest( request: ProviderClearCredentialStateRequest, diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/util/Fido2IntentUtils.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/util/Fido2IntentUtils.kt index 459ad49eb98..3549e46e605 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/util/Fido2IntentUtils.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/util/Fido2IntentUtils.kt @@ -18,7 +18,7 @@ import com.x8bit.bitwarden.ui.platform.manager.intent.EXTRA_KEY_USER_ID * Checks if this [Intent] contains a [Fido2CreateCredentialRequest] related to an ongoing FIDO 2 * credential creation process. */ -fun Intent.getFido2CredentialRequestOrNull(): Fido2CreateCredentialRequest? { +fun Intent.getFido2CreateCredentialRequestOrNull(): Fido2CreateCredentialRequest? { if (isBuildVersionBelow(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)) return null val systemRequest = PendingIntentHandler @@ -39,6 +39,7 @@ fun Intent.getFido2CredentialRequestOrNull(): Fido2CreateCredentialRequest? { packageName = systemRequest.callingAppInfo.packageName, signingInfo = systemRequest.callingAppInfo.signingInfo, origin = systemRequest.callingAppInfo.origin, + isUserVerified = systemRequest.biometricPromptResult?.isSuccessful ?: false, ) } @@ -67,6 +68,9 @@ fun Intent.getFido2AssertionRequestOrNull(): Fido2CredentialAssertionRequest? { val userId: String = getStringExtra(EXTRA_KEY_USER_ID) ?: return null + val isUserVerified = systemRequest.biometricPromptResult?.isSuccessful + ?: false + return Fido2CredentialAssertionRequest( userId = userId, cipherId = cipherId, @@ -76,6 +80,7 @@ fun Intent.getFido2AssertionRequestOrNull(): Fido2CredentialAssertionRequest? { packageName = systemRequest.callingAppInfo.packageName, signingInfo = systemRequest.callingAppInfo.signingInfo, origin = systemRequest.callingAppInfo.origin, + isUserVerified = isUserVerified, ) } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/FlagKey.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/FlagKey.kt index 0315863d95a..17821c25535 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/FlagKey.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/FlagKey.kt @@ -39,6 +39,8 @@ sealed class FlagKey { NewDevicePermanentDismiss, NewDeviceTemporaryDismiss, IgnoreEnvironmentCheck, + SingleTapPasskeyCreation, + SingleTapPasskeyAuthentication, ) } } @@ -171,6 +173,24 @@ sealed class FlagKey { override val isRemotelyConfigured: Boolean = false } + /** + * Data object holding the feature flag key to enable single tap passkey creation. + */ + data object SingleTapPasskeyCreation : FlagKey() { + override val keyName: String = "single-tap-passkey-creation" + override val defaultValue: Boolean = false + override val isRemotelyConfigured: Boolean = false + } + + /** + * Data object holding the feature flag key to enable single tap passkey authentication. + */ + data object SingleTapPasskeyAuthentication : FlagKey() { + override val keyName: String = "single-tap-passkey-authentication" + override val defaultValue: Boolean = false + override val isRemotelyConfigured: Boolean = false + } + //region Dummy keys for testing /** * Data object holding the key for a [Boolean] flag to be used in tests. diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/autofill/fido2/manager/Fido2CompletionManagerImpl.kt b/app/src/main/java/com/x8bit/bitwarden/ui/autofill/fido2/manager/Fido2CompletionManagerImpl.kt index 4f4a5d76466..3e61f631d23 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/autofill/fido2/manager/Fido2CompletionManagerImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/autofill/fido2/manager/Fido2CompletionManagerImpl.kt @@ -139,8 +139,7 @@ class Fido2CompletionManagerImpl( ) } - Fido2GetCredentialsResult.Error, - -> { + Fido2GetCredentialsResult.Error -> { PendingIntentHandler.setGetCredentialException( resultIntent, GetCredentialUnknownException(), diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/components/FeatureFlagListItems.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/components/FeatureFlagListItems.kt index 8e8f75ed672..42f68704f7a 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/components/FeatureFlagListItems.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/components/FeatureFlagListItems.kt @@ -36,6 +36,8 @@ fun FlagKey.ListItemContent( FlagKey.NewDevicePermanentDismiss, FlagKey.NewDeviceTemporaryDismiss, FlagKey.IgnoreEnvironmentCheck, + FlagKey.SingleTapPasskeyCreation, + FlagKey.SingleTapPasskeyAuthentication, -> BooleanFlagItem( label = flagKey.getDisplayLabel(), key = flagKey as FlagKey, @@ -87,4 +89,7 @@ private fun FlagKey.getDisplayLabel(): String = when (this) { FlagKey.NewDevicePermanentDismiss -> stringResource(R.string.new_device_permanent_dismiss) FlagKey.NewDeviceTemporaryDismiss -> stringResource(R.string.new_device_temporary_dismiss) FlagKey.IgnoreEnvironmentCheck -> stringResource(R.string.ignore_environment_check) + FlagKey.SingleTapPasskeyCreation -> stringResource(R.string.single_tap_passkey_creation) + FlagKey.SingleTapPasskeyAuthentication -> + stringResource(R.string.single_tap_passkey_authentication) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bb7c068a93d..2fd4848ffe7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1112,4 +1112,6 @@ Do you want to switch to this account? Review flow launched! Copy private key You can change your account email on the Bitwarden web app. + Single tap passkey creation + Single tap passkey sign-on diff --git a/app/src/test/java/com/x8bit/bitwarden/MainViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/MainViewModelTest.kt index ce5b5181060..1b0be1dd183 100644 --- a/app/src/test/java/com/x8bit/bitwarden/MainViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/MainViewModelTest.kt @@ -22,10 +22,10 @@ import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CredentialAssertionReq import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2GetCredentialsRequest import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2ValidateOriginResult import com.x8bit.bitwarden.data.autofill.fido2.model.createMockFido2CredentialAssertionRequest -import com.x8bit.bitwarden.data.autofill.fido2.model.createMockFido2CredentialRequest +import com.x8bit.bitwarden.data.autofill.fido2.model.createMockFido2CreateCredentialRequest import com.x8bit.bitwarden.data.autofill.fido2.model.createMockFido2GetCredentialsRequest import com.x8bit.bitwarden.data.autofill.fido2.util.getFido2AssertionRequestOrNull -import com.x8bit.bitwarden.data.autofill.fido2.util.getFido2CredentialRequestOrNull +import com.x8bit.bitwarden.data.autofill.fido2.util.getFido2CreateCredentialRequestOrNull import com.x8bit.bitwarden.data.autofill.fido2.util.getFido2GetCredentialsRequestOrNull import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManager import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManagerImpl @@ -136,7 +136,7 @@ class MainViewModelTest : BaseViewModelTest() { Intent::getAutofillSelectionDataOrNull, Intent::getCompleteRegistrationDataIntentOrNull, Intent::getFido2AssertionRequestOrNull, - Intent::getFido2CredentialRequestOrNull, + Intent::getFido2CreateCredentialRequestOrNull, Intent::getFido2GetCredentialsRequestOrNull, Intent::isAddTotpLoginItemFromAuthenticator, ) @@ -156,7 +156,7 @@ class MainViewModelTest : BaseViewModelTest() { Intent::getAutofillSelectionDataOrNull, Intent::getCompleteRegistrationDataIntentOrNull, Intent::getFido2AssertionRequestOrNull, - Intent::getFido2CredentialRequestOrNull, + Intent::getFido2CreateCredentialRequestOrNull, Intent::getFido2GetCredentialsRequestOrNull, Intent::isAddTotpLoginItemFromAuthenticator, ) @@ -612,6 +612,7 @@ class MainViewModelTest : BaseViewModelTest() { packageName = "com.x8bit.bitwarden", signingInfo = SigningInfo(), origin = "mockOrigin", + isUserVerified = true, ) val fido2Intent = createMockIntent( mockFido2CreateCredentialRequest = fido2CreateCredentialRequest, @@ -638,11 +639,13 @@ class MainViewModelTest : BaseViewModelTest() { ) } + @Suppress("MaxLineLength") @Test - fun `on ReceiveFirstIntent with fido2 request data should set the user to unverified`() { + fun `on ReceiveFirstIntent with fido2 create request data should set the user verification based on request`() { val viewModel = createViewModel() + val createCredentialRequest = createMockFido2CreateCredentialRequest(number = 1) val fido2Intent = createMockIntent( - mockFido2CreateCredentialRequest = createMockFido2CredentialRequest(number = 1), + mockFido2CreateCredentialRequest = createCredentialRequest, ) viewModel.trySendAction( @@ -652,7 +655,7 @@ class MainViewModelTest : BaseViewModelTest() { ) verify { - fido2CredentialManager.isUserVerified = false + fido2CredentialManager.isUserVerified = createCredentialRequest.isUserVerified ?: false } } @@ -667,6 +670,7 @@ class MainViewModelTest : BaseViewModelTest() { packageName = "com.x8bit.bitwarden", signingInfo = SigningInfo(), origin = "mockOrigin", + isUserVerified = true, ) val mockIntent = createMockIntent( mockFido2CreateCredentialRequest = fido2CreateCredentialRequest, @@ -697,6 +701,7 @@ class MainViewModelTest : BaseViewModelTest() { packageName = "com.x8bit.bitwarden", signingInfo = SigningInfo(), origin = "mockOrigin", + isUserVerified = true, ) val mockIntent = createMockIntent( mockFido2CreateCredentialRequest = fido2CreateCredentialRequest, @@ -1101,7 +1106,7 @@ private fun createMockIntent( every { getAutofillSelectionDataOrNull() } returns mockAutofillSelectionData every { getCompleteRegistrationDataIntentOrNull() } returns mockCompleteRegistrationData every { getFido2AssertionRequestOrNull() } returns mockFido2CredentialAssertionRequest - every { getFido2CredentialRequestOrNull() } returns mockFido2CreateCredentialRequest + every { getFido2CreateCredentialRequestOrNull() } returns mockFido2CreateCredentialRequest every { getFido2GetCredentialsRequestOrNull() } returns mockFido2GetCredentialsRequest every { isMyVaultShortcut } returns mockIsMyVaultShortcut every { isPasswordGeneratorShortcut } returns mockIsPasswordGeneratorShortcut diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/manager/Fido2CredentialManagerTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/manager/Fido2CredentialManagerTest.kt index 45c0d49a3cf..17d42cb9881 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/manager/Fido2CredentialManagerTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/manager/Fido2CredentialManagerTest.kt @@ -16,7 +16,7 @@ import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2RegisterCredentialResu import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2ValidateOriginResult import com.x8bit.bitwarden.data.autofill.fido2.model.PasskeyAssertionOptions import com.x8bit.bitwarden.data.autofill.fido2.model.PasskeyAttestationOptions -import com.x8bit.bitwarden.data.autofill.fido2.model.createMockFido2CredentialRequest +import com.x8bit.bitwarden.data.autofill.fido2.model.createMockFido2CreateCredentialRequest import com.x8bit.bitwarden.data.platform.util.asSuccess import com.x8bit.bitwarden.data.platform.util.decodeFromStringOrNull import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource @@ -170,7 +170,7 @@ class Fido2CredentialManagerTest { @Test fun `registerFido2Credential should construct ClientData DefaultWithCustomHash when callingAppInfo origin is populated`() = runTest { - val mockFido2CreateCredentialRequest = createMockFido2CredentialRequest( + val mockFido2CreateCredentialRequest = createMockFido2CreateCredentialRequest( number = 1, origin = "origin", signingInfo = mockSigningInfo, @@ -210,7 +210,7 @@ class Fido2CredentialManagerTest { every { apkContentsSigners } returns arrayOf(Signature(DEFAULT_APP_SIGNATURE)) every { hasMultipleSigners() } returns false } - val mockFido2Request = createMockFido2CredentialRequest( + val mockFido2Request = createMockFido2CreateCredentialRequest( number = 1, signingInfo = mockSigningInfo, ) @@ -243,7 +243,7 @@ class Fido2CredentialManagerTest { every { apkContentsSigners } returns arrayOf(Signature(DEFAULT_APP_SIGNATURE)) every { hasMultipleSigners() } returns false } - val mockFido2CreateCredentialRequest = createMockFido2CredentialRequest( + val mockFido2CreateCredentialRequest = createMockFido2CreateCredentialRequest( number = 1, origin = "origin", signingInfo = mockSigningInfo, @@ -283,7 +283,7 @@ class Fido2CredentialManagerTest { every { apkContentsSigners } returns arrayOf(Signature(DEFAULT_APP_SIGNATURE)) every { hasMultipleSigners() } returns false } - val mockFido2CreateCredentialRequest = createMockFido2CredentialRequest( + val mockFido2CreateCredentialRequest = createMockFido2CreateCredentialRequest( number = 1, origin = "origin", signingInfo = mockSigningInfo, @@ -328,7 +328,7 @@ class Fido2CredentialManagerTest { val mockSigningInfo = mockk { every { hasMultipleSigners() } returns true } - val mockFido2CredentialRequest = createMockFido2CredentialRequest( + val mockFido2CredentialRequest = createMockFido2CreateCredentialRequest( number = 1, origin = "origin", signingInfo = mockSigningInfo, @@ -353,7 +353,7 @@ class Fido2CredentialManagerTest { val mockSigningInfo = mockk { every { hasMultipleSigners() } returns true } - val mockFido2CredentialRequest = createMockFido2CredentialRequest( + val mockFido2CredentialRequest = createMockFido2CreateCredentialRequest( number = 1, signingInfo = mockSigningInfo, ) @@ -377,7 +377,7 @@ class Fido2CredentialManagerTest { every { apkContentsSigners } returns arrayOf(Signature(DEFAULT_APP_SIGNATURE)) every { hasMultipleSigners() } returns false } - val mockFido2CredentialRequest = createMockFido2CredentialRequest( + val mockFido2CredentialRequest = createMockFido2CreateCredentialRequest( number = 1, origin = "illegal empty spaces", signingInfo = mockSigningInfo, @@ -402,7 +402,7 @@ class Fido2CredentialManagerTest { every { apkContentsSigners } returns arrayOf(Signature(DEFAULT_APP_SIGNATURE)) every { hasMultipleSigners() } returns false } - val mockFido2CredentialRequest = createMockFido2CredentialRequest( + val mockFido2CredentialRequest = createMockFido2CreateCredentialRequest( number = 1, origin = "origin", signingInfo = mockSigningInfo, @@ -440,7 +440,7 @@ class Fido2CredentialManagerTest { @Test fun `registerFido2Credential should return Error when origin is null`() = runTest { - val mockAssertionRequest = createMockFido2CredentialRequest( + val mockAssertionRequest = createMockFido2CreateCredentialRequest( number = 1, origin = null, signingInfo = mockSigningInfo, diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/model/Fido2CredentialRequestUtil.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/model/Fido2CredentialRequestUtil.kt index a1833ec3f75..62a7f0c6a63 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/model/Fido2CredentialRequestUtil.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/model/Fido2CredentialRequestUtil.kt @@ -5,7 +5,7 @@ import android.content.pm.SigningInfo /** * Creates a mock [Fido2CreateCredentialRequest] with a given [number]. */ -fun createMockFido2CredentialRequest( +fun createMockFido2CreateCredentialRequest( number: Int, origin: String? = null, signingInfo: SigningInfo = SigningInfo(), @@ -16,4 +16,5 @@ fun createMockFido2CredentialRequest( packageName = "com.x8bit.bitwarden", signingInfo = signingInfo, origin = origin, + isUserVerified = true, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/processor/Fido2ProviderProcessorTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/processor/Fido2ProviderProcessorTest.kt index 098635ec87c..30a5d1b4b97 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/processor/Fido2ProviderProcessorTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/processor/Fido2ProviderProcessorTest.kt @@ -3,9 +3,11 @@ package com.x8bit.bitwarden.data.autofill.fido2.processor import android.app.PendingIntent import android.content.Context import android.graphics.drawable.Icon +import android.os.Build import android.os.Bundle import android.os.CancellationSignal import android.os.OutcomeReceiver +import androidx.biometric.BiometricManager import androidx.credentials.exceptions.CreateCredentialException import androidx.credentials.exceptions.CreateCredentialUnknownException import androidx.credentials.exceptions.GetCredentialException @@ -30,12 +32,16 @@ import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType import com.x8bit.bitwarden.data.autofill.fido2.manager.Fido2CredentialManager import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager import com.x8bit.bitwarden.data.platform.datasource.network.di.PlatformNetworkModule +import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager +import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState +import com.x8bit.bitwarden.data.platform.manager.model.FlagKey import com.x8bit.bitwarden.data.platform.repository.model.DataState import com.x8bit.bitwarden.data.platform.repository.model.Environment import com.x8bit.bitwarden.data.platform.util.asFailure import com.x8bit.bitwarden.data.platform.util.asSuccess +import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFido2CredentialAutofillView import com.x8bit.bitwarden.data.vault.repository.VaultRepository @@ -55,14 +61,15 @@ import io.mockk.unmockkConstructor import io.mockk.unmockkStatic import io.mockk.verify import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.serialization.encodeToString import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import java.time.Clock import java.time.Instant import java.time.ZoneOffset +import javax.crypto.Cipher class Fido2ProviderProcessorTest { @@ -87,6 +94,10 @@ class Fido2ProviderProcessorTest { private val fido2CredentialStore: Fido2CredentialStore = mockk() private val intentManager: IntentManager = mockk() private val dispatcherManager: DispatcherManager = FakeDispatcherManager() + private val biometricsEncryptionManager: BiometricsEncryptionManager = mockk() + private val featureFlagManager: FeatureFlagManager = mockk { + every { getFeatureFlag(FlagKey.SingleTapPasskeyCreation) } returns false + } private val cancellationSignal: CancellationSignal = mockk() private val json = PlatformNetworkModule.providesJson() @@ -102,15 +113,19 @@ class Fido2ProviderProcessorTest { fido2CredentialManager, intentManager, clock, + biometricsEncryptionManager, + featureFlagManager, dispatcherManager, ) mockkStatic(Icon::class) + mockkStatic(::isBuildVersionBelow) } @AfterEach fun tearDown() { unmockkStatic(Icon::class) + unmockkStatic(::isBuildVersionBelow) } @Test @@ -232,8 +247,9 @@ class Fido2ProviderProcessorTest { assertEquals(DEFAULT_USER_STATE.accounts[0].email, capturedEntry.accountName) } + @Suppress("MaxLineLength") @Test - fun `processCreateCredentialRequest should generate result entries for each user account`() { + fun `processCreateCredentialRequest should generate correct entries based on state`() { val request: BeginCreatePublicKeyCredentialRequest = mockk() val candidateQueryData: Bundle = mockk() val callback: OutcomeReceiver = @@ -256,6 +272,12 @@ class Fido2ProviderProcessorTest { any(), ) } returns mockIntent + every { + biometricsEncryptionManager.getOrCreateCipher(any()) + } returns mockk() + + every { featureFlagManager.getFeatureFlag(FlagKey.SingleTapPasskeyCreation) } returns true + every { isBuildVersionBelow(Build.VERSION_CODES.VANILLA_ICE_CREAM) } returns false fido2Processor.processCreateCredentialRequest(request, cancellationSignal, callback) @@ -272,6 +294,45 @@ class Fido2ProviderProcessorTest { DEFAULT_USER_STATE.accounts.forEachIndexed { index, mockAccount -> assertEquals(mockAccount.email, captureSlot.captured.createEntries[index].accountName) } + + // Verify all entries have biometric prompt data when feature flag is enabled + assertTrue(captureSlot.captured.createEntries.all { it.biometricPromptData != null }) { + "Expected all entries to have biometric prompt data." + } + + // Verify entries have the correct authenticators when cipher is not null + assertTrue( + captureSlot + .captured + .createEntries + .all { + it.biometricPromptData + ?.allowedAuthenticators == BiometricManager.Authenticators.BIOMETRIC_STRONG + }, + ) { "Expected all entries to have BIOMETRIC_STRONG authenticators." } + + // Verify entries have the correct authenticators when cipher is null + every { biometricsEncryptionManager.getOrCreateCipher(any()) } returns null + fido2Processor.processCreateCredentialRequest(request, cancellationSignal, callback) + assertTrue( + captureSlot + .captured + .createEntries + .all { + it.biometricPromptData + ?.allowedAuthenticators == BiometricManager.Authenticators.BIOMETRIC_WEAK + }, + ) { "Expected all entries to have BIOMETRIC_WEAK authenticators." } + + // Disable single tap feature flag to verify all entries do not have biometric prompt data + every { featureFlagManager.getFeatureFlag(FlagKey.SingleTapPasskeyCreation) } returns false + fido2Processor.processCreateCredentialRequest(request, cancellationSignal, callback) + assertTrue( + captureSlot + .captured + .createEntries + .all { it.biometricPromptData == null }, + ) { "Expected all entries to not have biometric prompt data." } } @Test diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/util/Fido2IntentUtilsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/util/Fido2IntentUtilsTest.kt index 7f0e708471d..226b8245a2e 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/util/Fido2IntentUtilsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/util/Fido2IntentUtilsTest.kt @@ -75,7 +75,7 @@ class Fido2IntentUtilsTest { PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent) } returns mockProviderRequest - val createRequest = intent.getFido2CredentialRequestOrNull() + val createRequest = intent.getFido2CreateCredentialRequestOrNull() assertEquals( Fido2CreateCredentialRequest( userId = "mockUserId", @@ -83,6 +83,7 @@ class Fido2IntentUtilsTest { packageName = mockCallingAppInfo.packageName, signingInfo = mockCallingAppInfo.signingInfo, origin = mockCallingAppInfo.origin, + isUserVerified = true, ), createRequest, ) @@ -94,7 +95,7 @@ class Fido2IntentUtilsTest { every { isBuildVersionBelow(34) } returns true - assertNull(intent.getFido2CredentialRequestOrNull()) + assertNull(intent.getFido2CreateCredentialRequestOrNull()) } @Suppress("MaxLineLength") @@ -106,7 +107,7 @@ class Fido2IntentUtilsTest { PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent) } returns null - assertNull(intent.getFido2CredentialRequestOrNull()) + assertNull(intent.getFido2CreateCredentialRequestOrNull()) } @Suppress("MaxLineLength") @@ -127,7 +128,7 @@ class Fido2IntentUtilsTest { PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent) } returns mockProviderRequest - assertNull(intent.getFido2CredentialRequestOrNull()) + assertNull(intent.getFido2CreateCredentialRequestOrNull()) } @Suppress("MaxLineLength") @@ -157,7 +158,7 @@ class Fido2IntentUtilsTest { PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent) } returns mockProviderRequest - assertNull(intent.getFido2CredentialRequestOrNull()) + assertNull(intent.getFido2CreateCredentialRequestOrNull()) } @Test diff --git a/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/util/SpecialCircumstanceExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/util/SpecialCircumstanceExtensionsTest.kt index ab26c0582bc..a5290f6bfbd 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/util/SpecialCircumstanceExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/util/SpecialCircumstanceExtensionsTest.kt @@ -152,6 +152,7 @@ class SpecialCircumstanceExtensionsTest { packageName = "mockPackageName", signingInfo = SigningInfo(), origin = "mockOrigin", + isUserVerified = true, ) assertEquals( fido2CreateCredentialRequest, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt index 730f8da3975..b64611114a5 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt @@ -670,6 +670,7 @@ class RootNavViewModelTest : BaseViewModelTest() { packageName = "com.x8bit.bitwarden", signingInfo = SigningInfo(), origin = "mockOrigin", + isUserVerified = true, ) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save(fido2CreateCredentialRequest) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt index c152bd8d34b..f364b57e3eb 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt @@ -21,7 +21,7 @@ import com.x8bit.bitwarden.data.autofill.fido2.manager.Fido2CredentialManager import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CreateCredentialRequest import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2RegisterCredentialResult import com.x8bit.bitwarden.data.autofill.fido2.model.UserVerificationRequirement -import com.x8bit.bitwarden.data.autofill.fido2.model.createMockFido2CredentialRequest +import com.x8bit.bitwarden.data.autofill.fido2.model.createMockFido2CreateCredentialRequest import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager @@ -359,6 +359,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { packageName = "mockPackageName-1", signingInfo = SigningInfo(), origin = null, + isUserVerified = true, ) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( fido2CreateCredentialRequest = fido2CreateCredentialRequest, @@ -781,6 +782,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { packageName = "mockPackageName", signingInfo = mockk(), origin = null, + isUserVerified = true, ) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( @@ -860,6 +862,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { packageName = "mockPackageName", signingInfo = mockk(), origin = null, + isUserVerified = false, ) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( @@ -940,7 +943,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { @Test fun `in add mode during fido2, SaveClick should skip user verification when user is verified`() = runTest { - val fido2CredentialRequest = createMockFido2CredentialRequest(number = 1) + val fido2CredentialRequest = createMockFido2CreateCredentialRequest(number = 1) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( fido2CreateCredentialRequest = fido2CredentialRequest, @@ -992,7 +995,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { @Test fun `in add mode during fido2, SaveClick should show fido2 error dialog when create options are null`() = runTest { - val fido2CredentialRequest = createMockFido2CredentialRequest(number = 1) + val fido2CredentialRequest = createMockFido2CreateCredentialRequest(number = 1) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( fido2CreateCredentialRequest = fido2CredentialRequest, @@ -1036,7 +1039,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { @Test fun `in add mode during fido2, SaveClick should emit fido user verification as optional when verification is PREFERRED`() = runTest { - val fido2CredentialRequest = createMockFido2CredentialRequest(number = 1) + val fido2CredentialRequest = createMockFido2CreateCredentialRequest(number = 1) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( fido2CreateCredentialRequest = fido2CredentialRequest, @@ -1081,7 +1084,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { @Test fun `in add mode during fido2, SaveClick should emit fido user verification as required when request user verification option is REQUIRED`() = runTest { - val fido2CredentialRequest = createMockFido2CredentialRequest(number = 1) + val fido2CredentialRequest = createMockFido2CreateCredentialRequest(number = 1) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( fido2CreateCredentialRequest = fido2CredentialRequest, @@ -1789,7 +1792,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { ), ), ) - val mockFido2CredentialRequest = createMockFido2CredentialRequest(number = 1) + val mockFido2CredentialRequest = createMockFido2CreateCredentialRequest(number = 1) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( fido2CreateCredentialRequest = mockFido2CredentialRequest, @@ -1840,7 +1843,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { notes = "mockNotes-1", ), ) - val mockFidoRequest = createMockFido2CredentialRequest(number = 1) + val mockFidoRequest = createMockFido2CreateCredentialRequest(number = 1) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( fido2CreateCredentialRequest = mockFidoRequest, ) @@ -1911,7 +1914,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { notes = "mockNotes-1", ), ) - val mockFidoRequest = createMockFido2CredentialRequest(number = 1) + val mockFidoRequest = createMockFido2CreateCredentialRequest(number = 1) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( fido2CreateCredentialRequest = mockFidoRequest, ) @@ -4083,7 +4086,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { fun `UserVerificationSuccess should display Fido2ErrorDialog when activeUserId is null`() { every { authRepository.activeUserId } returns null specialCircumstanceManager.specialCircumstance = - SpecialCircumstance.Fido2Save(createMockFido2CredentialRequest(number = 1)) + SpecialCircumstance.Fido2Save(createMockFido2CreateCredentialRequest(number = 1)) viewModel.trySendAction(VaultAddEditAction.Common.UserVerificationSuccess) @@ -4100,7 +4103,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { @Test fun `UserVerificationSuccess should set isUserVerified to true, and register FIDO 2 credential`() = runTest { - val mockRequest = createMockFido2CredentialRequest(number = 1) + val mockRequest = createMockFido2CreateCredentialRequest(number = 1) val mockResult = Fido2RegisterCredentialResult.Success( registrationResponse = "mockResponse", ) @@ -4144,7 +4147,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { @Test fun `Fido2RegisterCredentialResult Error should show toast and emit CompleteFido2Registration result`() = runTest { - val mockRequest = createMockFido2CredentialRequest(number = 1) + val mockRequest = createMockFido2CreateCredentialRequest(number = 1) val mockResult = Fido2RegisterCredentialResult.Error specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( fido2CreateCredentialRequest = mockRequest, @@ -4181,7 +4184,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { @Test fun `Fido2RegisterCredentialResult Success should show toast and emit CompleteFido2Registration result`() = runTest { - val mockRequest = createMockFido2CredentialRequest(number = 1) + val mockRequest = createMockFido2CreateCredentialRequest(number = 1) val mockResult = Fido2RegisterCredentialResult.Success( registrationResponse = "mockResponse", ) @@ -4220,7 +4223,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { @Test fun `Fido2RegisterCredentialResult Cancelled should emit CompleteFido2Registration result`() = runTest { - val mockRequest = createMockFido2CredentialRequest(number = 1) + val mockRequest = createMockFido2CreateCredentialRequest(number = 1) val mockResult = Fido2RegisterCredentialResult.Cancelled specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( fido2CreateCredentialRequest = mockRequest, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/Fido2CredentialRequestExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/Fido2CredentialRequestExtensionsTest.kt index 5e497f431e8..b8728ed561c 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/Fido2CredentialRequestExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/Fido2CredentialRequestExtensionsTest.kt @@ -53,6 +53,7 @@ class Fido2CredentialRequestExtensionsTest { packageName = "mockPackageName-1", signingInfo = SigningInfo(), origin = null, + isUserVerified = true, ) .toDefaultAddTypeContent( attestationOptions = createMockPasskeyAttestationOptions(1), @@ -89,6 +90,7 @@ class Fido2CredentialRequestExtensionsTest { packageName = "mockPackageName-1", signingInfo = SigningInfo(), origin = "www.test.com", + isUserVerified = true, ) .toDefaultAddTypeContent( attestationOptions = createMockPasskeyAttestationOptions(number = 1), diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt index 60f16c5d862..44592dfb6ae 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt @@ -26,7 +26,7 @@ import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2RegisterCredentialResu import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2ValidateOriginResult import com.x8bit.bitwarden.data.autofill.fido2.model.UserVerificationRequirement import com.x8bit.bitwarden.data.autofill.fido2.model.createMockFido2CredentialAssertionRequest -import com.x8bit.bitwarden.data.autofill.fido2.model.createMockFido2CredentialRequest +import com.x8bit.bitwarden.data.autofill.fido2.model.createMockFido2CreateCredentialRequest import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManager import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManagerImpl import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem @@ -204,6 +204,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { "com.x8bit.bitwarden", SigningInfo(), origin = null, + isUserVerified = true, ) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( fido2CreateCredentialRequest = fido2CreateCredentialRequest, @@ -452,7 +453,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { fun `ItemClick for vault item during FIDO 2 registration should show FIDO 2 error dialog when cipherView is null`() { val cipherView = createMockCipherView(number = 1) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( - fido2CreateCredentialRequest = createMockFido2CredentialRequest(number = 1), + fido2CreateCredentialRequest = createMockFido2CreateCredentialRequest(number = 1), ) mutableVaultDataStateFlow.value = DataState.Loaded( data = VaultData( @@ -481,7 +482,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { setupMockUri() val cipherView = createMockCipherView(number = 1) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( - fido2CreateCredentialRequest = createMockFido2CredentialRequest(number = 1), + fido2CreateCredentialRequest = createMockFido2CreateCredentialRequest(number = 1), ) mutableVaultDataStateFlow.value = DataState.Loaded( data = VaultData( @@ -516,7 +517,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { fido2Credentials = createMockSdkFido2CredentialList(number = 1), ) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( - fido2CreateCredentialRequest = createMockFido2CredentialRequest(number = 1), + fido2CreateCredentialRequest = createMockFido2CreateCredentialRequest(number = 1), ) mutableVaultDataStateFlow.value = DataState.Loaded( data = VaultData( @@ -559,7 +560,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { setupMockUri() val cipherView = createMockCipherView(number = 1, fido2Credentials = null) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( - fido2CreateCredentialRequest = createMockFido2CredentialRequest(number = 1), + fido2CreateCredentialRequest = createMockFido2CreateCredentialRequest(number = 1), ) mutableVaultDataStateFlow.value = DataState.Loaded( data = VaultData( @@ -607,7 +608,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { runTest { setupMockUri() val cipherView = createMockCipherView(number = 1) - val mockFido2CredentialRequest = createMockFido2CredentialRequest(number = 1) + val mockFido2CredentialRequest = createMockFido2CreateCredentialRequest(number = 1) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( fido2CreateCredentialRequest = mockFido2CredentialRequest, ) @@ -650,7 +651,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { fun `ItemClick for vault item during FIDO 2 registration should skip user verification when user is verified`() { setupMockUri() val cipherView = createMockCipherView(number = 1) - val mockFido2CredentialRequest = createMockFido2CredentialRequest(number = 1) + val mockFido2CredentialRequest = createMockFido2CreateCredentialRequest(number = 1) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( fido2CreateCredentialRequest = mockFido2CredentialRequest, ) @@ -1576,6 +1577,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { packageName = "com.x8bit.bitwarden", signingInfo = SigningInfo(), origin = "mockOrigin", + isUserVerified = true, ) specialCircumstanceManager.specialCircumstance = @@ -2158,6 +2160,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { "com.x8bit.bitwarden", SigningInfo(), origin = "com.x8bit.bitwarden", + isUserVerified = true, ) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( fido2CreateCredentialRequest, @@ -2208,6 +2211,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { packageName = "com.x8bit.bitwarden", signingInfo = SigningInfo(), origin = null, + isUserVerified = true, ) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( @@ -2239,6 +2243,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { packageName = "com.x8bit.bitwarden", signingInfo = SigningInfo(), origin = null, + isUserVerified = true, ) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( @@ -2270,6 +2275,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { packageName = "com.x8bit.bitwarden", signingInfo = SigningInfo(), origin = null, + isUserVerified = true, ) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( @@ -2301,6 +2307,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { packageName = "com.x8bit.bitwarden", signingInfo = SigningInfo(), origin = null, + isUserVerified = true, ) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( @@ -2332,6 +2339,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { packageName = "com.x8bit.bitwarden", signingInfo = SigningInfo(), origin = null, + isUserVerified = true, ) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( @@ -2363,6 +2371,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { packageName = "com.x8bit.bitwarden", signingInfo = SigningInfo(), origin = null, + isUserVerified = true, ) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( @@ -2464,7 +2473,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { fun `DismissFido2ErrorDialogClick should clear the dialog state then complete FIDO 2 registration based on state`() = runTest { specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( - createMockFido2CredentialRequest(number = 1), + createMockFido2CreateCredentialRequest(number = 1), ) val viewModel = createVaultItemListingViewModel() viewModel.trySendAction(VaultItemListingsAction.DismissFido2ErrorDialogClick) @@ -3367,7 +3376,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { fun `UserVerificationSuccess should display Fido2ErrorDialog when activeUserId is null`() { every { authRepository.activeUserId } returns null specialCircumstanceManager.specialCircumstance = - SpecialCircumstance.Fido2Save(createMockFido2CredentialRequest(number = 1)) + SpecialCircumstance.Fido2Save(createMockFido2CreateCredentialRequest(number = 1)) val viewModel = createVaultItemListingViewModel() viewModel.trySendAction( @@ -3390,7 +3399,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { @Test fun `UserVerificationSuccess should set isUserVerified to true, and register FIDO 2 credential when verification result is received`() = runTest { - val mockRequest = createMockFido2CredentialRequest(number = 1) + val mockRequest = createMockFido2CreateCredentialRequest(number = 1) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( fido2CreateCredentialRequest = mockRequest, ) @@ -4050,7 +4059,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { setupMockUri() val cipherView = createMockCipherView(number = 1) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( - fido2CreateCredentialRequest = createMockFido2CredentialRequest(number = 1), + fido2CreateCredentialRequest = createMockFido2CreateCredentialRequest(number = 1), ) mutableVaultDataStateFlow.value = DataState.Loaded( data = VaultData( @@ -4084,7 +4093,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { setupMockUri() val cipherView = createMockCipherView(number = 1) specialCircumstanceManager.specialCircumstance = SpecialCircumstance.Fido2Save( - fido2CreateCredentialRequest = createMockFido2CredentialRequest(number = 1), + fido2CreateCredentialRequest = createMockFido2CreateCredentialRequest(number = 1), ) mutableVaultDataStateFlow.value = DataState.Loaded( data = VaultData( diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataExtensionsTest.kt index e701ae2f0a3..ef75c869f32 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataExtensionsTest.kt @@ -828,6 +828,7 @@ class VaultItemListingDataExtensionsTest { packageName = "", signingInfo = SigningInfo(), origin = "https://www.test.com", + isUserVerified = true, ), fido2CredentialAutofillViews = null, totpData = null,