mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
Update the CreateAuthRequests API to poll for updates (#884)
This commit is contained in:
parent
1794223d02
commit
d6c2969332
5 changed files with 417 additions and 0 deletions
|
@ -7,6 +7,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.AuthRequestResult
|
|||
import com.x8bit.bitwarden.data.auth.repository.model.AuthRequestsResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.AuthState
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.CreateAuthRequestResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.DeleteAccountResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.KnownDeviceResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.LoginResult
|
||||
|
@ -209,6 +210,11 @@ interface AuthRepository : AuthenticatorProvider {
|
|||
*/
|
||||
suspend fun createAuthRequest(email: String): AuthRequestResult
|
||||
|
||||
/**
|
||||
* Creates a new authentication request and then continues to emit updates over time.
|
||||
*/
|
||||
fun createAuthRequestWithUpdates(email: String): Flow<CreateAuthRequestResult>
|
||||
|
||||
/**
|
||||
* Get an auth request by its [fingerprint].
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.x8bit.bitwarden.data.auth.repository
|
||||
|
||||
import android.os.SystemClock
|
||||
import com.bitwarden.core.AuthRequestResponse
|
||||
import com.bitwarden.crypto.HashPurpose
|
||||
import com.bitwarden.crypto.Kdf
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
|
@ -34,6 +35,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.AuthRequestResult
|
|||
import com.x8bit.bitwarden.data.auth.repository.model.AuthRequestsResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.AuthState
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.CreateAuthRequestResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.DeleteAccountResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.KnownDeviceResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.LoginResult
|
||||
|
@ -74,6 +76,8 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
|||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.currentCoroutineContext
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
|
@ -81,18 +85,25 @@ import kotlinx.coroutines.flow.StateFlow
|
|||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.isActive
|
||||
import java.time.Clock
|
||||
import javax.inject.Singleton
|
||||
|
||||
private const val PASSWORDLESS_NOTIFICATION_TIMEOUT_MILLIS: Long = 15L * 60L * 1_000L
|
||||
private const val PASSWORDLESS_NOTIFICATION_RETRY_INTERVAL_MILLIS: Long = 4L * 1_000L
|
||||
|
||||
/**
|
||||
* Default implementation of [AuthRepository].
|
||||
*/
|
||||
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
|
||||
@Singleton
|
||||
class AuthRepositoryImpl(
|
||||
private val clock: Clock,
|
||||
private val accountsService: AccountsService,
|
||||
private val authRequestsService: AuthRequestsService,
|
||||
private val devicesService: DevicesService,
|
||||
|
@ -789,6 +800,82 @@ class AuthRepositoryImpl(
|
|||
onSuccess = { AuthRequestResult.Success(it) },
|
||||
)
|
||||
|
||||
@Suppress("LongMethod")
|
||||
override fun createAuthRequestWithUpdates(
|
||||
email: String,
|
||||
): Flow<CreateAuthRequestResult> = flow {
|
||||
val initialResult = createNewAuthRequest(email)
|
||||
.getOrNull()
|
||||
?: run {
|
||||
emit(CreateAuthRequestResult.Error)
|
||||
return@flow
|
||||
}
|
||||
val authRequestResponse = initialResult.authRequestResponse
|
||||
var authRequest = initialResult.authRequest
|
||||
emit(CreateAuthRequestResult.Update(authRequest))
|
||||
|
||||
var isComplete = false
|
||||
while (currentCoroutineContext().isActive && !isComplete) {
|
||||
delay(timeMillis = PASSWORDLESS_NOTIFICATION_RETRY_INTERVAL_MILLIS)
|
||||
newAuthRequestService
|
||||
.getAuthRequestUpdate(
|
||||
requestId = authRequest.id,
|
||||
accessCode = authRequestResponse.accessCode,
|
||||
)
|
||||
.map { request ->
|
||||
AuthRequest(
|
||||
id = request.id,
|
||||
publicKey = request.publicKey,
|
||||
platform = request.platform,
|
||||
ipAddress = request.ipAddress,
|
||||
key = request.key,
|
||||
masterPasswordHash = request.masterPasswordHash,
|
||||
creationDate = request.creationDate,
|
||||
responseDate = request.responseDate,
|
||||
requestApproved = request.requestApproved ?: false,
|
||||
originUrl = request.originUrl,
|
||||
fingerprint = authRequest.fingerprint,
|
||||
)
|
||||
}
|
||||
.fold(
|
||||
onFailure = { emit(CreateAuthRequestResult.Error) },
|
||||
onSuccess = { updateAuthRequest ->
|
||||
when {
|
||||
updateAuthRequest.requestApproved -> {
|
||||
isComplete = true
|
||||
emit(
|
||||
CreateAuthRequestResult.Success(
|
||||
authRequest = updateAuthRequest,
|
||||
authRequestResponse = authRequestResponse,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
!updateAuthRequest.requestApproved &&
|
||||
updateAuthRequest.responseDate != null -> {
|
||||
isComplete = true
|
||||
emit(CreateAuthRequestResult.Declined)
|
||||
}
|
||||
|
||||
updateAuthRequest
|
||||
.creationDate
|
||||
.toInstant()
|
||||
.plusMillis(PASSWORDLESS_NOTIFICATION_TIMEOUT_MILLIS)
|
||||
.isBefore(clock.instant()) -> {
|
||||
isComplete = true
|
||||
emit(CreateAuthRequestResult.Expired)
|
||||
}
|
||||
|
||||
else -> {
|
||||
authRequest = updateAuthRequest
|
||||
emit(CreateAuthRequestResult.Update(authRequest))
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getAuthRequest(
|
||||
fingerprint: String,
|
||||
): AuthRequestResult =
|
||||
|
@ -1021,6 +1108,42 @@ class AuthRepositoryImpl(
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to create a new auth request for the given email and returns a [NewAuthRequestData]
|
||||
* with the [AuthRequest] and [AuthRequestResponse].
|
||||
*/
|
||||
private suspend fun createNewAuthRequest(
|
||||
email: String,
|
||||
): Result<NewAuthRequestData> =
|
||||
authSdkSource
|
||||
.getNewAuthRequest(email)
|
||||
.flatMap { authRequestResponse ->
|
||||
newAuthRequestService
|
||||
.createAuthRequest(
|
||||
email = email,
|
||||
publicKey = authRequestResponse.publicKey,
|
||||
deviceId = authDiskSource.uniqueAppId,
|
||||
accessCode = authRequestResponse.accessCode,
|
||||
fingerprint = authRequestResponse.fingerprint,
|
||||
)
|
||||
.map { request ->
|
||||
AuthRequest(
|
||||
id = request.id,
|
||||
publicKey = request.publicKey,
|
||||
platform = request.platform,
|
||||
ipAddress = request.ipAddress,
|
||||
key = request.key,
|
||||
masterPasswordHash = request.masterPasswordHash,
|
||||
creationDate = request.creationDate,
|
||||
responseDate = request.responseDate,
|
||||
requestApproved = request.requestApproved ?: false,
|
||||
originUrl = request.originUrl,
|
||||
fingerprint = authRequestResponse.fingerprint,
|
||||
)
|
||||
}
|
||||
.map { NewAuthRequestData(it, authRequestResponse) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the remembered two-factor token associated with the user's email, if applicable.
|
||||
*/
|
||||
|
@ -1069,3 +1192,11 @@ class AuthRepositoryImpl(
|
|||
?.copy(accounts = accounts)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for the [AuthRequest] and [AuthRequestResponse] data.
|
||||
*/
|
||||
private data class NewAuthRequestData(
|
||||
val authRequest: AuthRequest,
|
||||
val authRequestResponse: AuthRequestResponse,
|
||||
)
|
||||
|
|
|
@ -22,6 +22,7 @@ import dagger.Module
|
|||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import java.time.Clock
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
|
@ -35,6 +36,7 @@ object AuthRepositoryModule {
|
|||
@Singleton
|
||||
@Suppress("LongParameterList")
|
||||
fun providesAuthRepository(
|
||||
clock: Clock,
|
||||
accountsService: AccountsService,
|
||||
authRequestsService: AuthRequestsService,
|
||||
devicesService: DevicesService,
|
||||
|
@ -52,6 +54,7 @@ object AuthRepositoryModule {
|
|||
userLogoutManager: UserLogoutManager,
|
||||
pushManager: PushManager,
|
||||
): AuthRepository = AuthRepositoryImpl(
|
||||
clock = clock,
|
||||
accountsService = accountsService,
|
||||
authRequestsService = authRequestsService,
|
||||
devicesService = devicesService,
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package com.x8bit.bitwarden.data.auth.repository.model
|
||||
|
||||
import com.bitwarden.core.AuthRequestResponse
|
||||
|
||||
/**
|
||||
* Models result of creating a new login approval request.
|
||||
*/
|
||||
sealed class CreateAuthRequestResult {
|
||||
/**
|
||||
* Models the data returned when receiving an update for an auth request.
|
||||
*/
|
||||
data class Update(
|
||||
val authRequest: AuthRequest,
|
||||
) : CreateAuthRequestResult()
|
||||
|
||||
/**
|
||||
* Models the data returned when a auth request has been approved.
|
||||
*/
|
||||
data class Success(
|
||||
val authRequest: AuthRequest,
|
||||
val authRequestResponse: AuthRequestResponse,
|
||||
) : CreateAuthRequestResult()
|
||||
|
||||
/**
|
||||
* There was a generic error getting the user's auth requests.
|
||||
*/
|
||||
data object Error : CreateAuthRequestResult()
|
||||
|
||||
/**
|
||||
* The auth request has been declined.
|
||||
*/
|
||||
data object Declined : CreateAuthRequestResult()
|
||||
|
||||
/**
|
||||
* The auth request has expired.
|
||||
*/
|
||||
data object Expired : CreateAuthRequestResult()
|
||||
}
|
|
@ -45,6 +45,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.AuthRequestResult
|
|||
import com.x8bit.bitwarden.data.auth.repository.model.AuthRequestsResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.AuthState
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.CreateAuthRequestResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.DeleteAccountResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.KnownDeviceResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.LoginResult
|
||||
|
@ -103,11 +104,18 @@ import org.junit.jupiter.api.Assertions.assertNull
|
|||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
import java.time.ZoneOffset
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
@Suppress("LargeClass")
|
||||
class AuthRepositoryTest {
|
||||
|
||||
private val fixedClock: Clock = Clock.fixed(
|
||||
Instant.parse("2023-10-27T12:00:00Z"),
|
||||
ZoneOffset.UTC,
|
||||
)
|
||||
private val dispatcherManager: DispatcherManager = FakeDispatcherManager()
|
||||
private val accountsService: AccountsService = mockk()
|
||||
private val authRequestsService: AuthRequestsService = mockk()
|
||||
|
@ -192,6 +200,7 @@ class AuthRepositoryTest {
|
|||
private var elapsedRealtimeMillis = 123456789L
|
||||
|
||||
private val repository = AuthRepositoryImpl(
|
||||
clock = fixedClock,
|
||||
accountsService = accountsService,
|
||||
authRequestsService = authRequestsService,
|
||||
devicesService = devicesService,
|
||||
|
@ -2544,6 +2553,236 @@ class AuthRepositoryTest {
|
|||
assertEquals(expected, result)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `createAuthRequestWithUpdates with authSdkSource getNewAuthRequest error should emit Error`() =
|
||||
runTest {
|
||||
val email = "email@email.com"
|
||||
coEvery {
|
||||
authSdkSource.getNewAuthRequest(email = email)
|
||||
} returns Throwable("Fail").asFailure()
|
||||
|
||||
repository.createAuthRequestWithUpdates(email = email).test {
|
||||
assertEquals(CreateAuthRequestResult.Error, awaitItem())
|
||||
awaitComplete()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `createAuthRequestWithUpdates with newAuthRequestService createAuthRequest error should emit Error`() =
|
||||
runTest {
|
||||
val email = "email@email.com"
|
||||
val authRequestResponse = AUTH_REQUEST_RESPONSE
|
||||
coEvery {
|
||||
authSdkSource.getNewAuthRequest(email = email)
|
||||
} returns authRequestResponse.asSuccess()
|
||||
coEvery {
|
||||
newAuthRequestService.createAuthRequest(
|
||||
email = email,
|
||||
publicKey = authRequestResponse.publicKey,
|
||||
deviceId = fakeAuthDiskSource.uniqueAppId,
|
||||
accessCode = authRequestResponse.accessCode,
|
||||
fingerprint = authRequestResponse.fingerprint,
|
||||
)
|
||||
} returns Throwable("Fail").asFailure()
|
||||
|
||||
repository.createAuthRequestWithUpdates(email = email).test {
|
||||
assertEquals(CreateAuthRequestResult.Error, awaitItem())
|
||||
awaitComplete()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `createAuthRequestWithUpdates with createNewAuthRequest Success and getAuthRequestUpdate with approval should emit Success`() =
|
||||
runTest {
|
||||
val email = "email@email.com"
|
||||
val authRequestResponse = AUTH_REQUEST_RESPONSE
|
||||
val authRequestResponseJson = AuthRequestsResponseJson.AuthRequest(
|
||||
id = "1",
|
||||
publicKey = PUBLIC_KEY,
|
||||
platform = "Android",
|
||||
ipAddress = "192.168.0.1",
|
||||
key = "public",
|
||||
masterPasswordHash = "verySecureHash",
|
||||
creationDate = ZonedDateTime.parse("2024-09-13T00:00Z"),
|
||||
responseDate = null,
|
||||
requestApproved = false,
|
||||
originUrl = "www.bitwarden.com",
|
||||
)
|
||||
val updatedAuthRequestResponseJson = authRequestResponseJson.copy(
|
||||
requestApproved = true,
|
||||
)
|
||||
val authRequest = AuthRequest(
|
||||
id = authRequestResponseJson.id,
|
||||
publicKey = authRequestResponseJson.publicKey,
|
||||
platform = authRequestResponseJson.platform,
|
||||
ipAddress = authRequestResponseJson.ipAddress,
|
||||
key = authRequestResponseJson.key,
|
||||
masterPasswordHash = authRequestResponseJson.masterPasswordHash,
|
||||
creationDate = authRequestResponseJson.creationDate,
|
||||
responseDate = authRequestResponseJson.responseDate,
|
||||
requestApproved = authRequestResponseJson.requestApproved ?: false,
|
||||
originUrl = authRequestResponseJson.originUrl,
|
||||
fingerprint = authRequestResponse.fingerprint,
|
||||
)
|
||||
coEvery {
|
||||
authSdkSource.getNewAuthRequest(email = email)
|
||||
} returns authRequestResponse.asSuccess()
|
||||
coEvery {
|
||||
newAuthRequestService.createAuthRequest(
|
||||
email = email,
|
||||
publicKey = authRequestResponse.publicKey,
|
||||
deviceId = fakeAuthDiskSource.uniqueAppId,
|
||||
accessCode = authRequestResponse.accessCode,
|
||||
fingerprint = authRequestResponse.fingerprint,
|
||||
)
|
||||
} returns authRequestResponseJson.asSuccess()
|
||||
coEvery {
|
||||
newAuthRequestService.getAuthRequestUpdate(
|
||||
requestId = authRequest.id,
|
||||
accessCode = authRequestResponse.accessCode,
|
||||
)
|
||||
} returnsMany listOf(
|
||||
authRequestResponseJson.asSuccess(),
|
||||
updatedAuthRequestResponseJson.asSuccess(),
|
||||
)
|
||||
|
||||
repository.createAuthRequestWithUpdates(email = email).test {
|
||||
assertEquals(CreateAuthRequestResult.Update(authRequest), awaitItem())
|
||||
assertEquals(CreateAuthRequestResult.Update(authRequest), awaitItem())
|
||||
assertEquals(
|
||||
CreateAuthRequestResult.Success(
|
||||
authRequest = authRequest.copy(requestApproved = true),
|
||||
authRequestResponse = authRequestResponse,
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
awaitComplete()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `createAuthRequestWithUpdates with createNewAuthRequest Success and getAuthRequestUpdate with response date and no approval should emit Declined`() =
|
||||
runTest {
|
||||
val email = "email@email.com"
|
||||
val authRequestResponse = AUTH_REQUEST_RESPONSE
|
||||
val authRequestResponseJson = AuthRequestsResponseJson.AuthRequest(
|
||||
id = "1",
|
||||
publicKey = PUBLIC_KEY,
|
||||
platform = "Android",
|
||||
ipAddress = "192.168.0.1",
|
||||
key = "public",
|
||||
masterPasswordHash = "verySecureHash",
|
||||
creationDate = ZonedDateTime.parse("2024-09-13T00:00Z"),
|
||||
responseDate = null,
|
||||
requestApproved = false,
|
||||
originUrl = "www.bitwarden.com",
|
||||
)
|
||||
val updatedAuthRequestResponseJson = authRequestResponseJson.copy(
|
||||
responseDate = ZonedDateTime.parse("2024-09-13T00:00Z"),
|
||||
)
|
||||
val authRequest = AuthRequest(
|
||||
id = authRequestResponseJson.id,
|
||||
publicKey = authRequestResponseJson.publicKey,
|
||||
platform = authRequestResponseJson.platform,
|
||||
ipAddress = authRequestResponseJson.ipAddress,
|
||||
key = authRequestResponseJson.key,
|
||||
masterPasswordHash = authRequestResponseJson.masterPasswordHash,
|
||||
creationDate = authRequestResponseJson.creationDate,
|
||||
responseDate = authRequestResponseJson.responseDate,
|
||||
requestApproved = authRequestResponseJson.requestApproved ?: false,
|
||||
originUrl = authRequestResponseJson.originUrl,
|
||||
fingerprint = authRequestResponse.fingerprint,
|
||||
)
|
||||
coEvery {
|
||||
authSdkSource.getNewAuthRequest(email = email)
|
||||
} returns authRequestResponse.asSuccess()
|
||||
coEvery {
|
||||
newAuthRequestService.createAuthRequest(
|
||||
email = email,
|
||||
publicKey = authRequestResponse.publicKey,
|
||||
deviceId = fakeAuthDiskSource.uniqueAppId,
|
||||
accessCode = authRequestResponse.accessCode,
|
||||
fingerprint = authRequestResponse.fingerprint,
|
||||
)
|
||||
} returns authRequestResponseJson.asSuccess()
|
||||
coEvery {
|
||||
newAuthRequestService.getAuthRequestUpdate(
|
||||
requestId = authRequest.id,
|
||||
accessCode = authRequestResponse.accessCode,
|
||||
)
|
||||
} returns updatedAuthRequestResponseJson.asSuccess()
|
||||
|
||||
repository.createAuthRequestWithUpdates(email = email).test {
|
||||
assertEquals(CreateAuthRequestResult.Update(authRequest), awaitItem())
|
||||
assertEquals(CreateAuthRequestResult.Declined, awaitItem())
|
||||
awaitComplete()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `createAuthRequestWithUpdates with createNewAuthRequest Success and getAuthRequestUpdate with old creation date should emit Expired`() =
|
||||
runTest {
|
||||
val email = "email@email.com"
|
||||
val authRequestResponse = AUTH_REQUEST_RESPONSE
|
||||
val authRequestResponseJson = AuthRequestsResponseJson.AuthRequest(
|
||||
id = "1",
|
||||
publicKey = PUBLIC_KEY,
|
||||
platform = "Android",
|
||||
ipAddress = "192.168.0.1",
|
||||
key = "public",
|
||||
masterPasswordHash = "verySecureHash",
|
||||
creationDate = ZonedDateTime.parse("2024-09-13T00:00Z"),
|
||||
responseDate = null,
|
||||
requestApproved = false,
|
||||
originUrl = "www.bitwarden.com",
|
||||
)
|
||||
val updatedAuthRequestResponseJson = authRequestResponseJson.copy(
|
||||
creationDate = ZonedDateTime.parse("2023-09-13T00:00Z"),
|
||||
)
|
||||
val authRequest = AuthRequest(
|
||||
id = authRequestResponseJson.id,
|
||||
publicKey = authRequestResponseJson.publicKey,
|
||||
platform = authRequestResponseJson.platform,
|
||||
ipAddress = authRequestResponseJson.ipAddress,
|
||||
key = authRequestResponseJson.key,
|
||||
masterPasswordHash = authRequestResponseJson.masterPasswordHash,
|
||||
creationDate = authRequestResponseJson.creationDate,
|
||||
responseDate = authRequestResponseJson.responseDate,
|
||||
requestApproved = authRequestResponseJson.requestApproved ?: false,
|
||||
originUrl = authRequestResponseJson.originUrl,
|
||||
fingerprint = authRequestResponse.fingerprint,
|
||||
)
|
||||
coEvery {
|
||||
authSdkSource.getNewAuthRequest(email = email)
|
||||
} returns authRequestResponse.asSuccess()
|
||||
coEvery {
|
||||
newAuthRequestService.createAuthRequest(
|
||||
email = email,
|
||||
publicKey = authRequestResponse.publicKey,
|
||||
deviceId = fakeAuthDiskSource.uniqueAppId,
|
||||
accessCode = authRequestResponse.accessCode,
|
||||
fingerprint = authRequestResponse.fingerprint,
|
||||
)
|
||||
} returns authRequestResponseJson.asSuccess()
|
||||
coEvery {
|
||||
newAuthRequestService.getAuthRequestUpdate(
|
||||
requestId = authRequest.id,
|
||||
accessCode = authRequestResponse.accessCode,
|
||||
)
|
||||
} returns updatedAuthRequestResponseJson.asSuccess()
|
||||
|
||||
repository.createAuthRequestWithUpdates(email = email).test {
|
||||
assertEquals(CreateAuthRequestResult.Update(authRequest), awaitItem())
|
||||
assertEquals(CreateAuthRequestResult.Expired, awaitItem())
|
||||
awaitComplete()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getAuthRequest should return failure when getAuthRequests returns failure`() = runTest {
|
||||
val fingerprint = "fingerprint"
|
||||
|
|
Loading…
Reference in a new issue