يشير "مدير بيانات الاعتماد" إلى مجموعة من واجهات برمجة التطبيقات التي تم توفيرها في Android 14 والتي تتيح استخدام طرق تسجيل دخول متعدّدة، مثل اسم المستخدم-كلمة المرور ومفاتيح المرور وحلول تسجيل الدخول الموحّد (مثل ميزة "تسجيل الدخول باستخدام حساب Google"). عند استدعاء واجهة برمجة تطبيقات مدير بيانات الاعتماد، يجمع نظام Android بيانات الاعتماد من جميع مقدّمي بيانات الاعتماد الذين تم تثبيتهم على الجهاز. يصف هذا المستند مجموعة من واجهات برمجة التطبيقات التي توفر نقاط نهاية الدمج لمزودي بيانات الاعتماد هؤلاء.
ضبط إعدادات الجهاز
قبل تنفيذ الوظيفة في موفِّر بيانات الاعتماد، أكمِل خطوات الإعداد الموضّحة في الأقسام التالية.
تعريف التبعيات
في ملف build.gradle
الخاص بالوحدة، حدِّد تبعية باستخدام أحدث إصدار من مكتبة "مدير بيانات الاعتماد":
implementation "androidx.credentials:credentials:1.2.0-{latest}"
تعريف عنصر الخدمة في ملف البيان
في ملف البيان الخاص بتطبيقك AndroidManifest.xml
، عليك تضمين تعريف <service>
لفئة خدمة تعمل على توسيع نطاق فئة CredentialProviderService
من مكتبة androidx.credentials،
كما هو موضّح في المثال أدناه.
<service android:name=".MyCredentialProviderService"
android:enabled="true"
android:exported="true"
android:label="My Credential Provider"
android:icon="<any drawable icon>"
android:permission="android.permission.BIND_CREDENTIAL_PROVIDER_SERVICE">
<intent-filter>
<action android:name="android.service.credentials.CredentialProviderService"/>
</intent-filter>
<meta-data
android:name="android.credentials.provider"
android:resource="@xml/provider"/>
</service>
يُعد الإذن وفلتر الأهداف الموضحان أعلاه جزءًا لا يتجزأ من سير عمل "مدير بيانات الاعتماد" على النحو المتوقع. الإذن مطلوب حتى لا يتمكن سوى نظام Android من الربط بهذه الخدمة. يُستخدم فلتر الأهداف لاكتشاف هذه الخدمة كمزوّد بيانات اعتماد ليتم استخدامها بواسطة "مدير بيانات الاعتماد".
توضيح أنواع بيانات الاعتماد المتوافقة
في دليل res/xml
، أنشِئ ملفًا جديدًا باسم provider.xml
. في هذا الملف، عرِّف أنواع بيانات الاعتماد التي توفّرها الخدمة من خلال الثوابت المحددة لكل نوع من أنواع بيانات الاعتماد في المكتبة. في المثال التالي، تتيح الخدمة استخدام كلمات المرور التقليدية، بالإضافة إلى مفاتيح المرور، التي يتم تعريفها على أنّها TYPE_PASSWORD_CREDENTIAL
وTYPE_PUBLIC_KEY_CREDENTIAL
:
<?xml version="1.0" encoding="utf-8"?>
<credential-provider xmlns:android="http://schemas.android.com/apk/res/android">
<capabilities>
<capability name="android.credentials.TYPE_PASSWORD_CREDENTIAL" />
<capability name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" />
</capabilities>
</credential-provider>
في مستويات واجهات برمجة التطبيقات السابقة، يتكامل موفرو بيانات الاعتماد مع واجهات برمجة التطبيقات مثل ميزة الملء التلقائي لكلمات المرور والبيانات الأخرى. ويمكن لمقدمي البيانات هؤلاء استخدام البنية الأساسية الداخلية نفسها لتخزين أنواع بيانات الاعتماد الحالية، مع توسيع نطاقها ليشمل بيانات الاعتماد الأخرى، بما في ذلك مفاتيح المرور.
أسلوب على مرحلتين للتفاعل مع مقدّم الخدمة
يتفاعل "مدير بيانات الاعتماد" مع مزوِّدي بيانات الاعتماد في مرحلتين:
- المرحلة الأولى هي مرحلة البدء/الاستعلام حيث يرتبط النظام بخدمات
مقدم بيانات الاعتماد ويستدعي طرق
onBeginGetCredentialRequest()
أوonBeginCreateCredentialRequest()
أوonClearCredentialStateRequest()
مع طلباتBegin…
. على مقدّمي الخدمات معالجة هذه الطلبات والاستجابة بردودBegin…
، وملئها بالإدخالات التي تمثّل الخيارات المرئية التي سيتم عرضها في أداة اختيار الحساب. يجب أن يحتوي كل إدخال على مجموعةPendingIntent
. - بعد أن يختار المستخدم إدخالاً، تبدأ مرحلة الاختيار ويتم تنشيط
PendingIntent
المرتبطة بالإدخال، ما يؤدي إلى عرض نشاط مقدّم الخدمة المقابل. وبمجرد أن ينتهي المستخدم من التفاعل مع هذا النشاط، يجب على موفر بيانات الاعتماد تعيين الاستجابة لنتيجة النشاط قبل إنهائه. بعد ذلك، يتم إرسال هذه الاستجابة إلى تطبيق العميل الذي استدعى "مدير بيانات الاعتماد".
معالجة عملية إنشاء مفتاح المرور
معالجة طلبات البحث لإنشاء مفاتيح مرور
عندما يريد أحد تطبيقات العميل إنشاء مفتاح مرور وتخزينه باستخدام
مزوّد بيانات اعتماد، فإنّه يستدعي واجهة برمجة تطبيقات createCredential
. لمعالجة هذا الطلب في خدمة مقدِّم بيانات الاعتماد بحيث يتم تخزين مفتاح المرور فعليًا في مساحة التخزين، أكمِل الخطوات الموضّحة في الأقسام التالية.
- يمكنك إلغاء طريقة
onBeginCreateCredentialRequest()
في الخدمة الممتدة منCredentialProviderService
. - يمكنك التعامل مع
BeginCreateCredentialRequest
من خلال إنشاءBeginCreateCredentialResponse
مكافئة وتمريرها من خلال رد الاتصال. - أثناء إنشاء
BeginCreateCredentialResponse
، أضِفCreateEntries
المطلوب. يجب أن يتوافق كلCreateEntry
مع حساب يمكن فيه حفظ بيانات الاعتماد، كما يجب ضبطPendingIntent
مع البيانات الوصفية الأخرى المطلوبة.
يوضّح المثال التالي كيفية تنفيذ هذه الخطوات.
override fun onBeginCreateCredentialRequest(
request: BeginCreateCredentialRequest,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<BeginCreateCredentialResponse, CreateCredentialException>,
) {
val response: BeginCreateCredentialResponse? = processCreateCredentialRequest(request)
if (response != null) {
callback.onResult(response)
} else {
callback.onError(CreateCredentialUnknownException())
}
}
fun processCreateCredentialRequest(request: BeginCreateCredentialRequest): BeginCreateCredentialResponse? {
when (request) {
is BeginCreatePublicKeyCredentialRequest -> {
// Request is passkey type
return handleCreatePasskeyQuery(request)
}
}
// Request not supported
return null
}
private fun handleCreatePasskeyQuery(
request: BeginCreatePublicKeyCredentialRequest
): BeginCreateCredentialResponse {
// Adding two create entries - one for storing credentials to the 'Personal'
// account, and one for storing them to the 'Family' account. These
// accounts are local to this sample app only.
val createEntries: MutableList<CreateEntry> = mutableListOf()
createEntries.add( CreateEntry(
PERSONAL_ACCOUNT_ID,
createNewPendingIntent(PERSONAL_ACCOUNT_ID, CREATE_PASSKEY_INTENT)
))
createEntries.add( CreateEntry(
FAMILY_ACCOUNT_ID,
createNewPendingIntent(FAMILY_ACCOUNT_ID, CREATE_PASSKEY_INTENT)
))
return BeginCreateCredentialResponse(createEntries)
}
private fun createNewPendingIntent(accountId: String, action: String): PendingIntent {
val intent = Intent(action).setPackage(PACKAGE_NAME)
// Add your local account ID as an extra to the intent, so that when
// user selects this entry, the credential can be saved to this
// account
intent.putExtra(EXTRA_KEY_ACCOUNT_ID, accountId)
return PendingIntent.getActivity(
applicationContext, UNIQUE_REQ_CODE,
intent, (
PendingIntent.FLAG_MUTABLE
or PendingIntent.FLAG_UPDATE_CURRENT
)
)
}
يجب أن تتقيّد عملية إنشاء PendingIntent
بما يلي:
- يجب إعداد النشاط المقابل لعرض أي طلب مطلوب بشأن المقاييس الحيوية أو التأكيد أو التحديد المطلوب.
- أي بيانات مطلوبة يحتاجها مقدّم الخدمة عند استدعاء النشاط المقابل، يجب ضبطها كبيانات إضافية في الغرض المستخدَم لإنشاء
PendingIntent
، مثلaccountId
في عملية الإنشاء. - يجب إنشاء
PendingIntent
باستخدام العلامةPendingIntent.FLAG_MUTABLE
حتى يتمكّن النظام من إلحاق الطلب الأخير بالهدف الإضافي. - يجب ألا يتم إنشاء
PendingIntent
باستخدام العلامةPendingIntent.FLAG_ONE_SHOT
لأنّ المستخدم قد يختار إدخالاً، لذا عليك الرجوع وإعادة اختياره، ما سيؤدي إلى تنشيطPendingIntent
مرتين. - يجب إنشاء
PendingIntent
باستخدام رمز طلب فريد لكي يكون لكل إدخالPendingIntent
مطابق خاص به.
اختيار الاسم المعرِّف لطلبات إنشاء مفاتيح المرور
- عندما يختار المستخدم
CreateEntry
التي تمت تعبئتها سابقًا، يتم استدعاءPendingIntent
المقابل ويتم إنشاءActivity
. - بعد استدعاء طريقة
onCreate
في "نشاطك"، يمكنك الوصول إلى الغرض ذي الصلة وتمريره إلى صفPendingIntentHander
للحصول علىProviderCreateCredentialRequest
. - استخرِج
requestJson
وcallingAppInfo
وclientDataHash
من الطلب. - استخرِج
accountId
المحلي من النية الإضافية. هذا نموذج تنفيذ خاص بالتطبيق وليس مطلوبًا. يمكن استخدام رقم تعريف الحساب هذا لتخزين بيانات الاعتماد هذه مقابل رقم تعريف الحساب المحدد هذا. - تحقَّق من صحة
requestJson
. يستخدم المثال أدناه فئات البيانات المحلية مثلPublicKeyCredentialCreationOptions
لتحويل JSON الإدخال إلى فئة منظَّمة وفقًا لمواصفات WebAuthn. بصفتك موفِّر بيانات اعتماد، يمكنك استبداله بمحللك اللغوي. - تحقَّق من رابط مادة العرض لتطبيق الاتصال إذا كانت المكالمة واردة من تطبيق Android أصلي.
- عرض إشعار للمصادقة يستخدم المثال أدناه واجهة برمجة التطبيقات Biometric API لنظام التشغيل Android.
- عند نجاح المصادقة، أنشِئ
credentialId
ومفتاحَي مفاتيح. - احفظ المفتاح الخاص في قاعدة البيانات المحلية مقابل
callingAppInfo.packageName
. - أنشِئ استجابة JSON لواجهة برمجة تطبيقات مصادقة الويب التي تتألّف من المفتاح العام والرمز
credentialId
. يستخدم المثال أدناه فئات الخدمات المحلية مثلAuthenticatorAttestationResponse
وFidoPublicKeyCredential
التي تساعد في إنشاء JSON استنادًا إلى المواصفات المذكورة سابقًا.وبصفتك موفِّر بيانات اعتماد، يمكنك استبدال هذه الفئات بأدوات الإنشاء الخاصة بك. - أنشِئ
CreatePublicKeyCredentialResponse
باستخدام JSON أعلاه. - اضبط
CreatePublicKeyCredentialResponse
كعنصر إضافي علىIntent
حتىPendingIntentHander.setCreateCredentialResponse()
، واضبط هذا القصد على نتيجة النشاط. - أنهِ النشاط.
يوضح مثال الرمز أدناه هذه الخطوات. يجب معالجة هذا الرمز في
فئة النشاط بمجرد استدعاء onCreate()
.
val request =
PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
val accountId = intent.getStringExtra(CredentialsRepo.EXTRA_KEY_ACCOUNT_ID)
if (request != null && request.callingRequest is CreatePublicKeyCredentialRequest) {
val publicKeyRequest: CreatePublicKeyCredentialRequest =
request.callingRequest as CreatePublicKeyCredentialRequest
createPasskey(
publicKeyRequest.requestJson,
request.callingAppInfo,
publicKeyRequest.clientDataHash,
accountId
)
}
fun createPasskey(
requestJson: String,
callingAppInfo: CallingAppInfo?,
clientDataHash: ByteArray?,
accountId: String?
) {
val request = PublicKeyCredentialCreationOptions(requestJson)
val biometricPrompt = BiometricPrompt(
this,
<executor>,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(
errorCode: Int, errString: CharSequence
) {
super.onAuthenticationError(errorCode, errString)
finish()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
finish()
}
override fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult
) {
super.onAuthenticationSucceeded(result)
// Generate a credentialId
val credentialId = ByteArray(32)
SecureRandom().nextBytes(credentialId)
// Generate a credential key pair
val spec = ECGenParameterSpec("secp256r1")
val keyPairGen = KeyPairGenerator.getInstance("EC");
keyPairGen.initialize(spec)
val keyPair = keyPairGen.genKeyPair()
// Save passkey in your database as per your own implementation
// Create AuthenticatorAttestationResponse object to pass to
// FidoPublicKeyCredential
val response = AuthenticatorAttestationResponse(
requestOptions = request,
credentialId = credentialId,
credentialPublicKey = getPublicKeyFromKeyPair(keyPair),
origin = appInfoToOrigin(callingAppInfo),
up = true,
uv = true,
be = true,
bs = true,
packageName = callingAppInfo.packageName
)
val credential = FidoPublicKeyCredential(
rawId = credentialId, response = response
)
val result = Intent()
val createPublicKeyCredResponse =
CreatePublicKeyCredentialResponse(credential.json())
// Set the CreateCredentialResponse as the result of the Activity
PendingIntentHandler.setCreateCredentialResponse(
result, createPublicKeyCredResponse
)
setResult(Activity.RESULT_OK, result)
finish()
}
}
)
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Use your screen lock")
.setSubtitle("Create passkey for ${request.rp.name}")
.setAllowedAuthenticators(
BiometricManager.Authenticators.BIOMETRIC_STRONG
/* or BiometricManager.Authenticators.DEVICE_CREDENTIAL */
)
.build()
biometricPrompt.authenticate(promptInfo)
}
fun appInfoToOrigin(info: CallingAppInfo): String {
val cert = info.signingInfo.apkContentsSigners[0].toByteArray()
val md = MessageDigest.getInstance("SHA-256");
val certHash = md.digest(cert)
// This is the format for origin
return "android:apk-key-hash:${b64Encode(certHash)}"
}
التعامل مع الطلبات الخاصة بطلبات إنشاء كلمات المرور
للتعامل مع طلبات البحث في طلبات إنشاء كلمة المرور، قم بما يلي:
- داخل طريقة
processCreateCredentialRequest()
المذكورة في القسم السابق، أضِف حالة أخرى داخل كتلة المفاتيح للتعامل مع طلبات كلمات المرور. - أثناء إنشاء السمة
BeginCreateCredentialResponse
، أضِف السمةCreateEntries
المطلوبة. - يجب أن يتوافق كل
CreateEntry
مع حساب يمكن فيه حفظ بيانات الاعتماد، ويجب ضبطPendingIntent
عليه بالإضافة إلى بيانات وصفية أخرى.
يوضّح المثال التالي كيفية تنفيذ هذه الخطوات:
fun processCreateCredentialRequest(
request: BeginCreateCredentialRequest
): BeginCreateCredentialResponse? {
when (request) {
is BeginCreatePublicKeyCredentialRequest -> {
// Request is passkey type
return handleCreatePasskeyQuery(request)
}
is BeginCreatePasswordCredentialRequest -> {
// Request is password type
return handleCreatePasswordQuery(request)
}
}
return null
}
private fun handleCreatePasswordQuery(
request: BeginCreatePasswordCredentialRequest
): BeginCreateCredentialResponse {
val createEntries: MutableList<CreateEntry> = mutableListOf()
// Adding two create entries - one for storing credentials to the 'Personal'
// account, and one for storing them to the 'Family' account. These
// accounts are local to this sample app only.
createEntries.add(
CreateEntry(
PERSONAL_ACCOUNT_ID,
createNewPendingIntent(PERSONAL_ACCOUNT_ID, CREATE_PASSWORD_INTENT)
)
)
createEntries.add(
CreateEntry(
FAMILY_ACCOUNT_ID,
createNewPendingIntent(FAMILY_ACCOUNT_ID, CREATE_PASSWORD_INTENT)
)
)
return BeginCreateCredentialResponse(createEntries)
}
اختيار الأسماء المعرِّفة لطلبات إنشاء كلمة المرور
عندما يختار المستخدم CreateEntry
تمت تعبئته، يتم تنفيذ النشاط المرتبط في السمة PendingIntent
وعرض النشاط المرتبط به. يمكنك الوصول إلى
النية المرتبطة التي تم تمريرها في onCreate
وإدراجها في الفئة
PendingIntentHander
للحصول على طريقة ProviderCreateCredentialRequest
.
يوضّح المثال التالي كيفية تنفيذ هذه العملية. يجب التعامل مع هذا الرمز في طريقة onCreate()
في "نشاطك".
val createRequest = PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
val accountId = intent.getStringExtra(CredentialsRepo.EXTRA_KEY_ACCOUNT_ID)
val request: CreatePasswordRequest = createRequest.callingRequest as CreatePasswordRequest
// Fetch the ID and password from the request and save it in your database
<your_database>.addNewPassword(
PasswordInfo(
request.id,
request.password,
createRequest.callingAppInfo.packageName
)
)
//Set the final response back
val result = Intent()
val response = CreatePasswordResponse()
PendingIntentHandler.setCreateCredentialResponse(result, response)
setResult(Activity.RESULT_OK, result)
this@<activity>.finish()
التعامل مع عملية تسجيل دخول المستخدم
يتم التعامل مع تسجيل دخول المستخدم من خلال الخطوات التالية:
- عندما يحاول تطبيق عميل تسجيل دخول مستخدم، يعمل على إعداد مثيل
GetCredentialRequest
. - ينشر إطار عمل Android هذا الطلب لجميع موفِّري بيانات الاعتماد السارية من خلال الربط بهذه الخدمات.
- بعد ذلك، تتلقّى خدمة مقدّم الخدمة عنصر
BeginGetCredentialRequest
يحتوي على قائمة تتضمّنBeginGetCredentialOption
، يحتوي كل منها على مَعلَمات يمكن استخدامها لاسترداد بيانات الاعتماد المطابِقة.
لمعالجة هذا الطلب في خدمة موفِّر بيانات الاعتماد، أكمِل الخطوات التالية:
يمكنك إلغاء طريقة
onBeginGetCredentialRequest()
لمعالجة الطلب. يُرجى العِلم أنّه إذا كانت بيانات الاعتماد مقفلة، يمكنك ضبطAuthenticationAction
على الفور في الاستجابة واستدعاء معاودة الاتصال.private val unlockEntryTitle = "Authenticate to continue" override fun onBeginGetCredentialRequest( request: BeginGetCredentialRequest, cancellationSignal: CancellationSignal, callback: OutcomeReceiver<BeginGetCredentialResponse, GetCredentialException>, ) { if (isAppLocked()) { callback.onResult(BeginGetCredentialResponse( authenticationActions = mutableListOf(AuthenticationAction( unlockEntryTitle, createUnlockPendingIntent()) ) ) ) return } try { response = processGetCredentialRequest(request) callback.onResult(response) } catch (e: GetCredentialException) { callback.onError(GetCredentialUnknownException()) } }
بالنسبة إلى مقدّمي الخدمات الذين يطلبون فتح قفل بيانات الاعتماد قبل عرض أي
credentialEntries
، عليهم إعداد هدف في انتظار المراجعة ينقل المستخدم إلى مسار فتح قفل التطبيق:private fun createUnlockPendingIntent(): PendingIntent { val intent = Intent(UNLOCK_INTENT).setPackage(PACKAGE_NAME) return PendingIntent.getActivity( applicationContext, UNIQUE_REQUEST_CODE, intent, ( PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) ) }
يمكنك استرداد بيانات الاعتماد من قاعدة البيانات المحلية وإعدادها باستخدام
CredentialEntries
لعرضها على أداة الاختيار. بالنسبة إلى مفاتيح المرور، يمكنك ضبطcredentialId
كعنصر إضافي في الغرض من أجل معرفة بيانات الاعتماد التي يتم ربطها عندما يختار المستخدم هذا الإدخال.companion object { // These intent actions are specified for corresponding activities // that are to be invoked through the PendingIntent(s) private const val GET_PASSKEY_INTENT_ACTION = "PACKAGE_NAME.GET_PASSKEY" private const val GET_PASSWORD_INTENT_ACTION = "PACKAGE_NAME.GET_PASSWORD" } fun processGetCredentialsRequest( request: BeginGetCredentialRequest ): BeginGetCredentialResponse { val callingPackage = request.callingAppInfo?.packageName val credentialEntries: MutableList<CredentialEntry> = mutableListOf() for (option in request.beginGetCredentialOptions) { when (option) { is BeginGetPasswordOption -> { credentialEntries.addAll( populatePasswordData( callingPackage, option ) ) } is BeginGetPublicKeyCredentialOption -> { credentialEntries.addAll( populatePasskeyData( callingPackage, option ) ) ) } else -> { Log.i(TAG, "Request not supported") } } } return BeginGetCredentialResponse(credentialEntries) }
الاستعلام عن بيانات الاعتماد من قاعدة البيانات وإنشاء مفتاح المرور وإدخالات كلمة المرور للملء.
private fun populatePasskeyData( callingAppInfo: CallingAppInfo, option: BeginGetPublicKeyCredentialOption ): List<CredentialEntry> { val passkeyEntries: MutableList<CredentialEntry> = mutableListOf() val request = PublicKeyCredentialRequestOptions(option.requestJson) // Get your credentials from database where you saved during creation flow val creds = <getCredentialsFromInternalDb(request.rpId)> val passkeys = creds.passkeys for (passkey in passkeys) { val data = Bundle() data.putString("credId", passkey.credId) passkeyEntries.add( PublicKeyCredentialEntry( context = applicationContext, username = passkey.username, pendingIntent = createNewPendingIntent( GET_PASSKEY_INTENT_ACTION, data ), beginPublicKeyCredentialOption = option, displayName = passkey.displayName, icon = passkey.icon ) ) } return passkeyEntries } // Fetch password credentials and create password entries to populate to // the user private fun populatePasswordData( callingPackage: String, option: BeginGetPasswordOption ): List<CredentialEntry> { val passwordEntries: MutableList<CredentialEntry> = mutableListOf() // Get your password credentials from database where you saved during // creation flow val creds = <getCredentialsFromInternalDb(callingPackage)> val passwords = creds.passwords for (password in passwords) { passwordEntries.add( PasswordCredentialEntry( context = applicationContext, username = password.username, pendingIntent = createNewPendingIntent( GET_PASSWORD_INTENT ), beginGetPasswordOption = option displayName = password.username, icon = password.icon ) ) } return passwordEntries } private fun createNewPendingIntent( action: String, extra: Bundle? = null ): PendingIntent { val intent = Intent(action).setPackage(PACKAGE_NAME) if (extra != null) { intent.putExtra("CREDENTIAL_DATA", extra) } return PendingIntent.getActivity( applicationContext, UNIQUE_REQUEST_CODE, intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) ) }
بعد طلب بيانات الاعتماد وملؤها، عليك الآن معالجة مرحلة اختيار بيانات الاعتماد التي يختارها المستخدم، سواء كانت مفتاح مرور أو كلمة مرور.
معالجة اختيار المستخدمين لمفاتيح المرور
- في طريقة
onCreate
للنشاط المقابل، استرِدّ الغرض المرتبط وانقله إلىPendingIntentHandler.retrieveProviderGetCredentialRequest()
. - استخرِج
GetPublicKeyCredentialOption
من الطلب الذي تم استرداده أعلاه. بعد ذلك، استخرِجrequestJson
وclientDataHash
من هذا الخيار. - استخرِج
credentialId
من عنصر intent الإضافي، الذي تمت تعبئته من خلال موفِّر بيانات الاعتماد عند إعدادPendingIntent
المقابل. - استخرِج مفتاح المرور من قاعدة البيانات المحلية باستخدام معلمات الطلب التي تم الوصول إليها أعلاه.
تأكَّد من أنّ مفتاح المرور صالح مع البيانات الوصفية المستخلصة وإثبات ملكية المستخدم.
val getRequest = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent) val publicKeyRequest = getRequest.credentialOption as GetPublicKeyCredentialOption val requestInfo = intent.getBundleExtra("CREDENTIAL_DATA") val credIdEnc = requestInfo.getString("credId") // Get the saved passkey from your database based on the credential ID // from the publickeyRequest val passkey = <your database>.getPasskey(credIdEnc) // Decode the credential ID, private key and user ID val credId = b64Decode(credIdEnc) val privateKey = b64Decode(passkey.credPrivateKey) val uid = b64Decode(passkey.uid) val origin = appInfoToOrigin(getRequest.callingAppInfo) val packageName = getRequest.callingAppInfo.packageName validatePasskey( publicKeyRequest.requestJson, origin, packageName, uid, passkey.username, credId, privateKey )
للتحقق من صحة المستخدم، اعرض طلبًا بالمقاييس الحيوية (أو طريقة تأكيد أخرى). يستخدم مقتطف الرمز أدناه واجهة برمجة التطبيقات Android Biometric API.
بعد نجاح المصادقة، أنشِئ استجابة JSON استنادًا إلى مواصفات W3 Web Authentication Assertion. وفي مقتطف الرمز أدناه، تُستخدَم فئات البيانات المساعدة، مثل
AuthenticatorAssertionResponse
، لأخذ المَعلمات المنظَّمة وتحويلها إلى تنسيق JSON المطلوب. وتحتوي الاستجابة على توقيع رقمي من المفتاح الخاص لبيانات اعتماد WebAuthn. يمكن لخادم الجهة المعتمدة التحقق من هذا التوقيع لمصادقة مستخدم قبل تسجيل الدخول.أنشِئ
PublicKeyCredential
باستخدام JSON الذي تم إنشاؤه أعلاه، واضبطه علىGetCredentialResponse
نهائية. قم بتعيين هذا الرد النهائي على نتيجة هذا النشاط.
يوضّح المثال التالي كيفية تنفيذ هذه الخطوات:
val request = PublicKeyCredentialRequestOptions(requestJson)
val privateKey: ECPrivateKey = convertPrivateKey(privateKeyBytes)
val biometricPrompt = BiometricPrompt(
this,
<executor>,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(
errorCode: Int, errString: CharSequence
) {
super.onAuthenticationError(errorCode, errString)
finish()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
finish()
}
override fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult
) {
super.onAuthenticationSucceeded(result)
val response = AuthenticatorAssertionResponse(
requestOptions = request,
credentialId = credId,
origin = origin,
up = true,
uv = true,
be = true,
bs = true,
userHandle = uid,
packageName = packageName
)
val sig = Signature.getInstance("SHA256withECDSA");
sig.initSign(privateKey)
sig.update(response.dataToSign())
response.signature = sig.sign()
val credential = FidoPublicKeyCredential(
rawId = credId, response = response
)
val result = Intent()
val passkeyCredential = PublicKeyCredential(credential.json)
PendingIntentHandler.setGetCredentialResponse(
result, GetCredentialResponse(passkeyCredential)
)
setResult(RESULT_OK, result)
finish()
}
}
)
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Use your screen lock")
.setSubtitle("Use passkey for ${request.rpId}")
.setAllowedAuthenticators(
BiometricManager.Authenticators.BIOMETRIC_STRONG
/* or BiometricManager.Authenticators.DEVICE_CREDENTIAL */
)
.build()
biometricPrompt.authenticate(promptInfo)
التعامل مع اختيار المستخدم لمصادقة كلمة المرور
- في النشاط المقابل، عليك الوصول إلى الغرض الذي تم تمريره إلى
onCreate
واستخراجProviderGetCredentialRequest
باستخدامPendingIntentHandler
. استخدِم
GetPasswordOption
في الطلب لاسترداد بيانات اعتماد كلمة المرور لاسم الحزمة الواردة.val getRequest = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent) val passwordOption = getRequest.credentialOption as GetPasswordCredentialOption val username = passwordOption.username // Fetch the credentials for the calling app package name val creds = <your_database>.getCredentials(callingAppInfo.packageName) val passwords = creds.passwords val it = passwords.iterator() var password = "" while (it.hasNext() == true) { val passwordItemCurrent = it.next() if (passwordItemCurrent.username == username) { password = passwordItemCurrent.password break } }
بعد استرداد بيانات اعتماد كلمة المرور المحدّدة، اضبط الاستجابة.
// Set the response back val result = Intent() val passwordCredential = PasswordCredential(username, password) PendingIntentHandler.setGetCredentialResponse( result, GetCredentialResponse(passwordCredential) ) setResult(Activity.RESULT_OK, result) finish()
التعامل مع اختيار إدخال إجراء المصادقة
كما ذكرنا سابقًا، يمكن لموفّر بيانات الاعتماد ضبط
AuthenticationAction
إذا كانت بيانات الاعتماد مقفلة. إذا اختار المستخدم هذا
الإدخال، يتم استدعاء النشاط المقابل لمجموعة إجراءات الغرض في
PendingIntent
. ويمكن بعد ذلك عرض مسار مصادقة باستخدام المقاييس الحيوية أو آلية مشابهة لفتح بيانات الاعتماد. بعد نجاح العملية،
على موفِّر بيانات الاعتماد إنشاء BeginGetCredentialResponse
، على غرار الطريقة الموضَّحة أعلاه، وذلك لأنّه تم فتح قفل بيانات الاعتماد. يجب بعد ذلك ضبط هذه الاستجابة من خلال طريقة
PendingIntentHandler.setBeginGetCredentialResponse()
قبل ضبط
القصد المعدّ على النتيجة وانتهاء النشاط.
محو طلبات بيانات الاعتماد
قد يطلب أحد تطبيقات العميل محو أي حالة تم الاحتفاظ بها لاختيار بيانات الاعتماد،
على سبيل المثال، قد يتذكر موفِّر بيانات الاعتماد بيانات الاعتماد التي تم اختيارها سابقًا
ولا يعرضها إلا في المرة القادمة. يطلب تطبيق العميل واجهة برمجة التطبيقات هذه
ويتوقع محو التحديد الثابت. يمكن لخدمة موفِّر بيانات الاعتماد
معالجة هذا الطلب من خلال تجاوز طريقة
onClearCredentialStateRequest()
:
override fun onClearCredentialStateRequest(
request: android.service.credentials.ClearCredentialStateRequest,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<Void?, ClearCredentialException>,
) {
// Delete any maintained state as appropriate.
}
الحصول على قائمة مسموح بها للتطبيقات المميّزة
تُجري التطبيقات المميّزة، مثل متصفِّحات الويب، طلبات "مدير بيانات الاعتماد" نيابةً عن
جهات أخرى معتمَدة من خلال ضبط مَعلمة origin
في طريقتَي "مدير
بيانات الاعتماد" GetCredentialRequest()
و
CreatePublicKeyCredentialRequest()
. لمعالجة هذه الطلبات، يسترد موفِّر بيانات الاعتماد origin
باستخدام واجهة برمجة التطبيقات getOrigin()
.
لاسترداد origin
، يحتاج تطبيق موفر بيانات الاعتماد إلى تمرير قائمة
بالمتصلين المتميزين والموثوقين إلى
androidx.credentials.provider.CallingAppInfo's getOrigin()
API. يجب أن تكون هذه القائمة المسموح بها
كائن JSON صالحًا. يتم عرض origin
إذا كانت packageName
والملفات المرجعية للشهادة التي تم الحصول عليها من signingInfo
تتطابق مع ملفات التطبيق التي تم العثور عليها في privilegedAllowlist
التي تم تمريرها إلى واجهة برمجة تطبيقات getOrigin()
. بعد الحصول على
القيمة origin
، يجب أن يعتبر تطبيق موفّر الخدمة هذا الطلب كطلب مميّز وأن يضبط origin
على بيانات العميل
في AuthenticatorResponse
، بدلاً من حساب
origin
باستخدام توقيع تطبيق الاتصال.
في حال استرداد origin
، يمكنك استخدام عنصر clientDataHash
المتوفّر مباشرةً في CreatePublicKeyCredentialRequest()
أو GetPublicKeyCredentialOption()
بدلاً من التجميع والتجزئةclientDataJSON
أثناء طلب التوقيع. لتجنّب مشاكل تحليل JSON، يمكنك ضبط
قيمة عنصر نائب لـ clientDataJSON
في استجابة المصادقة والتأكيد.
يستخدم "مدير كلمات المرور في Google" قائمة مسموح بها متاحة بشكل علني للمكالمات الواردة إلى getOrigin()
. وبصفتك موفِّر بيانات اعتماد، يمكنك استخدام هذه القائمة أو تقديم قائمة خاصة بك بتنسيق JSON الموضح في واجهة برمجة التطبيقات. ويعتمد تحديد القائمة التي يتم استخدامها على
الموفّر. للحصول على إمكانية الوصول بامتياز مع موفري بيانات الاعتماد
التابعين لجهات خارجية، يُرجى الرجوع إلى الوثائق التي تقدمها الجهة الخارجية.
تفعيل مقدّمي الخدمات على أحد الأجهزة
على المستخدمين تفعيل مقدّم الخدمة من خلال إعدادات الجهاز > كلمات المرور والحسابات > مقدِّم الخدمة > تفعيل أو إيقاف.
في نظام التشغيل Android 14 أو الإصدارات الأحدث، اطلب واجهة برمجة التطبيقات createSettingsPendingIntent()
لعرض هدف في انتظار المراجعة عند استدعاؤه، ما يؤدي إلى عرض شاشة تسمح للمستخدم بتفعيل موفِّر بيانات الاعتماد.
fun createSettingsPendingIntent(): PendingIntent