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

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

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

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

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

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

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

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

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

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

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

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

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

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

يتوفّر "مدير بيانات الاعتماد" على الإصدار 4.4 من نظام التشغيل Android (المستوى 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 لحفظ بيانات الاعتماد.

لاستخدام سمة 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"
  }
}

ضبط القيم في authenticatorFilter

لا يمكن ضبط المَعلمة 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 التطبيق أو الموقع الإلكتروني الذي ينبع منه الطلب، وتستخدمه مفاتيح المرور للحماية من هجمات التصيّد الاحتيالي. يجب أن يتحقق خادم تطبيقك من العميل. مصدر البيانات استنادًا إلى قائمة مسموح بها من التطبيقات والمواقع الإلكترونية الموافَق عليها إذا تلقّى الخادم طلبًا من تطبيق أو موقع إلكتروني من مصدر غير معروف، يجب رفض الطلب.

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

بالنسبة إلى تطبيقات Android، يضبط وكيل المستخدم origin تلقائيًا على توقيع تطبيق الاتصال. يجب التحقق من هذا التوقيع كتطابق على خادمك مع والتحقق من صحة المتصل بواجهة برمجة تطبيقات مفتاح المرور. origin Android هو معرّف موارد منتظم (URI) مشتق من تجزئة 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. يوضح مثال بايثون هذا كيفية تحويل تنسيق التجزئة إلى تنسيق التنسيق السداسي العشري المفصول بنقطتين:

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 بموفِّر مفاتيح مرور، يمكن للجهات المحظورة استخدام مصدر من المنتدى. هو مستودع للمعرّفات AAGUID. ابحث عن AAGUID على للعثور على اسم موفِّر مفتاح المرور ورمزه.

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

تحديد الأخطاء الشائعة وحلّها

يعرض الجدول التالي العديد من رموز الأخطاء والأوصاف الشائعة، ويقدّم بعض المعلومات عن أسبابها:

رمز الخطأ ووصفه السبب
عند تعذُّر تسجيل الدخول عند البدء: 16: تم حظر المتصل مؤقتًا بسبب انتهاك بسبب عدد كبير جدًا من رسائل المطالبة بتسجيل الدخول التي تم إلغاؤها.

في حال مواجهة فترة توقُّف مدتها 24 ساعة أثناء التطوير، يجب يمكنك إعادة ضبطها من خلال محو "خدمات Google Play" مساحة تخزين التطبيقات.

لإيقاف فترة التوقّف هذه على جهاز اختبار أو محاكي، انتقِل إلى إلى تطبيق Dialer وأدخل الرمز التالي: *#*#66382723#*#* يمحو تطبيق Dialer جميع الإدخالات وقد ، ولكن ليست هناك رسالة تأكيد.

عند تعذُّر تسجيل الدخول عند البدء: 8: خطأ داخلي غير معروف.
  1. لم يتم إعداد الجهاز بشكلٍ صحيح باستخدام حساب Google.
  2. يتم إنشاء ملف JSON الخاص بمفتاح المرور بشكل غير صحيح.
CreatePublicKeyCredentialDomException: The incoming request cannot be validated رقم تعريف حزمة التطبيق غير مسجَّل في خادمك. تحقّق من ذلك في عملية الدمج من جهة الخادم.
CreateCredentialUnknownException: أثناء حفظ كلمة المرور، تم العثور على ردّ خطأ في كلمة المرور من نقرة واحدة 16: تخطّي حفظ كلمة المرور لأنّه من المحتمل أن يُطلَب من المستخدِم استخدام ميزة "الملء التلقائي" من Android لا يحدث هذا الخطأ إلا على الإصدار 13 من نظام التشغيل Android والإصدارات الأقدم، ولا يحدث إلا إذا كانت Google هي مزوّد ميزة "الملء التلقائي". في هذه الحالة، تظهر للمستخدمين رسالة تطلب منهم حفظ كلمة المرور من ميزة "الملء التلقائي" ويتم حفظها في "مدير كلمات المرور في Google". يُرجى العلم أنّ بيانات الاعتماد المحفوظة باستخدام ميزة "الملء التلقائي من Google" تتم مشاركتها باتجاهَين مع واجهة برمجة التطبيقات Credential Manager API. نتيجةً لذلك، يمكن تجاهل هذا الخطأ بأمان.

مصادر إضافية

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