[PM-8137] Introduce user verification prompt (#3447)

This commit is contained in:
Patrick Honkonen 2024-07-11 18:22:54 -04:00 committed by GitHub
parent 9b240ddf5f
commit a84694b100
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 81 additions and 9 deletions

View file

@ -13,6 +13,12 @@ interface BiometricsManager {
*/
val isBiometricsSupported: Boolean
/**
* Returns `true` if the device supports performing user verification with biometrics or device
* credentials, `false` otherwise.
*/
val isUserVerificationSupported: Boolean
/**
* Display a prompt for setting up or verifying biometrics.
*/
@ -23,4 +29,14 @@ interface BiometricsManager {
onError: () -> Unit,
cipher: Cipher,
)
/**
* Display a prompt for performing user verification with biometrics or device credentials.
*/
fun promptUserVerification(
onSuccess: () -> Unit,
onCancel: () -> Unit,
onLockOut: () -> Unit,
onError: () -> Unit,
)
}

View file

@ -22,7 +22,46 @@ class BiometricsManagerImpl(
private val fragmentActivity: FragmentActivity get() = activity as FragmentActivity
override val isBiometricsSupported: Boolean
get() = when (biometricManager.canAuthenticate(Authenticators.BIOMETRIC_STRONG)) {
get() = canAuthenticate(Authenticators.BIOMETRIC_STRONG)
override val isUserVerificationSupported: Boolean
get() = canAuthenticate(
authenticators = Authenticators.BIOMETRIC_STRONG or Authenticators.DEVICE_CREDENTIAL,
)
override fun promptBiometrics(
onSuccess: (cipher: Cipher?) -> Unit,
onCancel: () -> Unit,
onLockOut: () -> Unit,
onError: () -> Unit,
cipher: Cipher,
) {
configureAndDisplayPrompt(
onSuccess = onSuccess,
onCancel = onCancel,
onLockOut = onLockOut,
onError = onError,
cipher = cipher,
)
}
override fun promptUserVerification(
onSuccess: () -> Unit,
onCancel: () -> Unit,
onLockOut: () -> Unit,
onError: () -> Unit,
) {
configureAndDisplayPrompt(
onSuccess = { onSuccess() },
onCancel = onCancel,
onLockOut = onLockOut,
onError = onError,
cipher = null,
)
}
private fun canAuthenticate(authenticators: Int): Boolean =
when (biometricManager.canAuthenticate(authenticators)) {
BiometricManager.BIOMETRIC_SUCCESS -> true
BiometricManager.BIOMETRIC_STATUS_UNKNOWN,
BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED,
@ -35,12 +74,12 @@ class BiometricsManagerImpl(
else -> false
}
override fun promptBiometrics(
onSuccess: (cipher: Cipher?) -> Unit,
private fun configureAndDisplayPrompt(
onSuccess: (Cipher?) -> Unit,
onCancel: () -> Unit,
onLockOut: () -> Unit,
onError: () -> Unit,
cipher: Cipher,
cipher: Cipher?,
) {
val biometricPrompt = BiometricPrompt(
fragmentActivity,
@ -79,13 +118,28 @@ class BiometricsManagerImpl(
},
)
val promptInfo = BiometricPrompt.PromptInfo.Builder()
val promptInfoBuilder = BiometricPrompt.PromptInfo.Builder()
.setTitle(activity.getString(R.string.bitwarden))
.setDescription(activity.getString(R.string.biometrics_direction))
.setNegativeButtonText(activity.getString(R.string.cancel))
.setAllowedAuthenticators(Authenticators.BIOMETRIC_STRONG)
.build()
biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher))
cipher
?.let {
promptInfoBuilder
.setDescription(activity.getString(R.string.biometrics_direction))
.setAllowedAuthenticators(Authenticators.BIOMETRIC_STRONG)
.setNegativeButtonText(activity.getString(R.string.cancel))
biometricPrompt.authenticate(
promptInfoBuilder.build(),
BiometricPrompt.CryptoObject(it),
)
}
?: run {
promptInfoBuilder
.setDescription(activity.getString(R.string.user_verification_direction))
.setAllowedAuthenticators(
Authenticators.BIOMETRIC_STRONG or Authenticators.DEVICE_CREDENTIAL,
)
biometricPrompt.authenticate(promptInfoBuilder.build())
}
}
}

View file

@ -925,4 +925,6 @@ Do you want to switch to this account?</string>
<string name="launch_web_authn">Launch WebAuthn</string>
<string name="there_was_an_error_starting_web_authn_two_factor_authentication">There was an error starting WebAuthn two factor authentication</string>
<string name="self_hosted_server_url">Self-hosted server URL</string>
<string name="passkey_operation_failed_because_user_could_not_be_verified">Passkey operation failed because user could not be verified.</string>
<string name="user_verification_direction">User verification</string>
</resources>