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

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

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

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

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

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

نظرة عامة

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

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

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

أضِف الاعتماديات التالية إلى ملف 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"
}

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

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

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

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

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

استخدِم مكتبة متوافقة مع معيار 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: أدرِج معرّفات بيانات الاعتماد في مصفوفة لمنع إنشاء مفتاح مرور مكرّر إذا كان هناك مفتاح مرور حالي لدى موفّر بيانات الاعتماد نفسه.

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

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

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

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

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

إنشاء مفتاح مرور تلقائيًا

يمكنك إنشاء مفتاح مرور تلقائيًا للمستخدم بعد تسجيل الدخول بنجاح باستخدام كلمة المرور من خلال ضبط المَعلمة isConditional على true في CreatePublicKeyCredentialRequest أثناء إنشاء مفتاح مرور. إذا لم يكن لدى المستخدم مفتاح مرور، سيحاول تطبيقك تلقائيًا إنشاء مفتاح في الخلفية وتخزينه في مقدّم بيانات الاعتماد الخاص بالمستخدم، مثل &quot;مدير كلمات المرور في Google&quot;. للاطّلاع على مثال حول كيفية تنفيذ ذلك، راجِع العينة العامة.

مثال على الإشعار الذي يعرضه &quot;مدير كلمات المرور في Google&quot; بعد إنشاء مفتاح مرور
الشكل 2: إشعار من "مدير كلمات المرور في Google"

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

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

الردّ الذي تتلقّاه بعد إجراء عملية ناجحة لاستدعاء 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}")
    }
}

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

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

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

للحصول على بصمة الإصبع 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>
    

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

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

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

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

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

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

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

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

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

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

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

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

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