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

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

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

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

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

إرسال ملاحظات

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

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

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

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

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

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

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

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

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

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

Kotlin

dependencies {
    implementation("androidx.credentials:credentials:1.3.0-alpha03")

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

رائع

dependencies {
    implementation "androidx.credentials:credentials:1.3.0-alpha03"

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

الاحتفاظ بالفئات في ملف 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 باسترداد /.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)

تحديد حقول بيانات الاعتماد

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

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

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

ضبط قيم authenticatorAnimation

لا يمكن ضبط المعلَمة 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.

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

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

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

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

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

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

مراجع إضافية

لمزيد من المعلومات عن واجهة برمجة تطبيقات "إدارة بيانات الاعتماد" ومفاتيح المرور، يمكنك الاطّلاع على المراجع التالية: