设备凭据(PIN)使用BiometricPrompt会销毁调用活动。

huangapple go评论65阅读模式
英文:

Device credential (PIN) using BiometricPrompt destroys calling activity

问题

我们目前在一些安卓设备上面临着奇怪的BiometricPrompt行为。

问题是与品牌/制造商无关的,因为在一些三星设备上它可以工作,但在其他设备上不能。

问题:

为了批准用户在她的账户上进行的某些交易,我们要求她使用她的设备凭据或生物特征来进行身份验证。
底层我们正在使用BiometricPrompt
我们面临的问题是,即在调用BiometricPrompt.authenticate()方法之后,设备凭据屏幕被显示出来(具体来说只有PIN),我们的底层活动被销毁(两者之间只有几毫秒),我们输入PIN码,点击继续,设备凭据屏幕消失,然后我们看到设备本身的主屏幕。
这个问题只发生在用户设置了PIN作为设备凭据的情况下。
图案和生物特征都能正常工作。
此外,PIN在其他一些安卓设备上也能工作。

代码:

以下是我们调用的代码:

suspend fun authenticate(
    activity: FragmentActivity
): AuthenticationResult = suspendCoroutine { continuation ->
    @Suppress("DEPRECATION") // 我们知道这个问题。如果可能的话,我们会使用“现代”方法。
    val promptInfo = BiometricPrompt.PromptInfo.Builder()
        .setTitle(activity.getString(R.string.biometric_prompt_title))
        .setDeviceCredentialAllowed(true)
        .build()
    activity.createBiometricPrompt(continuation).authenticate(promptInfo)
}

我们的authenticate()方法是在主线程上使用Dispatchers.Main调用的。

以下是createBiometricPrompt()方法的代码:

fun FragmentActivity.createBiometricPrompt(continuation: Continuation<AuthenticationResult>): BiometricPrompt {
    return BiometricPrompt(
        this,
        object : BiometricPrompt.AuthenticationCallback() {
            override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
                continuation.resume(AuthenticationResult.Error(errorCode, errString))
            }

            override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
                continuation.resume(AuthenticationResult.Succeeded(result))
            }

            override fun onAuthenticationFailed() {
                Timber.i("onAuthenticationFailed")
            }
        }
    )
}

因为我们向注册为第二因素的设备发送推送,所以我们创建一个PendingIntent来呈现交易活动。
我们使用以下代码创建活动:

fun createPendingIntent(context: Context, transaction: Transaction): PendingIntent =
    Intent(context, TransactionActivity::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_NO_HISTORY or
                Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
        putExtra(INTENT_EXTRA_TRANSACTION_APPROVAL, transaction)
    }.let { intent ->
        val flags = PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
        PendingIntent.getActivity(context, 1484, intent, flags)
    }

我们在登录期间使用相同的代码。
在那里,我们的代码在所有设备上都没有任何问题。
因此,我猜想我们在创建Intent/PendingIntent时可能做错了什么? 🤷

你们中有人有什么想法,可能是什么导致了我们的问题?

已尝试的解决方案:

  • 在我们的活动中添加了日志。可以确认我们的活动的onDestroy被调用。
  • Intent中删除了所有flags
英文:

we are currently facing a weird BiometricPrompt behaviour on some Android devices.
The problem is brand/manufacturer independent because it works e.g. on some Samsung devices but on others doesn't.

The Issue

To approve some transaction made by the user to her account we require her to authenticate herself using her device credentials or biometry.
Under the hood we are using BiometricPrompt.
The problem we are facing is that immediately after calling the BiometricPrompt.authenticate() method the device credential screen is shown (specifically only PIN), our underlying activity gets destroyed (there are just some milliseconds in between), we enter the Pin-Code, click continue, the device credential screens disappears and we see the home screen of the device itself.
The problem only occurs while the users has set up a PIN as device credential.
Pattern and biometry are working fine.
Also, PIN is working on some other Android devices.

Code

Here is the code we are calling:

suspend fun authenticate(
    activity: FragmentActivity
): AuthenticationResult = suspendCoroutine { continuation ->
    @Suppress("DEPRECATION") // We are aware of this. If possible we use the "Modern" approach.
    val promptInfo = BiometricPrompt.PromptInfo.Builder()
        .setTitle(activity.getString(R.string.biometric_prompt_title))
        .setDeviceCredentialAllowed(true)
        .build()
    activity.createBiometricPrompt(continuation).authenticate(promptInfo)
}

Our authenticate() method is called on the main thread using Dispatchers.Main.

Here is the code of the createBiometricPrompt() method:

fun FragmentActivity.createBiometricPrompt(continuation: Continuation<AuthenticationResult>): BiometricPrompt {
    return BiometricPrompt(
        this,
        object : BiometricPrompt.AuthenticationCallback() {
            override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
                continuation.resume(AuthenticationResult.Error(errorCode, errString))
            }

            override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
                continuation.resume(AuthenticationResult.Succeeded(result))
            }

            override fun onAuthenticationFailed() {
                Timber.i("onAuthenticationFailed")
            }
        }
    )
}

Since we are sending a push to the device which is registered as the second factor we create a PendingIntent to present the transaction activity.
We create the activity with the following code:

fun createPendingIntent(context: Context, transaction: Transaction): PendingIntent =
    Intent(context, TransactionActivity::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_NO_HISTORY or
                Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
        putExtra(INTENT_EXTRA_TRANSACTION_APPROVAL, transaction)
    }.let { intent ->
        val flags = PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
        PendingIntent.getActivity(context, 1484, intent, flags)
    }

We use the same code during login.
There our code works without any issues on all devices.
Therefore my guess that we might do something wrong creating the Intent/PendingIntent? 🤷

Does any of you has an idea what might cause our problem?

Already tried solutions

  • Added logs to our activity. Can confirm that onDestroy of our activity is called
  • removed all flags from Intent

答案1

得分: 1

我刚找到了解决我的问题的方法。关于文档从通知启动活动,他们建议使用TaskStackBuilder来构建您的PendingIntent。使用TaskStackBuilder时,我们的活动不会在PIN屏幕显示时被关闭。

英文:

I just found a solution to my problem.
Regarding the documentation here Start an Activity from a Notification they recommend using TaskStackBuilder to build your PendingIntent.
Using TaskStackBuilder our activity isn't killed while the PIN screen gets/is shown.

huangapple
  • 本文由 发表于 2023年3月31日 18:00:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/75897200.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定