mirror of
https://github.com/bitwarden/android.git
synced 2026-10-23 03:23:53 -05:00
[PM-15906] Implement single tap passkey flows (#4547)
This commit is contained in:
1 parent
bf60f8f9e3
commit
e9159cc8c1
22 files changed
+286
-98
No files matched your search
@@ -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
|
||||
@@ -263,7 +263,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()
|
||||
@@ -324,25 +324,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,
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
+1
@@ -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(
|
||||
|
||||
+1
@@ -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)
|
||||
|
||||
+80
-18
@@ -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,16 @@ 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)
|
||||
|
||||
if (isVaultUnlocked &&
|
||||
featureFlagManager.getFeatureFlag(FlagKey.SingleTapPasskeyCreation)
|
||||
) {
|
||||
biometricsEncryptionManager
|
||||
.getOrCreateCipher(userId)
|
||||
?.let { entryBuilder.setBiometricPromptDataIfSupported(cipher = it) }
|
||||
}
|
||||
return entryBuilder.build()
|
||||
}
|
||||
|
||||
override fun processGetCredentialRequest(
|
||||
@@ -261,30 +280,73 @@ class Fido2ProviderProcessorImpl(
|
||||
): List<CredentialEntry> =
|
||||
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,
|
||||
),
|
||||
)
|
||||
.build()
|
||||
|
||||
if (featureFlagManager.getFeatureFlag(FlagKey.SingleTapPasskeyAuthentication)) {
|
||||
biometricsEncryptionManager
|
||||
.getOrCreateCipher(userId)
|
||||
?.let {
|
||||
publicKeyEntryBuilder
|
||||
.setBiometricPromptDataIfSupported(cipher = it)
|
||||
}
|
||||
}
|
||||
publicKeyEntryBuilder.build()
|
||||
}
|
||||
|
||||
private fun PublicKeyCredentialEntry.Builder.setBiometricPromptDataIfSupported(
|
||||
cipher: Cipher,
|
||||
): PublicKeyCredentialEntry.Builder {
|
||||
return if (isBuildVersionBelow(Build.VERSION_CODES.VANILLA_ICE_CREAM)) {
|
||||
this
|
||||
} else {
|
||||
setBiometricPromptData(
|
||||
biometricPromptData = BiometricPromptData
|
||||
.Builder()
|
||||
.buildPromptDataWithCipher(cipher),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun CreateEntry.Builder.setBiometricPromptDataIfSupported(
|
||||
cipher: Cipher,
|
||||
): CreateEntry.Builder {
|
||||
return if (isBuildVersionBelow(Build.VERSION_CODES.VANILLA_ICE_CREAM)) {
|
||||
this
|
||||
} else {
|
||||
setBiometricPromptData(
|
||||
biometricPromptData = BiometricPromptData
|
||||
.Builder()
|
||||
.buildPromptDataWithCipher(cipher),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
|
||||
private fun BiometricPromptData.Builder.buildPromptDataWithCipher(
|
||||
cipher: Cipher,
|
||||
): BiometricPromptData = BiometricPromptData.Builder()
|
||||
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
|
||||
.setCryptoObject(BiometricPrompt.CryptoObject(cipher))
|
||||
.build()
|
||||
|
||||
override fun processClearCredentialStateRequest(
|
||||
request: ProviderClearCredentialStateRequest,
|
||||
cancellationSignal: CancellationSignal,
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ sealed class FlagKey<out T : Any> {
|
||||
NewDeviceTemporaryDismiss,
|
||||
IgnoreEnvironmentCheck,
|
||||
MutualTls,
|
||||
SingleTapPasskeyCreation,
|
||||
SingleTapPasskeyAuthentication,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -181,6 +183,24 @@ sealed class FlagKey<out T : Any> {
|
||||
override val isRemotelyConfigured: Boolean = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Data object holding the feature flag key to enable single tap passkey creation.
|
||||
*/
|
||||
data object SingleTapPasskeyCreation : FlagKey<Boolean>() {
|
||||
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<Boolean>() {
|
||||
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.
|
||||
|
||||
+5
@@ -39,6 +39,8 @@ fun <T : Any> FlagKey<T>.ListItemContent(
|
||||
FlagKey.NewDeviceTemporaryDismiss,
|
||||
FlagKey.IgnoreEnvironmentCheck,
|
||||
FlagKey.MutualTls,
|
||||
FlagKey.SingleTapPasskeyCreation,
|
||||
FlagKey.SingleTapPasskeyAuthentication,
|
||||
-> BooleanFlagItem(
|
||||
label = flagKey.getDisplayLabel(),
|
||||
key = flagKey as FlagKey<Boolean>,
|
||||
@@ -92,4 +94,7 @@ private fun <T : Any> FlagKey<T>.getDisplayLabel(): String = when (this) {
|
||||
FlagKey.NewDeviceTemporaryDismiss -> stringResource(R.string.new_device_temporary_dismiss)
|
||||
FlagKey.IgnoreEnvironmentCheck -> stringResource(R.string.ignore_environment_check)
|
||||
FlagKey.MutualTls -> stringResource(R.string.mutual_tls)
|
||||
FlagKey.SingleTapPasskeyCreation -> stringResource(R.string.single_tap_passkey_creation)
|
||||
FlagKey.SingleTapPasskeyAuthentication ->
|
||||
stringResource(R.string.single_tap_passkey_authentication)
|
||||
}
|
||||
@@ -1114,11 +1114,13 @@ Do you want to switch to this account?</string>
|
||||
<string name="you_can_change_your_account_email_on_the_bitwarden_web_app">You can change your account email on the Bitwarden web app.</string>
|
||||
<string name="login_credentials">Login Credentials</string>
|
||||
<string name="autofill_options">Autofill Options</string>
|
||||
<string name="use_this_button_to_generate_a_new_unique_password">Use this button to generate a new unique password.</string>
|
||||
<string name="use_this_button_to_generate_a_new_unique_password">Use this button to generate a new unique password.</string>
|
||||
<string name="coachmark_1_of_3">1 of 3</string>
|
||||
<string name="coachmark_2_of_3">2 of 3</string>
|
||||
<string name="you_ll_only_need_to_set_up_authenticator_key">You’ll only need to set up Authenticator Key for logins that require two-factor authentication with a code. The key will continuously generate six-digit codes you can use to log in.</string>
|
||||
<string name="coachmark_3_of_3">3 of 3</string>
|
||||
<string name="you_must_add_a_web_address_to_use_autofill_to_access_this_account">You must add a web address to use autofill to access this account.</string>
|
||||
<string name="mutual_tls">Mutual TLS</string>
|
||||
<string name="single_tap_passkey_creation">Single tap passkey creation</string>
|
||||
<string name="single_tap_passkey_authentication">Single tap passkey sign-on</string>
|
||||
</resources>
|
||||
@@ -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
|
||||
@@ -139,7 +139,7 @@ class MainViewModelTest : BaseViewModelTest() {
|
||||
Intent::getAutofillSelectionDataOrNull,
|
||||
Intent::getCompleteRegistrationDataIntentOrNull,
|
||||
Intent::getFido2AssertionRequestOrNull,
|
||||
Intent::getFido2CredentialRequestOrNull,
|
||||
Intent::getFido2CreateCredentialRequestOrNull,
|
||||
Intent::getFido2GetCredentialsRequestOrNull,
|
||||
Intent::isAddTotpLoginItemFromAuthenticator,
|
||||
)
|
||||
@@ -159,7 +159,7 @@ class MainViewModelTest : BaseViewModelTest() {
|
||||
Intent::getAutofillSelectionDataOrNull,
|
||||
Intent::getCompleteRegistrationDataIntentOrNull,
|
||||
Intent::getFido2AssertionRequestOrNull,
|
||||
Intent::getFido2CredentialRequestOrNull,
|
||||
Intent::getFido2CreateCredentialRequestOrNull,
|
||||
Intent::getFido2GetCredentialsRequestOrNull,
|
||||
Intent::isAddTotpLoginItemFromAuthenticator,
|
||||
)
|
||||
@@ -647,6 +647,7 @@ class MainViewModelTest : BaseViewModelTest() {
|
||||
packageName = "com.x8bit.bitwarden",
|
||||
signingInfo = SigningInfo(),
|
||||
origin = "mockOrigin",
|
||||
isUserVerified = true,
|
||||
)
|
||||
val fido2Intent = createMockIntent(
|
||||
mockFido2CreateCredentialRequest = fido2CreateCredentialRequest,
|
||||
@@ -673,11 +674,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(
|
||||
@@ -687,7 +690,7 @@ class MainViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
|
||||
verify {
|
||||
fido2CredentialManager.isUserVerified = false
|
||||
fido2CredentialManager.isUserVerified = createCredentialRequest.isUserVerified ?: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -702,6 +705,7 @@ class MainViewModelTest : BaseViewModelTest() {
|
||||
packageName = "com.x8bit.bitwarden",
|
||||
signingInfo = SigningInfo(),
|
||||
origin = "mockOrigin",
|
||||
isUserVerified = true,
|
||||
)
|
||||
val mockIntent = createMockIntent(
|
||||
mockFido2CreateCredentialRequest = fido2CreateCredentialRequest,
|
||||
@@ -732,6 +736,7 @@ class MainViewModelTest : BaseViewModelTest() {
|
||||
packageName = "com.x8bit.bitwarden",
|
||||
signingInfo = SigningInfo(),
|
||||
origin = "mockOrigin",
|
||||
isUserVerified = true,
|
||||
)
|
||||
val mockIntent = createMockIntent(
|
||||
mockFido2CreateCredentialRequest = fido2CreateCredentialRequest,
|
||||
@@ -1139,7 +1144,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
|
||||
|
||||
+10
-10
@@ -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<SigningInfo> {
|
||||
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<SigningInfo> {
|
||||
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,
|
||||
|
||||
+2
@@ -5,6 +5,7 @@ import android.content.pm.SigningInfo
|
||||
fun createMockFido2CredentialAssertionRequest(
|
||||
number: Int = 1,
|
||||
userId: String = "mockUserId-$number",
|
||||
isUserVerified: Boolean = false,
|
||||
): Fido2CredentialAssertionRequest =
|
||||
Fido2CredentialAssertionRequest(
|
||||
userId = userId,
|
||||
@@ -15,4 +16,5 @@ fun createMockFido2CredentialAssertionRequest(
|
||||
packageName = "mockPackageName-$number",
|
||||
signingInfo = SigningInfo(),
|
||||
origin = "mockOrigin-$number",
|
||||
isUserVerified = isUserVerified,
|
||||
)
|
||||
+2
-1
@@ -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,
|
||||
)
|
||||
+72
-22
@@ -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,11 @@ 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
|
||||
every { getFeatureFlag(FlagKey.SingleTapPasskeyAuthentication) } returns false
|
||||
}
|
||||
private val cancellationSignal: CancellationSignal = mockk()
|
||||
|
||||
private val json = PlatformNetworkModule.providesJson()
|
||||
@@ -102,15 +114,20 @@ class Fido2ProviderProcessorTest {
|
||||
fido2CredentialManager,
|
||||
intentManager,
|
||||
clock,
|
||||
biometricsEncryptionManager,
|
||||
featureFlagManager,
|
||||
dispatcherManager,
|
||||
)
|
||||
|
||||
mockkStatic(Icon::class)
|
||||
mockkStatic(::isBuildVersionBelow)
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
unmockkStatic(Icon::class)
|
||||
unmockkStatic(::isBuildVersionBelow)
|
||||
unmockkConstructor(PublicKeyCredentialEntry.Builder::class)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -232,8 +249,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<BeginCreateCredentialResponse, CreateCredentialException> =
|
||||
@@ -256,6 +274,11 @@ class Fido2ProviderProcessorTest {
|
||||
any(),
|
||||
)
|
||||
} returns mockIntent
|
||||
every {
|
||||
biometricsEncryptionManager.getOrCreateCipher(any())
|
||||
} returns mockk<Cipher>()
|
||||
every { featureFlagManager.getFeatureFlag(FlagKey.SingleTapPasskeyCreation) } returns true
|
||||
every { isBuildVersionBelow(Build.VERSION_CODES.VANILLA_ICE_CREAM) } returns false
|
||||
|
||||
fido2Processor.processCreateCredentialRequest(request, cancellationSignal, callback)
|
||||
|
||||
@@ -272,6 +295,42 @@ 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 no biometric prompt data when cipher is null
|
||||
every { biometricsEncryptionManager.getOrCreateCipher(any()) } returns null
|
||||
fido2Processor.processCreateCredentialRequest(request, cancellationSignal, callback)
|
||||
assertTrue(
|
||||
captureSlot
|
||||
.captured
|
||||
.createEntries
|
||||
.all { it.biometricPromptData == null },
|
||||
) { "Expected all entries to have null biometric prompt data." }
|
||||
|
||||
// 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
|
||||
@@ -497,32 +556,23 @@ class Fido2ProviderProcessorTest {
|
||||
anyConstructed<PublicKeyCredentialEntry.Builder>().build()
|
||||
} returns mockPublicKeyCredentialEntry
|
||||
every { Icon.createWithResource(context, any()) } returns mockk<Icon>()
|
||||
every {
|
||||
biometricsEncryptionManager.getOrCreateCipher(any())
|
||||
} returns mockk<Cipher>()
|
||||
every {
|
||||
featureFlagManager.getFeatureFlag(FlagKey.SingleTapPasskeyAuthentication)
|
||||
} returns true
|
||||
every { isBuildVersionBelow(Build.VERSION_CODES.VANILLA_ICE_CREAM) } returns false
|
||||
|
||||
fido2Processor.processGetCredentialRequest(request, cancellationSignal, callback)
|
||||
|
||||
verify(exactly = 0) { callback.onError(any()) }
|
||||
// TODO: [PM-9515] Uncomment when SDK bug is fixed.
|
||||
// verify(exactly = 1) {
|
||||
// callback.onResult(any())
|
||||
// intentManager.createFido2GetCredentialPendingIntent(
|
||||
// action = "com.x8bit.bitwarden.fido2.ACTION_GET_PASSKEY",
|
||||
// credentialId = mockFido2CredentialAutofillViews.first().credentialId.toString(),
|
||||
// cipherId = mockFido2CredentialAutofillViews.first().cipherId,
|
||||
// requestCode = any(),
|
||||
// )
|
||||
// }
|
||||
// coVerify(exactly = 1) {
|
||||
// vaultRepository.silentlyDiscoverCredentials(
|
||||
// userId = DEFAULT_USER_STATE.activeUserId,
|
||||
// fido2CredentialStore = fido2CredentialStore,
|
||||
// relyingPartyId = "mockRelyingPartyId-1",
|
||||
// )
|
||||
// }
|
||||
|
||||
assertEquals(1, captureSlot.captured.credentialEntries.size)
|
||||
assertEquals(mockPublicKeyCredentialEntry, captureSlot.captured.credentialEntries.first())
|
||||
|
||||
unmockkConstructor(PublicKeyCredentialEntry.Builder::class)
|
||||
// Verify all entries have biometric prompt data when feature flag is enabled
|
||||
verify(exactly = captureSlot.captured.credentialEntries.size) {
|
||||
anyConstructed<PublicKeyCredentialEntry.Builder>().setBiometricPromptData(any())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-5
@@ -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 = false,
|
||||
),
|
||||
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
|
||||
@@ -199,6 +200,7 @@ class Fido2IntentUtilsTest {
|
||||
packageName = mockCallingAppInfo.packageName,
|
||||
signingInfo = mockCallingAppInfo.signingInfo,
|
||||
origin = mockCallingAppInfo.origin,
|
||||
isUserVerified = false,
|
||||
),
|
||||
assertionRequest,
|
||||
)
|
||||
|
||||
+1
@@ -152,6 +152,7 @@ class SpecialCircumstanceExtensionsTest {
|
||||
packageName = "mockPackageName",
|
||||
signingInfo = SigningInfo(),
|
||||
origin = "mockOrigin",
|
||||
isUserVerified = true,
|
||||
)
|
||||
assertEquals(
|
||||
fido2CreateCredentialRequest,
|
||||
|
||||
+4
@@ -121,6 +121,8 @@ private val DEFAULT_MAP_VALUE: Map<FlagKey<Any>, Any> = mapOf(
|
||||
FlagKey.NewDevicePermanentDismiss to true,
|
||||
FlagKey.IgnoreEnvironmentCheck to true,
|
||||
FlagKey.MutualTls to true,
|
||||
FlagKey.SingleTapPasskeyCreation to true,
|
||||
FlagKey.SingleTapPasskeyAuthentication to true,
|
||||
)
|
||||
|
||||
private val UPDATED_MAP_VALUE: Map<FlagKey<Any>, Any> = mapOf(
|
||||
@@ -138,6 +140,8 @@ private val UPDATED_MAP_VALUE: Map<FlagKey<Any>, Any> = mapOf(
|
||||
FlagKey.NewDevicePermanentDismiss to false,
|
||||
FlagKey.IgnoreEnvironmentCheck to false,
|
||||
FlagKey.MutualTls to false,
|
||||
FlagKey.SingleTapPasskeyCreation to false,
|
||||
FlagKey.SingleTapPasskeyAuthentication to false,
|
||||
)
|
||||
|
||||
private val DEFAULT_STATE = DebugMenuState(
|
||||
|
||||
+1
@@ -670,6 +670,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
|
||||
packageName = "com.x8bit.bitwarden",
|
||||
signingInfo = SigningInfo(),
|
||||
origin = "mockOrigin",
|
||||
isUserVerified = true,
|
||||
)
|
||||
specialCircumstanceManager.specialCircumstance =
|
||||
SpecialCircumstance.Fido2Save(fido2CreateCredentialRequest)
|
||||
|
||||
+16
-13
@@ -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<SigningInfo>(),
|
||||
origin = null,
|
||||
isUserVerified = true,
|
||||
)
|
||||
specialCircumstanceManager.specialCircumstance =
|
||||
SpecialCircumstance.Fido2Save(
|
||||
@@ -860,6 +862,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
||||
packageName = "mockPackageName",
|
||||
signingInfo = mockk<SigningInfo>(),
|
||||
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,
|
||||
|
||||
+2
@@ -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),
|
||||
|
||||
+21
-12
@@ -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(
|
||||
|
||||
+1
@@ -828,6 +828,7 @@ class VaultItemListingDataExtensionsTest {
|
||||
packageName = "",
|
||||
signingInfo = SigningInfo(),
|
||||
origin = "https://www.test.com",
|
||||
isUserVerified = true,
|
||||
),
|
||||
fido2CredentialAutofillViews = null,
|
||||
totpData = null,
|
||||
|
||||
Reference in new issue
Block a user