From 5761e9510ac8fbd5953f1bf42681bb4c69391771 Mon Sep 17 00:00:00 2001
From: David Perez <david@livefront.com>
Date: Wed, 21 Aug 2024 14:24:34 -0500
Subject: [PATCH] PM-11248: Add isUsingKeyConnector flag to UserState (#3798)

---
 .../auth/datasource/disk/AuthDiskSource.kt    |   5 +
 .../datasource/disk/AuthDiskSourceImpl.kt     |  14 +++
 .../auth/repository/AuthRepositoryImpl.kt     |  11 +-
 .../repository/model/UserKeyConnectorState.kt |   9 ++
 .../data/auth/repository/model/UserState.kt   |   2 +
 .../util/AuthDiskSourceExtensions.kt          |  42 +++++++
 .../util/UserStateJsonExtensions.kt           |   5 +
 .../com/x8bit/bitwarden/MainViewModelTest.kt  |   1 +
 .../datasource/disk/AuthDiskSourceTest.kt     |  20 ++++
 .../disk/util/FakeAuthDiskSource.kt           |  18 ++-
 .../auth/repository/AuthRepositoryTest.kt     |  16 +++
 .../util/AuthDiskSourceExtensionsTest.kt      | 109 ++++++++++++++++++
 .../util/UserStateJsonExtensionsTest.kt       |  22 ++++
 .../processor/Fido2ProviderProcessorTest.kt   |   1 +
 .../accountsetup/SetupUnlockViewModelTest.kt  |   1 +
 .../feature/landing/LandingViewModelTest.kt   |   6 +
 .../auth/feature/login/LoginViewModelTest.kt  |   1 +
 .../RemovePasswordViewModelTest.kt            |   1 +
 .../TrustedDeviceViewModelTest.kt             |   1 +
 .../vaultunlock/VaultUnlockViewModelTest.kt   |   3 +
 .../feature/rootnav/RootNavViewModelTest.kt   |  17 +++
 .../feature/search/SearchViewModelTest.kt     |   1 +
 .../AccountSecurityViewModelTest.kt           |   1 +
 .../DeleteAccountViewModelTest.kt             |   1 +
 .../LoginApprovalViewModelTest.kt             |   1 +
 .../exportvault/ExportVaultViewModelTest.kt   |   1 +
 .../generator/GeneratorViewModelTest.kt       |   1 +
 .../send/addsend/AddSendViewModelTest.kt      |   1 +
 .../addedit/VaultAddEditViewModelTest.kt      |   1 +
 .../addedit/util/CipherViewExtensionsTest.kt  |   1 +
 .../attachments/AttachmentsViewModelTest.kt   |   1 +
 .../feature/item/VaultItemViewModelTest.kt    |   1 +
 .../VaultItemListingViewModelTest.kt          |   1 +
 .../VaultMoveToOrganizationViewModelTest.kt   |   1 +
 .../VaultMoveToOrganizationExtensionsTest.kt  |   1 +
 .../vault/feature/vault/VaultViewModelTest.kt |   4 +
 .../vault/util/UserStateExtensionsTest.kt     |  10 ++
 37 files changed, 330 insertions(+), 3 deletions(-)
 create mode 100644 app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/UserKeyConnectorState.kt

diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSource.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSource.kt
index e11a09db1..1adde822e 100644
--- a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSource.kt
+++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSource.kt
@@ -50,6 +50,11 @@ interface AuthDiskSource {
      */
     fun getShouldUseKeyConnector(userId: String): Boolean?
 
+    /**
+     * Retrieves the state indicating that the user should use a key connector as a flow.
+     */
+    fun getShouldUseKeyConnectorFlow(userId: String): Flow<Boolean?>
+
     /**
      * Stores the boolean indicating that the user should use a key connector.
      */
diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceImpl.kt
index 1b75a2dde..49ee720d3 100644
--- a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceImpl.kt
+++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceImpl.kt
@@ -57,6 +57,8 @@ class AuthDiskSourceImpl(
     AuthDiskSource {
 
     private val inMemoryPinProtectedUserKeys = mutableMapOf<String, String?>()
+    private val mutableShouldUseKeyConnectorFlowMap =
+        mutableMapOf<String, MutableSharedFlow<Boolean?>>()
     private val mutableOrganizationsFlowMap =
         mutableMapOf<String, MutableSharedFlow<List<SyncResponseJson.Profile.Organization>?>>()
     private val mutablePoliciesFlowMap =
@@ -129,6 +131,10 @@ class AuthDiskSourceImpl(
         // indefinitely unless the TDE flow explicitly removes them.
     }
 
+    override fun getShouldUseKeyConnectorFlow(userId: String): Flow<Boolean?> =
+        getMutableShouldUseKeyConnectorFlowMap(userId = userId)
+            .onSubscription { emit(getShouldUseKeyConnector(userId = userId)) }
+
     override fun getShouldUseKeyConnector(
         userId: String,
     ): Boolean? = getBoolean(key = USES_KEY_CONNECTOR.appendIdentifier(userId))
@@ -138,6 +144,7 @@ class AuthDiskSourceImpl(
             key = USES_KEY_CONNECTOR.appendIdentifier(userId),
             value = shouldUseKeyConnector,
         )
+        getMutableShouldUseKeyConnectorFlowMap(userId = userId).tryEmit(shouldUseKeyConnector)
     }
 
     override fun getShouldTrustDevice(
@@ -381,6 +388,13 @@ class AuthDiskSourceImpl(
                 putString(key = UNIQUE_APP_ID_KEY, value = it)
             }
 
+    private fun getMutableShouldUseKeyConnectorFlowMap(
+        userId: String,
+    ): MutableSharedFlow<Boolean?> =
+        mutableShouldUseKeyConnectorFlowMap.getOrPut(userId) {
+            bufferedMutableSharedFlow(replay = 1)
+        }
+
     private fun getMutableOrganizationsFlow(
         userId: String,
     ): MutableSharedFlow<List<SyncResponseJson.Profile.Organization>?> =
diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt
index 1384c2749..c65f65b0f 100644
--- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt
+++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt
@@ -56,6 +56,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.SendVerificationEmailResul
 import com.x8bit.bitwarden.data.auth.repository.model.SetPasswordResult
 import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult
 import com.x8bit.bitwarden.data.auth.repository.model.UserAccountTokens
+import com.x8bit.bitwarden.data.auth.repository.model.UserKeyConnectorState
 import com.x8bit.bitwarden.data.auth.repository.model.UserOrganizations
 import com.x8bit.bitwarden.data.auth.repository.model.UserState
 import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult
@@ -74,6 +75,8 @@ import com.x8bit.bitwarden.data.auth.repository.util.toUserState
 import com.x8bit.bitwarden.data.auth.repository.util.toUserStateJsonWithPassword
 import com.x8bit.bitwarden.data.auth.repository.util.userAccountTokens
 import com.x8bit.bitwarden.data.auth.repository.util.userAccountTokensFlow
+import com.x8bit.bitwarden.data.auth.repository.util.userKeyConnectorStateFlow
+import com.x8bit.bitwarden.data.auth.repository.util.userKeyConnectorStateList
 import com.x8bit.bitwarden.data.auth.repository.util.userOrganizationsList
 import com.x8bit.bitwarden.data.auth.repository.util.userOrganizationsListFlow
 import com.x8bit.bitwarden.data.auth.repository.util.userSwitchingChangesFlow
@@ -238,6 +241,7 @@ class AuthRepositoryImpl(
         authDiskSource.userStateFlow,
         authDiskSource.userAccountTokensFlow,
         authDiskSource.userOrganizationsListFlow,
+        authDiskSource.userKeyConnectorStateFlow,
         vaultRepository.vaultUnlockDataStateFlow,
         mutableHasPendingAccountAdditionStateFlow,
         // Ignore the data in the merge, but trigger an update when they emit.
@@ -249,12 +253,14 @@ class AuthRepositoryImpl(
         val userStateJson = array[0] as UserStateJson?
         val userAccountTokens = array[1] as List<UserAccountTokens>
         val userOrganizationsList = array[2] as List<UserOrganizations>
-        val vaultState = array[3] as List<VaultUnlockData>
-        val hasPendingAccountAddition = array[4] as Boolean
+        val userIsUsingKeyConnectorList = array[3] as List<UserKeyConnectorState>
+        val vaultState = array[4] as List<VaultUnlockData>
+        val hasPendingAccountAddition = array[5] as Boolean
         userStateJson?.toUserState(
             vaultState = vaultState,
             userAccountTokens = userAccountTokens,
             userOrganizationsList = userOrganizationsList,
+            userIsUsingKeyConnectorList = userIsUsingKeyConnectorList,
             hasPendingAccountAddition = hasPendingAccountAddition,
             isBiometricsEnabledProvider = ::isBiometricsEnabled,
             vaultUnlockTypeProvider = ::getVaultUnlockType,
@@ -272,6 +278,7 @@ class AuthRepositoryImpl(
                     vaultState = vaultRepository.vaultUnlockDataStateFlow.value,
                     userAccountTokens = authDiskSource.userAccountTokens,
                     userOrganizationsList = authDiskSource.userOrganizationsList,
+                    userIsUsingKeyConnectorList = authDiskSource.userKeyConnectorStateList,
                     hasPendingAccountAddition = mutableHasPendingAccountAdditionStateFlow.value,
                     isBiometricsEnabledProvider = ::isBiometricsEnabled,
                     vaultUnlockTypeProvider = ::getVaultUnlockType,
diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/UserKeyConnectorState.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/UserKeyConnectorState.kt
new file mode 100644
index 000000000..c16fc56e4
--- /dev/null
+++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/UserKeyConnectorState.kt
@@ -0,0 +1,9 @@
+package com.x8bit.bitwarden.data.auth.repository.model
+
+/**
+ * Associates [isUsingKeyConnector] with the given [userId].
+ */
+data class UserKeyConnectorState(
+    val userId: String,
+    val isUsingKeyConnector: Boolean?,
+)
diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/UserState.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/UserState.kt
index 801848099..ced9a7c88 100644
--- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/UserState.kt
+++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/UserState.kt
@@ -50,6 +50,7 @@ data class UserState(
      * @property isBiometricsEnabled Indicates that the biometrics mechanism for unlocking the
      * user's vault is enabled.
      * @property vaultUnlockType The mechanism by which the user's vault may be unlocked.
+     * @property isUsingKeyConnector Indicates if the account is currently using a key connector.
      */
     data class Account(
         val userId: String,
@@ -67,6 +68,7 @@ data class UserState(
         val organizations: List<Organization>,
         val isBiometricsEnabled: Boolean,
         val vaultUnlockType: VaultUnlockType = VaultUnlockType.MASTER_PASSWORD,
+        val isUsingKeyConnector: Boolean,
     ) {
         /**
          * Indicates that the user does or does not have a means to manually unlock the vault.
diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensions.kt
index a7020459d..e127b6f57 100644
--- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensions.kt
+++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensions.kt
@@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.auth.repository.util
 
 import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
 import com.x8bit.bitwarden.data.auth.repository.model.UserAccountTokens
+import com.x8bit.bitwarden.data.auth.repository.model.UserKeyConnectorState
 import com.x8bit.bitwarden.data.auth.repository.model.UserOrganizations
 import com.x8bit.bitwarden.data.auth.repository.model.UserSwitchingData
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -100,6 +101,47 @@ val AuthDiskSource.userAccountTokensFlow: Flow<List<UserAccountTokens>>
         }
         .distinctUntilChanged()
 
+/**
+ * Returns the current list of [UserKeyConnectorState].
+ */
+val AuthDiskSource.userKeyConnectorStateList: List<UserKeyConnectorState>
+    get() = this
+        .userState
+        ?.accounts
+        .orEmpty()
+        .map { (userId, _) ->
+            UserKeyConnectorState(
+                userId = userId,
+                isUsingKeyConnector = this.getShouldUseKeyConnector(userId = userId),
+            )
+        }
+
+/**
+ * Returns a [Flow] that emits distinct updates to [UserKeyConnectorState].
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+val AuthDiskSource.userKeyConnectorStateFlow: Flow<List<UserKeyConnectorState>>
+    get() = this
+        .userStateFlow
+        .flatMapLatest { userStateJson ->
+            combine(
+                userStateJson
+                    ?.accounts
+                    .orEmpty()
+                    .map { (userId, _) ->
+                        this
+                            .getShouldUseKeyConnectorFlow(userId = userId)
+                            .map {
+                                UserKeyConnectorState(
+                                    userId = userId,
+                                    isUsingKeyConnector = it,
+                                )
+                            }
+                    },
+            ) { it.toList() }
+        }
+        .distinctUntilChanged()
+
 /**
  * Returns a [Flow] that emits every time the active user is changed.
  */
diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensions.kt
index a0634b76d..a57a77cd3 100644
--- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensions.kt
+++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensions.kt
@@ -3,6 +3,7 @@ package com.x8bit.bitwarden.data.auth.repository.util
 import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
 import com.x8bit.bitwarden.data.auth.datasource.network.model.UserDecryptionOptionsJson
 import com.x8bit.bitwarden.data.auth.repository.model.UserAccountTokens
+import com.x8bit.bitwarden.data.auth.repository.model.UserKeyConnectorState
 import com.x8bit.bitwarden.data.auth.repository.model.UserOrganizations
 import com.x8bit.bitwarden.data.auth.repository.model.UserState
 import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType
@@ -79,6 +80,7 @@ fun UserStateJson.toUserState(
     vaultState: List<VaultUnlockData>,
     userAccountTokens: List<UserAccountTokens>,
     userOrganizationsList: List<UserOrganizations>,
+    userIsUsingKeyConnectorList: List<UserKeyConnectorState>,
     hasPendingAccountAddition: Boolean,
     isBiometricsEnabledProvider: (userId: String) -> Boolean,
     vaultUnlockTypeProvider: (userId: String) -> VaultUnlockType,
@@ -133,6 +135,9 @@ fun UserStateJson.toUserState(
                     needsMasterPassword = needsMasterPassword,
                     hasMasterPassword = decryptionOptions?.hasMasterPassword != false,
                     trustedDevice = trustedDevice,
+                    isUsingKeyConnector = userIsUsingKeyConnectorList
+                        .find { it.userId == userId }
+                        ?.isUsingKeyConnector == true,
                 )
             },
         hasPendingAccountAddition = hasPendingAccountAddition,
diff --git a/app/src/test/java/com/x8bit/bitwarden/MainViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/MainViewModelTest.kt
index 5d8672feb..629235dc6 100644
--- a/app/src/test/java/com/x8bit/bitwarden/MainViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/MainViewModelTest.kt
@@ -778,6 +778,7 @@ private val DEFAULT_ACCOUNT = UserState.Account(
     needsMasterPassword = false,
     trustedDevice = null,
     hasMasterPassword = true,
+    isUsingKeyConnector = false,
 )
 
 private val DEFAULT_USER_STATE = UserState(
diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceTest.kt
index eff273e2b..67c02106b 100644
--- a/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceTest.kt
@@ -139,6 +139,26 @@ class AuthDiskSourceTest {
         assertFalse(authDiskSource.getShouldUseKeyConnector(userId = userId) ?: true)
     }
 
+    @Test
+    fun `getShouldUseKeyConnectorFlow should react to changes in getShouldUseKeyConnector`() =
+        runTest {
+            val mockUserId = "mockUserId"
+            authDiskSource.getShouldUseKeyConnectorFlow(userId = mockUserId).test {
+                assertNull(authDiskSource.getShouldUseKeyConnector(userId = mockUserId))
+                assertNull(awaitItem())
+                authDiskSource.storeShouldUseKeyConnector(
+                    userId = mockUserId,
+                    shouldUseKeyConnector = true,
+                )
+                assertEquals(true, awaitItem())
+                authDiskSource.storeShouldUseKeyConnector(
+                    userId = mockUserId,
+                    shouldUseKeyConnector = false,
+                )
+                assertEquals(false, awaitItem())
+            }
+        }
+
     @Test
     fun `shouldTrustDevice should pull from and update SharedPreferences`() {
         val userId = "userId"
diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/disk/util/FakeAuthDiskSource.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/disk/util/FakeAuthDiskSource.kt
index 528b6d1dc..6101ddf17 100644
--- a/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/disk/util/FakeAuthDiskSource.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/disk/util/FakeAuthDiskSource.kt
@@ -18,6 +18,8 @@ class FakeAuthDiskSource : AuthDiskSource {
     override var rememberedEmailAddress: String? = null
     override var rememberedOrgIdentifier: String? = null
 
+    private val mutableShouldUseKeyConnectorFlowMap =
+        mutableMapOf<String, MutableSharedFlow<Boolean?>>()
     private val mutableOrganizationsFlowMap =
         mutableMapOf<String, MutableSharedFlow<List<SyncResponseJson.Profile.Organization>?>>()
     private val mutablePoliciesFlowMap =
@@ -68,17 +70,24 @@ class FakeAuthDiskSource : AuthDiskSource {
         storedBiometricKeys.remove(userId)
         storedOrganizationKeys.remove(userId)
 
+        mutableShouldUseKeyConnectorFlowMap.remove(userId)
         mutableOrganizationsFlowMap.remove(userId)
         mutablePoliciesFlowMap.remove(userId)
         mutableAccountTokensFlowMap.remove(userId)
     }
 
+    override fun getShouldUseKeyConnectorFlow(
+        userId: String,
+    ): Flow<Boolean?> = getMutableShouldUseKeyConnectorFlow(userId = userId)
+        .onSubscription { emit(getShouldUseKeyConnector(userId = userId)) }
+
     override fun getShouldUseKeyConnector(
         userId: String,
-    ): Boolean = storedShouldUseKeyConnector[userId] ?: false
+    ): Boolean? = storedShouldUseKeyConnector[userId]
 
     override fun storeShouldUseKeyConnector(userId: String, shouldUseKeyConnector: Boolean?) {
         storedShouldUseKeyConnector[userId] = shouldUseKeyConnector
+        getMutableShouldUseKeyConnectorFlow(userId = userId).tryEmit(shouldUseKeyConnector)
     }
 
     override fun getShouldTrustDevice(userId: String): Boolean =
@@ -354,6 +363,13 @@ class FakeAuthDiskSource : AuthDiskSource {
 
     //region Private helper functions
 
+    private fun getMutableShouldUseKeyConnectorFlow(
+        userId: String,
+    ): MutableSharedFlow<Boolean?> =
+        mutableShouldUseKeyConnectorFlowMap.getOrPut(userId) {
+            bufferedMutableSharedFlow(replay = 1)
+        }
+
     private fun getMutableOrganizationsFlow(
         userId: String,
     ): MutableSharedFlow<List<SyncResponseJson.Profile.Organization>?> =
diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt
index 9de005225..affdd9838 100644
--- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt
@@ -72,6 +72,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.ResetPasswordResult
 import com.x8bit.bitwarden.data.auth.repository.model.SendVerificationEmailResult
 import com.x8bit.bitwarden.data.auth.repository.model.SetPasswordResult
 import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult
+import com.x8bit.bitwarden.data.auth.repository.model.UserKeyConnectorState
 import com.x8bit.bitwarden.data.auth.repository.model.UserOrganizations
 import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult
 import com.x8bit.bitwarden.data.auth.repository.model.ValidatePinResult
@@ -316,6 +317,7 @@ class AuthRepositoryTest {
                 vaultState = VAULT_UNLOCK_DATA,
                 userAccountTokens = emptyList(),
                 userOrganizationsList = emptyList(),
+                userIsUsingKeyConnectorList = emptyList(),
                 hasPendingAccountAddition = false,
                 isBiometricsEnabledProvider = { false },
                 vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD },
@@ -340,6 +342,7 @@ class AuthRepositoryTest {
                 vaultState = VAULT_UNLOCK_DATA,
                 userAccountTokens = emptyList(),
                 userOrganizationsList = emptyList(),
+                userIsUsingKeyConnectorList = emptyList(),
                 hasPendingAccountAddition = false,
                 isBiometricsEnabledProvider = { false },
                 vaultUnlockTypeProvider = { VaultUnlockType.PIN },
@@ -355,6 +358,7 @@ class AuthRepositoryTest {
                 vaultState = emptyVaultState,
                 userAccountTokens = emptyList(),
                 userOrganizationsList = emptyList(),
+                userIsUsingKeyConnectorList = emptyList(),
                 hasPendingAccountAddition = false,
                 isBiometricsEnabledProvider = { false },
                 vaultUnlockTypeProvider = { VaultUnlockType.PIN },
@@ -382,6 +386,7 @@ class AuthRepositoryTest {
                 vaultState = emptyVaultState,
                 userAccountTokens = emptyList(),
                 userOrganizationsList = USER_ORGANIZATIONS,
+                userIsUsingKeyConnectorList = USER_SHOULD_USER_KEY_CONNECTOR,
                 hasPendingAccountAddition = false,
                 isBiometricsEnabledProvider = { false },
                 vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD },
@@ -597,6 +602,7 @@ class AuthRepositoryTest {
             vaultState = VAULT_UNLOCK_DATA,
             userAccountTokens = emptyList(),
             userOrganizationsList = emptyList(),
+            userIsUsingKeyConnectorList = emptyList(),
             hasPendingAccountAddition = false,
             isBiometricsEnabledProvider = { false },
             vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD },
@@ -606,6 +612,7 @@ class AuthRepositoryTest {
             vaultState = VAULT_UNLOCK_DATA,
             userAccountTokens = emptyList(),
             userOrganizationsList = emptyList(),
+            userIsUsingKeyConnectorList = emptyList(),
             hasPendingAccountAddition = false,
             isBiometricsEnabledProvider = { false },
             vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD },
@@ -4752,6 +4759,7 @@ class AuthRepositoryTest {
             vaultState = VAULT_UNLOCK_DATA,
             userAccountTokens = emptyList(),
             userOrganizationsList = emptyList(),
+            userIsUsingKeyConnectorList = emptyList(),
             hasPendingAccountAddition = false,
             isBiometricsEnabledProvider = { false },
             vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD },
@@ -4784,6 +4792,7 @@ class AuthRepositoryTest {
             vaultState = VAULT_UNLOCK_DATA,
             userAccountTokens = emptyList(),
             userOrganizationsList = emptyList(),
+            userIsUsingKeyConnectorList = emptyList(),
             hasPendingAccountAddition = false,
             isBiometricsEnabledProvider = { false },
             vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD },
@@ -4814,6 +4823,7 @@ class AuthRepositoryTest {
             vaultState = VAULT_UNLOCK_DATA,
             userAccountTokens = emptyList(),
             userOrganizationsList = emptyList(),
+            userIsUsingKeyConnectorList = emptyList(),
             hasPendingAccountAddition = false,
             isBiometricsEnabledProvider = { false },
             vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD },
@@ -5596,6 +5606,12 @@ class AuthRepositoryTest {
                 organizations = ORGANIZATIONS.toOrganizations(),
             ),
         )
+        private val USER_SHOULD_USER_KEY_CONNECTOR = listOf(
+            UserKeyConnectorState(
+                userId = USER_ID_1,
+                isUsingKeyConnector = null,
+            ),
+        )
         private val VAULT_UNLOCK_DATA = listOf(
             VaultUnlockData(
                 userId = USER_ID_1,
diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt
index 359b09158..3965d1f04 100644
--- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt
@@ -8,6 +8,7 @@ import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
 import com.x8bit.bitwarden.data.auth.datasource.disk.util.FakeAuthDiskSource
 import com.x8bit.bitwarden.data.auth.repository.model.Organization
 import com.x8bit.bitwarden.data.auth.repository.model.UserAccountTokens
+import com.x8bit.bitwarden.data.auth.repository.model.UserKeyConnectorState
 import com.x8bit.bitwarden.data.auth.repository.model.UserOrganizations
 import com.x8bit.bitwarden.data.auth.repository.model.UserSwitchingData
 import com.x8bit.bitwarden.data.vault.datasource.network.model.OrganizationType
@@ -220,6 +221,114 @@ class AuthDiskSourceExtensionsTest {
         )
     }
 
+    @Test
+    fun `userKeyConnectorStateFlow should emit whenever there are changes to key connector data`() =
+        runTest {
+            val mockAccounts = mapOf(
+                "userId1" to mockk<AccountJson>(),
+                "userId2" to mockk<AccountJson>(),
+                "userId3" to mockk<AccountJson>(),
+            )
+            val userStateJson = mockk<UserStateJson> {
+                every { accounts } returns mockAccounts
+            }
+            authDiskSource.apply {
+                userState = userStateJson
+                storeShouldUseKeyConnector(
+                    userId = "userId1",
+                    shouldUseKeyConnector = false,
+                )
+            }
+
+            authDiskSource.userKeyConnectorStateFlow.test {
+                assertEquals(
+                    listOf(
+                        UserKeyConnectorState(
+                            userId = "userId1",
+                            isUsingKeyConnector = false,
+                        ),
+                        UserKeyConnectorState(
+                            userId = "userId2",
+                            isUsingKeyConnector = null,
+                        ),
+                        UserKeyConnectorState(
+                            userId = "userId3",
+                            isUsingKeyConnector = null,
+                        ),
+                    ),
+                    awaitItem(),
+                )
+
+                authDiskSource.storeShouldUseKeyConnector(
+                    userId = "userId2",
+                    shouldUseKeyConnector = true,
+                )
+
+                assertEquals(
+                    listOf(
+                        UserKeyConnectorState(
+                            userId = "userId1",
+                            isUsingKeyConnector = false,
+                        ),
+                        UserKeyConnectorState(
+                            userId = "userId2",
+                            isUsingKeyConnector = true,
+                        ),
+                        UserKeyConnectorState(
+                            userId = "userId3",
+                            isUsingKeyConnector = null,
+                        ),
+                    ),
+                    awaitItem(),
+                )
+            }
+        }
+
+    @Test
+    fun `userKeyConnectorStateList should return data for all available users`() {
+        val mockAccounts = mapOf(
+            "userId1" to mockk<AccountJson>(),
+            "userId2" to mockk<AccountJson>(),
+            "userId3" to mockk<AccountJson>(),
+        )
+        val userStateJson = mockk<UserStateJson> {
+            every { accounts } returns mockAccounts
+        }
+        authDiskSource.apply {
+            userState = userStateJson
+            storeShouldUseKeyConnector(
+                userId = "userId1",
+                shouldUseKeyConnector = false,
+            )
+            storeShouldUseKeyConnector(
+                userId = "userId2",
+                shouldUseKeyConnector = true,
+            )
+            storeShouldUseKeyConnector(
+                userId = "userId3",
+                shouldUseKeyConnector = null,
+            )
+        }
+
+        assertEquals(
+            listOf(
+                UserKeyConnectorState(
+                    userId = "userId1",
+                    isUsingKeyConnector = false,
+                ),
+                UserKeyConnectorState(
+                    userId = "userId2",
+                    isUsingKeyConnector = true,
+                ),
+                UserKeyConnectorState(
+                    userId = "userId3",
+                    isUsingKeyConnector = null,
+                ),
+            ),
+            authDiskSource.userKeyConnectorStateList,
+        )
+    }
+
     @Test
     fun `userOrganizationsListFlow should emit whenever there are changes to organization data`() =
         runTest {
diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt
index c83513632..81446042b 100644
--- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt
@@ -11,6 +11,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceUserD
 import com.x8bit.bitwarden.data.auth.datasource.network.model.UserDecryptionOptionsJson
 import com.x8bit.bitwarden.data.auth.repository.model.Organization
 import com.x8bit.bitwarden.data.auth.repository.model.UserAccountTokens
+import com.x8bit.bitwarden.data.auth.repository.model.UserKeyConnectorState
 import com.x8bit.bitwarden.data.auth.repository.model.UserOrganizations
 import com.x8bit.bitwarden.data.auth.repository.model.UserState
 import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType
@@ -239,6 +240,7 @@ class UserStateJsonExtensionsTest {
                         needsMasterPassword = false,
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             ),
@@ -296,6 +298,12 @@ class UserStateJsonExtensionsTest {
                             ),
                         ),
                     ),
+                    userIsUsingKeyConnectorList = listOf(
+                        UserKeyConnectorState(
+                            userId = "activeUserId",
+                            isUsingKeyConnector = false,
+                        ),
+                    ),
                     hasPendingAccountAddition = false,
                     isBiometricsEnabledProvider = { false },
                     vaultUnlockTypeProvider = { VaultUnlockType.PIN },
@@ -334,6 +342,7 @@ class UserStateJsonExtensionsTest {
                         needsMasterPassword = true,
                         trustedDevice = null,
                         hasMasterPassword = false,
+                        isUsingKeyConnector = false,
                     ),
                 ),
                 hasPendingAccountAddition = true,
@@ -387,6 +396,12 @@ class UserStateJsonExtensionsTest {
                             ),
                         ),
                     ),
+                    userIsUsingKeyConnectorList = listOf(
+                        UserKeyConnectorState(
+                            userId = "activeUserId",
+                            isUsingKeyConnector = null,
+                        ),
+                    ),
                     hasPendingAccountAddition = true,
                     isBiometricsEnabledProvider = { true },
                     vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD },
@@ -431,6 +446,7 @@ class UserStateJsonExtensionsTest {
                             hasResetPasswordPermission = false,
                         ),
                         hasMasterPassword = false,
+                        isUsingKeyConnector = true,
                     ),
                 ),
                 hasPendingAccountAddition = true,
@@ -487,6 +503,12 @@ class UserStateJsonExtensionsTest {
                             ),
                         ),
                     ),
+                    userIsUsingKeyConnectorList = listOf(
+                        UserKeyConnectorState(
+                            userId = "activeUserId",
+                            isUsingKeyConnector = true,
+                        ),
+                    ),
                     hasPendingAccountAddition = true,
                     isBiometricsEnabledProvider = { false },
                     vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD },
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 c1b73518b..45a5a3c8e 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
@@ -531,6 +531,7 @@ private fun createMockAccounts(number: Int): List<UserState.Account> {
                 isBiometricsEnabled = false,
                 vaultUnlockType = VaultUnlockType.MASTER_PASSWORD,
                 hasMasterPassword = true,
+                isUsingKeyConnector = false,
             ),
         )
     }
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModelTest.kt
index 281da3091..c123c1850 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModelTest.kt
@@ -301,6 +301,7 @@ private val DEFAULT_USER_STATE: UserState = UserState(
             needsMasterPassword = false,
             trustedDevice = null,
             hasMasterPassword = true,
+            isUsingKeyConnector = false,
         ),
     ),
 )
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt
index 261e6fb63..0feb64ec4 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt
@@ -85,6 +85,7 @@ class LandingViewModelTest : BaseViewModelTest() {
                     needsMasterPassword = false,
                     trustedDevice = null,
                     hasMasterPassword = true,
+                    isUsingKeyConnector = false,
                 ),
             ),
         )
@@ -220,6 +221,7 @@ class LandingViewModelTest : BaseViewModelTest() {
             needsMasterPassword = false,
             trustedDevice = null,
             hasMasterPassword = true,
+            isUsingKeyConnector = false,
         )
         val userState = UserState(
             activeUserId = "activeUserId",
@@ -274,6 +276,7 @@ class LandingViewModelTest : BaseViewModelTest() {
                 needsMasterPassword = false,
                 trustedDevice = null,
                 hasMasterPassword = true,
+                isUsingKeyConnector = false,
             )
             val userState = UserState(
                 activeUserId = "activeUserId",
@@ -332,6 +335,7 @@ class LandingViewModelTest : BaseViewModelTest() {
                 needsMasterPassword = false,
                 trustedDevice = null,
                 hasMasterPassword = true,
+                isUsingKeyConnector = false,
             )
             val userState = UserState(
                 activeUserId = "activeUserId",
@@ -506,6 +510,7 @@ class LandingViewModelTest : BaseViewModelTest() {
             isBiometricsEnabled = false,
             vaultUnlockType = VaultUnlockType.MASTER_PASSWORD,
             hasMasterPassword = true,
+            isUsingKeyConnector = false,
         )
 
         val userState = UserState(
@@ -539,6 +544,7 @@ class LandingViewModelTest : BaseViewModelTest() {
             isBiometricsEnabled = false,
             vaultUnlockType = VaultUnlockType.MASTER_PASSWORD,
             hasMasterPassword = true,
+            isUsingKeyConnector = false,
         )
 
         val userState = UserState(
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt
index 3e89ff96e..e71e218a2 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt
@@ -128,6 +128,7 @@ class LoginViewModelTest : BaseViewModelTest() {
                     needsMasterPassword = false,
                     trustedDevice = null,
                     hasMasterPassword = true,
+                    isUsingKeyConnector = false,
                 ),
             ),
         )
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt
index 64d4a1d12..2bdf9b2f0 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt
@@ -98,6 +98,7 @@ private val DEFAULT_ACCOUNT = UserState.Account(
     needsMasterPassword = false,
     trustedDevice = null,
     hasMasterPassword = true,
+    isUsingKeyConnector = false,
 )
 
 private val DEFAULT_USER_STATE = UserState(
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModelTest.kt
index 344daf850..f20fed662 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModelTest.kt
@@ -273,6 +273,7 @@ private val DEFAULT_ACCOUNT = UserState.Account(
     needsMasterPassword = false,
     trustedDevice = TRUSTED_DEVICE,
     hasMasterPassword = false,
+    isUsingKeyConnector = false,
 )
 
 private val DEFAULT_USER_STATE = UserState(
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt
index 2e2580748..1a89b874a 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt
@@ -213,6 +213,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() {
                         needsMasterPassword = false,
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             )
@@ -250,6 +251,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() {
                         needsMasterPassword = false,
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             )
@@ -1008,6 +1010,7 @@ private val DEFAULT_ACCOUNT = UserState.Account(
     needsMasterPassword = false,
     trustedDevice = null,
     hasMasterPassword = true,
+    isUsingKeyConnector = false,
 )
 
 private val DEFAULT_USER_STATE = UserState(
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 d8b4ed4f2..be3cdb104 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
@@ -74,6 +74,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
                         needsMasterPassword = false,
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             ),
@@ -106,6 +107,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
                         needsMasterPassword = false,
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             ),
@@ -135,6 +137,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
                         needsMasterPassword = true,
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             ),
@@ -172,6 +175,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
                             hasResetPasswordPermission = false,
                         ),
                         hasMasterPassword = false,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             ),
@@ -207,6 +211,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
                             hasResetPasswordPermission = false,
                         ),
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             ),
@@ -242,6 +247,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
                             hasResetPasswordPermission = false,
                         ),
                         hasMasterPassword = false,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             ),
@@ -275,6 +281,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
                         needsMasterPassword = false,
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
                 hasPendingAccountAddition = true,
@@ -308,6 +315,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
                         needsMasterPassword = false,
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             ),
@@ -346,6 +354,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
                         needsMasterPassword = false,
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             ),
@@ -384,6 +393,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
                         needsMasterPassword = false,
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             ),
@@ -428,6 +438,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
                         needsMasterPassword = false,
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             ),
@@ -473,6 +484,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
                         needsMasterPassword = false,
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             ),
@@ -513,6 +525,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
                         needsMasterPassword = false,
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             ),
@@ -552,6 +565,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
                         needsMasterPassword = false,
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             ),
@@ -626,6 +640,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
                         needsMasterPassword = false,
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             ),
@@ -675,6 +690,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
                         needsMasterPassword = false,
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             ),
@@ -712,6 +728,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
                         needsMasterPassword = false,
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             ),
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt
index 82830c325..a2c102c87 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt
@@ -1417,6 +1417,7 @@ private val DEFAULT_USER_STATE = UserState(
             needsMasterPassword = false,
             trustedDevice = null,
             hasMasterPassword = true,
+            isUsingKeyConnector = false,
         ),
     ),
 )
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt
index 6151f5208..e4d228c6c 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt
@@ -640,6 +640,7 @@ private val DEFAULT_USER_STATE = UserState(
             needsMasterPassword = false,
             trustedDevice = null,
             hasMasterPassword = true,
+            isUsingKeyConnector = false,
         ),
     ),
 )
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccount/DeleteAccountViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccount/DeleteAccountViewModelTest.kt
index c4eee9bfa..29d2fecf3 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccount/DeleteAccountViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccount/DeleteAccountViewModelTest.kt
@@ -241,6 +241,7 @@ private val DEFAULT_USER_STATE: UserState = UserState(
                 hasResetPasswordPermission = true,
             ),
             hasMasterPassword = true,
+            isUsingKeyConnector = false,
         ),
     ),
 )
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/loginapproval/LoginApprovalViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/loginapproval/LoginApprovalViewModelTest.kt
index eff843a77..8349c9d2c 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/loginapproval/LoginApprovalViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/loginapproval/LoginApprovalViewModelTest.kt
@@ -369,6 +369,7 @@ private val DEFAULT_USER_STATE = UserState(
             needsMasterPassword = false,
             trustedDevice = null,
             hasMasterPassword = true,
+            isUsingKeyConnector = false,
         ),
     ),
 )
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/exportvault/ExportVaultViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/exportvault/ExportVaultViewModelTest.kt
index 0ee8af27d..cfecfc57d 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/exportvault/ExportVaultViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/exportvault/ExportVaultViewModelTest.kt
@@ -721,6 +721,7 @@ private val DEFAULT_USER_STATE = UserState(
             needsMasterPassword = false,
             trustedDevice = null,
             hasMasterPassword = true,
+            isUsingKeyConnector = false,
         ),
     ),
 )
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt
index d0194e771..1605764a8 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt
@@ -2405,6 +2405,7 @@ private val DEFAULT_USER_STATE = UserState(
             needsMasterPassword = false,
             trustedDevice = null,
             hasMasterPassword = true,
+            isUsingKeyConnector = false,
         ),
     ),
 )
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/addsend/AddSendViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/addsend/AddSendViewModelTest.kt
index 973860609..3d1dd75ee 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/addsend/AddSendViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/addsend/AddSendViewModelTest.kt
@@ -1100,6 +1100,7 @@ class AddSendViewModelTest : BaseViewModelTest() {
             needsMasterPassword = false,
             trustedDevice = null,
             hasMasterPassword = true,
+            isUsingKeyConnector = false,
         )
 
         private val DEFAULT_USER_STATE = UserState(
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 2646112c6..e2286d589 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
@@ -3888,6 +3888,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
                     needsMasterPassword = false,
                     trustedDevice = null,
                     hasMasterPassword = true,
+                    isUsingKeyConnector = false,
                 ),
             ),
             hasPendingAccountAddition = false,
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt
index 71a556a4a..c5690ee51 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt
@@ -444,6 +444,7 @@ class CipherViewExtensionsTest {
             needsMasterPassword = false,
             trustedDevice = null,
             hasMasterPassword = true,
+            isUsingKeyConnector = false,
         )
 }
 
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModelTest.kt
index 263b491f9..75abe4b69 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModelTest.kt
@@ -561,6 +561,7 @@ private val DEFAULT_USER_STATE = UserState(
             needsMasterPassword = false,
             trustedDevice = null,
             hasMasterPassword = true,
+            isUsingKeyConnector = false,
         ),
     ),
 )
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt
index dbfcee9af..2f73e1e81 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt
@@ -2578,6 +2578,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
                     needsMasterPassword = false,
                     trustedDevice = null,
                     hasMasterPassword = true,
+                    isUsingKeyConnector = false,
                 ),
             ),
         )
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 ac020bb9f..6e34418e4 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
@@ -3784,6 +3784,7 @@ private val DEFAULT_ACCOUNT = UserState.Account(
     needsMasterPassword = false,
     trustedDevice = null,
     hasMasterPassword = true,
+    isUsingKeyConnector = false,
 )
 
 private val DEFAULT_USER_STATE = UserState(
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt
index c524ab4d5..fd80aadfa 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt
@@ -509,6 +509,7 @@ private val DEFAULT_USER_STATE = UserState(
             ),
             trustedDevice = null,
             hasMasterPassword = true,
+            isUsingKeyConnector = false,
         ),
     ),
 )
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt
index 9b0405ae0..9c0eac5fa 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt
@@ -124,6 +124,7 @@ private fun createMockUserState(hasOrganizations: Boolean = true): UserState =
                 },
                 trustedDevice = null,
                 hasMasterPassword = true,
+                isUsingKeyConnector = false,
             ),
         ),
     )
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt
index 05eddb3e7..fee9dd579 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt
@@ -194,6 +194,7 @@ class VaultViewModelTest : BaseViewModelTest() {
                         ),
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             )
@@ -276,6 +277,7 @@ class VaultViewModelTest : BaseViewModelTest() {
                         ),
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             )
@@ -1525,6 +1527,7 @@ private val DEFAULT_USER_STATE = UserState(
             needsMasterPassword = false,
             trustedDevice = null,
             hasMasterPassword = true,
+            isUsingKeyConnector = false,
         ),
         UserState.Account(
             userId = "lockedUserId",
@@ -1541,6 +1544,7 @@ private val DEFAULT_USER_STATE = UserState(
             needsMasterPassword = false,
             trustedDevice = null,
             hasMasterPassword = true,
+            isUsingKeyConnector = false,
         ),
     ),
 )
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt
index b11b6e0ce..97aa844d7 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt
@@ -83,6 +83,7 @@ class UserStateExtensionsTest {
                         ),
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                     UserState.Account(
                         userId = "lockedUserId",
@@ -106,6 +107,7 @@ class UserStateExtensionsTest {
                         ),
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                     UserState.Account(
                         userId = "unlockedUserId",
@@ -133,6 +135,7 @@ class UserStateExtensionsTest {
                         ),
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                     UserState.Account(
                         userId = "loggedOutUserId",
@@ -160,6 +163,7 @@ class UserStateExtensionsTest {
                         ),
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             )
@@ -202,6 +206,7 @@ class UserStateExtensionsTest {
                 ),
                 trustedDevice = null,
                 hasMasterPassword = true,
+                isUsingKeyConnector = false,
             )
                 .toAccountSummary(isActive = true),
         )
@@ -242,6 +247,7 @@ class UserStateExtensionsTest {
                 ),
                 trustedDevice = null,
                 hasMasterPassword = true,
+                isUsingKeyConnector = false,
             )
                 .toAccountSummary(isActive = false),
         )
@@ -286,6 +292,7 @@ class UserStateExtensionsTest {
                         ),
                         trustedDevice = null,
                         hasMasterPassword = true,
+                        isUsingKeyConnector = false,
                     ),
                 ),
             )
@@ -311,6 +318,7 @@ class UserStateExtensionsTest {
                 needsMasterPassword = false,
                 trustedDevice = null,
                 hasMasterPassword = true,
+                isUsingKeyConnector = false,
             )
                 .toVaultFilterData(isIndividualVaultDisabled = false),
         )
@@ -363,6 +371,7 @@ class UserStateExtensionsTest {
                 ),
                 trustedDevice = null,
                 hasMasterPassword = true,
+                isUsingKeyConnector = false,
             )
                 .toVaultFilterData(
                     isIndividualVaultDisabled = false,
@@ -416,6 +425,7 @@ class UserStateExtensionsTest {
                 ),
                 trustedDevice = null,
                 hasMasterPassword = true,
+                isUsingKeyConnector = false,
             )
                 .toVaultFilterData(
                     isIndividualVaultDisabled = true,