نقل البيانات من FIDO2 إلى "مدير بيانات الاعتماد"

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

أسباب نقل البيانات من FIDO2 إلى "مدير بيانات الاعتماد"

في معظم الحالات، عليك نقل موفّر مصادقة تطبيق Android إلى "مدير بيانات الاعتماد". تتضمن أسباب نقل البيانات إلى "مدير بيانات الاعتماد" ما يلي:

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

لبدء عملية نقل البيانات من FIDO2 إلى "مدير بيانات الاعتماد"، اتّبِع الخطوات أدناه.

تحديث التبعيات

  1. يُرجى تحديث مكوّن Kotlin الإضافي في إصدار Build.gradle لمشروعك إلى الإصدار 1.8.10 أو الإصدارات الأحدث.

    plugins {
      //…
        id 'org.jetbrains.kotlin.android' version '1.8.10' apply false
      //…
    }
    
  2. في version.gradle لمشروعك، يمكنك تعديل التبعيات لاستخدام "مدير بيانات الاعتماد" و"مصادقة خدمات Play".

    dependencies {
      // ...
      // Credential Manager:
      implementation 'androidx.credentials:credentials:<latest-version>'
    
      // Play Services Authentication:
      // Optional - needed for credentials support from play services, for devices running
      // Android 13 and below:
      implementation 'androidx.credentials:credentials-play-services-auth:<latest-version>'
      // ...
    }
    
  3. استبدل تهيئة FIDO بتهيئة مدير بيانات الاعتماد. أضِف هذا البيان في الفئة التي تستخدمها لإنشاء مفاتيح المرور وطرق تسجيل الدخول إليها:

    val credMan = CredentialManager.create(context)
    

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

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

الشكل 1. يوضّح هذا الشكل طريقة تبادل البيانات بين التطبيق والخادم عند إنشاء مفتاح مرور باستخدام "مدير بيانات الاعتماد".
  1. للحصول على المعلَمات اللازمة التي يتم إرسالها إلى الطريقة createCredential() أثناء إنشاء مفتاح المرور، أضِف name("residentKey").value("required") على النحو الموضَّح في مواصفات WebAuthn) إلى طلب خادم registerRequest().

    suspend fun registerRequest(sessionId: String ... {
        // ...
        .method("POST", jsonRequestBody {
            name("attestation").value("none")
            name("authenticatorSelection").objectValue {
                name("residentKey").value("required")
            }
        }).build()
        // ...
    }
    
  2. اضبط النوع return لـ registerRequest() وجميع الدوال الفرعية على JSONObject.

    suspend fun registerRequest(sessionId: String): ApiResult<JSONObject> {
        val call = client.newCall(
            Request.Builder()
                .url("$BASE_URL/<your api url>")
                .addHeader("Cookie", formatCookie(sessionId))
                .method("POST", jsonRequestBody {
                    name("attestation").value("none")
                    name("authenticatorSelection").objectValue {
                        name("authenticatorAttachment").value("platform")
                        name("userVerification").value("required")
                        name("residentKey").value("required")
                    }
                }).build()
        )
        val response = call.await()
        return response.result("Error calling the api") {
            parsePublicKeyCredentialCreationOptions(
                body ?: throw ApiException("Empty response from the api call")
            )
        }
    }
    
  3. أزِل بأمان أي طرق تعالج مشغّل الأهداف واستدعاءات نتائج النشاط من العرض.

  4. بما أنّ السمة registerRequest() تعرض الآن JSONObject، لن تحتاج إلى إنشاء PendingIntent. استبدِل الغرض المعروض بـ JSONObject. عليك تعديل طلبات مشغِّل التطبيقات لطلب createCredential() من واجهة برمجة تطبيقات مدير بيانات الاعتماد. طلب بيانات من واجهة برمجة التطبيقات createCredential()

    suspend fun createPasskey(
        activity: Activity,
        requestResult: JSONObject
        ): CreatePublicKeyCredentialResponse? {
            val request = CreatePublicKeyCredentialRequest(requestResult.toString())
            var response: CreatePublicKeyCredentialResponse? = null
            try {
                response = credMan.createCredential(
                    request as CreateCredentialRequest,
                    activity
                ) as CreatePublicKeyCredentialResponse
            } catch (e: CreateCredentialException) {
    
                showErrorAlert(activity, e)
    
                return null
            }
            return response
        }
    
  5. بعد نجاح الاستدعاء، أرسل الاستجابة إلى الخادم. يتشابه الطلب والاستجابة لهذه المكالمة مع تنفيذ FIDO2، لذلك لا يلزم إجراء أي تغييرات.

المصادقة باستخدام مفاتيح المرور

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

الشكل 2. مسار مصادقة مفتاح المرور في "مدير بيانات الاعتماد"
  1. يتطابق طلب تسجيل الدخول إلى الخادم للحصول على المعلومات اللازمة التي يتم إرسالها إلى طلب getCredential() مع عملية تنفيذ FIDO2. ولا يلزم إجراء أي تغييرات.
  2. على غرار استدعاء طلب التسجيل، تكون الاستجابة المعروضة بتنسيق JSONObject.

    /**
     * @param sessionId The session ID to be used for the sign-in.
     * @param credentialId The credential ID of this device.
     * @return a JSON object.
     */
    suspend fun signinRequest(): ApiResult<JSONObject> {
        val call = client.newCall(Builder().url(buildString {
            append("$BASE_URL/signinRequest")
        }).method("POST", jsonRequestBody {})
            .build()
        )
        val response = call.await()
        return response.result("Error calling /signinRequest") {
            parsePublicKeyCredentialRequestOptions(
                body ?: throw ApiException("Empty response from /signinRequest")
            )
        }
    }
    
    /**
     * @param sessionId The session ID to be used for the sign-in.
     * @param response The JSONObject for signInResponse.
     * @param credentialId id/rawId.
     * @return A list of all the credentials registered on the server,
     * including the newly-registered one.
     */
    suspend fun signinResponse(
        sessionId: String, response: JSONObject, credentialId: String
        ): ApiResult<Unit> {
    
            val call = client.newCall(
                Builder().url("$BASE_URL/signinResponse")
                    .addHeader("Cookie",formatCookie(sessionId))
                    .method("POST", jsonRequestBody {
                        name("id").value(credentialId)
                        name("type").value(PUBLIC_KEY.toString())
                        name("rawId").value(credentialId)
                        name("response").objectValue {
                            name("clientDataJSON").value(
                                response.getString("clientDataJSON")
                            )
                            name("authenticatorData").value(
                                response.getString("authenticatorData")
                            )
                            name("signature").value(
                                response.getString("signature")
                            )
                            name("userHandle").value(
                                response.getString("userHandle")
                            )
                        }
                    }).build()
            )
            val apiResponse = call.await()
            return apiResponse.result("Error calling /signingResponse") {
            }
        }
    
  3. قم بأمان بإزالة أي طرق تتعامل مع مشغّل الأهداف واستدعاءات نتائج النشاط من عرضك.

  4. بما أنّ السمة signInRequest() تعرض الآن JSONObject، لن تحتاج إلى إنشاء PendingIntent. استبدِل الغرض الذي تم إرجاعه بـ JSONObject، واستدعِ getCredential() من طرق واجهة برمجة التطبيقات.

    suspend fun getPasskey(
        activity: Activity,
        creationResult: JSONObject
        ): GetCredentialResponse? {
            Toast.makeText(
                activity,
                "Fetching previously stored credentials",
                Toast.LENGTH_SHORT)
                .show()
            var result: GetCredentialResponse? = null
            try {
                val request= GetCredentialRequest(
                    listOf(
                        GetPublicKeyCredentialOption(
                            creationResult.toString(),
                            null
                        ),
                        GetPasswordOption()
                    )
                )
                result = credMan.getCredential(activity, request)
                if (result.credential is PublicKeyCredential) {
                    val publicKeycredential = result.credential as PublicKeyCredential
                    Log.i("TAG", "Passkey ${publicKeycredential.authenticationResponseJson}")
                    return result
                }
            } catch (e: Exception) {
                showErrorAlert(activity, e)
            }
            return result
        }
    
  5. وبمجرد نجاح الاتصال، أرسل الرد إلى الخادم للتحقق من صحة المستخدم ومصادقته. وتتشابه معلمات الطلب والاستجابة لاستدعاء واجهة برمجة التطبيقات هذا مع تنفيذ FIDO2، لذا لا حاجة إلى إجراء أي تغييرات.

مراجع إضافية