mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 07:05:35 +03:00
Password encoding and decoding handing in TwoFactorLoginNavigation (#3951)
This commit is contained in:
parent
37b5dc7de3
commit
bcdbea13bf
8 changed files with 23 additions and 33 deletions
|
@ -111,7 +111,7 @@ fun NavGraphBuilder.authGraph(
|
||||||
onNavigateToTwoFactorLogin = { emailAddress ->
|
onNavigateToTwoFactorLogin = { emailAddress ->
|
||||||
navController.navigateToTwoFactorLogin(
|
navController.navigateToTwoFactorLogin(
|
||||||
emailAddress = emailAddress,
|
emailAddress = emailAddress,
|
||||||
base64EncodedPassword = null,
|
password = null,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -154,7 +154,7 @@ fun NavGraphBuilder.authGraph(
|
||||||
onNavigateToTwoFactorLogin = { emailAddress, password ->
|
onNavigateToTwoFactorLogin = { emailAddress, password ->
|
||||||
navController.navigateToTwoFactorLogin(
|
navController.navigateToTwoFactorLogin(
|
||||||
emailAddress = emailAddress,
|
emailAddress = emailAddress,
|
||||||
base64EncodedPassword = password,
|
password = password,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -163,7 +163,7 @@ fun NavGraphBuilder.authGraph(
|
||||||
onNavigateToTwoFactorLogin = {
|
onNavigateToTwoFactorLogin = {
|
||||||
navController.navigateToTwoFactorLogin(
|
navController.navigateToTwoFactorLogin(
|
||||||
emailAddress = it,
|
emailAddress = it,
|
||||||
base64EncodedPassword = null,
|
password = null,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -94,7 +94,7 @@ fun LoginScreen(
|
||||||
}
|
}
|
||||||
|
|
||||||
is LoginEvent.NavigateToTwoFactorLogin -> {
|
is LoginEvent.NavigateToTwoFactorLogin -> {
|
||||||
onNavigateToTwoFactorLogin(event.emailAddress, event.base64EncodedPassword)
|
onNavigateToTwoFactorLogin(event.emailAddress, event.password)
|
||||||
}
|
}
|
||||||
|
|
||||||
is LoginEvent.ShowToast -> {
|
is LoginEvent.ShowToast -> {
|
||||||
|
|
|
@ -12,7 +12,6 @@ import com.x8bit.bitwarden.data.auth.repository.model.KnownDeviceResult
|
||||||
import com.x8bit.bitwarden.data.auth.repository.model.LoginResult
|
import com.x8bit.bitwarden.data.auth.repository.model.LoginResult
|
||||||
import com.x8bit.bitwarden.data.auth.repository.util.CaptchaCallbackTokenResult
|
import com.x8bit.bitwarden.data.auth.repository.util.CaptchaCallbackTokenResult
|
||||||
import com.x8bit.bitwarden.data.auth.repository.util.generateUriForCaptcha
|
import com.x8bit.bitwarden.data.auth.repository.util.generateUriForCaptcha
|
||||||
import com.x8bit.bitwarden.data.platform.datasource.network.util.base64UrlEncode
|
|
||||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||||
|
@ -160,9 +159,7 @@ class LoginViewModel @Inject constructor(
|
||||||
sendEvent(
|
sendEvent(
|
||||||
LoginEvent.NavigateToTwoFactorLogin(
|
LoginEvent.NavigateToTwoFactorLogin(
|
||||||
emailAddress = state.emailAddress,
|
emailAddress = state.emailAddress,
|
||||||
// Base64 URL encode the password to prevent corruption of escapable chars
|
password = state.passwordInput,
|
||||||
// when sending via navArgs.
|
|
||||||
base64EncodedPassword = state.passwordInput.base64UrlEncode(),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -345,7 +342,7 @@ sealed class LoginEvent {
|
||||||
*/
|
*/
|
||||||
data class NavigateToTwoFactorLogin(
|
data class NavigateToTwoFactorLogin(
|
||||||
val emailAddress: String,
|
val emailAddress: String,
|
||||||
val base64EncodedPassword: String?,
|
val password: String?,
|
||||||
) : LoginEvent()
|
) : LoginEvent()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -28,7 +28,7 @@ fun NavGraphBuilder.trustedDeviceGraph(navController: NavHostController) {
|
||||||
onNavigateToTwoFactorLogin = {
|
onNavigateToTwoFactorLogin = {
|
||||||
navController.navigateToTwoFactorLogin(
|
navController.navigateToTwoFactorLogin(
|
||||||
emailAddress = it,
|
emailAddress = it,
|
||||||
base64EncodedPassword = null,
|
password = null,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,6 +5,8 @@ import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
import androidx.navigation.NavOptions
|
import androidx.navigation.NavOptions
|
||||||
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
|
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
|
||||||
|
import com.x8bit.bitwarden.data.platform.datasource.network.util.base64UrlDecodeOrNull
|
||||||
|
import com.x8bit.bitwarden.data.platform.datasource.network.util.base64UrlEncode
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.composableWithSlideTransitions
|
import com.x8bit.bitwarden.ui.platform.base.util.composableWithSlideTransitions
|
||||||
|
|
||||||
private const val EMAIL_ADDRESS = "email_address"
|
private const val EMAIL_ADDRESS = "email_address"
|
||||||
|
@ -15,14 +17,12 @@ private const val TWO_FACTOR_LOGIN_ROUTE =
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to retrieve Two-Factor Login arguments from the [SavedStateHandle].
|
* Class to retrieve Two-Factor Login arguments from the [SavedStateHandle].
|
||||||
*
|
|
||||||
* @property base64EncodedPassword Base64 URL encoded password input.
|
|
||||||
*/
|
*/
|
||||||
@OmitFromCoverage
|
@OmitFromCoverage
|
||||||
data class TwoFactorLoginArgs(val emailAddress: String, val base64EncodedPassword: String?) {
|
data class TwoFactorLoginArgs(val emailAddress: String, val password: String?) {
|
||||||
constructor(savedStateHandle: SavedStateHandle) : this(
|
constructor(savedStateHandle: SavedStateHandle) : this(
|
||||||
emailAddress = checkNotNull(savedStateHandle[EMAIL_ADDRESS]) as String,
|
emailAddress = checkNotNull(savedStateHandle[EMAIL_ADDRESS]) as String,
|
||||||
base64EncodedPassword = savedStateHandle[PASSWORD],
|
password = savedStateHandle.get<String>(PASSWORD)?.base64UrlDecodeOrNull(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,11 +31,11 @@ data class TwoFactorLoginArgs(val emailAddress: String, val base64EncodedPasswor
|
||||||
*/
|
*/
|
||||||
fun NavController.navigateToTwoFactorLogin(
|
fun NavController.navigateToTwoFactorLogin(
|
||||||
emailAddress: String,
|
emailAddress: String,
|
||||||
base64EncodedPassword: String?,
|
password: String?,
|
||||||
navOptions: NavOptions? = null,
|
navOptions: NavOptions? = null,
|
||||||
) {
|
) {
|
||||||
this.navigate(
|
this.navigate(
|
||||||
route = "$TWO_FACTOR_LOGIN_PREFIX/$emailAddress?$PASSWORD=$base64EncodedPassword",
|
route = "$TWO_FACTOR_LOGIN_PREFIX/$emailAddress?$PASSWORD=${password?.base64UrlEncode()}",
|
||||||
navOptions = navOptions,
|
navOptions = navOptions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ import com.x8bit.bitwarden.data.auth.repository.util.WebAuthResult
|
||||||
import com.x8bit.bitwarden.data.auth.repository.util.generateUriForCaptcha
|
import com.x8bit.bitwarden.data.auth.repository.util.generateUriForCaptcha
|
||||||
import com.x8bit.bitwarden.data.auth.repository.util.generateUriForWebAuth
|
import com.x8bit.bitwarden.data.auth.repository.util.generateUriForWebAuth
|
||||||
import com.x8bit.bitwarden.data.auth.util.YubiKeyResult
|
import com.x8bit.bitwarden.data.auth.util.YubiKeyResult
|
||||||
import com.x8bit.bitwarden.data.platform.datasource.network.util.base64UrlDecodeOrNull
|
|
||||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||||
import com.x8bit.bitwarden.data.platform.repository.util.baseWebVaultUrlOrDefault
|
import com.x8bit.bitwarden.data.platform.repository.util.baseWebVaultUrlOrDefault
|
||||||
import com.x8bit.bitwarden.ui.auth.feature.twofactorlogin.util.button
|
import com.x8bit.bitwarden.ui.auth.feature.twofactorlogin.util.button
|
||||||
|
@ -72,7 +71,7 @@ class TwoFactorLoginViewModel @Inject constructor(
|
||||||
isRememberMeEnabled = false,
|
isRememberMeEnabled = false,
|
||||||
captchaToken = null,
|
captchaToken = null,
|
||||||
email = args.emailAddress,
|
email = args.emailAddress,
|
||||||
password = args.base64EncodedPassword?.base64UrlDecodeOrNull(),
|
password = args.password,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -12,7 +12,6 @@ import com.x8bit.bitwarden.data.auth.repository.model.LoginResult
|
||||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||||
import com.x8bit.bitwarden.data.auth.repository.util.CaptchaCallbackTokenResult
|
import com.x8bit.bitwarden.data.auth.repository.util.CaptchaCallbackTokenResult
|
||||||
import com.x8bit.bitwarden.data.auth.repository.util.generateUriForCaptcha
|
import com.x8bit.bitwarden.data.auth.repository.util.generateUriForCaptcha
|
||||||
import com.x8bit.bitwarden.data.platform.datasource.network.util.base64UrlEncode
|
|
||||||
import com.x8bit.bitwarden.data.platform.repository.model.Environment
|
import com.x8bit.bitwarden.data.platform.repository.model.Environment
|
||||||
import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentRepository
|
import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentRepository
|
||||||
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
||||||
|
@ -62,7 +61,7 @@ class LoginViewModelTest : BaseViewModelTest() {
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
fun tearDown() {
|
fun tearDown() {
|
||||||
unmockkStatic(::generateUriForCaptcha, String::base64UrlEncode)
|
unmockkStatic(::generateUriForCaptcha)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -340,34 +339,30 @@ class LoginViewModelTest : BaseViewModelTest() {
|
||||||
@Test
|
@Test
|
||||||
fun `LoginButtonClick login returns TwoFactorRequired should base64 URL encode password and emit NavigateToTwoFactorLogin`() =
|
fun `LoginButtonClick login returns TwoFactorRequired should base64 URL encode password and emit NavigateToTwoFactorLogin`() =
|
||||||
runTest {
|
runTest {
|
||||||
mockkStatic(String::base64UrlEncode)
|
val password = "password"
|
||||||
val decodedPassword = "password"
|
|
||||||
val encodedPassword = "base64EncodedPassword"
|
|
||||||
every { decodedPassword.base64UrlEncode() } returns encodedPassword
|
|
||||||
coEvery {
|
coEvery {
|
||||||
authRepository.login(
|
authRepository.login(
|
||||||
email = EMAIL,
|
email = EMAIL,
|
||||||
password = decodedPassword,
|
password = password,
|
||||||
captchaToken = null,
|
captchaToken = null,
|
||||||
)
|
)
|
||||||
} returns LoginResult.TwoFactorRequired
|
} returns LoginResult.TwoFactorRequired
|
||||||
|
|
||||||
val viewModel = createViewModel(
|
val viewModel = createViewModel(
|
||||||
state = DEFAULT_STATE.copy(
|
state = DEFAULT_STATE.copy(
|
||||||
passwordInput = decodedPassword,
|
passwordInput = password,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
viewModel.eventFlow.test {
|
viewModel.eventFlow.test {
|
||||||
viewModel.trySendAction(LoginAction.LoginButtonClick)
|
viewModel.trySendAction(LoginAction.LoginButtonClick)
|
||||||
verify { decodedPassword.base64UrlEncode() }
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
DEFAULT_STATE.copy(passwordInput = decodedPassword),
|
DEFAULT_STATE.copy(passwordInput = password),
|
||||||
viewModel.stateFlow.value,
|
viewModel.stateFlow.value,
|
||||||
)
|
)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
LoginEvent.NavigateToTwoFactorLogin(
|
LoginEvent.NavigateToTwoFactorLogin(
|
||||||
EMAIL,
|
emailAddress = EMAIL,
|
||||||
encodedPassword,
|
password = password,
|
||||||
),
|
),
|
||||||
awaitItem(),
|
awaitItem(),
|
||||||
)
|
)
|
||||||
|
@ -375,7 +370,7 @@ class LoginViewModelTest : BaseViewModelTest() {
|
||||||
coVerify {
|
coVerify {
|
||||||
authRepository.login(
|
authRepository.login(
|
||||||
email = EMAIL,
|
email = EMAIL,
|
||||||
password = decodedPassword,
|
password = password,
|
||||||
captchaToken = null,
|
captchaToken = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ import com.x8bit.bitwarden.data.auth.repository.util.generateUriForCaptcha
|
||||||
import com.x8bit.bitwarden.data.auth.repository.util.generateUriForWebAuth
|
import com.x8bit.bitwarden.data.auth.repository.util.generateUriForWebAuth
|
||||||
import com.x8bit.bitwarden.data.auth.util.YubiKeyResult
|
import com.x8bit.bitwarden.data.auth.util.YubiKeyResult
|
||||||
import com.x8bit.bitwarden.data.platform.datasource.network.util.base64UrlDecodeOrNull
|
import com.x8bit.bitwarden.data.platform.datasource.network.util.base64UrlDecodeOrNull
|
||||||
import com.x8bit.bitwarden.data.platform.datasource.network.util.base64UrlEncode
|
|
||||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||||
import com.x8bit.bitwarden.data.platform.repository.model.Environment
|
import com.x8bit.bitwarden.data.platform.repository.model.Environment
|
||||||
import com.x8bit.bitwarden.data.platform.repository.util.baseWebVaultUrlOrDefault
|
import com.x8bit.bitwarden.data.platform.repository.util.baseWebVaultUrlOrDefault
|
||||||
|
@ -66,7 +65,7 @@ class TwoFactorLoginViewModelTest : BaseViewModelTest() {
|
||||||
mockkStatic(
|
mockkStatic(
|
||||||
::generateUriForCaptcha,
|
::generateUriForCaptcha,
|
||||||
::generateUriForWebAuth,
|
::generateUriForWebAuth,
|
||||||
String::base64UrlEncode,
|
String::base64UrlDecodeOrNull,
|
||||||
)
|
)
|
||||||
mockkStatic(Uri::class)
|
mockkStatic(Uri::class)
|
||||||
every {
|
every {
|
||||||
|
|
Loading…
Reference in a new issue