توضّح هذه الصفحة كيفية إنشاء مفتاح استعادة وتسجيل الدخول باستخدامه وحذفه.
مهارات Android
العرض على GitHubاستعادة بيانات الاعتماد
android skills add restore-credentialsالتوافق مع الإصدارات
تعمل ميزة "استعادة بيانات الاعتماد" في Credential Manager على الأجهزة التي تعمل بالإصدار 9 من Android (المستوى 28 من واجهة برمجة التطبيقات) والإصدارات الأحدث، والإصدار الأساسي 242200000 (الإصدار 24.22) من "خدمات Google Play" (GMS) والإصدارات الأحدث، والإصدار 1.5.0 والإصدارات الأحدث من مكتبة androidx.credentials. على الأجهزة التي لا تتضمّن "خدمات Google Play" أو التي تتضمّن إصدارًا قديمًا من "خدمات Google للأجهزة الجوّالة"،
يمكنك رصد CreateCredentialProviderConfigurationException وGetCredentialProviderConfigurationException ليعود تطبيقك بسلاسة إلى تسجيل الدخول العادي.
المتطلبات الأساسية
اضبط خادم الطرف المعتمِد في WebAuthn على غرار خادم مفاتيح المرور. إذا سبق لك إعداد خادم لمعالجة المصادقة باستخدام مفاتيح المرور، يمكنك إعادة استخدام نقاط نهاية التسجيل والتأكيد نفسها في WebAuthn لمفاتيح الاستعادة.
الطلبات التابعة
أضِف التبعيات التالية إلى ملف build.gradle الخاص بوحدة تطبيقك:
Kotlin
dependencies { implementation("androidx.credentials:credentials:1.7.0-alpha02") implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha02") }
Groovy
dependencies { implementation "androidx.credentials:credentials:1.7.0-alpha02" implementation "androidx.credentials:credentials-play-services-auth:1.7.0-alpha02" }
تتوفّر ميزة "استعادة بيانات الاعتماد" في الإصدار 1.5.0 والإصدارات الأحدث من مكتبة androidx.credentials. ومع ذلك، ننصح باستخدام أحدث الإصدارات الثابتة من التبعيات حيثما أمكن.
تأثير android:allowBackup في ميزة "استعادة بيانات الاعتماد"
تتحكّم سمة البيان android:allowBackup في الاحتفاظ بنسخة احتياطية من بيانات التطبيق،
والتي تتفاعل مع واجهة Restore Credentials بثلاث طرق مختلفة:
- استعادة نقل المفتاح (مستقل عن
allowBackup): تنقل "خدمات Google Play" مفتاحَي التشفيرRestoreCredentialباستخدام النسخة الاحتياطية المشفّرة بشكل تام بين الأطراف في السحابة الإلكترونية أو النقل المحلي من جهاز إلى آخر (D2D) بغض النظر عمّا إذا كانandroid:allowBackupهوtrueأوfalse. بالنسبة إلى عمليات النقل من جهاز إلى آخر، يتم دائمًا نقل مفتاح الاستعادة، ولا يتم نقله فقط أثناء الاحتفاظ بنسخة احتياطية على السحابة الإلكترونية عندما يختار المستخدم إيقاف الاحتفاظ بنسخة احتياطية على السحابة الإلكترونية في إعدادات Android. - استعادة البيانات في الخلفية في
BackupAgent(يتطلبandroid:allowBackup="true"): تسجيل الدخول في الخلفية بدون إرسال إشعارات قبل أن يفتح المستخدم التطبيق على الجهاز الجديد (BackupAgent.onRestoreFinished()) لا يتم تشغيله إلا عند ضبطandroid:allowBackup="true"وandroid:backupAgentفي ملف البيان. إذا كانت القيمةandroid:allowBackup="false"، تتم استعادة الجلسة عندما يشغّل المستخدمActivityالرئيسي لأول مرة. يمكنك أيضًا تنفيذ كلا الأسلوبين لجعل عملية الاستعادة أكثر فعالية. - اختبار ميزة "الاحتفاظ بنسخة احتياطية في السحابة الإلكترونية" في "استوديو Android" (يتطلّب
android:allowBackup="true"): يتطلّب اختبار نوع الاحتفاظ بنسخة احتياطية في السحابة الإلكترونية في أداة "الاحتفاظ بنسخة احتياطية والاستعادة" في "استوديو Android"android:allowBackup="true"، بينما يعمل اختبار ميزة من جهاز إلى جهاز بغض النظر عنallowBackup.
نظرة عامة
تتّبع ميزة "استعادة بيانات الاعتماد" دورة حياة من ثلاث مراحل على أجهزة المستخدم السابقة والجديدة:
- إنشاء مفتاح استعادة على الجهاز السابق: بعد تسجيل المستخدم الدخول (أو عند بدء تشغيل التطبيق إذا كان المستخدم قد سجّل الدخول من قبل ولم يتم تسجيل أي مفتاح استعادة في مساحة التخزين المحلية)، يمكنك جلب خيارات الإنشاء من خادم الجهة المعتمدة واستدعاء
credentialManager.createCredential(). - تسجيل الدخول باستخدام مفتاح الاستعادة على الجهاز الجديد: يمكنك استرداد
RestoreCredentialالمنقول باستخدام استراتيجية من مستويين: أولاً، تلقائيًا فيBackupAgent.onRestoreFinished()(عندandroid:allowBackup="true") حتى تُستأنف المزامنة في الخلفية والإشعارات الفورية على الفور، وثانيًا كإجراء احتياطي في مشغّل التطبيقاتActivity.onCreate()عند تشغيل التطبيق لأول مرة. - حذف مفتاح الاستعادة عند تسجيل الخروج: عندما يسجّل المستخدم الخروج أو
يُبطل الخادم جلسته، يمكنك استدعاء
credentialManager.clearCredentialState()باستخدامClearCredentialStateRequest(ClearCredentialStateRequest.TYPE_CLEAR_RESTORE_CREDENTIAL)ومحو علامة المزامنة المحلية.
إنشاء مفتاح استعادة
يجب أن يغطي تطبيقك جميع حالات تسجيل دخول المستخدم لضمان إنشاء مفتاح استعادة للمستخدمين النشطين. أنشئ مفتاح الاستعادة في الحالات التالية:
- إذا سجّل المستخدم الدخول ولم يتم إنشاء مفتاح استعادة (مثل طريقة
onCreate()لـActivityالرئيسي). - عندما يسجّل المستخدم الدخول أو يكمل عملية تسجيل حساب جديد
لتجنُّب استدعاء createCredential() في كل مرة يتم فيها تشغيل التطبيق، يمكنك تخزين علامة مزامنة محلية مثل has_synced_restore_credential داخل مساحة التخزين المحلية. لا تخزِّن
هذه العلامة في ملف SharedPreferences أو DataStore التلقائي الذي يتم الاحتفاظ بنسخة احتياطية منه ما لم يتم
استبعاد هذا الملف في data_extraction_rules.xml، وإلا ستنسخ ميزة "الاحتفاظ التلقائي بنسخة احتياطية"
has_synced_restore_credential = true على الجهاز الجديد وتمنع الجهاز الجديد
من إنشاء مفتاح الاستعادة الخاص به أو تدويره.
إنشاء مثيل لـ Credential Manager
استخدِم سياق نشاط تطبيقك لإنشاء مثيل لكائن CredentialManager.
// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)
الحصول على خيارات إنشاء بيانات الاعتماد من خادم التطبيق
استخدِم مكتبة متوافقة مع معيار FIDO في خادم تطبيقك لإرسال ملف JSON الخاص بـ PublicKeyCredentialCreationOptions إلى تطبيق العميل، وهو مطلوب لإنشاء بيانات اعتماد الاستعادة. لمزيد من المعلومات حول التنفيذ من جهة الخادم، يُرجى الاطّلاع على الإرشادات من جهة الخادم.
يتّبع ملف JSON لخيارات الإنشاء البنية نفسها المتّبعة في تسجيل مفتاح المرور:
{
"challenge": "<base64url-encoded challenge>",
"rp": {
"name": "<relying party name>",
"id": "<relying party host name>"
},
"user": {
"id": "<base64url-encoded user ID>",
"name": "<user name>",
"displayName": "<user display name>"
},
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
}
],
"attestation": "none",
"excludeCredentials": [
{
"id": "<base64url-encoded credential ID to exclude>",
"type": "public-key"
}
],
"authenticatorSelection": {
"requireResidentKey": true,
"residentKey": "required",
"userVerification": "required"
}
}
تأكَّد من أنّ ملف JSON لخيارات الإنشاء يستوفي المتطلبات التالية:
-
challengeوuser.id: يتم ترميز القيمتَين كسلاسل Base64URL بدون حشو (يتم استبدال+بـ-و/بـ_وإزالة=في نهاية السلسلة). تؤدي أحرف Base64 العادية أو معرّفات الأعداد الصحيحة الأولية إلى حدوثCreateRestoreCredentialDomExceptionأوIllegalArgumentException. -
rp.id: اضبط هذا الإعداد على نطاق الجهة المعتمدة (على سبيل المثال،example.com). pubKeyCredParams: يجب تضمين{"type": "public-key", "alg": -7}(ES256) و{"type": "public-key", "alg": -257}(RS256).
إنشاء مفتاح الاستعادة
بعد تلقّي ملف JSON لخيارات إنشاء المفتاح العام من الخادم، يمكنك تضمين ملف JSON في عنصر CreateRestoreCredentialRequest واستدعاء createCredential() على عنصر CredentialManager.
// createRestoreRequest contains the details sent by the server
val response = credentialManager.createCredential(context, createRestoreRequest)
نقاط رئيسية حول الرمز
يحتوي عنصر
CreateRestoreCredentialRequestعلى الحقول التالية:-
requestJson: خيارات إنشاء بيانات الاعتماد التي يرسلها خادم التطبيق بتنسيق Web Authentication API لـPublicKeyCredentialCreationOptionsJSON.
isCloudBackupEnabled: حقلBooleanلتحديد ما إذا كان يجب الاحتفاظ بنسخة احتياطية من مفتاح الاستعادة في السحابة الإلكترونية. تكون قيمة هذا الإعداد تلقائيًاtrue. يحتوي هذا الحقل على القيم التالية:-
true: (يُنصح به) تتيح هذه القيمة الاحتفاظ بنسخة احتياطية من مفاتيح الاستعادة في السحابة الإلكترونية إذا كان المستخدم قد فعّل ميزتَي "الاحتفاظ بنسخة احتياطية مع Google" و"التشفير التام بين الأطراف"، مثل قفل الشاشة. -
false: تحفظ هذه القيمة المفتاح على الجهاز وليس في السحابة الإلكترونية. لا يتوفّر المفتاح على الجهاز الجديد إذا اختار المستخدم الاستعادة من السحابة الإلكترونية، ولكنّه يظل ينتقل أثناء الإعداد المحلي من جهاز إلى آخر (D2D) باستخدام كابل أو Wi-Fi.
-
-
التعامل مع استجابة إنشاء بيانات الاعتماد
تعرض واجهة برمجة التطبيقات Credential Manager API استجابة من النوع
CreateRestoreCredentialResponse. تحتوي هذه الاستجابة على استجابة تسجيل بيانات اعتماد المفتاح العام بتنسيق JSON.
أرسِل response.registrationResponseJson من تطبيقك إلى خادم الجهة المعتمدة للتحقّق من المفتاح العام وتخزينه مع حساب المستخدم، ثم سجِّل علامة المزامنة المحلية في مساحة التخزين المحلية. لمزيد من المعلومات حول عملية التحقّق من جهة الخادم، يُرجى الاطّلاع على الإرشادات المتعلّقة بمفاتيح المرور.
أثناء عملية إنشاء مفتاح الاستعادة، يمكنك معالجة الاستثناءات التالية:
CreateRestoreCredentialDomException: يحدث هذا الاستثناء إذا كانrequestJsonغير صالح ولا يتّبع تنسيق WebAuthn لـPublicKeyCredentialCreationOptionsJSON.-
E2eeUnavailableException: يحدث هذا الاستثناء إذا كانت قيمةisCloudBackupEnabledهيtrue، ولكن جهاز المستخدم لا يتضمّن ميزة الاحتفاظ بنسخة احتياطية من البيانات أو ميزة التشفير التام بين الأطراف، مثل قفل الشاشة.
لضمان إنشاء بيانات اعتماد الاستعادة لعمليات النقل المحلية من جهاز إلى جهاز (D2D) حتى في حال عدم توفّر ميزة "التشفير التام بين الأطراف" (E2EE) في السحابة الإلكترونية، يمكنك رصدE2eeUnavailableExceptionوإعادة محاولة تنفيذcreateCredential()على الفور باستخدامCreateRestoreCredentialRequest(requestJson, isCloudBackupEnabled = false). -
IllegalArgumentException: يحدث هذا الاستثناء إذا كانت السمةrequestJsonفارغة أو لا تتوافق مع تنسيق JSON أو لا تتضمّن سمةuser.idصالحة بترميز Base64URL ومتوافقة مع مواصفات WebAuthn.
تسجيل الدخول باستخدام مفتاح استعادة
استخدِم ميزة "استعادة بيانات الاعتماد" لتسجيل دخول المستخدم تلقائيًا أثناء عملية إعداد الجهاز.
الحصول على خيارات استرداد بيانات الاعتماد من خادم التطبيق
قبل طلب getCredential() على الجهاز الجديد، اطلب تحدّي JSON
PublicKeyCredentialRequestOptions من WebAuthn من خادم تطبيقك. بما أنّ التطبيق على الجهاز الجديد لا يعرف بعد المستخدم الذي يسجّل الدخول، يجب أن تكون نقطة النهاية هذه غير مصادَق عليها (اكتشاف بدون مستخدم). للحصول على إرشادات ذات صلة بالخادم، يُرجى الاطّلاع على تسجيل الدخول باستخدام مفتاح مرور ودليل المصادقة من جهة الخادم.
تتضمّن خيارات الاسترداد بتنسيق JSON البنية التالية:
{
"challenge": "<your app challenge>",
"allowCredentials": [],
"rpId": "<your app server domain>"
}
اضبط خيارات الاسترداد بتنسيق JSON على الخادم على النحو التالي:
-
allowCredentials: يمكنك حذف هذا الحقل أو تمرير مصفوفة فارغة ([]) لأنّ حساب المستخدم غير معروف قبل الاستعادة. بعد عرضgetCredential()، يحدّد الخادم المستخدم من الحقلresponse.userHandle(الذي يتطابق معuser.idالمسجَّل أثناء الإنشاء) ويبحث عن المفتاح العام من خلالid(credentialId). -
userVerification: يجب ضبطها على"discouraged"(أو"preferred"). بما أنّ ميزة "استعادة بيانات الاعتماد" تعمل في الخلفية بدون واجهة مستخدم للمقاييس الحيوية أو قفل الشاشة، يتجاهل Android قيمة"required"ويضبطها على"discouraged"، ويتم ضبط علامة "تم التحقّق من المستخدم" (uv) علىfalseفي بيانات المصادقة التي يتم إرجاعها إلى خادمك. اضبط أداة التحقّق من خادم WebAuthn لقبولuv = falseلتأكيدات مفتاح الاستعادة.
الحصول على مفتاح الاستعادة
للحصول على مفتاح الاستعادة على الجهاز الجديد، عليك تضمين requestJson الخادم في
GetRestoreCredentialOption داخل GetCredentialRequest واستدعاء
getCredential() في عنصر CredentialManager.
نفِّذ استراتيجية استعادة من مستويين ليتم تسجيل دخول المستخدمين سواء تم تشغيل استعادة بيانات التطبيق في الخلفية أم لا:
- المستوى 1 (الاستعادة في الخلفية في
BackupAgent.onRestoreFinished()): في حال تفعيل ميزة الاحتفاظ بنسخة احتياطية من بيانات التطبيق (android:allowBackup="true")، يمكنك إنشاء فئة فرعية منBackupAgentوإلغاءonRestoreFinished()(لا تستخدِمonRestore()، التي يتم استدعاؤها فقط للنسخ الاحتياطية القديمة التي تستخدم أزواج المفتاح/القيمة). بما أنّ دالةonRestoreFinished()متزامنة وgetCredential()هي دالةsuspendفي Kotlin، يمكنك ربط الاستدعاء باستخدامrunBlocking(Dispatchers.IO). يتم رصد الخطأNoCredentialExceptionبدون إشعار المستخدم في حال عدم توفّر مفتاح استعادة. - المستوى 2 (الاحتياطي في المقدّمة في مشغّل التطبيقات
Activity.onCreate()): عند تشغيل التطبيق لأول مرة في حال عدم توفّر جلسة محلية، يمكنك استدعاءgetCredential()منlifecycleScopeقبل عرض شاشة تسجيل الدخول. يعالج هذا الإجراء التطبيقات التي تتضمّنandroid:allowBackup="false"ويستردّها إذا كان الجهاز غير متصل بالإنترنت عند تشغيلonRestoreFinished().
إذا أضفت BackupAgent جديدًا إلى تطبيق يستخدم ميزة "الاحتفاظ التلقائي بنسخة احتياطية" مع
android:allowBackup="true"، عليك أيضًا ضبط android:fullBackupOnly="true" على
عنصر <application> في AndroidManifest.xml لكي يواصل Android استخدام
ميزة "الاحتفاظ التلقائي بنسخة احتياطية" بدلاً من التبديل إلى ميزة "الاحتفاظ بنسخة احتياطية من أزواج المفتاح/القيمة" القديمة.
// Fetch the options required to get the restore key
val authenticationJson = fetchAuthenticationJson()
// Create the GetRestoreCredentialRequest object
val options = GetRestoreCredentialOption(authenticationJson)
val getRequest = GetCredentialRequest(listOf(options))
val response = credentialManager.getCredential(context, getRequest)
// Type-check and extract the restore credential
val credential = response.credential as RestoreCredential
تعرض واجهة Credential Manager API السمة GetCredentialResponse التي تكون سمتها credential هي RestoreCredential (وليس PublicKeyCredential) وتحتوي على authenticationResponseJson.
التعامل مع ردّ تسجيل الدخول
أرسِل restoreCredential.authenticationResponseJson من التطبيق إلى خادم الطرف المعتمِد للتحقّق من صحة بيان التشفير وإصدار الرمز المميز للجلسة للمستخدم. للاطّلاع على تفاصيل عملية التحقّق من جهة الخادم، يُرجى مراجعة مقالة تسجيل الدخول باستخدام
مفتاح مرور.
عند تصميم مساحة التخزين في الخلفية وسياسات مراحل النشاط لمفاتيح الاستعادة، يُرجى اتّباع الإرشادات التالية:
- التمييز بين مفاتيح الاستعادة ومفاتيح مرور المستخدمين: يمكنك وضع علامة على مفاتيح الاستعادة في
قاعدة البيانات لإخفائها من شاشات إدارة مفاتيح المرور الموجّهة للمستخدمين
واستثنائها من عمليات التحقّق من المستخدم (
uv = true). - إتاحة استخدام أجهزة نشطة متعددة لكل مستخدم: يمكن أن يمتلك المستخدم أجهزة متعددة (مثل هاتف وجهاز لوحي). يجب السماح باستخدام عدة مفاتيح استعادة نشطة لكل حساب مستخدم (على سبيل المثال، مفاتيح حسب الجهاز أو رقم تعريف التثبيت) بدلاً من استبدال صف واحد.
- ضبط مدة صلاحية مناسبة وتنظيف المفاتيح المعزولة: يؤدي إلغاء تثبيت تطبيق أو محو بياناته إلى حذف مفتاح الاستعادة على الجهاز بدون إشعار الخادم. يجب الاحتفاظ بمفاتيح الاستعادة لفترة كافية تتيح ترقية الأجهزة (حتى إذا سجّل المستخدم الخروج من الجهاز القديم بعد نقل البيانات)، ويجب حذف المفاتيح القديمة استنادًا إلى مدة صلاحية عدم النشاط أو حدود المفاتيح لكل مستخدم.
حذف مفتاح الاستعادة
لا يحتفظ Credential Manager بالبيانات ولا يطّلع على نشاط المستخدم، لذا لا يحذف مفاتيح الاستعادة تلقائيًا بعد الاستخدام. لحذف مفتاح استعادة، يمكنك استدعاء الطريقة
clearCredentialState(). لأسباب تتعلق بالأمان، احذف المفتاح عندما يسجّل المستخدم الخروج حتى لا يتم تسجيل دخوله تلقائيًا عند تشغيل التطبيق في المرة التالية.
لا تتم إزالة مفاتيح الاستعادة إلا في الحالات التالية:
- الإجراءات على مستوى النظام: يلغي المستخدمون تثبيت التطبيق أو يمحون بياناته.
- الطلبات على مستوى التطبيق: يمكنك حذف المفتاح آليًا من خلال طلب
clearCredentialState()عند معالجة تسجيل خروج المستخدم في رمز تطبيقك.
بما أنّ الإجراء التلقائي ClearCredentialStateRequest() يمحو فقط حالة الجلسة العادية ويترك مفتاح الاستعادة سليمًا، يمكنك إجراء محو مزدوج عند تسجيل الخروج:
- يمكنك استدعاء
credentialManager.clearCredentialState(ClearCredentialStateRequest())لمحو حالة جلسة موفّر بيانات الاعتماد النشطة. - يمكنك الاتصال بالرقم
credentialManager.clearCredentialState()باستخدامClearCredentialStateRequest(ClearCredentialStateRequest.TYPE_CLEAR_RESTORE_CREDENTIAL)لحذفRestoreCredentialالمخزَّنة. - احذف علامة
has_synced_restore_credentialالمحلية من مساحة التخزين المحلية.
// Create a ClearCredentialStateRequest object
val clearRequest = ClearCredentialStateRequest(TYPE_CLEAR_RESTORE_CREDENTIAL)
// When the user logs out, delete the restore key
val response = credentialManager.clearCredentialState(clearRequest)
الخطوات التالية
- اختبار ميزة "استعادة بيانات الاعتماد": يمكنك اختبار عمليات الاستعادة من السحابة الإلكترونية ومن جهاز إلى آخر (D2D) باستخدام "استوديو Android" أو
adb shell bmgr. - اختبار النسخ الاحتياطي والاستعادة المبرمَجَين: يمكنك كتابة اختبارات مضيف JUnit 5 باستخدام
BackupRestoreControllerللتحقّق من عمليات استعادةCLOUD_ENCRYPTEDوDEVICE_TO_DEVICEفي CI.