تسجيل دخول المستخدم باستخدام "مدير بيانات الاعتماد"

مدير بيانات الاعتماد هي واجهة برمجة تطبيقات في Jetpack تتيح طرقًا متعددة لتسجيل الدخول، مثل اسم المستخدم وكلمة المرور ومفاتيح المرور وحلول تسجيل الدخول الموحّدة (مثل ميزة "تسجيل الدخول باستخدام حساب Google") في واجهة برمجة تطبيقات واحدة، ما يسهّل عملية الدمج بين المطوّرين.

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

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

تساعدنا ملاحظاتك في تحسين واجهة برمجة تطبيقات Credential Manager API. شارِك أي مشاكل تجدها أو أفكارًا لتحسين واجهة برمجة التطبيقات باستخدام الرابط التالي:

تقديم ملاحظات

لمحة عن مفاتيح المرور

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

تعتمد مفاتيح المرور على WebAuthn (المصادقة على الويب)، وهو معيار تم تطويره بشكلٍ مشترك من قِبل تحالف FIDO واتحاد شبكة الويب العالمية (W3C). يستخدم WebAuthn تشفير المفتاح العام لمصادقة المستخدم. يمكن للموقع الإلكتروني أو التطبيق الذي سجّل المستخدم الدخول إليه الاطّلاع على المفتاح العام وتخزينه، ولكن لا يمكنه أبدًا الاطّلاع على المفتاح الخاص. ويتم الحفاظ على سريّة المفتاح الخاص وسلامته. ولأنّ المفتاح فريد ويرتبط بالموقع الإلكتروني أو التطبيق، لا يمكن خداع المستخدمين من خلال سرقة مفاتيح المرور، ما يضيف المزيد من الأمان.

يسمح "مدير بيانات الاعتماد" للمستخدمين بإنشاء مفاتيح مرور وتخزينها في مدير كلمات المرور في Google.

اطّلِع على مقالة مصادقة المستخدمين باستخدام مفاتيح المرور للحصول على إرشادات حول كيفية تنفيذ مسارات مصادقة مفاتيح المرور السلسة باستخدام "مدير بيانات الاعتماد".

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

لاستخدام "مدير بيانات الاعتماد"، أكمِل الخطوات الواردة في هذا القسم.

استخدام إصدار حديث من النظام الأساسي

إنّ "مدير بيانات الاعتماد" متوفّر على نظام التشغيل Android 4.4 (المستوى 19 من واجهة برمجة التطبيقات) والإصدارات الأحدث.

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

أضِف التبعيات التالية إلى نص إنشاء وحدة تطبيقك:

Kotlin

dependencies {
    implementation("androidx.credentials:credentials:1.5.0-alpha05")

    // optional - needed for credentials support from play services, for devices running
    // Android 13 and below.
    implementation("androidx.credentials:credentials-play-services-auth:1.5.0-alpha05")
}

Groovy

dependencies {
    implementation "androidx.credentials:credentials:1.5.0-alpha05"

    // optional - needed for credentials support from play services, for devices running
    // Android 13 and below.
    implementation "androidx.credentials:credentials-play-services-auth:1.5.0-alpha05"
}

الحفاظ على الفئات في ملف ProGuard

في ملف proguard-rules.pro الخاص بالوحدة، أضِف التوجيهات التالية:

-if class androidx.credentials.CredentialManager
-keep class androidx.credentials.playservices.** {
  *;
}

اطّلِع على مزيد من المعلومات عن كيفية تصغير تطبيقك وتشفيره وتحسينه.

إتاحة روابط تنقل إلى مواد عرض رقمية

لتفعيل ميزة مفاتيح المرور في تطبيقك على Android، اربط تطبيقك بأحد المواقع الإلكترونية التي يملكها. يمكنك الإعلان عن عملية الربط هذه من خلال اتّباع الخطوات التالية:

  1. أنشِئ ملف روابط التنقل إلى مواد العرض الرقمية بتنسيق JSON. على سبيل المثال، للإشارة إلى أنّه يمكن للمواقع الإلكترونية https://signin.example.com وتطبيق Android الذي يحمل اسم الحزمةcom.example مشاركة بيانات اعتماد تسجيل الدخول، أنشئ ملفًا باسم assetlinks.json يتضمّن المحتوى التالي:

    [
      {
        "relation" : [
          "delegate_permission/common.handle_all_urls",
          "delegate_permission/common.get_login_creds"
        ],
        "target" : {
          "namespace" : "android_app",
          "package_name" : "com.example.android",
          "sha256_cert_fingerprints" : [
            SHA_HEX_VALUE
          ]
        }
      }
    ]
    

    حقل relation هو صفيف من سلسلة واحدة أو أكثر تصف العلاقة التي يتمّ الإعلان عنها. للإقرار بأنّ التطبيقات والمواقع الإلكترونية تشارك ملف تعريف اعتماد تسجيل الدخول، حدِّد العلاقات على أنّها delegate_permission/handle_all_urls و delegate_permission/common.get_login_creds.

    حقل target هو عنصر يحدّد مادة العرض التي ينطبق عليها البيان. تحدِّد الحقول التالية موقعًا إلكترونيًا:

    namespace web
    site

    يجب أن يكون عنوان URL للموقع الإلكتروني بالتنسيق https://domain[:optional_port]، على سبيل المثال https://www.example.com.

    يجب أن يكون domain مؤهَّلاً بالكامل، ويجب حذف optional_port عند استخدام المنفذ 443 ل بروتوكول HTTPS.

    لا يمكن أن يكون هدف site سوى نطاق جذر: لا يمكنك الحد من ربط التطبيق بدليل فرعي معيّن. لا تُدرِج مسارًا في عنوان URL، مثل شرطة سفلية.

    لا تُعدّ النطاقات الفرعية مطابقة: أي إذا حدّدت domain على أنّه www.example.com، لن يكون النطاق www.counter.example.com مرتبطًا بتطبيقك.

    تحدِّد الحقول التالية تطبيق Android:

    namespace android_app
    package_name اسم الحزمة الذي تم تعريفه في بيان التطبيق على سبيل المثال، com.example.android
    sha256_cert_fingerprints ملفات مرجعية SHA256 لملف شهادة التوقيع لتطبيقك
  2. استضِف ملف JSON الذي يتضمّن روابط تنقل إلى مواد عرض رقمية في الموقع التالي على نطاق تسجيل الدخول:

    https://domain[:optional_port]/.well-known/assetlinks.json
    

    على سبيل المثال، إذا كان نطاق تسجيل الدخول هو signin.example.com، يمكنك استضافة ملف JSON على https://signin.example.com/.well-known/assetlinks.json.

    يجب أن يكون نوع MIME لملف "رابط مواد العرض الرقمية" بتنسيق JSON. تأكَّد من أنّه يُرسِل الخادم عنوان Content-Type: application/json في الاستجابة.

  3. تأكَّد من أنّ المضيف يسمح لمحرّك بحث Google باسترداد ملف "رابط التنقل إلى مادة العرض الرقمية". إذا كان لديك ملف robots.txt، يجب أن يسمح لوكيل Googlebot ب retrieving /.well-known/assetlinks.json. يمكن لمعظم المواقع الإلكترونية السماح لأي وكيل مبرمَج باسترداد الملفات في مسار /.well-known/ حتى تتمكّن خدمات أخرى من الوصول إلى البيانات الوصفية في هذه الملفات:

    User-agent: *
    Allow: /.well-known/
    
  4. أضِف السطر التالي إلى ملف البيان ضمن <application>:

    <meta-data android:name="asset_statements" android:resource="@string/asset_statements" />
    
  5. إذا كنت تستخدم تسجيل الدخول بكلمة المرور من خلال "مدير بيانات الاعتماد"، اتّبِع هذه الخطوة لضبط إعدادات ربط الأصول الرقمية في البيان. هذه الخطوة ليست مطلوبة إذا كنت تستخدم مفاتيح المرور فقط.

    حدِّد عملية الربط في تطبيق Android. أضِف عنصرًا يحدِّد ملفّات assetlinks.json المطلوب تحميلها. يجب ترميز أي علامات اقتباس أحادية وعلامات اقتباس مزدوجة تستخدمها في السلسلة. مثلاً:

    <string name="asset_statements" translatable="false">
    [{
      \"include\": \"https://signin.example.com/.well-known/assetlinks.json\"
    }]
    </string>
    
    > GET /.well-known/assetlinks.json HTTP/1.1
    > User-Agent: curl/7.35.0
    > Host: signin.example.com
    
    < HTTP/1.1 200 OK
    < Content-Type: application/json
    

إعداد مدير بيانات الاعتماد

لضبط كائن CredentialManager وإعداده، أضِف منطقًا مشابهًا لما يلي:

Kotlin

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

Java

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

الإشارة إلى حقول بيانات الاعتماد

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

لاستخدام سمة isCredential، أضِفها إلى "الملفّات التجارية" ذات الصلة:

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

سجِّل الدخول بحساب المستخدم.

لاسترداد جميع خيارات مفتاح المرور وكلمة المرور المرتبطة بحساب المستخدِم، يُرجى إكمال الخطوات التالية:

  1. يمكنك إعداد خيارات مصادقة كلمة المرور ومفتاح المرور:

    Kotlin

    // Retrieves the user's saved password for your app from their
    // password provider.
    val getPasswordOption = GetPasswordOption()
    
    // Get passkey from the user's public key credential provider.
    val getPublicKeyCredentialOption = GetPublicKeyCredentialOption(
        requestJson = requestJson
    )

    Java

    // Retrieves the user's saved password for your app from their
    // password provider.
    GetPasswordOption getPasswordOption = new GetPasswordOption();
    
    // Get passkey from the user's public key credential provider.
    GetPublicKeyCredentialOption getPublicKeyCredentialOption =
            new GetPublicKeyCredentialOption(requestJson);
  2. استخدِم الخيارات التي تم استردادها من الخطوة السابقة لإنشاء طلب تسجيل الدخول.

    Kotlin

    val getCredRequest = GetCredentialRequest(
        listOf(getPasswordOption, getPublicKeyCredentialOption)
    )

    Java

    GetCredentialRequest getCredRequest = new GetCredentialRequest.Builder()
        .addCredentialOption(getPasswordOption)
        .addCredentialOption(getPublicKeyCredentialOption)
        .build();
  3. ابدأ عملية تسجيل الدخول:

    Kotlin

    coroutineScope.launch {
        try {
            val result = credentialManager.getCredential(
                // Use an activity-based context to avoid undefined system UI
                // launching behavior.
                context = activityContext,
                request = getCredRequest
            )
            handleSignIn(result)
        } catch (e : GetCredentialException) {
            handleFailure(e)
        }
    }
    
    fun handleSignIn(result: GetCredentialResponse) {
        // Handle the successfully returned credential.
        val credential = result.credential
    
        when (credential) {
            is PublicKeyCredential -> {
                val responseJson = credential.authenticationResponseJson
                // Share responseJson i.e. a GetCredentialResponse on your server to
                // validate and  authenticate
            }
            is PasswordCredential -> {
                val username = credential.id
                val password = credential.password
                // Use id and password to send to your server to validate
                // and authenticate
            }
          is CustomCredential -> {
              // If you are also using any external sign-in libraries, parse them
              // here with the utility functions provided.
              if (credential.type == ExampleCustomCredential.TYPE)  {
              try {
                  val ExampleCustomCredential = ExampleCustomCredential.createFrom(credential.data)
                  // Extract the required credentials and complete the authentication as per
                  // the federated sign in or any external sign in library flow
                  } catch (e: ExampleCustomCredential.ExampleCustomCredentialParsingException) {
                      // Unlikely to happen. If it does, you likely need to update the dependency
                      // version of your external sign-in library.
                      Log.e(TAG, "Failed to parse an ExampleCustomCredential", e)
                  }
              } else {
                // Catch any unrecognized custom credential type here.
                Log.e(TAG, "Unexpected type of credential")
              }
            } else -> {
                // Catch any unrecognized credential type here.
                Log.e(TAG, "Unexpected type of credential")
            }
        }
    }

    Java

    credentialManager.getCredentialAsync(
        // Use activity based context to avoid undefined
        // system UI launching behavior
        activity,
        getCredRequest,
        cancellationSignal,
        <executor>,
        new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() {
            @Override
            public void onResult(GetCredentialResponse result) {
                handleSignIn(result);
            }
    
            @Override
            public void onError(GetCredentialException e) {
                handleFailure(e);
            }
        }
    );
    
    public void handleSignIn(GetCredentialResponse result) {
        // Handle the successfully returned credential.
        Credential credential = result.getCredential();
        if (credential instanceof PublicKeyCredential) {
            String responseJson = ((PublicKeyCredential) credential).getAuthenticationResponseJson();
            // Share responseJson i.e. a GetCredentialResponse on your server to validate and authenticate
        } else if (credential instanceof PasswordCredential) {
            String username = ((PasswordCredential) credential).getId();
            String password = ((PasswordCredential) credential).getPassword();
            // Use id and password to send to your server to validate and authenticate
        } else if (credential instanceof CustomCredential) {
            if (ExampleCustomCredential.TYPE.equals(credential.getType())) {
                try {
                    ExampleCustomCredential customCred = ExampleCustomCredential.createFrom(customCredential.getData());
                    // Extract the required credentials and complete the
                    // authentication as per the federated sign in or any external
                    // sign in library flow
                } catch (ExampleCustomCredential.ExampleCustomCredentialParsingException e) {
                    // Unlikely to happen. If it does, you likely need to update the
                    // dependency version of your external sign-in library.
                    Log.e(TAG, "Failed to parse an ExampleCustomCredential", e);
                }
            } else {
                // Catch any unrecognized custom credential type here.
                Log.e(TAG, "Unexpected type of credential");
            }
        } else {
            // Catch any unrecognized credential type here.
            Log.e(TAG, "Unexpected type of credential");
        }
    }

يوضّح المثال التالي كيفية تنسيق طلب JSON عند حصولك على مفتاح مرور:

{
  "challenge": "T1xCsnxM2DNL2KdK5CLa6fMhD7OBqho6syzInk_n-Uo",
  "allowCredentials": [],
  "timeout": 1800000,
  "userVerification": "required",
  "rpId": "credential-manager-app-test.glitch.me"
}

يوضّح المثال التالي الشكل الذي قد تظهر به استجابة JSON بعد الحصول على مستند اعتماد مفاتيح عامة:

{
  "id": "KEDetxZcUfinhVi6Za5nZQ",
  "type": "public-key",
  "rawId": "KEDetxZcUfinhVi6Za5nZQ",
  "response": {
    "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiVDF4Q3NueE0yRE5MMktkSzVDTGE2Zk1oRDdPQnFobzZzeXpJbmtfbi1VbyIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
    "authenticatorData": "j5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGQdAAAAAA",
    "signature": "MEUCIQCO1Cm4SA2xiG5FdKDHCJorueiS04wCsqHhiRDbbgITYAIgMKMFirgC2SSFmxrh7z9PzUqr0bK1HZ6Zn8vZVhETnyQ",
    "userHandle": "2HzoHm_hY0CjuEESY9tY6-3SdjmNHOoNqaPDcZGzsr0"
  }
}

التعامل مع الاستثناءات في حال عدم توفّر بيانات اعتماد

في بعض الحالات، قد لا يتوفّر لدى المستخدم أي بيانات اعتماد، أو قد لا يمنح المستخدم موافقته على استخدام بيانات اعتماد متاحة. في حال استدعاء getCredential() ولم يتم العثور على أي بيانات اعتماد، يتم عرض NoCredentialException. في هذه الحالة، من المفترض أن تتعامل علامتك البرمجية مع NoCredentialException الحالات.

Kotlin

try {
  val credential = credentialManager.getCredential(credentialRequest)
} catch (e: NoCredentialException) {
  Log.e("CredentialManager", "No credential available", e)
}

Java

try {
  Credential credential = credentialManager.getCredential(credentialRequest);
} catch (NoCredentialException e) {
  Log.e("CredentialManager", "No credential available", e);
}

على نظام التشغيل Android 14 أو الإصدارات الأحدث، يمكنك تقليل وقت الاستجابة عند عرض ملف تعريف العميل أداة الاختيار باستخدام الطريقة prepareGetCredential() قبل استدعاء getCredential().

Kotlin

val response = credentialManager.prepareGetCredential(
  GetCredentialRequest(
    listOf(
      <getPublicKeyCredentialOption>,
      <getPasswordOption>
    )
  )
}

Java

GetCredentialResponse response = credentialManager.prepareGetCredential(
  new GetCredentialRequest(
    Arrays.asList(
      new PublicKeyCredentialOption(),
      new PasswordOption()
    )
  )
);

لا تستدعي الطريقة prepareGetCredential() عناصر واجهة المستخدم. يساعدك فقط في تنفيذ عمل التحضير حتى تتمكّن لاحقًا من بدء عملية get-credential المتبقية (التي تتضمّن واجهات مستخدم) من خلال واجهة برمجة التطبيقات getCredential().

يتم عرض البيانات المخزّنة مؤقتًا في عنصر PrepareGetCredentialResponse. إذا كانت هناك بيانات اعتماد حالية، سيتم تخزين النتائج مؤقتًا، ويمكنك بعد ذلك بدء واجهة برمجة التطبيقات getCredential() المتبقية لعرض أداة اختيار الحساب باستخدام البيانات المخزّنة مؤقتًا.

مسارات التسجيل

يمكنك تسجيل مستخدم للمصادقة باستخدام إما مفتاح مرور أو كلمة مرور.

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

لمنح المستخدمين خيار تسجيل مفتاح مرور واستخدامه لإعادة المصادقة، سجِّل بيانات اعتماد مستخدم باستخدام عنصر CreatePublicKeyCredentialRequest.

Kotlin

fun createPasskey(requestJson: String, preferImmediatelyAvailableCredentials: Boolean) {
    val createPublicKeyCredentialRequest = CreatePublicKeyCredentialRequest(
        // Contains the request in JSON format. Uses the standard WebAuthn
        // web JSON spec.
        requestJson = requestJson,
        // Defines whether you prefer to use only immediately available
        // credentials, not hybrid credentials, to fulfill this request.
        // This value is false by default.
        preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials,
    )

    // Execute CreateCredentialRequest asynchronously to register credentials
    // for a user account. Handle success and failure cases with the result and
    // exceptions, respectively.
    coroutineScope.launch {
        try {
            val result = credentialManager.createCredential(
                // Use an activity-based context to avoid undefined system
                // UI launching behavior
                context = activityContext,
                request = createPublicKeyCredentialRequest,
            )
            handlePasskeyRegistrationResult(result)
        } catch (e : CreateCredentialException){
            handleFailure(e)
        }
    }
}

fun handleFailure(e: CreateCredentialException) {
    when (e) {
        is CreatePublicKeyCredentialDomException -> {
            // Handle the passkey DOM errors thrown according to the
            // WebAuthn spec.
            handlePasskeyError(e.domError)
        }
        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 CreateCredentialUnknownException -> ...
        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}")
    }
}

Java

public void createPasskey(String requestJson, boolean preferImmediatelyAvailableCredentials) {
    CreatePublicKeyCredentialRequest createPublicKeyCredentialRequest =
            // `requestJson` contains the request in JSON format. Uses the standard
            // WebAuthn web JSON spec.
            // `preferImmediatelyAvailableCredentials` defines whether you prefer
            // to only use immediately available credentials, not  hybrid credentials,
            // to fulfill this request. This value is false by default.
            new CreatePublicKeyCredentialRequest(
                requestJson, preferImmediatelyAvailableCredentials);

    // Execute CreateCredentialRequest asynchronously to register credentials
    // for a user account. Handle success and failure cases with the result and
    // exceptions, respectively.
    credentialManager.createCredentialAsync(
        // Use an activity-based context to avoid undefined system
        // UI launching behavior
        requireActivity(),
        createPublicKeyCredentialRequest,
        cancellationSignal,
        executor,
        new CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>() {
            @Override
            public void onResult(CreateCredentialResponse result) {
                handleSuccessfulCreatePasskeyResult(result);
            }

            @Override
            public void onError(CreateCredentialException e) {
                if (e instanceof CreatePublicKeyCredentialDomException) {
                    // Handle the passkey DOM errors thrown according to the
                    // WebAuthn spec.
                    handlePasskeyError(((CreatePublicKeyCredentialDomException)e).getDomError());
                } else if (e instanceof CreateCredentialCancellationException) {
                    // The user intentionally canceled the operation and chose not
                    // to register the credential.
                } else if (e instanceof CreateCredentialInterruptedException) {
                    // Retry-able error. Consider retrying the call.
                } else if (e instanceof CreateCredentialProviderConfigurationException) {
                    // Your app is missing the provider configuration dependency.
                    // Most likely, you're missing the
                    // "credentials-play-services-auth" module.
                } else if (e instanceof CreateCredentialUnknownException) {
                } else if (e instanceof 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.getClass().getName());
                }
            }
        }
    );
}

تنسيق طلب JSON

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

توضّح لك مقالة المدونة هذه حول توفير مصادقة سلسة لتطبيقاتك كيفية تنسيق طلب JSON عند إنشاء مفاتيح المرور وعند مصادقة باستخدام مفاتيح المرور. ويوضّح أيضًا سبب عدم فعالية كلمات المرور كحلٍّ فعال للمصادقة، وكيفية الاستفادة من بيانات الاعتماد الحالية للمقاييس الحيوية، وكيفية ربط تطبيقك بموقع إلكتروني تملكه، وكيفية إنشاء مفاتيح المرور، وكيفية المصادقة باستخدام مفاتيح المرور.

{
  "challenge": "abc123",
  "rp": {
    "name": "Credential Manager example",
    "id": "credential-manager-test.example.com"
  },
  "user": {
    "id": "def456",
    "name": "helloandroid@gmail.com",
    "displayName": "helloandroid@gmail.com"
  },
  "pubKeyCredParams": [
    {
      "type": "public-key",
      "alg": -7
    },
    {
      "type": "public-key",
      "alg": -257
    }
  ],
  "timeout": 1800000,
  "attestation": "none",
  "excludeCredentials": [
    {"id": "ghi789", "type": "public-key"},
    {"id": "jkl012", "type": "public-key"}
  ],
  "authenticatorSelection": {
    "authenticatorAttachment": "platform",
    "requireResidentKey": true,
    "residentKey": "required",
    "userVerification": "required"
  }
}

ضبط قيم authenticatorAttachment

لا يمكن ضبط المَعلمة authenticatorAttachment إلا عند إنشاء بيانات الاعتماد. ويمكنك تحديد platform أو cross-platform أو عدم تحديد أي قيمة. في معظم الحالات، لا يُنصح باستخدام أي قيمة.

  • platform: لتسجيل جهاز المستخدم الحالي أو مطالبة مستخدم كلمة المرور بالترقية إلى مفاتيح المرور بعد تسجيل الدخول، اضبط authenticatorAttachment على platform.
  • cross-platform: تُستخدَم هذه القيمة بشكل شائع عند تسجيل بيانات اعتماد تتعلّق بعوامل متعددة ، ولا تُستخدَم في سياق مفتاح المرور.
  • بدون قيمة: لمنح المستخدمين المرونة في إنشاء مفاتيح المرور على أجهزتهم المفضّلة (مثل إعدادات الحساب)، يجب عدم تحديد المَعلمة authenticatorAttachment عندما يختار أحد المستخدمين إضافة مفتاح مرور. في معظم الحالات، يُعدّ عدم تحديد المَعلمة هو الخيار الأفضل.

منع إنشاء مفاتيح مرور مكرّرة

أدرِج أرقام تعريف بيانات الاعتماد في الصفيف الاختياري excludeCredentials لمنع إنشاء مفتاح مرور جديد إذا كان هناك مفتاح حالي من مقدّم مفتاح المرور نفسه.

معالجة استجابة JSON

يعرض مقتطف الرمز البرمجي التالي مثالاً على استجابة JSON لإنشاء بيانات اعتماد مفتاح عمومي. تعرَّف على مزيد من المعلومات حول كيفية التعامل مع بيانات اعتماد المفتاح العام الذي تم إرجاعه.

{
  "id": "KEDetxZcUfinhVi6Za5nZQ",
  "type": "public-key",
  "rawId": "KEDetxZcUfinhVi6Za5nZQ",
  "response": {
    "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoibmhrUVhmRTU5SmI5N1Z5eU5Ka3ZEaVh1Y01Fdmx0ZHV2Y3JEbUdyT0RIWSIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
    "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUj5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGRdAAAAAAAAAAAAAAAAAAAAAAAAAAAAEChA3rcWXFH4p4VYumWuZ2WlAQIDJiABIVgg4RqZaJyaC24Pf4tT-8ONIZ5_Elddf3dNotGOx81jj3siWCAWXS6Lz70hvC2g8hwoLllOwlsbYatNkO2uYFO-eJID6A"
  }
}

التحقّق من المصدر من ملف JSON لبيانات العميل

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

في حالة الويب، يشير origin إلى أصل الموقع الإلكتروني نفسه الذي تم تسجيل الدخول باستخدام بيانات الاعتماد فيه. على سبيل المثال، إذا كان عنوان URL هو https://www.example.com:8443/store?category=shoes#athletic، يكون origin هو https://www.example.com:8443.

بالنسبة إلى تطبيقات Android، يضبط وكيل المستخدم تلقائيًا origin على توقيع التطبيق المُرسِل. يجب التحقّق من هذا التوقيع على أنّه مطابق على خادمك لتأكيد هوية المُرسِل لواجهة برمجة التطبيقات Passkey API. ‫origin في نظام التشغيل Android هو عنوان URL مشتق من تجزئة SHA-256 لشهادة توقيع APK، مثل:

android:apk-key-hash:<sha256_hash-of-apk-signing-cert>

يمكن العثور على تجزئات SHA-256 لشهادات التوقيع من ملف تخزين المفاتيح من خلال تشغيل الأمر الطرفي التالي:

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

يتم استخدام تنسيق سداسي عشري مُحدَّد بفواصل لقيم تجزئة SHA-256 (91:F7:CB:F9:D6:81…)، ويتم ترميز قيم origin في Android باستخدام base64url. يوضّح مثال Python هذا كيفية تحويل تنسيق التجزئة إلى تنسيق سداسي عشري متوافق ومفصَّل بفواصل:

import binascii
import base64
fingerprint = '91:F7:CB:F9:D6:81:53:1B:C7:A5:8F:B8:33:CC:A1:4D:AB:ED:E5:09:C5'
print("android:apk-key-hash:" + base64.urlsafe_b64encode(binascii.a2b_hex(fingerprint.replace(':', ''))).decode('utf8').replace('=', ''))

استبدِل قيمة fingerprint بقيمتك الخاصة. في ما يلي مثال على نتيجة:

android:apk-key-hash:kffL-daBUxvHpY-4M8yhTavt5QnFEI2LsexohxrGPYU

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

حفظ كلمة مرور مستخدم

إذا قدّم المستخدم اسم مستخدم وكلمة مرور لعملية المصادقة في تطبيقك، يمكنك تسجيل بيانات اعتماد المستخدم التي يمكن استخدامها لمصادقة المستخدم. لإجراء ذلك، أنشئ عنصر CreatePasswordRequest:

Kotlin

fun registerPassword(username: String, password: String) {
    // Initialize a CreatePasswordRequest object.
    val createPasswordRequest =
            CreatePasswordRequest(id = username, password = password)

    // Create credential and handle result.
    coroutineScope.launch {
        try {
            val result =
                credentialManager.createCredential(
                    // Use an activity based context to avoid undefined
                    // system UI launching behavior.
                    activityContext,
                    createPasswordRequest
                  )
            handleRegisterPasswordResult(result)
        } catch (e: CreateCredentialException) {
            handleFailure(e)
        }
    }
}

Java

void registerPassword(String username, String password) {
    // Initialize a CreatePasswordRequest object.
    CreatePasswordRequest createPasswordRequest =
        new CreatePasswordRequest(username, password);

    // Register the username and password.
    credentialManager.createCredentialAsync(
        // Use an activity-based context to avoid undefined
        // system UI launching behavior
        requireActivity(),
        createPasswordRequest,
        cancellationSignal,
        executor,
        new CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>() {
            @Override
            public void onResult(CreateCredentialResponse result) {
                handleResult(result);
            }

            @Override
            public void onError(CreateCredentialException e) {
                handleFailure(e);
            }
        }
    );
}

دعم استرداد بيانات الاعتماد

إذا لم يعُد بإمكان المستخدم الوصول إلى الجهاز الذي كان يخزّن فيه بيانات الاعتماد، قد يحتاج إلى استردادها من نسخة احتياطية آمنة على الإنترنت. للاطّلاع على معلومات إضافية حول كيفية تفعيل عملية استرداد بيانات الاعتماد هذه، يُرجى قراءة القسم بعنوان "استرداد إمكانية الوصول أو إضافة أجهزة جديدة" في مشاركة المدونة هذه: أمان مفاتيح المرور في "مدير كلمات المرور من Google".

إتاحة أدوات إدارة كلمات المرور التي تتضمّن عناوين URL معروفة لنقاط نهاية مفاتيح المرور

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

  1. بالنسبة إلى جهة اعتماد في https://example.com، التي لديها موقع إلكتروني إلى جانب تطبيقات Android وiOS، سيكون عنوان URL المعروف هو https://example.com/.well-known/passkey-endpoints.
  2. عندما يتم الاستعلام عن عنوان URL، يجب أن تستخدم الاستجابة المخطط التالي

    {
      "enroll": "https://example.com/account/manage/passkeys/create"
      "manage": "https://example.com/account/manage/passkeys"
    }
    
  3. لفتح هذا الرابط مباشرةً في تطبيقك بدلاً من فتحه على الويب، استخدِم روابط تطبيقات Android.

  4. يمكن العثور على مزيد من التفاصيل في الشرح المتعلّق بموضوع عنوان URL المعروف لنقاط نهاية مفتاح المرور على GitHub.

مساعدة المستخدمين في إدارة مفاتيح مرورهم من خلال توضيح مقدّم الخدمة الذي أنشأها

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

على سبيل المثال، إذا أنشأ المستخدم مفتاح مرور على جهاز يعمل بنظام التشغيل Android باستخدام "مدير كلمات المرور في Google"، سيتلقّى الجهة المحظورة بعد ذلك معرّف AAGUID الذي يبدو على النحو التالي: "ea9b8d66-4d01-1d21-3ce4-b6b48cb575d4". يمكن للطرف الموثوق به إضافة تعليقات توضيحية إلى مفتاح المرور في قائمة مفاتيح المرور للإشارة إلى أنّه تم إنشاؤه باستخدام "مدير كلمات المرور في Google".

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

يمكنك الاطّلاع على مزيد من المعلومات عن دمج AAGUID.

تحديد المشاكل الشائعة وحلّها

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

مصادر إضافية

لمزيد من المعلومات عن واجهة برمجة التطبيقات Credential Manager API ومفاتيح المرور، اطّلِع على الموارد التالية: