تتمثل إحدى طرق حماية المعلومات الحساسة أو المحتوى المميز داخل تطبيقك في طلب المصادقة بالمقاييس الحيوية، مثل استخدام ميزة التعرّف على الوجه أو التعرف على البصمات. يشرح هذا الدليل كيفية دعم خطوات تسجيل الدخول باستخدام المقاييس الحيوية في تطبيقك.
يُرجى توضيح أنواع المصادقة التي يتيحها تطبيقك.
لتحديد أنواع المصادقة التي يتيحها تطبيقك، استخدِم واجهة BiometricManager.Authenticators
. يتيح لك النظام الإعلان عن أنواع المصادقة التالية:
BIOMETRIC_STRONG
- المصادقة باستخدام مقاييس حيوية من الفئة 3، على النحو المحدّد في صفحة تعريف التوافق مع Android
BIOMETRIC_WEAK
- المصادقة باستخدام مقاييس حيوية من الفئة 2، على النحو المحدّد في صفحة تعريف التوافق مع Android
DEVICE_CREDENTIAL
- المصادقة باستخدام بيانات اعتماد قفل الشاشة، رقم التعريف الشخصي أو النقش أو كلمة المرور الخاصة بالمستخدم
لبدء استخدام برنامج مصادقة، يحتاج المستخدم إلى إنشاء رقم تعريف شخصي أو نقش أو كلمة مرور. إذا لم يكن لدى المستخدم حساب بالفعل، فسيطلب منه تدفق تسجيل المقاييس الحيوية إنشاء واحد.
لتحديد أنواع المصادقة بالمقاييس الحيوية التي يقبلها تطبيقك، أدخِل
نوع مصادقة أو مجموعة أنواع بتاتًا في الإجراء
setAllowedAuthenticators()
. يعرض مقتطف الرمز التالي كيفية إتاحة المصادقة باستخدام
بيانات اعتماد من الفئة 3 أو بيانات اعتماد قفل الشاشة.
Kotlin
// Lets the user authenticate using either a Class 3 biometric or // their lock screen credential (PIN, pattern, or password). promptInfo = BiometricPrompt.PromptInfo.Builder() .setTitle("Biometric login for my app") .setSubtitle("Log in using your biometric credential") .setAllowedAuthenticators(BIOMETRIC_STRONG or DEVICE_CREDENTIAL) .build()
Java
// Lets user authenticate using either a Class 3 biometric or // their lock screen credential (PIN, pattern, or password). promptInfo = new BiometricPrompt.PromptInfo.Builder() .setTitle("Biometric login for my app") .setSubtitle("Log in using your biometric credential") .setAllowedAuthenticators(BIOMETRIC_STRONG | DEVICE_CREDENTIAL) .build();
لا يمكن استخدام المجموعات التالية من أنواع برامج المصادقة على
Android 10 (المستوى 29 من واجهة برمجة التطبيقات) والإصدارات الأقدم: DEVICE_CREDENTIAL
وBIOMETRIC_STRONG | DEVICE_CREDENTIAL
. للتأكّد من توفّر رقم تعريف شخصي
أو نقش أو كلمة مرور على نظام التشغيل Android 10 والإصدارات الأقدم، استخدِم طريقة
KeyguardManager.isDeviceSecure()
.
التأكّد من توفّر المصادقة بالمقاييس الحيوية
بعد تحديد عناصر المصادقة التي يتيحها تطبيقك، تحقَّق مما إذا كانت هذه العناصر متاحة. لإجراء ذلك، أدخِل مجموعة الأنواع
نفسها على مستوى البت التي ضمّنتها في تعريفك باستخدام
طريقة
setAllowedAuthenticators()
في طريقة
canAuthenticate()
.
إذا لزم الأمر، استدعِ الإجراء
ACTION_BIOMETRIC_ENROLL
المطلوب. وفي نية إضافية، قدِّم مجموعة برامج المصادقة التي يقبلها
تطبيقك. ويطلب هذا الغرض من المستخدم تسجيل بيانات اعتماد برنامج مصادقة يقبلها تطبيقك.
Kotlin
val biometricManager = BiometricManager.from(this) when (biometricManager.canAuthenticate(BIOMETRIC_STRONG or DEVICE_CREDENTIAL)) { BiometricManager.BIOMETRIC_SUCCESS -> Log.d("MY_APP_TAG", "App can authenticate using biometrics.") BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> Log.e("MY_APP_TAG", "No biometric features available on this device.") BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> Log.e("MY_APP_TAG", "Biometric features are currently unavailable.") BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> { // Prompts the user to create credentials that your app accepts. val enrollIntent = Intent(Settings.ACTION_BIOMETRIC_ENROLL).apply { putExtra(Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, BIOMETRIC_STRONG or DEVICE_CREDENTIAL) } startActivityForResult(enrollIntent, REQUEST_CODE) } }
Java
BiometricManager biometricManager = BiometricManager.from(this); switch (biometricManager.canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL)) { case BiometricManager.BIOMETRIC_SUCCESS: Log.d("MY_APP_TAG", "App can authenticate using biometrics."); break; case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE: Log.e("MY_APP_TAG", "No biometric features available on this device."); break; case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE: Log.e("MY_APP_TAG", "Biometric features are currently unavailable."); break; case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED: // Prompts the user to create credentials that your app accepts. final Intent enrollIntent = new Intent(Settings.ACTION_BIOMETRIC_ENROLL); enrollIntent.putExtra(Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, BIOMETRIC_STRONG | DEVICE_CREDENTIAL); startActivityForResult(enrollIntent, REQUEST_CODE); break; }
تحديد كيفية مصادقة المستخدم
بعد مصادقة المستخدم، يمكنك التأكد مما إذا كان المستخدم قد صادق باستخدام
بيانات اعتماد الجهاز أو بيانات اعتماد مقاييس حيوية عن طريق الاتصال بـ
getAuthenticationType()
.
عرض طلب تسجيل الدخول
لعرض طلب للنظام الذي يطلب من المستخدم المصادقة باستخدام بيانات الاعتماد الحيوية، استخدِم مكتبة المقاييس الحيوية. ويتوافق مربع الحوار هذا الذي يوفره النظام عبر التطبيقات التي تستخدمه، مما يوفر تجربة مستخدم أكثر جدارة بالثقة. يظهر مربع حوار نموذجي في الشكل 1.
لإضافة المصادقة بالمقاييس الحيوية إلى تطبيقك باستخدام مكتبة المقاييس الحيوية، أكمِل الخطوات التالية:
في ملف
build.gradle
الخاص بوحدة التطبيق، أضِف عنصرًا اعتمادًا على مكتبةandroidx.biometric
.في النشاط أو الجزء الذي يستضيف مربّع حوار تسجيل الدخول باستخدام المقاييس الحيوية، يمكنك عرض مربّع الحوار باستخدام المنطق الموضّح في مقتطف الرمز التالي:
Kotlin
private lateinit var executor: Executor private lateinit var biometricPrompt: BiometricPrompt private lateinit var promptInfo: BiometricPrompt.PromptInfo override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_login) executor = ContextCompat.getMainExecutor(this) biometricPrompt = BiometricPrompt(this, executor, object : BiometricPrompt.AuthenticationCallback() { override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { super.onAuthenticationError(errorCode, errString) Toast.makeText(applicationContext, "Authentication error: $errString", Toast.LENGTH_SHORT) .show() } override fun onAuthenticationSucceeded( result: BiometricPrompt.AuthenticationResult) { super.onAuthenticationSucceeded(result) Toast.makeText(applicationContext, "Authentication succeeded!", Toast.LENGTH_SHORT) .show() } override fun onAuthenticationFailed() { super.onAuthenticationFailed() Toast.makeText(applicationContext, "Authentication failed", Toast.LENGTH_SHORT) .show() } }) promptInfo = BiometricPrompt.PromptInfo.Builder() .setTitle("Biometric login for my app") .setSubtitle("Log in using your biometric credential") .setNegativeButtonText("Use account password") .build() // Prompt appears when user clicks "Log in". // Consider integrating with the keystore to unlock cryptographic operations, // if needed by your app. val biometricLoginButton = findViewById<Button>(R.id.biometric_login) biometricLoginButton.setOnClickListener { biometricPrompt.authenticate(promptInfo) } }
Java
private Executor executor; private BiometricPrompt biometricPrompt; private BiometricPrompt.PromptInfo promptInfo; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); executor = ContextCompat.getMainExecutor(this); biometricPrompt = new BiometricPrompt(MainActivity.this, executor, new BiometricPrompt.AuthenticationCallback() { @Override public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) { super.onAuthenticationError(errorCode, errString); Toast.makeText(getApplicationContext(), "Authentication error: " + errString, Toast.LENGTH_SHORT) .show(); } @Override public void onAuthenticationSucceeded( @NonNull BiometricPrompt.AuthenticationResult result) { super.onAuthenticationSucceeded(result); Toast.makeText(getApplicationContext(), "Authentication succeeded!", Toast.LENGTH_SHORT).show(); } @Override public void onAuthenticationFailed() { super.onAuthenticationFailed(); Toast.makeText(getApplicationContext(), "Authentication failed", Toast.LENGTH_SHORT) .show(); } }); promptInfo = new BiometricPrompt.PromptInfo.Builder() .setTitle("Biometric login for my app") .setSubtitle("Log in using your biometric credential") .setNegativeButtonText("Use account password") .build(); // Prompt appears when user clicks "Log in". // Consider integrating with the keystore to unlock cryptographic operations, // if needed by your app. Button biometricLoginButton = findViewById(R.id.biometric_login); biometricLoginButton.setOnClickListener(view -> { biometricPrompt.authenticate(promptInfo); }); }
استخدام حل تشفير يعتمد على المصادقة
لحماية المعلومات الحساسة بشكل أكبر داخل تطبيقك، يمكنك دمج التشفير
في سير عمل المصادقة بالمقاييس الحيوية باستخدام مثيل CryptoObject
.
يتيح إطار العمل استخدام كائنات التشفير التالية:
Signature
وCipher
و
Mac
.
بعد أن يقوم المستخدم بالمصادقة بنجاح باستخدام طلب بالمقاييس الحيوية، يمكن لتطبيقك إجراء عملية تشفير. على سبيل المثال، إذا أجريت المصادقة باستخدام عنصر
Cipher
، يمكن لتطبيقك إجراء التشفير وفك التشفير باستخدام عنصر
SecretKey
.
تستعرض الأقسام التالية أمثلة على استخدام عنصر Cipher
وعنصر SecretKey
لتشفير البيانات. يستخدم كل مثال الطرق
التالية:
Kotlin
private fun generateSecretKey(keyGenParameterSpec: KeyGenParameterSpec) { val keyGenerator = KeyGenerator.getInstance( KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore") keyGenerator.init(keyGenParameterSpec) keyGenerator.generateKey() } private fun getSecretKey(): SecretKey { val keyStore = KeyStore.getInstance("AndroidKeyStore") // Before the keystore can be accessed, it must be loaded. keyStore.load(null) return keyStore.getKey(KEY_NAME, null) as SecretKey } private fun getCipher(): Cipher { return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7) }
Java
private void generateSecretKey(KeyGenParameterSpec keyGenParameterSpec) { KeyGenerator keyGenerator = KeyGenerator.getInstance( KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); keyGenerator.init(keyGenParameterSpec); keyGenerator.generateKey(); } private SecretKey getSecretKey() { KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); // Before the keystore can be accessed, it must be loaded. keyStore.load(null); return ((SecretKey)keyStore.getKey(KEY_NAME, null)); } private Cipher getCipher() { return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7); }
المصادقة باستخدام بيانات الاعتماد الحيوية فقط
إذا كان تطبيقك يستخدم مفتاحًا سريًا يتطلب بيانات اعتماد المقاييس الحيوية لفتح القفل، على المستخدم مصادقة بيانات اعتماد المقاييس الحيوية في كل مرة قبل أن يصل التطبيق إلى المفتاح.
لتشفير المعلومات الحساسة فقط بعد مصادقة المستخدم باستخدام بيانات اعتماد المقاييس الحيوية، أكمِل الخطوات التالية:
يمكنك إنشاء مفتاح يستخدم إعدادات
KeyGenParameterSpec
التالية:Kotlin
generateSecretKey(KeyGenParameterSpec.Builder( KEY_NAME, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_CBC) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) .setUserAuthenticationRequired(true) // Invalidate the keys if the user has registered a new biometric // credential, such as a new fingerprint. Can call this method only // on Android 7.0 (API level 24) or higher. The variable // "invalidatedByBiometricEnrollment" is true by default. .setInvalidatedByBiometricEnrollment(true) .build())
Java
generateSecretKey(new KeyGenParameterSpec.Builder( KEY_NAME, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_CBC) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) .setUserAuthenticationRequired(true) // Invalidate the keys if the user has registered a new biometric // credential, such as a new fingerprint. Can call this method only // on Android 7.0 (API level 24) or higher. The variable // "invalidatedByBiometricEnrollment" is true by default. .setInvalidatedByBiometricEnrollment(true) .build());
ابدأ سير عمل المصادقة بالمقاييس الحيوية الذي يتضمّن رمزًا:
Kotlin
biometricLoginButton.setOnClickListener { // Exceptions are unhandled within this snippet. val cipher = getCipher() val secretKey = getSecretKey() cipher.init(Cipher.ENCRYPT_MODE, secretKey) biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher)) }
Java
biometricLoginButton.setOnClickListener(view -> { // Exceptions are unhandled within this snippet. Cipher cipher = getCipher(); SecretKey secretKey = getSecretKey(); cipher.init(Cipher.ENCRYPT_MODE, secretKey); biometricPrompt.authenticate(promptInfo, new BiometricPrompt.CryptoObject(cipher)); });
ضمن استدعاءات المصادقة بالمقاييس الحيوية، استخدِم المفتاح السري لتشفير المعلومات الحساسة:
Kotlin
override fun onAuthenticationSucceeded( result: BiometricPrompt.AuthenticationResult) { val encryptedInfo: ByteArray = result.cryptoObject.cipher?.doFinal( // plaintext-string text is whatever data the developer would like // to encrypt. It happens to be plain-text in this example, but it // can be anything plaintext-string.toByteArray(Charset.defaultCharset()) ) Log.d("MY_APP_TAG", "Encrypted information: " + Arrays.toString(encryptedInfo)) }
Java
@Override public void onAuthenticationSucceeded( @NonNull BiometricPrompt.AuthenticationResult result) { // NullPointerException is unhandled; use Objects.requireNonNull(). byte[] encryptedInfo = result.getCryptoObject().getCipher().doFinal( // plaintext-string text is whatever data the developer would like // to encrypt. It happens to be plain-text in this example, but it // can be anything plaintext-string.getBytes(Charset.defaultCharset())); Log.d("MY_APP_TAG", "Encrypted information: " + Arrays.toString(encryptedInfo)); }
المصادقة باستخدام المقاييس الحيوية أو بيانات اعتماد شاشة القفل
يمكنك استخدام مفتاح سري يسمح بإجراء المصادقة باستخدام بيانات اعتماد المقاييس الحيوية أو بيانات اعتماد شاشة القفل (رقم التعريف الشخصي أو النقش أو كلمة المرور). عند تهيئة هذا المفتاح، حدد فترة زمنية للصلاحية. وخلال هذه الفترة الزمنية، يمكن لتطبيقك إجراء عمليات تشفير متعددة بدون أن يحتاج المستخدم إلى إعادة المصادقة.
لتشفير المعلومات الحساسة بعد مصادقة المستخدم باستخدام المقاييس الحيوية أو بيانات اعتماد شاشة القفل، أكمِل الخطوات التالية:
يمكنك إنشاء مفتاح يستخدم إعدادات
KeyGenParameterSpec
التالية:Kotlin
generateSecretKey(KeyGenParameterSpec.Builder( KEY_NAME, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_CBC) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) .setUserAuthenticationRequired(true) .setUserAuthenticationParameters(VALIDITY_DURATION_SECONDS, ALLOWED_AUTHENTICATORS) .build())
Java
generateSecretKey(new KeyGenParameterSpec.Builder( KEY_NAME, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_CBC) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) .setUserAuthenticationRequired(true) .setUserAuthenticationParameters(VALIDITY_DURATION_SECONDS, ALLOWED_AUTHENTICATORS) .build());
خلال فترة زمنية تبلغ
VALIDITY_DURATION_SECONDS
بعد مصادقة المستخدم، يمكنك تشفير المعلومات الحساسة:Kotlin
private fun encryptSecretInformation() { // Exceptions are unhandled for getCipher() and getSecretKey(). val cipher = getCipher() val secretKey = getSecretKey() try { cipher.init(Cipher.ENCRYPT_MODE, secretKey) val encryptedInfo: ByteArray = cipher.doFinal( // plaintext-string text is whatever data the developer would // like to encrypt. It happens to be plain-text in this example, // but it can be anything plaintext-string.toByteArray(Charset.defaultCharset())) Log.d("MY_APP_TAG", "Encrypted information: " + Arrays.toString(encryptedInfo)) } catch (e: InvalidKeyException) { Log.e("MY_APP_TAG", "Key is invalid.") } catch (e: UserNotAuthenticatedException) { Log.d("MY_APP_TAG", "The key's validity timed out.") biometricPrompt.authenticate(promptInfo) }
Java
private void encryptSecretInformation() { // Exceptions are unhandled for getCipher() and getSecretKey(). Cipher cipher = getCipher(); SecretKey secretKey = getSecretKey(); try { // NullPointerException is unhandled; use Objects.requireNonNull(). ciper.init(Cipher.ENCRYPT_MODE, secretKey); byte[] encryptedInfo = cipher.doFinal( // plaintext-string text is whatever data the developer would // like to encrypt. It happens to be plain-text in this example, // but it can be anything plaintext-string.getBytes(Charset.defaultCharset())); } catch (InvalidKeyException e) { Log.e("MY_APP_TAG", "Key is invalid."); } catch (UserNotAuthenticatedException e) { Log.d("MY_APP_TAG", "The key's validity timed out."); biometricPrompt.authenticate(promptInfo); } }
المصادقة باستخدام مفاتيح المصادقة لكل استخدام
يمكنك توفير دعم لمفاتيح المصادقة لكل استخدام في مثيل BiometricPrompt
. ويتطلب هذا المفتاح من المستخدم تقديم بيانات اعتماد المقاييس الحيوية أو بيانات اعتماد الجهاز في كل مرة يحتاج فيها تطبيقك إلى الوصول إلى البيانات التي يحميها ذلك المفتاح. قد تكون مفاتيح المصادقة لكل استخدام مفيدة في المعاملات العالية القيمة، مثل إجراء عملية دفع كبيرة أو تعديل السجلات الصحية لشخصٍ ما.
لربط كائن BiometricPrompt
بمفتاح مصادقة لكل استخدام، يمكنك إضافة رمز برمجي يشبه ما يلي:
Kotlin
val authPerOpKeyGenParameterSpec = KeyGenParameterSpec.Builder("myKeystoreAlias", key-purpose) // Accept either a biometric credential or a device credential. // To accept only one type of credential, include only that type as the // second argument. .setUserAuthenticationParameters(0 /* duration */, KeyProperties.AUTH_BIOMETRIC_STRONG or KeyProperties.AUTH_DEVICE_CREDENTIAL) .build()
Java
KeyGenParameterSpec authPerOpKeyGenParameterSpec = new KeyGenParameterSpec.Builder("myKeystoreAlias", key-purpose) // Accept either a biometric credential or a device credential. // To accept only one type of credential, include only that type as the // second argument. .setUserAuthenticationParameters(0 /* duration */, KeyProperties.AUTH_BIOMETRIC_STRONG | KeyProperties.AUTH_DEVICE_CREDENTIAL) .build();
المصادقة بدون إجراء صريح من المستخدم
بشكل تلقائي، يطلب النظام من المستخدمين تنفيذ إجراء محدّد، مثل الضغط على زر، بعد قبول بيانات اعتماد المقاييس الحيوية. يُفضَّل استخدام هذه الإعدادات إذا كان تطبيقك يعرض مربّع الحوار لتأكيد إجراء حساس أو شديد الخطورة، مثل إجراء عملية شراء.
ومع ذلك، إذا كان تطبيقك يعرض مربّع حوار للمصادقة بالمقاييس الحيوية لتنفيذ إجراء أقل خطورة،
يمكنك تقديم تلميح للنظام يفيد بأنّ المستخدم لا يحتاج
إلى تأكيد المصادقة. يمكن أن يسمح هذا التلميح للمستخدم بعرض المحتوى في تطبيقك بسرعة أكبر بعد إعادة المصادقة باستخدام أسلوب سلبي، مثل التعرّف على الوجه أو قزحية العين. لتقديم هذا التلميح، أدخِل false
في طريقة
setConfirmationRequired()
.
ويوضح الشكل 2 نسختين من مربع الحوار نفسه. يتطلب أحد الإصدارين إجراءً واضحًا من المستخدم، بينما لا يتطلب الإصدار الآخر ذلك.
يوضّح مقتطف الرمز التالي كيفية عرض مربّع حوار لا يتطلب إجراءً صريحًا من المستخدم لإكمال عملية المصادقة:
Kotlin
// Lets the user authenticate without performing an action, such as pressing a // button, after their biometric credential is accepted. promptInfo = BiometricPrompt.PromptInfo.Builder() .setTitle("Biometric login for my app") .setSubtitle("Log in using your biometric credential") .setNegativeButtonText("Use account password") .setConfirmationRequired(false) .build()
Java
// Lets the user authenticate without performing an action, such as pressing a // button, after their biometric credential is accepted. promptInfo = new BiometricPrompt.PromptInfo.Builder() .setTitle("Biometric login for my app") .setSubtitle("Log in using your biometric credential") .setNegativeButtonText("Use account password") .setConfirmationRequired(false) .build();
السماح بالرجوع إلى بيانات الاعتماد غير الحيوية
إذا أردت أن يسمح تطبيقك بالمصادقة باستخدام المقاييس الحيوية أو بيانات اعتماد الجهاز، يمكنك توضيح أنّ تطبيقك يتوافق مع بيانات اعتماد الأجهزة من خلال تضمين DEVICE_CREDENTIAL
في مجموعة القيم التي تدخِلها في setAllowedAuthenticators()
.
وإذا كان تطبيقك يستخدم حاليًا createConfirmDeviceCredentialIntent()
أو setDeviceCredentialAllowed()
لتوفير هذه الإمكانية، يمكنك التبديل إلى استخدام setAllowedAuthenticators()
.
مراجع إضافية
لمزيد من المعلومات حول المصادقة بالمقاييس الحيوية على Android، يُرجى الاطّلاع على المراجع التالية.