mirror of
https://github.com/bitwarden/android.git
synced 2025-02-16 11:59:57 +03:00
[PM-8137] Respond to SDK user verification callbacks implicitly (#3448)
This commit is contained in:
parent
27747b6cb9
commit
c6d05b4631
10 changed files with 188 additions and 258 deletions
|
@ -2,8 +2,6 @@ package com.x8bit.bitwarden.data.autofill.fido2.manager
|
|||
|
||||
import androidx.credentials.provider.CallingAppInfo
|
||||
import com.bitwarden.fido.ClientData
|
||||
import com.bitwarden.sdk.CheckUserResult
|
||||
import com.bitwarden.sdk.CipherViewWrapper
|
||||
import com.bitwarden.sdk.Fido2CredentialStore
|
||||
import com.bitwarden.vault.CipherView
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.datasource.network.model.DigitalAssetLinkResponseJson
|
||||
|
@ -60,27 +58,25 @@ class Fido2CredentialManagerImpl(
|
|||
val origin = fido2CredentialRequest.origin
|
||||
?: fido2CredentialRequest.callingAppInfo.getAppOrigin()
|
||||
|
||||
return vaultSdkSource.registerFido2Credential(
|
||||
request = RegisterFido2CredentialRequest(
|
||||
userId = userId,
|
||||
origin = origin,
|
||||
requestJson = """{"publicKey": ${fido2CredentialRequest.requestJson}}""",
|
||||
clientData = clientData,
|
||||
selectedCipherView = selectedCipherView,
|
||||
isUserVerificationSupported = true,
|
||||
),
|
||||
fido2CredentialStore = this,
|
||||
// TODO: [PM-8137] Determine if user verification is supported
|
||||
checkUser = { _, _ -> CheckUserResult(true, true) },
|
||||
checkUserAndPickCredential = { _, _ -> CipherViewWrapper(selectedCipherView) },
|
||||
)
|
||||
return vaultSdkSource
|
||||
.registerFido2Credential(
|
||||
request = RegisterFido2CredentialRequest(
|
||||
userId = userId,
|
||||
origin = origin,
|
||||
requestJson = """{"publicKey": ${fido2CredentialRequest.requestJson}}""",
|
||||
clientData = clientData,
|
||||
selectedCipherView = selectedCipherView,
|
||||
// User verification is handled prior to engaging the SDK. We always respond
|
||||
// `true` so that the SDK does not fail if the relying party requests UV.
|
||||
isUserVerificationSupported = true,
|
||||
),
|
||||
fido2CredentialStore = this,
|
||||
)
|
||||
.map { it.toAndroidAttestationResponse() }
|
||||
.mapCatching { json.encodeToString(it) }
|
||||
.fold(
|
||||
onSuccess = { Fido2RegisterCredentialResult.Success(it) },
|
||||
onFailure = {
|
||||
Fido2RegisterCredentialResult.Error
|
||||
},
|
||||
onFailure = { Fido2RegisterCredentialResult.Error },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,15 +8,10 @@ import com.bitwarden.core.InitUserCryptoRequest
|
|||
import com.bitwarden.core.UpdatePasswordResponse
|
||||
import com.bitwarden.crypto.TrustDeviceResponse
|
||||
import com.bitwarden.exporters.ExportFormat
|
||||
import com.bitwarden.fido.CheckUserOptions
|
||||
import com.bitwarden.fido.ClientData
|
||||
import com.bitwarden.fido.Fido2CredentialAutofillView
|
||||
import com.bitwarden.fido.PublicKeyCredentialAuthenticatorAssertionResponse
|
||||
import com.bitwarden.fido.PublicKeyCredentialAuthenticatorAttestationResponse
|
||||
import com.bitwarden.sdk.CheckUserResult
|
||||
import com.bitwarden.sdk.CipherViewWrapper
|
||||
import com.bitwarden.sdk.Fido2CredentialStore
|
||||
import com.bitwarden.sdk.UiHint
|
||||
import com.bitwarden.send.Send
|
||||
import com.bitwarden.send.SendView
|
||||
import com.bitwarden.vault.Attachment
|
||||
|
@ -27,12 +22,12 @@ import com.bitwarden.vault.CipherListView
|
|||
import com.bitwarden.vault.CipherView
|
||||
import com.bitwarden.vault.Collection
|
||||
import com.bitwarden.vault.CollectionView
|
||||
import com.bitwarden.vault.Fido2CredentialNewView
|
||||
import com.bitwarden.vault.Folder
|
||||
import com.bitwarden.vault.FolderView
|
||||
import com.bitwarden.vault.PasswordHistory
|
||||
import com.bitwarden.vault.PasswordHistoryView
|
||||
import com.bitwarden.vault.TotpResponse
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.AuthenticateFido2CredentialRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.RegisterFido2CredentialRequest
|
||||
import java.io.File
|
||||
|
@ -420,53 +415,23 @@ interface VaultSdkSource {
|
|||
/**
|
||||
* Register a new FIDO 2 credential to a cipher.
|
||||
*
|
||||
* @param checkUser Receives [CheckUserOptions] and [UiHint] indicating what interactions and
|
||||
* prompts must be presented to the user during registration. A [CheckUserResult] is expected
|
||||
* when interactions are completed.
|
||||
* @param checkUserAndPickCredential Receives [CheckUserOptions] indicating user
|
||||
* verification requirements and a [Fido2CredentialNewView] representing the newly registered
|
||||
* credential. A [CipherViewWrapper] containing the selectedCipherView updated with the
|
||||
* [Fido2CredentialNewView] is expected in response.
|
||||
*
|
||||
* @return Result of the FIDO 2 credential registration. If successful, a
|
||||
* [PublicKeyCredentialAuthenticatorAttestationResponse] is provided.
|
||||
*/
|
||||
suspend fun registerFido2Credential(
|
||||
request: RegisterFido2CredentialRequest,
|
||||
fido2CredentialStore: Fido2CredentialStore,
|
||||
checkUser: suspend (CheckUserOptions, UiHint?) -> CheckUserResult,
|
||||
checkUserAndPickCredential: suspend (
|
||||
options: CheckUserOptions,
|
||||
newCredential: Fido2CredentialNewView,
|
||||
) -> CipherViewWrapper,
|
||||
): Result<PublicKeyCredentialAuthenticatorAttestationResponse>
|
||||
|
||||
/**
|
||||
* Authenticate a user with a FIDO 2 credential.
|
||||
*
|
||||
* @param userId Active user's ID.
|
||||
* @param origin Origin of the relying party request.
|
||||
* @param requestJson JSON provided by the relying party.
|
||||
* @param clientData Client metadata about the relying party or calling application.
|
||||
* @param isVerificationSupported Whether user verification can be performed on this device.
|
||||
* @param checkUser Receives [CheckUserOptions] and [UiHint] indicating what interactions and
|
||||
* prompts must be presented to the user for registration to complete. A [CheckUserResult] is
|
||||
* expected when interactions are completed.
|
||||
* @param pickCredentialForAuthentication Receives a collection of [CipherView]s that can be
|
||||
* chosen to perform authentication with.
|
||||
*
|
||||
* @return Result of the FIDO 2 credential registration. If successful, a
|
||||
* [PublicKeyCredentialAuthenticatorAttestationResponse] is provided.
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
suspend fun authenticateFido2Credential(
|
||||
userId: String,
|
||||
origin: String,
|
||||
requestJson: String,
|
||||
clientData: ClientData,
|
||||
isVerificationSupported: Boolean,
|
||||
checkUser: suspend (CheckUserOptions, UiHint?) -> CheckUserResult,
|
||||
pickCredentialForAuthentication: suspend (List<CipherView>) -> CipherViewWrapper,
|
||||
request: AuthenticateFido2CredentialRequest,
|
||||
fido2CredentialStore: Fido2CredentialStore,
|
||||
): Result<PublicKeyCredentialAuthenticatorAssertionResponse>
|
||||
|
||||
|
|
|
@ -7,18 +7,13 @@ import com.bitwarden.core.InitUserCryptoRequest
|
|||
import com.bitwarden.core.UpdatePasswordResponse
|
||||
import com.bitwarden.crypto.TrustDeviceResponse
|
||||
import com.bitwarden.exporters.ExportFormat
|
||||
import com.bitwarden.fido.CheckUserOptions
|
||||
import com.bitwarden.fido.ClientData
|
||||
import com.bitwarden.fido.Fido2CredentialAutofillView
|
||||
import com.bitwarden.fido.PublicKeyCredentialAuthenticatorAssertionResponse
|
||||
import com.bitwarden.fido.PublicKeyCredentialAuthenticatorAttestationResponse
|
||||
import com.bitwarden.sdk.BitwardenException
|
||||
import com.bitwarden.sdk.CheckUserResult
|
||||
import com.bitwarden.sdk.CipherViewWrapper
|
||||
import com.bitwarden.sdk.Client
|
||||
import com.bitwarden.sdk.ClientVault
|
||||
import com.bitwarden.sdk.Fido2CredentialStore
|
||||
import com.bitwarden.sdk.UiHint
|
||||
import com.bitwarden.send.Send
|
||||
import com.bitwarden.send.SendView
|
||||
import com.bitwarden.vault.Attachment
|
||||
|
@ -28,14 +23,13 @@ import com.bitwarden.vault.CipherListView
|
|||
import com.bitwarden.vault.CipherView
|
||||
import com.bitwarden.vault.Collection
|
||||
import com.bitwarden.vault.CollectionView
|
||||
import com.bitwarden.vault.Fido2CredentialNewView
|
||||
import com.bitwarden.vault.Folder
|
||||
import com.bitwarden.vault.FolderView
|
||||
import com.bitwarden.vault.PasswordHistory
|
||||
import com.bitwarden.vault.PasswordHistoryView
|
||||
import com.bitwarden.vault.TotpResponse
|
||||
import com.x8bit.bitwarden.data.platform.manager.SdkClientManager
|
||||
import com.x8bit.bitwarden.data.platform.util.asFailure
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.AuthenticateFido2CredentialRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.Fido2CredentialAuthenticationUserInterfaceImpl
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.Fido2CredentialRegistrationUserInterfaceImpl
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult
|
||||
|
@ -452,11 +446,6 @@ class VaultSdkSourceImpl(
|
|||
override suspend fun registerFido2Credential(
|
||||
request: RegisterFido2CredentialRequest,
|
||||
fido2CredentialStore: Fido2CredentialStore,
|
||||
checkUser: suspend (CheckUserOptions, UiHint?) -> CheckUserResult,
|
||||
checkUserAndPickCredential: suspend (
|
||||
options: CheckUserOptions,
|
||||
newCredential: Fido2CredentialNewView,
|
||||
) -> CipherViewWrapper,
|
||||
): Result<PublicKeyCredentialAuthenticatorAttestationResponse> = runCatching {
|
||||
callbackFlow {
|
||||
try {
|
||||
|
@ -465,9 +454,8 @@ class VaultSdkSourceImpl(
|
|||
.fido2()
|
||||
.client(
|
||||
userInterface = Fido2CredentialRegistrationUserInterfaceImpl(
|
||||
selectedCipherView = request.selectedCipherView,
|
||||
isVerificationSupported = request.isUserVerificationSupported,
|
||||
checkUser = checkUser,
|
||||
checkUserAndPickCredentialForCreation = checkUserAndPickCredential,
|
||||
),
|
||||
credentialStore = fido2CredentialStore,
|
||||
)
|
||||
|
@ -480,10 +468,9 @@ class VaultSdkSourceImpl(
|
|||
)
|
||||
|
||||
send(result)
|
||||
} catch (e: BitwardenException) {
|
||||
e.asFailure()
|
||||
} finally {
|
||||
close()
|
||||
} catch (e: BitwardenException) {
|
||||
close(e)
|
||||
}
|
||||
awaitClose()
|
||||
}
|
||||
|
@ -492,40 +479,32 @@ class VaultSdkSourceImpl(
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
override suspend fun authenticateFido2Credential(
|
||||
userId: String,
|
||||
origin: String,
|
||||
requestJson: String,
|
||||
clientData: ClientData,
|
||||
isVerificationSupported: Boolean,
|
||||
checkUser: suspend (CheckUserOptions, UiHint?) -> CheckUserResult,
|
||||
pickCredentialForAuthentication: suspend (List<CipherView>) -> CipherViewWrapper,
|
||||
request: AuthenticateFido2CredentialRequest,
|
||||
fido2CredentialStore: Fido2CredentialStore,
|
||||
): Result<PublicKeyCredentialAuthenticatorAssertionResponse> = runCatching {
|
||||
callbackFlow {
|
||||
try {
|
||||
val client = getClient(userId)
|
||||
val client = getClient(request.userId)
|
||||
.platform()
|
||||
.fido2()
|
||||
.client(
|
||||
userInterface = Fido2CredentialAuthenticationUserInterfaceImpl(
|
||||
isVerificationSupported = isVerificationSupported,
|
||||
checkUser = checkUser,
|
||||
pickCredentialForAuthentication = pickCredentialForAuthentication,
|
||||
selectedCipherView = request.selectedCipherView,
|
||||
isVerificationSupported = request.isUserVerificationSupported,
|
||||
),
|
||||
credentialStore = fido2CredentialStore,
|
||||
)
|
||||
|
||||
val result = client.authenticate(
|
||||
origin = origin,
|
||||
request = requestJson,
|
||||
clientData = clientData,
|
||||
origin = request.origin,
|
||||
request = request.requestJson,
|
||||
clientData = request.clientData,
|
||||
)
|
||||
|
||||
send(result)
|
||||
} catch (e: BitwardenException) {
|
||||
e.asFailure()
|
||||
} finally {
|
||||
close()
|
||||
} catch (e: BitwardenException) {
|
||||
close(e)
|
||||
}
|
||||
|
||||
awaitClose()
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk.model
|
||||
|
||||
import com.bitwarden.fido.ClientData
|
||||
import com.bitwarden.vault.CipherView
|
||||
|
||||
/**
|
||||
* Models a FIDO 2 authentication request to the Bitwarden SDK.
|
||||
*
|
||||
* @param userId User whom the credential is being authenticated for.
|
||||
* @param origin Origin of the Relying Party. This can either be a Relying Party's URL or their
|
||||
* application fingerprint.
|
||||
* @param requestJson Authentication request JSON received from the OS.
|
||||
* @param clientData Metadata containing either privileged application certificate hash or Android
|
||||
* package name of the Relying Party.
|
||||
* @param selectedCipherView [CipherView] containing the FIDO 2 credential being authenticated.
|
||||
* @param isUserVerificationSupported Whether the device or application are capable of performing
|
||||
* user verification.
|
||||
*/
|
||||
data class AuthenticateFido2CredentialRequest(
|
||||
val userId: String,
|
||||
val origin: String,
|
||||
val requestJson: String,
|
||||
val clientData: ClientData,
|
||||
val selectedCipherView: CipherView,
|
||||
val isUserVerificationSupported: Boolean,
|
||||
)
|
|
@ -15,14 +15,13 @@ import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
|
|||
*/
|
||||
@OmitFromCoverage
|
||||
class Fido2CredentialAuthenticationUserInterfaceImpl(
|
||||
private val selectedCipherView: CipherView,
|
||||
private val isVerificationSupported: Boolean,
|
||||
private val checkUser: suspend (CheckUserOptions, UiHint?) -> CheckUserResult,
|
||||
private val pickCredentialForAuthentication: suspend (List<CipherView>) -> CipherViewWrapper,
|
||||
) : Fido2UserInterface {
|
||||
override suspend fun checkUser(
|
||||
options: CheckUserOptions,
|
||||
hint: UiHint,
|
||||
): CheckUserResult = checkUser.invoke(options, hint)
|
||||
): CheckUserResult = CheckUserResult(true, true)
|
||||
|
||||
override suspend fun checkUserAndPickCredentialForCreation(
|
||||
options: CheckUserOptions,
|
||||
|
@ -33,5 +32,5 @@ class Fido2CredentialAuthenticationUserInterfaceImpl(
|
|||
|
||||
override suspend fun pickCredentialForAuthentication(
|
||||
availableCredentials: List<CipherView>,
|
||||
): CipherViewWrapper = pickCredentialForAuthentication.invoke(availableCredentials)
|
||||
): CipherViewWrapper = CipherViewWrapper(selectedCipherView)
|
||||
}
|
||||
|
|
|
@ -15,23 +15,22 @@ import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
|
|||
*/
|
||||
@OmitFromCoverage
|
||||
class Fido2CredentialRegistrationUserInterfaceImpl(
|
||||
private val selectedCipherView: CipherView,
|
||||
private val isVerificationSupported: Boolean,
|
||||
private val checkUser: suspend (CheckUserOptions, UiHint?) -> CheckUserResult,
|
||||
private val checkUserAndPickCredentialForCreation: suspend (
|
||||
options: CheckUserOptions,
|
||||
newCredential: Fido2CredentialNewView,
|
||||
) -> CipherViewWrapper,
|
||||
) : Fido2UserInterface {
|
||||
|
||||
override suspend fun checkUser(
|
||||
options: CheckUserOptions,
|
||||
hint: UiHint,
|
||||
): CheckUserResult = checkUser.invoke(options, hint)
|
||||
): CheckUserResult = CheckUserResult(true, true)
|
||||
|
||||
override suspend fun checkUserAndPickCredentialForCreation(
|
||||
options: CheckUserOptions,
|
||||
newCredential: Fido2CredentialNewView,
|
||||
): CheckUserAndPickCredentialForCreationResult = throw IllegalStateException()
|
||||
): CheckUserAndPickCredentialForCreationResult = CheckUserAndPickCredentialForCreationResult(
|
||||
cipher = CipherViewWrapper(selectedCipherView),
|
||||
checkUserResult = CheckUserResult(true, true),
|
||||
)
|
||||
|
||||
override suspend fun isVerificationEnabled(): Boolean = isVerificationSupported
|
||||
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
@file:OmitFromCoverage
|
||||
|
||||
package com.x8bit.bitwarden.data.vault.datasource.sdk.util
|
||||
|
||||
import android.util.Base64
|
||||
import com.bitwarden.fido.PublicKeyCredentialAuthenticatorAttestationResponse
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2AttestationResponse
|
||||
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
|
||||
|
||||
/**
|
||||
* Converts the SDK attestation response to a [Fido2AttestationResponse] that can be serialized into
|
||||
|
@ -28,11 +25,11 @@ fun PublicKeyCredentialAuthenticatorAttestationResponse.toAndroidAttestationResp
|
|||
clientExtensionResults = clientExtensionResults
|
||||
.credProps
|
||||
?.rk
|
||||
?.let {
|
||||
?.let { residentKey ->
|
||||
Fido2AttestationResponse.ClientExtensionResults(
|
||||
credentialProperties = Fido2AttestationResponse
|
||||
.ClientExtensionResults
|
||||
.CredentialProperties(residentKey = it),
|
||||
.CredentialProperties(residentKey = residentKey),
|
||||
)
|
||||
},
|
||||
authenticatorAttachment = authenticatorAttachment,
|
||||
|
|
|
@ -322,8 +322,6 @@ class Fido2CredentialManagerTest {
|
|||
mockVaultSdkSource.registerFido2Credential(
|
||||
request = capture(requestCaptureSlot),
|
||||
fido2CredentialStore = any(),
|
||||
checkUser = any(),
|
||||
checkUserAndPickCredential = any(),
|
||||
)
|
||||
} coAnswers {
|
||||
mockRegistrationResponse
|
||||
|
@ -362,8 +360,6 @@ class Fido2CredentialManagerTest {
|
|||
mockVaultSdkSource.registerFido2Credential(
|
||||
request = capture(requestCaptureSlot),
|
||||
fido2CredentialStore = any(),
|
||||
checkUser = any(),
|
||||
checkUserAndPickCredential = any(),
|
||||
)
|
||||
} coAnswers {
|
||||
mockRegistrationResponse
|
||||
|
@ -399,8 +395,6 @@ class Fido2CredentialManagerTest {
|
|||
mockVaultSdkSource.registerFido2Credential(
|
||||
request = capture(requestCaptureSlot),
|
||||
fido2CredentialStore = any(),
|
||||
checkUser = any(),
|
||||
checkUserAndPickCredential = any(),
|
||||
)
|
||||
} coAnswers {
|
||||
mockRegistrationResponse
|
||||
|
@ -441,8 +435,6 @@ class Fido2CredentialManagerTest {
|
|||
mockVaultSdkSource.registerFido2Credential(
|
||||
request = capture(requestCaptureSlot),
|
||||
fido2CredentialStore = any(),
|
||||
checkUser = any(),
|
||||
checkUserAndPickCredential = any(),
|
||||
)
|
||||
} coAnswers {
|
||||
mockRegistrationResponse
|
||||
|
@ -515,8 +507,6 @@ class Fido2CredentialManagerTest {
|
|||
mockVaultSdkSource.registerFido2Credential(
|
||||
request = any(),
|
||||
fido2CredentialStore = any(),
|
||||
checkUser = any(),
|
||||
checkUserAndPickCredential = any(),
|
||||
)
|
||||
} coAnswers {
|
||||
mockRegistrationResponse
|
||||
|
|
|
@ -7,14 +7,11 @@ import com.bitwarden.core.InitUserCryptoRequest
|
|||
import com.bitwarden.core.UpdatePasswordResponse
|
||||
import com.bitwarden.crypto.TrustDeviceResponse
|
||||
import com.bitwarden.exporters.ExportFormat
|
||||
import com.bitwarden.fido.CheckUserOptions
|
||||
import com.bitwarden.fido.ClientData
|
||||
import com.bitwarden.fido.Fido2CredentialAutofillView
|
||||
import com.bitwarden.fido.PublicKeyCredentialAuthenticatorAssertionResponse
|
||||
import com.bitwarden.fido.PublicKeyCredentialAuthenticatorAttestationResponse
|
||||
import com.bitwarden.fido.Verification
|
||||
import com.bitwarden.sdk.BitwardenException
|
||||
import com.bitwarden.sdk.CheckUserResult
|
||||
import com.bitwarden.sdk.CipherViewWrapper
|
||||
import com.bitwarden.sdk.Client
|
||||
import com.bitwarden.sdk.ClientAuth
|
||||
import com.bitwarden.sdk.ClientCiphers
|
||||
|
@ -27,8 +24,6 @@ import com.bitwarden.sdk.ClientPlatform
|
|||
import com.bitwarden.sdk.ClientSends
|
||||
import com.bitwarden.sdk.ClientVault
|
||||
import com.bitwarden.sdk.Fido2CredentialStore
|
||||
import com.bitwarden.sdk.Fido2UserInterface
|
||||
import com.bitwarden.sdk.UiHint
|
||||
import com.bitwarden.send.Send
|
||||
import com.bitwarden.send.SendView
|
||||
import com.bitwarden.vault.Attachment
|
||||
|
@ -46,6 +41,7 @@ import com.bitwarden.vault.TotpResponse
|
|||
import com.x8bit.bitwarden.data.platform.manager.SdkClientManager
|
||||
import com.x8bit.bitwarden.data.platform.util.asFailure
|
||||
import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.AuthenticateFido2CredentialRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.RegisterFido2CredentialRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView
|
||||
|
@ -58,11 +54,11 @@ import io.mockk.just
|
|||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.runs
|
||||
import io.mockk.slot
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.security.MessageDigest
|
||||
|
||||
|
@ -1016,26 +1012,12 @@ class VaultSdkSourceTest {
|
|||
every { digest(any()) } returns DEFAULT_SIGNATURE.toByteArray()
|
||||
}
|
||||
|
||||
val mockCipherView = createMockCipherView(1)
|
||||
val mockAttestation = mockk<PublicKeyCredentialAuthenticatorAttestationResponse>()
|
||||
|
||||
coEvery { fido2.register(any(), any(), any()) } returns mockAttestation
|
||||
|
||||
val result = vaultSdkSource.registerFido2Credential(
|
||||
request = RegisterFido2CredentialRequest(
|
||||
userId = "mockUserId",
|
||||
origin = "www.bitwarden.com",
|
||||
requestJson = "requestJson",
|
||||
clientData = ClientData.DefaultWithCustomHash(
|
||||
hash = DEFAULT_SIGNATURE.toByteArray(),
|
||||
),
|
||||
selectedCipherView = mockCipherView,
|
||||
isUserVerificationSupported = true,
|
||||
),
|
||||
checkUser = { _, _ -> CheckUserResult(true, true) },
|
||||
checkUserAndPickCredential = { _, _ ->
|
||||
CipherViewWrapper(mockCipherView)
|
||||
},
|
||||
DEFAULT_FIDO_2_REGISTER_CREDENTIAL_REQUEST,
|
||||
fido2CredentialStore = mockFido2CredentialStore,
|
||||
)
|
||||
|
||||
|
@ -1047,58 +1029,23 @@ class VaultSdkSourceTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `registerFido2Credential should invoke checkUser when called by the SDK`() = runTest {
|
||||
val checkUserResult = CheckUserResult(true, true)
|
||||
|
||||
val checkUserOptionsSlot = slot<CheckUserOptions>()
|
||||
val uiHintSlot = slot<UiHint>()
|
||||
val mockUserInterface = mockk<Fido2UserInterface> {
|
||||
fun `registerFido2Credential should return Failure when BitwardenException is thrown`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
checkUser(
|
||||
capture(checkUserOptionsSlot),
|
||||
capture(uiHintSlot),
|
||||
fido2.register(
|
||||
any(),
|
||||
any(),
|
||||
any(),
|
||||
)
|
||||
} returns checkUserResult
|
||||
}
|
||||
} throws BitwardenException.E("mockException")
|
||||
|
||||
val mockCipherView = createMockCipherView(number = 1)
|
||||
val mockAttestation = mockk<PublicKeyCredentialAuthenticatorAttestationResponse>()
|
||||
val mockCheckUserOptions = CheckUserOptions(true, Verification.REQUIRED)
|
||||
coEvery { fido2.register(any(), any(), any()) } coAnswers {
|
||||
mockUserInterface.checkUser(
|
||||
mockCheckUserOptions,
|
||||
UiHint.InformNoCredentialsFound,
|
||||
val result = vaultSdkSource.registerFido2Credential(
|
||||
DEFAULT_FIDO_2_REGISTER_CREDENTIAL_REQUEST,
|
||||
fido2CredentialStore = mockFido2CredentialStore,
|
||||
)
|
||||
mockAttestation
|
||||
|
||||
assertTrue(result.isFailure)
|
||||
}
|
||||
vaultSdkSource.registerFido2Credential(
|
||||
request = RegisterFido2CredentialRequest(
|
||||
userId = "mockUserId",
|
||||
origin = "www.bitwarden.com",
|
||||
requestJson = "requestJson",
|
||||
clientData = ClientData.DefaultWithCustomHash(
|
||||
hash = DEFAULT_SIGNATURE.toByteArray(),
|
||||
),
|
||||
selectedCipherView = mockCipherView,
|
||||
isUserVerificationSupported = true,
|
||||
),
|
||||
checkUser = { _, _ -> checkUserResult },
|
||||
checkUserAndPickCredential = { _, _ -> CipherViewWrapper(mockCipherView) },
|
||||
fido2CredentialStore = mockFido2CredentialStore,
|
||||
)
|
||||
|
||||
coVerify { mockUserInterface.checkUser(any(), any()) }
|
||||
|
||||
assertEquals(
|
||||
mockCheckUserOptions,
|
||||
checkUserOptionsSlot.captured,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
UiHint.InformNoCredentialsFound,
|
||||
uiHintSlot.captured,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
|
@ -1109,21 +1056,15 @@ class VaultSdkSourceTest {
|
|||
every { digest(any()) } returns DEFAULT_SIGNATURE.toByteArray()
|
||||
}
|
||||
|
||||
val mockCipherView = createMockCipherView(1)
|
||||
val mockAssertion = mockk<PublicKeyCredentialAuthenticatorAssertionResponse>()
|
||||
|
||||
coEvery { fido2.authenticate(any(), any(), any()) } returns mockAssertion
|
||||
|
||||
val result = vaultSdkSource.authenticateFido2Credential(
|
||||
userId = "mockUserId",
|
||||
origin = "www.bitwarden.com",
|
||||
requestJson = "requestJson",
|
||||
clientData = ClientData.DefaultWithCustomHash(DEFAULT_SIGNATURE.toByteArray()),
|
||||
isVerificationSupported = true,
|
||||
checkUser = { _, _ -> CheckUserResult(true, true) },
|
||||
pickCredentialForAuthentication = { CipherViewWrapper(mockCipherView) },
|
||||
fido2CredentialStore = mockFido2CredentialStore,
|
||||
)
|
||||
val result = vaultSdkSource
|
||||
.authenticateFido2Credential(
|
||||
DEFAULT_FIDO_2_AUTH_REQUEST,
|
||||
fido2CredentialStore = mockFido2CredentialStore,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
mockAssertion.asSuccess(),
|
||||
|
@ -1133,53 +1074,88 @@ class VaultSdkSourceTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `authenticateFido2Credential should invoke checkUser when called by the SDK`() = runTest {
|
||||
val checkUserResult = CheckUserResult(true, true)
|
||||
|
||||
val checkUserOptionsSlot = slot<CheckUserOptions>()
|
||||
val uiHintSlot = slot<UiHint>()
|
||||
val mockUserInterface = mockk<Fido2UserInterface> {
|
||||
fun `authenticateFido2Credential should return Failure when BitwardenException is thrown`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
checkUser(
|
||||
capture(checkUserOptionsSlot),
|
||||
capture(uiHintSlot),
|
||||
fido2.authenticate(
|
||||
any(),
|
||||
any(),
|
||||
any(),
|
||||
)
|
||||
} returns checkUserResult
|
||||
} throws BitwardenException.E("mockException")
|
||||
|
||||
val result = vaultSdkSource
|
||||
.authenticateFido2Credential(
|
||||
DEFAULT_FIDO_2_AUTH_REQUEST,
|
||||
fido2CredentialStore = mockFido2CredentialStore,
|
||||
)
|
||||
|
||||
assertTrue(result.isFailure)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `decryptFido2CredentialAutofillViews should return results when successful`() = runTest {
|
||||
val mockCipherView = createMockCipherView(number = 1)
|
||||
val mockAssertion = mockk<PublicKeyCredentialAuthenticatorAssertionResponse>()
|
||||
val mockCheckUserOptions = CheckUserOptions(true, Verification.REQUIRED)
|
||||
coEvery { fido2.authenticate(any(), any(), any()) } coAnswers {
|
||||
mockUserInterface.checkUser(
|
||||
mockCheckUserOptions,
|
||||
UiHint.InformNoCredentialsFound,
|
||||
)
|
||||
mockAssertion
|
||||
}
|
||||
vaultSdkSource.authenticateFido2Credential(
|
||||
val mockAutofillView = Fido2CredentialAutofillView(
|
||||
credentialId = byteArrayOf(0),
|
||||
cipherId = "mockCipherId",
|
||||
rpId = "mockRpId",
|
||||
userNameForUi = "mockUserNameForUi",
|
||||
userHandle = "mockUserHandle".toByteArray(),
|
||||
)
|
||||
val autofillViews = listOf(mockAutofillView)
|
||||
|
||||
coEvery {
|
||||
clientFido2.decryptFido2AutofillCredentials(mockCipherView)
|
||||
} returns autofillViews
|
||||
|
||||
val result = vaultSdkSource.decryptFido2CredentialAutofillViews(
|
||||
userId = "mockUserId",
|
||||
origin = "www.bitwarden.com",
|
||||
requestJson = "requestJson",
|
||||
clientData = ClientData.DefaultWithCustomHash(DEFAULT_SIGNATURE.toByteArray()),
|
||||
isVerificationSupported = true,
|
||||
checkUser = { _, _ -> checkUserResult },
|
||||
pickCredentialForAuthentication = { CipherViewWrapper(mockCipherView) },
|
||||
fido2CredentialStore = mockFido2CredentialStore,
|
||||
)
|
||||
|
||||
coVerify { mockUserInterface.checkUser(any(), any()) }
|
||||
|
||||
assertEquals(
|
||||
mockCheckUserOptions,
|
||||
checkUserOptionsSlot.captured,
|
||||
cipherViews = arrayOf(mockCipherView),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
UiHint.InformNoCredentialsFound,
|
||||
uiHintSlot.captured,
|
||||
autofillViews.asSuccess(),
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `decryptFido2CredentialAutofillViews should return Failure when Bitwarden exception is thrown`() =
|
||||
runTest {
|
||||
val mockCipherView = createMockCipherView(number = 1)
|
||||
coEvery {
|
||||
clientFido2.decryptFido2AutofillCredentials(mockCipherView)
|
||||
} throws BitwardenException.E("mockException")
|
||||
|
||||
val result = vaultSdkSource.decryptFido2CredentialAutofillViews(
|
||||
userId = "mockUserId",
|
||||
cipherViews = arrayOf(mockCipherView),
|
||||
)
|
||||
|
||||
assertTrue(result.isFailure)
|
||||
}
|
||||
}
|
||||
|
||||
private const val DEFAULT_SIGNATURE = "0987654321ABCDEF"
|
||||
private val DEFAULT_FIDO_2_REGISTER_CREDENTIAL_REQUEST = RegisterFido2CredentialRequest(
|
||||
userId = "mockUserId",
|
||||
origin = "www.bitwarden.com",
|
||||
requestJson = "requestJson",
|
||||
clientData = ClientData.DefaultWithCustomHash(
|
||||
DEFAULT_SIGNATURE.toByteArray(),
|
||||
),
|
||||
isUserVerificationSupported = true,
|
||||
selectedCipherView = createMockCipherView(number = 1),
|
||||
)
|
||||
val DEFAULT_FIDO_2_AUTH_REQUEST = AuthenticateFido2CredentialRequest(
|
||||
userId = "mockUserId",
|
||||
origin = "www.bitwarden.com",
|
||||
requestJson = "requestJson",
|
||||
clientData = ClientData.DefaultWithCustomHash(
|
||||
DEFAULT_SIGNATURE.toByteArray(),
|
||||
),
|
||||
isUserVerificationSupported = true,
|
||||
selectedCipherView = createMockCipherView(number = 1),
|
||||
)
|
||||
|
|
|
@ -6,31 +6,34 @@ import com.x8bit.bitwarden.data.autofill.fido2.datasource.network.model.PublicKe
|
|||
* Returns a mock FIDO 2 [PublicKeyCredentialCreationOptions] object to simulate a credential
|
||||
* creation request.
|
||||
*/
|
||||
fun createMockPublicKeyCredentialCreationOptions(number: Int) =
|
||||
PublicKeyCredentialCreationOptions(
|
||||
authenticatorSelection = PublicKeyCredentialCreationOptions
|
||||
.AuthenticatorSelectionCriteria(),
|
||||
challenge = "mockPublicKeyCredentialCreationOptionsChallenge-$number",
|
||||
excludeCredentials = listOf(
|
||||
PublicKeyCredentialCreationOptions.PublicKeyCredentialDescriptor(
|
||||
type = "mockPublicKeyCredentialDescriptorType-$number",
|
||||
id = "mockPublicKeyCredentialDescriptorId-$number",
|
||||
transports = listOf("mockPublicKeyCredentialDescriptorTransports-$number"),
|
||||
),
|
||||
@Suppress("MaxLineLength")
|
||||
fun createMockPublicKeyCredentialCreationOptions(
|
||||
number: Int,
|
||||
userVerificationRequirement: PublicKeyCredentialCreationOptions.AuthenticatorSelectionCriteria.UserVerificationRequirement? = null,
|
||||
) = PublicKeyCredentialCreationOptions(
|
||||
authenticatorSelection = PublicKeyCredentialCreationOptions
|
||||
.AuthenticatorSelectionCriteria(userVerification = userVerificationRequirement),
|
||||
challenge = "mockPublicKeyCredentialCreationOptionsChallenge-$number",
|
||||
excludeCredentials = listOf(
|
||||
PublicKeyCredentialCreationOptions.PublicKeyCredentialDescriptor(
|
||||
type = "mockPublicKeyCredentialDescriptorType-$number",
|
||||
id = "mockPublicKeyCredentialDescriptorId-$number",
|
||||
transports = listOf("mockPublicKeyCredentialDescriptorTransports-$number"),
|
||||
),
|
||||
pubKeyCredParams = listOf(
|
||||
PublicKeyCredentialCreationOptions.PublicKeyCredentialParameters(
|
||||
type = "PublicKeyCredentialParametersType-$number",
|
||||
alg = number.toLong(),
|
||||
),
|
||||
),
|
||||
pubKeyCredParams = listOf(
|
||||
PublicKeyCredentialCreationOptions.PublicKeyCredentialParameters(
|
||||
type = "PublicKeyCredentialParametersType-$number",
|
||||
alg = number.toLong(),
|
||||
),
|
||||
relyingParty = PublicKeyCredentialCreationOptions.PublicKeyCredentialRpEntity(
|
||||
name = "mockPublicKeyCredentialRpEntityName-$number",
|
||||
id = "mockPublicKeyCredentialRpEntity-$number",
|
||||
),
|
||||
user = PublicKeyCredentialCreationOptions.PublicKeyCredentialUserEntity(
|
||||
name = "mockPublicKeyCredentialUserEntityName-$number",
|
||||
id = "mockPublicKeyCredentialUserEntityId-$number",
|
||||
displayName = "mockPublicKeyCredentialUserEntityDisplayName-$number",
|
||||
),
|
||||
)
|
||||
),
|
||||
relyingParty = PublicKeyCredentialCreationOptions.PublicKeyCredentialRpEntity(
|
||||
name = "mockPublicKeyCredentialRpEntityName-$number",
|
||||
id = "mockPublicKeyCredentialRpEntity-$number",
|
||||
),
|
||||
user = PublicKeyCredentialCreationOptions.PublicKeyCredentialUserEntity(
|
||||
name = "mockPublicKeyCredentialUserEntityName-$number",
|
||||
id = "mockPublicKeyCredentialUserEntityId-$number",
|
||||
displayName = "mockPublicKeyCredentialUserEntityDisplayName-$number",
|
||||
),
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue