mirror of
https://github.com/bitwarden/android.git
synced 2025-03-16 19:28:44 +03:00
Add setup for WebAuthn (#1294)
This commit is contained in:
parent
77a7cb0e51
commit
ae38b5d7ed
10 changed files with 263 additions and 17 deletions
|
@ -124,6 +124,9 @@ sealed class GetTokenResponseJson {
|
|||
@SerialName("TwoFactorProviders2")
|
||||
val authMethodsData: Map<TwoFactorAuthMethod, JsonObject?>,
|
||||
|
||||
@SerialName("TwoFactorProviders")
|
||||
val twoFactorProviders: List<String>?,
|
||||
|
||||
@SerialName("CaptchaBypassToken")
|
||||
val captchaToken: String?,
|
||||
|
||||
|
|
|
@ -2,8 +2,11 @@ package com.x8bit.bitwarden.data.auth.datasource.network.util
|
|||
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.GetTokenResponseJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorAuthMethod
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.util.base64UrlDecodeOrNull
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.contentOrNull
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
|
||||
/**
|
||||
|
@ -58,3 +61,53 @@ val GetTokenResponseJson.TwoFactorRequired?.twoFactorDisplayEmail: String
|
|||
*/
|
||||
private val Map<TwoFactorAuthMethod, JsonObject?>.duo: JsonObject?
|
||||
get() = get(TwoFactorAuthMethod.DUO) ?: get(TwoFactorAuthMethod.DUO_ORGANIZATION)
|
||||
|
||||
/**
|
||||
* If it exists, return the identifier for the relying party used with Web AuthN two-factor
|
||||
* authentication.
|
||||
*/
|
||||
val GetTokenResponseJson.TwoFactorRequired?.webAuthRpId: String?
|
||||
get() = this
|
||||
?.authMethodsData
|
||||
?.get(TwoFactorAuthMethod.WEB_AUTH)
|
||||
?.get("rpId")
|
||||
?.jsonPrimitive
|
||||
?.contentOrNull
|
||||
|
||||
/**
|
||||
* If it exists, return the type of user verification needed to complete the Web AuthN two-factor
|
||||
* authentication.
|
||||
*/
|
||||
val GetTokenResponseJson.TwoFactorRequired?.webAuthUserVerification: String?
|
||||
get() = this
|
||||
?.authMethodsData
|
||||
?.get(TwoFactorAuthMethod.WEB_AUTH)
|
||||
?.get("userVerification")
|
||||
?.jsonPrimitive
|
||||
?.contentOrNull
|
||||
|
||||
/**
|
||||
* If it exists, return the challenge that the authenticator need to solve to complete the
|
||||
* Web AuthN two-factor authentication.
|
||||
*/
|
||||
val GetTokenResponseJson.TwoFactorRequired?.webAuthChallenge: String?
|
||||
get() = this
|
||||
?.authMethodsData
|
||||
?.get(TwoFactorAuthMethod.WEB_AUTH)
|
||||
?.get("challenge")
|
||||
?.jsonPrimitive
|
||||
?.contentOrNull
|
||||
|
||||
/**
|
||||
* If it exists, return the credentials allowed to be used to solve the challenge to complete the
|
||||
* Web AuthN two-factor authentication.
|
||||
*/
|
||||
val GetTokenResponseJson.TwoFactorRequired?.webAuthAllowCredentials: List<String>?
|
||||
get() = this
|
||||
?.authMethodsData
|
||||
?.get(TwoFactorAuthMethod.WEB_AUTH)
|
||||
?.get("allowCredentials")
|
||||
?.jsonArray
|
||||
?.mapNotNull {
|
||||
it.jsonObject["id"]?.jsonPrimitive?.contentOrNull?.base64UrlDecodeOrNull()
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.x8bit.bitwarden.data.auth.repository.util.CaptchaCallbackTokenResult
|
|||
import com.x8bit.bitwarden.data.auth.repository.util.DuoCallbackTokenResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.generateUriForCaptcha
|
||||
import com.x8bit.bitwarden.data.auth.util.YubiKeyResult
|
||||
import com.x8bit.bitwarden.ui.auth.feature.twofactorlogin.util.button
|
||||
import com.x8bit.bitwarden.ui.auth.feature.twofactorlogin.util.imageRes
|
||||
import com.x8bit.bitwarden.ui.auth.feature.twofactorlogin.util.isDuo
|
||||
import com.x8bit.bitwarden.ui.auth.feature.twofactorlogin.util.shouldUseNfc
|
||||
|
@ -456,12 +457,7 @@ data class TwoFactorLoginState(
|
|||
/**
|
||||
* The text to display for the button given the [authMethod].
|
||||
*/
|
||||
val buttonText: Text
|
||||
get() = if (authMethod.isDuo) {
|
||||
R.string.launch_duo.asText()
|
||||
} else {
|
||||
R.string.continue_text.asText()
|
||||
}
|
||||
val buttonText: Text get() = authMethod.button
|
||||
|
||||
/**
|
||||
* Indicates if the screen should be listening for NFC events from the operating system.
|
||||
|
|
|
@ -20,6 +20,7 @@ val TwoFactorAuthMethod.title: Text
|
|||
|
||||
TwoFactorAuthMethod.EMAIL -> R.string.email.asText()
|
||||
TwoFactorAuthMethod.RECOVERY_CODE -> R.string.recovery_code_title.asText()
|
||||
TwoFactorAuthMethod.WEB_AUTH -> R.string.fido2_authenticate_web_authn.asText()
|
||||
TwoFactorAuthMethod.YUBI_KEY -> R.string.yubi_key_title.asText()
|
||||
else -> "".asText()
|
||||
}
|
||||
|
@ -38,10 +39,31 @@ fun TwoFactorAuthMethod.description(email: String): Text = when (this) {
|
|||
}
|
||||
|
||||
TwoFactorAuthMethod.EMAIL -> R.string.enter_verification_code_email.asText(email)
|
||||
TwoFactorAuthMethod.WEB_AUTH -> R.string.continue_to_complete_web_authn_verfication.asText()
|
||||
TwoFactorAuthMethod.YUBI_KEY -> R.string.yubi_key_instruction.asText()
|
||||
else -> "".asText()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the button label for the given auth method.
|
||||
*/
|
||||
val TwoFactorAuthMethod.button: Text
|
||||
get() = when (this) {
|
||||
TwoFactorAuthMethod.DUO,
|
||||
TwoFactorAuthMethod.DUO_ORGANIZATION,
|
||||
-> R.string.launch_duo.asText()
|
||||
|
||||
TwoFactorAuthMethod.AUTHENTICATOR_APP,
|
||||
TwoFactorAuthMethod.EMAIL,
|
||||
TwoFactorAuthMethod.YUBI_KEY,
|
||||
TwoFactorAuthMethod.U2F,
|
||||
TwoFactorAuthMethod.REMEMBER,
|
||||
TwoFactorAuthMethod.RECOVERY_CODE,
|
||||
-> R.string.continue_text.asText()
|
||||
|
||||
TwoFactorAuthMethod.WEB_AUTH -> R.string.launch_web_authn.asText()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a boolean indicating if the given auth method uses Duo.
|
||||
*/
|
||||
|
|
|
@ -15,4 +15,6 @@
|
|||
<string name="password_protected" translatable="false">Password Protected</string>
|
||||
<string name="password_used_to_export" translatable="false">This password will be used to export and import this file</string>
|
||||
<string name="autofill_suggestion" translatable="false">Autofill suggestion</string>
|
||||
<string name="continue_to_complete_web_authn_verfication" translatable="false">Continue to complete WebAuthn verification.</string>
|
||||
<string name="launch_web_authn" translatable="false">Launch WebAuthn</string>
|
||||
</resources>
|
||||
|
|
|
@ -385,7 +385,8 @@ private const val TWO_FACTOR_BODY_JSON = """
|
|||
{
|
||||
"TwoFactorProviders2": {"1": {"Email": "ex***@email.com"}, "0": {"Email": null}},
|
||||
"SsoEmail2faSessionToken": "exampleToken",
|
||||
"CaptchaBypassToken": "BWCaptchaBypass_ABCXYZ"
|
||||
"CaptchaBypassToken": "BWCaptchaBypass_ABCXYZ",
|
||||
"TwoFactorProviders": ["1", "3", "0"]
|
||||
}
|
||||
"""
|
||||
private val TWO_FACTOR_BODY = GetTokenResponseJson.TwoFactorRequired(
|
||||
|
@ -395,6 +396,7 @@ private val TWO_FACTOR_BODY = GetTokenResponseJson.TwoFactorRequired(
|
|||
),
|
||||
ssoToken = "exampleToken",
|
||||
captchaToken = "BWCaptchaBypass_ABCXYZ",
|
||||
twoFactorProviders = listOf("1", "3", "0"),
|
||||
)
|
||||
|
||||
private const val LOGIN_SUCCESS_JSON = """
|
||||
|
|
|
@ -2,10 +2,12 @@ package com.x8bit.bitwarden.data.auth.datasource.network.util
|
|||
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.GetTokenResponseJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorAuthMethod
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonNull
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
|
@ -21,6 +23,7 @@ class TwoFactorRequiredExtensionTest {
|
|||
),
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
assertEquals(
|
||||
listOf(
|
||||
|
@ -43,6 +46,7 @@ class TwoFactorRequiredExtensionTest {
|
|||
),
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
assertEquals("Bitwarden", subject.twoFactorDuoAuthUrl)
|
||||
}
|
||||
|
@ -58,6 +62,7 @@ class TwoFactorRequiredExtensionTest {
|
|||
),
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
assertEquals("Bitwarden", subject.twoFactorDuoAuthUrl)
|
||||
}
|
||||
|
@ -70,6 +75,7 @@ class TwoFactorRequiredExtensionTest {
|
|||
),
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
assertNull(subject.twoFactorDuoAuthUrl)
|
||||
}
|
||||
|
@ -85,6 +91,7 @@ class TwoFactorRequiredExtensionTest {
|
|||
),
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
assertEquals("ex***@email.com", subject.twoFactorDisplayEmail)
|
||||
}
|
||||
|
@ -97,6 +104,7 @@ class TwoFactorRequiredExtensionTest {
|
|||
),
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
assertEquals("", subject.twoFactorDisplayEmail)
|
||||
}
|
||||
|
@ -112,7 +120,115 @@ class TwoFactorRequiredExtensionTest {
|
|||
),
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
assertEquals(TwoFactorAuthMethod.AUTHENTICATOR_APP, subject.preferredAuthMethod)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `twoFactorDuoAuthUrl returns the expected value for DUO`() {
|
||||
val authUrl = "vault.bitwarden.com"
|
||||
val subject = GetTokenResponseJson.TwoFactorRequired(
|
||||
authMethodsData = mapOf(
|
||||
TwoFactorAuthMethod.DUO to JsonObject(
|
||||
mapOf("AuthUrl" to JsonPrimitive(authUrl)),
|
||||
),
|
||||
),
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
assertEquals(authUrl, subject.twoFactorDuoAuthUrl)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `twoFactorDuoAuthUrl returns the expected value for DUO_ORGANIZATION`() {
|
||||
val authUrl = "vault.bitwarden.com"
|
||||
val subject = GetTokenResponseJson.TwoFactorRequired(
|
||||
authMethodsData = mapOf(
|
||||
TwoFactorAuthMethod.DUO_ORGANIZATION to JsonObject(
|
||||
mapOf("AuthUrl" to JsonPrimitive(authUrl)),
|
||||
),
|
||||
),
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
assertEquals(authUrl, subject.twoFactorDuoAuthUrl)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `webAuthRpId returns the expected value`() {
|
||||
val rpId = "vault.bitwarden.com"
|
||||
val subject = GetTokenResponseJson.TwoFactorRequired(
|
||||
authMethodsData = mapOf(
|
||||
TwoFactorAuthMethod.WEB_AUTH to JsonObject(
|
||||
mapOf("rpId" to JsonPrimitive(rpId)),
|
||||
),
|
||||
),
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
assertEquals(rpId, subject.webAuthRpId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `webAuthUserVerification returns the expected value`() {
|
||||
val userVerification = "discouraged"
|
||||
val subject = GetTokenResponseJson.TwoFactorRequired(
|
||||
authMethodsData = mapOf(
|
||||
TwoFactorAuthMethod.WEB_AUTH to JsonObject(
|
||||
mapOf("userVerification" to JsonPrimitive(userVerification)),
|
||||
),
|
||||
),
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
assertEquals(userVerification, subject.webAuthUserVerification)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `webAuthChallenge returns the expected value`() {
|
||||
val challenge = "987t34478t9rxq7t8n"
|
||||
val subject = GetTokenResponseJson.TwoFactorRequired(
|
||||
authMethodsData = mapOf(
|
||||
TwoFactorAuthMethod.WEB_AUTH to JsonObject(
|
||||
mapOf("challenge" to JsonPrimitive(challenge)),
|
||||
),
|
||||
),
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
assertEquals(challenge, subject.webAuthChallenge)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `webAuthAllowCredentials returns the expected value`() {
|
||||
val credential = "98426435782"
|
||||
val subject = GetTokenResponseJson.TwoFactorRequired(
|
||||
authMethodsData = mapOf(
|
||||
TwoFactorAuthMethod.WEB_AUTH to JsonObject(
|
||||
mapOf(
|
||||
"allowCredentials" to JsonArray(
|
||||
listOf(
|
||||
JsonObject(
|
||||
mapOf(
|
||||
"type" to JsonPrimitive("public-key"),
|
||||
"id" to JsonPrimitive(credential),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
assertNotNull(subject.webAuthAllowCredentials)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1632,13 +1632,19 @@ class AuthRepositoryTest {
|
|||
authMethodsData = TWO_FACTOR_AUTH_METHODS_DATA,
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
.asSuccess()
|
||||
val result = repository.login(email = EMAIL, password = PASSWORD, captchaToken = null)
|
||||
assertEquals(LoginResult.TwoFactorRequired, result)
|
||||
assertEquals(
|
||||
repository.twoFactorResponse,
|
||||
GetTokenResponseJson.TwoFactorRequired(TWO_FACTOR_AUTH_METHODS_DATA, null, null),
|
||||
GetTokenResponseJson.TwoFactorRequired(
|
||||
authMethodsData = TWO_FACTOR_AUTH_METHODS_DATA,
|
||||
twoFactorProviders = null,
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
),
|
||||
)
|
||||
assertEquals(AuthState.Unauthenticated, repository.authStateFlow.value)
|
||||
coVerify { identityService.preLogin(email = EMAIL) }
|
||||
|
@ -1675,6 +1681,7 @@ class AuthRepositoryTest {
|
|||
authMethodsData = TWO_FACTOR_AUTH_METHODS_DATA,
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
.asSuccess()
|
||||
val firstResult = repository.login(email = EMAIL, password = PASSWORD, captchaToken = null)
|
||||
|
@ -2068,7 +2075,12 @@ class AuthRepositoryTest {
|
|||
uniqueAppId = UNIQUE_APP_ID,
|
||||
)
|
||||
} returns GetTokenResponseJson
|
||||
.TwoFactorRequired(TWO_FACTOR_AUTH_METHODS_DATA, null, null)
|
||||
.TwoFactorRequired(
|
||||
authMethodsData = TWO_FACTOR_AUTH_METHODS_DATA,
|
||||
twoFactorProviders = null,
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
)
|
||||
.asSuccess()
|
||||
val result = repository.login(
|
||||
email = EMAIL,
|
||||
|
@ -2082,7 +2094,12 @@ class AuthRepositoryTest {
|
|||
assertEquals(LoginResult.TwoFactorRequired, result)
|
||||
assertEquals(
|
||||
repository.twoFactorResponse,
|
||||
GetTokenResponseJson.TwoFactorRequired(TWO_FACTOR_AUTH_METHODS_DATA, null, null),
|
||||
GetTokenResponseJson.TwoFactorRequired(
|
||||
authMethodsData = TWO_FACTOR_AUTH_METHODS_DATA,
|
||||
twoFactorProviders = null,
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
),
|
||||
)
|
||||
assertEquals(AuthState.Unauthenticated, repository.authStateFlow.value)
|
||||
coVerify {
|
||||
|
@ -2115,7 +2132,12 @@ class AuthRepositoryTest {
|
|||
uniqueAppId = UNIQUE_APP_ID,
|
||||
)
|
||||
} returns GetTokenResponseJson
|
||||
.TwoFactorRequired(TWO_FACTOR_AUTH_METHODS_DATA, null, null)
|
||||
.TwoFactorRequired(
|
||||
authMethodsData = TWO_FACTOR_AUTH_METHODS_DATA,
|
||||
twoFactorProviders = null,
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
)
|
||||
.asSuccess()
|
||||
val firstResult = repository.login(
|
||||
email = EMAIL,
|
||||
|
@ -2721,6 +2743,7 @@ class AuthRepositoryTest {
|
|||
authMethodsData = TWO_FACTOR_AUTH_METHODS_DATA,
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
.asSuccess()
|
||||
val result = repository.login(
|
||||
|
@ -2734,7 +2757,12 @@ class AuthRepositoryTest {
|
|||
assertEquals(LoginResult.TwoFactorRequired, result)
|
||||
assertEquals(
|
||||
repository.twoFactorResponse,
|
||||
GetTokenResponseJson.TwoFactorRequired(TWO_FACTOR_AUTH_METHODS_DATA, null, null),
|
||||
GetTokenResponseJson.TwoFactorRequired(
|
||||
authMethodsData = TWO_FACTOR_AUTH_METHODS_DATA,
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
),
|
||||
)
|
||||
assertEquals(AuthState.Unauthenticated, repository.authStateFlow.value)
|
||||
coVerify {
|
||||
|
@ -2771,6 +2799,7 @@ class AuthRepositoryTest {
|
|||
authMethodsData = TWO_FACTOR_AUTH_METHODS_DATA,
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
.asSuccess()
|
||||
|
||||
|
@ -4124,6 +4153,7 @@ class AuthRepositoryTest {
|
|||
authMethodsData = TWO_FACTOR_AUTH_METHODS_DATA,
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
.asSuccess()
|
||||
val firstResult = repository.login(email = EMAIL, password = PASSWORD, captchaToken = null)
|
||||
|
|
|
@ -257,6 +257,7 @@ class TwoFactorLoginViewModelTest : BaseViewModelTest() {
|
|||
authMethodsData = authMethodsData,
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
every { authRepository.twoFactorResponse } returns response
|
||||
val mockkUri = mockk<Uri>()
|
||||
|
@ -291,6 +292,7 @@ class TwoFactorLoginViewModelTest : BaseViewModelTest() {
|
|||
authMethodsData = authMethodsData,
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
every { authRepository.twoFactorResponse } returns response
|
||||
val viewModel = createViewModel(
|
||||
|
@ -625,9 +627,10 @@ class TwoFactorLoginViewModelTest : BaseViewModelTest() {
|
|||
)
|
||||
private val TWO_FACTOR_RESPONSE =
|
||||
GetTokenResponseJson.TwoFactorRequired(
|
||||
TWO_FACTOR_AUTH_METHODS_DATA,
|
||||
null,
|
||||
null,
|
||||
authMethodsData = TWO_FACTOR_AUTH_METHODS_DATA,
|
||||
captchaToken = null,
|
||||
ssoToken = null,
|
||||
twoFactorProviders = null,
|
||||
)
|
||||
|
||||
private val DEFAULT_STATE = TwoFactorLoginState(
|
||||
|
|
|
@ -20,7 +20,7 @@ class TwoFactorAuthMethodExtensionTest {
|
|||
TwoFactorAuthMethod.DUO_ORGANIZATION to R.string.duo_org_title.asText(
|
||||
R.string.organization.asText(),
|
||||
),
|
||||
TwoFactorAuthMethod.WEB_AUTH to "".asText(),
|
||||
TwoFactorAuthMethod.WEB_AUTH to R.string.fido2_authenticate_web_authn.asText(),
|
||||
TwoFactorAuthMethod.RECOVERY_CODE to R.string.recovery_code_title.asText(),
|
||||
)
|
||||
.forEach { (type, title) ->
|
||||
|
@ -48,7 +48,8 @@ class TwoFactorAuthMethodExtensionTest {
|
|||
.asText()
|
||||
.concat(" ".asText())
|
||||
.concat(R.string.follow_the_steps_from_duo_to_finish_logging_in.asText()),
|
||||
TwoFactorAuthMethod.WEB_AUTH to "".asText(),
|
||||
TwoFactorAuthMethod.WEB_AUTH to
|
||||
R.string.continue_to_complete_web_authn_verfication.asText(),
|
||||
TwoFactorAuthMethod.RECOVERY_CODE to "".asText(),
|
||||
)
|
||||
.forEach { (type, title) ->
|
||||
|
@ -59,6 +60,24 @@ class TwoFactorAuthMethodExtensionTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `button returns the expected value`() {
|
||||
mapOf(
|
||||
TwoFactorAuthMethod.AUTHENTICATOR_APP to R.string.continue_text.asText(),
|
||||
TwoFactorAuthMethod.EMAIL to R.string.continue_text.asText(),
|
||||
TwoFactorAuthMethod.DUO to R.string.launch_duo.asText(),
|
||||
TwoFactorAuthMethod.YUBI_KEY to R.string.continue_text.asText(),
|
||||
TwoFactorAuthMethod.U2F to R.string.continue_text.asText(),
|
||||
TwoFactorAuthMethod.REMEMBER to R.string.continue_text.asText(),
|
||||
TwoFactorAuthMethod.DUO_ORGANIZATION to R.string.launch_duo.asText(),
|
||||
TwoFactorAuthMethod.WEB_AUTH to R.string.launch_web_authn.asText(),
|
||||
TwoFactorAuthMethod.RECOVERY_CODE to R.string.continue_text.asText(),
|
||||
)
|
||||
.forEach { (type, buttonLabel) ->
|
||||
assertEquals(buttonLabel, type.button)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isDuo returns the expected value`() {
|
||||
mapOf(
|
||||
|
|
Loading…
Add table
Reference in a new issue