إنشاء مفتاح مرور

قبل أن يتمكّن المستخدمون من إثبات الهوية باستخدام مفاتيح المرور، يجب أن يسجّل تطبيقك مفتاح المرور أو ينشئه لحساباتهم أولاً.

لإنشاء مفتاح المرور، احصل على التفاصيل المطلوبة من خادم تطبيقك، ثم استدعِ Credential Manager API الذي يعرض مفتاحًا عامًا ومفتاحًا خاصًا. يتم تخزين المفتاح الخاص الذي تم إرجاعه في موفّر بيانات اعتماد، مثل "مدير كلمات المرور في Google"، كمفتاح مرور. يتم تخزين المفتاح العام على خادم التطبيق.

يتم تخزين مفاتيح المرور في مقدّم بيانات الاعتماد، ويتم تخزين المفاتيح العامة على خادم التطبيق
الشكل 1: إنشاء مفاتيح مرور

المتطلّبات الأساسية

تأكَّد من إعداد روابط مواد العرض الرقمية واستهداف الأجهزة التي تعمل بنظام التشغيل Android 9 (المستوى 28 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث.

نظرة عامة

يركّز هذا الدليل على التغييرات المطلوبة في تطبيق العميل الخاص بالجهة المعتمِدة لإنشاء مفتاح مرور، ويقدّم نظرة عامة موجزة على عملية تنفيذ خادم تطبيق الجهة المعتمِدة. لمزيد من المعلومات عن عملية الربط من جهة الخادم، يُرجى الاطّلاع على تسجيل مفتاح المرور من جهة الخادم.

  1. إضافة التبعيات إلى تطبيقك: أضِف مكتبات Credential Manager المطلوبة.
  2. إنشاء مثيل لـ Credential Manager: أنشئ مثيلاً لـ Credential Manager.
  3. الحصول على خيارات إنشاء بيانات الاعتماد من خادم التطبيق: من خادم تطبيقك، أرسِل إلى تطبيق العميل التفاصيل المطلوبة لإنشاء مفتاح المرور، مثل معلومات عن التطبيق والمستخدم، بالإضافة إلى challenge وحقول أخرى.
  4. طلب مفتاح مرور: في تطبيقك، استخدِم التفاصيل التي تلقّيتها من خادم التطبيق لإنشاء عنصر GetPublicKeyCredentialOption واستخدِم هذا العنصر لاستدعاء الطريقة credentialManager.getCredential() من أجل إنشاء مفتاح مرور.
  5. التعامل مع الردّ على إنشاء مفتاح المرور: عند تلقّي بيانات الاعتماد على تطبيق العميل، عليك ترميز المفتاح العام وتسلسله ثم إرساله إلى خادم التطبيق. يجب أيضًا التعامل مع كل الاستثناءات التي يمكن أن تحدث في حال إنشاء مفتاح مرور.
  6. إثبات ملكية المفتاح العام وحفظه على الخادم: أكمِل الخطوات من جهة الخادم لإثبات ملكية بيانات الاعتماد، ثم احفظ المفتاح العام.
  7. إرسال إشعار إلى المستخدم: لإعلام المستخدم بأنّه تم إنشاء مفتاح المرور.

‫1. إضافة التبعيات إلى تطبيقك

أضِف التبعيات التالية إلى ملف build.gradle الخاص بوحدة تطبيقك:

Kotlin

dependencies {
    implementation("androidx.credentials:credentials:1.6.0-beta03")
    implementation("androidx.credentials:credentials-play-services-auth:1.6.0-beta03")
}

Groovy

dependencies {
    implementation "androidx.credentials:credentials:1.6.0-beta03"
    implementation "androidx.credentials:credentials-play-services-auth:1.6.0-beta03"
}

‫2- إنشاء مثيل لـ Credential Manager

استخدِم سياق تطبيقك أو نشاطك لإنشاء عنصر CredentialManager.

// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)

3- الحصول على خيارات إنشاء بيانات الاعتماد من خادم تطبيقك

عندما ينقر المستخدم على الزر "إنشاء مفتاح مرور" أو عندما يسجّل مستخدم جديد، أرسِل طلبًا من تطبيقك إلى خادم التطبيق للحصول على المعلومات المطلوبة لبدء عملية تسجيل مفتاح المرور.

استخدِم مكتبة متوافقة مع FIDO في خادم تطبيقك لإرسال المعلومات المطلوبة لإنشاء مفتاح مرور إلى تطبيق العميل، مثل معلومات عن المستخدم والتطبيق وخصائص الإعداد الإضافية. لمزيد من المعلومات، يمكنك الاطّلاع على تسجيل مفتاح المرور من جهة الخادم.

في تطبيق العميل، فكِّ ترميز خيارات إنشاء المفتاح العام التي أرسلها خادم التطبيق. ويتم عادةً تمثيلها بتنسيق JSON. لمزيد من المعلومات حول طريقة فك الترميز هذه لبرامج الويب، يُرجى الاطّلاع على الترميز وفك الترميز. بالنسبة إلى تطبيقات عميل Android، عليك التعامل مع فك الترميز بشكل منفصل.

يعرض المقتطف التالي بنية خيارات إنشاء المفتاح العام التي يرسلها خادم التطبيق:

{
  "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"
  }
}

تشمل الحقول الرئيسية في خيارات إنشاء المفتاح العام ما يلي:

  • challenge: هي سلسلة عشوائية ينشئها الخادم وتُستخدَم لمنع هجمات إعادة الإرسال.
  • rp: تفاصيل حول التطبيق.
    • rp.name: اسم التطبيق
    • rp.id: نطاق التطبيق أو نطاقه الفرعي
  • user: تفاصيل حول المستخدم
    • id: المعرّف الفريد للمستخدم. يجب ألا تتضمّن هذه القيمة معلومات تحدِّد الهوية الشخصية، مثل عناوين البريد الإلكتروني أو أسماء المستخدمين. يمكنك استخدام قيمة عشوائية مؤلّفة من 16 بايت.
    • name: معرّف فريد للحساب الذي سيتعرّف عليه المستخدم، مثل عنوان بريده الإلكتروني أو اسم المستخدم. سيظهر هذا الاسم في أداة اختيار الحساب. في حال استخدام اسم مستخدم، استخدِم القيمة نفسها المستخدَمة في مصادقة كلمة المرور.
    • displayName: اسم اختياري سهل الاستخدام للحساب، وهو مخصّص للعرض في أداة اختيار الحساب.
  • authenticatorSelection: تفاصيل حول الجهاز الذي سيتم استخدامه للمصادقة.

    • authenticatorAttachment: تشير إلى أداة المصادقة المفضّلة. في ما يلي القيم المحتمَلة: - platform: تُستخدَم هذه القيمة للمصادقة المضمّنة في جهاز المستخدم، مثل مستشعر بصمة الإصبع. - cross-platform: تُستخدَم هذه القيمة للأجهزة المتنقلة، مثل مفاتيح الأمان. ولا يُستخدَم عادةً في سياق مفتاح المرور. - غير محدّد (يُنصح به): يتيح عدم تحديد هذه القيمة للمستخدمين المرونة في إنشاء مفاتيح مرور على أجهزتهم المفضّلة. في معظم الحالات، يكون عدم تحديد المَعلمة هو الخيار الأفضل.
      • requireResidentKey: لإنشاء مفتاح مرور، اضبط قيمة الحقل Boolean على true.
      • residentKey: لإنشاء مفتاح مرور، اضبط القيمة على required.
      • userVerification: تُستخدَم لتحديد متطلبات التحقّق من هوية المستخدم أثناء تسجيل مفتاح مرور. القيم المحتملة هي كما يلي: - preferred: استخدِم هذه القيمة إذا كانت الأولوية لديك هي تجربة المستخدم على الحماية، مثلاً في البيئات التي تتسبّب فيها عملية إثبات هوية المستخدم في حدوث مشاكل أكثر من الحماية. - required: استخدِم هذه القيمة إذا كان من المطلوب استدعاء طريقة لإثبات هوية المستخدم متوفّرة على الجهاز. - discouraged: استخدِم هذه القيمة إذا كان من غير المستحسن استخدام طريقة لإثبات هوية المستخدم.
        لمزيد من المعلومات حول userVerification، يمكنك الاطّلاع على النظرة المتعمّقة حول ميزة "إثبات هوية المستخدم".
  • excludeCredentials: أدرِج معرّفات بيانات الاعتماد في مصفوفة لمنع إنشاء مفتاح مرور مكرّر إذا كان هناك مفتاح مرور آخر يتضمّن موفّر بيانات الاعتماد نفسه.

‫4- طلب مفتاح مرور

بعد تحليل خيارات إنشاء المفتاح العام من جهة الخادم، أنشئ مفتاح مرور من خلال تضمين هذه الخيارات في عنصر CreatePublicKeyCredentialRequest واستدعاء createCredential().

يتضمّن createPublicKeyCredentialRequest ما يلي:

  • requestJson: خيارات إنشاء بيانات الاعتماد التي يرسلها خادم التطبيق
  • preferImmediatelyAvailableCredentials: هذا حقل اختياري من النوع Boolean يحدّد ما إذا كان سيتم استخدام بيانات الاعتماد المتوفّرة محليًا أو بيانات الاعتماد التي تمت مزامنتها مع موفّر بيانات الاعتماد فقط لتلبية الطلب، بدلاً من بيانات الاعتماد من مفاتيح الأمان أو مسارات مفاتيح مختلطة. في ما يلي الاستخدامات المحتملة:
    • false (القيمة التلقائية): استخدِم هذه القيمة إذا تم استدعاء Credential Manager من خلال إجراء صريح اتّخذه المستخدم.
    • true: استخدِم هذه القيمة إذا تم استدعاء &quot;مدير بيانات الاعتماد&quot; بشكل انتهازي، مثلاً عند فتح التطبيق لأول مرة.
      إذا ضبطت القيمة على true ولم تتوفّر أي بيانات اعتماد على الفور، لن يعرض &quot;مدير بيانات الاعتماد&quot; أي واجهة مستخدم وسيفشل الطلب على الفور، وسيتم عرض NoCredentialException لطلبات get وCreateCredentialNoCreateOptionException لطلبات create.
  • origin: يتم ضبط هذا الحقل تلقائيًا لتطبيقات Android. بالنسبة إلى المتصفّحات والتطبيقات التي تتطلّب امتيازات مماثلة والتي تحتاج إلى ضبط origin، يُرجى الاطّلاع على إجراء طلبات إلى "مدير بيانات الاعتماد" نيابةً عن جهات أخرى للتطبيقات التي تتطلّب امتيازات.
  • isConditional: هذا حقل اختياري يكون الإعداد التلقائي له هو false. عند ضبط هذا الخيار على true، وفي حال لم يكن لدى المستخدم مفتاح مرور، سيتم إنشاؤه تلقائيًا نيابةً عنه في المرة التالية التي يسجّل فيها الدخول باستخدام كلمة مرور محفوظة. يتم تخزين مفتاح المرور في مقدّم خدمة بيانات الاعتماد الخاص بالمستخدم. تتطلّب ميزة الإنشاء الشرطي أحدث إصدار من androidx.credentials.

يؤدي استدعاء الدالة createCredential() إلى تشغيل واجهة المستخدم المضمّنة في Credential Manager على شكل ورقة في أسفل الشاشة، والتي تطلب من المستخدم استخدام مفتاح مرور واختيار مقدّم خدمة بيانات اعتماد وحساب للتخزين. ومع ذلك، إذا تم ضبط isConditional على true، لن تظهر واجهة المستخدم الخاصة بورقة البيانات السفلية، وسيتم إنشاء مفتاح المرور تلقائيًا.

5. التعامل مع الردّ

بعد إثبات هوية المستخدم باستخدام قفل الشاشة على الجهاز، يتم إنشاء مفتاح مرور وتخزينه في موفّر بيانات الاعتماد الذي اختاره المستخدم.

الردّ الذي تتلقّاه بعد إجراء عملية ناجحة لاستدعاء createCredential() هو عنصر PublicKeyCredential.

يبدو PublicKeyCredential على النحو التالي:

{
  "id": "<identifier>",
  "type": "public-key",
  "rawId": "<identifier>",
  "response": {
    "clientDataJSON": "<ArrayBuffer encoded object with the origin and signed challenge>",
    "attestationObject": "<ArrayBuffer encoded object with the public key and other information.>"
  },
  "authenticatorAttachment": "platform"
}

في تطبيق العميل، يمكنك تسلسل العنصر وإرساله إلى خادم التطبيق.

أضِف رمزًا للتعامل مع حالات الفشل كما هو موضّح في المقتطف التالي:

fun handleFailure(e: CreateCredentialException) {
    when (e) {
        is CreatePublicKeyCredentialDomException -> {
            // Handle the passkey DOM errors thrown according to the
            // WebAuthn spec.
        }
        is CreateCredentialCancellationException -> {
            // The user intentionally canceled the operation and chose not
            // to register the credential.
        }
        is CreateCredentialInterruptedException -> {
            // Retry-able error. Consider retrying the call.
        }
        is CreateCredentialProviderConfigurationException -> {
            // Your app is missing the provider configuration dependency.
            // Most likely, you're missing the
            // "credentials-play-services-auth" module.
        }
        is CreateCredentialCustomException -> {
            // You have encountered an error from a 3rd-party SDK. If you
            // make the API call with a request object that's a subclass of
            // CreateCustomCredentialRequest using a 3rd-party SDK, then you
            // should check for any custom exception type constants within
            // that SDK to match with e.type. Otherwise, drop or log the
            // exception.
        }
        else -> Log.w(TAG, "Unexpected exception type ${e::class.java.name}")
    }
}

6. التحقّق من المفتاح العام وحفظه على خادم التطبيق

على خادم التطبيق، عليك إثبات ملكية بيانات اعتماد المفتاح العام ثم حفظ المفتاح العام.

للتحقّق من مصدر بيانات اعتماد المفتاح العام، قارِنها بقائمة مسموح بها تضم التطبيقات التي تمت الموافقة عليها. إذا كان للمفتاح مصدر غير معروف، ارفضه.

للحصول على بصمة الإصبع SHA 256 للتطبيق، اتّبِع الخطوات التالية:

  1. اطبع شهادة توقيع تطبيق الإصدار من خلال تنفيذ الأمر التالي في الوحدة الطرفية:

    keytool -list -keystore <path-to-apk-signing-keystore>
    

    في الردّ، حدِّد الملف المرجعي لشهادة التوقيع SHA 256، المشار إليه على النحو التالي: Certificate fingerprints block : SHA256.

  2. ترميز الملف المرجعي SHA256 باستخدام ترميز base64url يوضّح مثال Python التالي كيفية ترميز البصمة بشكل صحيح:

    import binascii
    import base64
    fingerprint = '<SHA256 finerprint>' # your app's SHA256 fingerprint
    print(base64.urlsafe_b64encode(binascii.a2b_hex(fingerprint.replace(':', ''))).decode('utf8').replace('=', ''))
    
  3. أضِف android:apk-key-hash: إلى بداية الناتج من الخطوة السابقة، ليظهر لك ناتج مشابه لما يلي:

    android:apk-key-hash:<encoded SHA 256 fingerprint>
    

    يجب أن تتطابق النتيجة مع مصدر مسموح به على خادم تطبيقك. إذا كانت لديك شهادات توقيع متعددة، مثل شهادات تصحيح الأخطاء وإصدار التطبيق أو تطبيقات متعددة، كرِّر العملية واقبل جميع المصادر على أنّها صالحة على خادم التطبيق.

7. إشعار المستخدم

بعد إنشاء مفتاح المرور بنجاح، أرسِل إشعارًا إلى المستخدمين بشأن مفتاح المرور وأبلِغهم بأنّه يمكنهم إدارة مفاتيح المرور من تطبيق مقدّم بيانات الاعتماد أو من داخل إعدادات التطبيق. إعلام المستخدمين باستخدام مربّع حوار أو إشعار أو شريط إعلام مخصّص بما أنّ إنشاء مفتاح مرور غير متوقّع من قِبل جهة ضارة يتطلّب إرسال تنبيه أمان فوري، ننصحك بإضافة وسائل تواصل خارجية، مثل رسالة إلكترونية، إلى هذه الطرق داخل التطبيق.

تحسين تجربة المستخدم

لتحسين تجربة المستخدم أثناء تنفيذ عملية التسجيل باستخدام Credential Manager، ننصحك بإضافة وظيفة لاستعادة بيانات الاعتماد وإخفاء مربّعات الحوار الخاصة بميزة "الملء التلقائي".

إضافة وظيفة لاستعادة بيانات الاعتماد على جهاز جديد

للسماح للمستخدمين بتسجيل الدخول إلى حساباتهم بسهولة على جهاز جديد، عليك تنفيذ وظيفة استعادة بيانات الاعتماد. تتيح إضافة بيانات اعتماد الاستعادة باستخدام BackupAgent للمستخدمين تسجيل الدخول عند فتح تطبيقك الذي تم استعادته على جهاز جديد، ما يتيح لهم استخدام تطبيقك على الفور.

إيقاف ميزة "الملء التلقائي" في حقول بيانات الاعتماد (اختياري)

بالنسبة إلى شاشات التطبيق التي يُتوقّع أن يستخدم فيها المستخدمون واجهة المستخدم في ورقة البيانات السفلية الخاصة بـ "مدير بيانات الاعتماد" للمصادقة، أضِف السمة isCredential إلى حقلَي اسم المستخدم وكلمة المرور. يؤدي ذلك إلى منع مربّعات حوار الملء التلقائي (FillDialog وSaveDialog) من التداخل مع واجهة المستخدم الخاصة بالبطاقة السفلية في Credential Manager.

يتوافق السمة isCredential مع الإصدار 14 من نظام التشغيل Android والإصدارات الأحدث.

يوضّح المثال التالي كيفية إضافة السمة isCredential إلى حقول اسم المستخدم وكلمة المرور ذات الصلة في طرق العرض ذات الصلة لتطبيقك:

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:isCredential="true" />

الخطوات التالية