المصادقة باستخدام Apple على Android

يمكنك السماح للمستخدمين بالمصادقة باستخدام Firebase باستخدام معرّف Apple من خلال استخدام حزمة تطوير البرامج (SDK) لمنصّة Firebase لتنفيذ عملية تسجيل الدخول من خلال بروتوكول OAuth 2.0 من البداية إلى النهاية.

قبل البدء

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

الانضمام إلى "برنامج المطوّرين من Apple"

لا يمكن لأعضاء برنامج Apple Developer فقط ضبط إعدادات ميزة "تسجيل الدخول باستخدام حساب Apple".

ضبط ميزة "تسجيل الدخول باستخدام حساب Apple"

على موقع Apple Developer الإلكتروني، اتّبِع الخطوات التالية:

  1. اربط موقعك الإلكتروني بتطبيقك كما هو موضّح في القسم الأول من ضبط ميزة "تسجيل الدخول باستخدام حساب Apple" على الويب. عند الطلب، سجِّل عنوان URL التالي كعنوان URL لصفحة العودة:

    https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler

    يمكنك الحصول على رقم تعريف مشروعك على Firebase في Firebase صفحة إعدادات وحدة التحكّم.

    عند الانتهاء، سجِّل رقم تعريف الخدمة الجديد الذي ستحتاج إليه في القسم التالي.

  2. أنشئ مفتاحًا خاصًا لميزة "تسجيل الدخول باستخدام حساب Apple". ستحتاج إلى المفتاح الخاص الجديد ومعرّف المفتاح في القسم التالي.
  3. إذا كنت تستخدم أيًا من ميزات Firebase Authentication التي تُرسِل رسائل إلكترونية إلى المستخدمين، بما في ذلك تسجيل الدخول باستخدام رابط البريد الإلكتروني وإثبات ملكية عنوان البريد الإلكتروني وإلغاء تغيير الحساب وغيرها، عليك ضبط خدمة Apple الخاصة لترحيل البريد الإلكتروني وتسجيل noreply@YOUR_FIREBASE_PROJECT_ID.firebaseapp.com (أو نطاق نموذج البريد الإلكتروني المخصّص) حتى تتمكّن Apple من إعادة توجيه الرسائل الإلكترونية المُرسَلة من Firebase Authentication إلى عناوين البريد الإلكتروني المجهولة الهوية من Apple.

تفعيل Apple كموفّر لميزة تسجيل الدخول

  1. أضِف Firebase إلى مشروع Android. احرص على تسجيل توقيع SHA-1 لتطبيقك عند إعداده في وحدة تحكّم Firebase.
  2. في وحدة تحكّم Firebase، افتح قسم Auth. في علامة التبويب طريقة تسجيل الدخول، فعِّل موفِّر Apple. حدِّد رقم تعريف الخدمة الذي أنشأته في القسم السابق. في قسم إعداد مسار رمز OAuth، حدِّد رقم تعريف فريقك في Apple والمفتاح الخاص ورقم تعريف المفتاح اللذين أنشأتهما في القسم السابق.

الالتزام بمتطلبات Apple المتعلّقة بالبيانات المخفية الهوية

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

ويشمل ذلك الحصول على أي موافقة مطلوبة من المستخدم قبل ربط أي معلومات شخصية تحدّد الهوية مباشرةً بمعرّف Apple مجمّع. عند استخدام Firebase Authentication، قد يشمل ذلك الخطوات التالية:

  • ربط عنوان بريد إلكتروني بمعرّف Apple مخفي الهوية أو العكس
  • ربط رقم هاتف بمعرّف Apple مجهول الهوية أو العكس
  • ربط بيانات اعتماد اجتماعية غير مجهولة الهوية (Facebook أو Google أو غير ذلك) بحساب Apple ID مُخفّى الهوية أو العكس

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

التعامل مع مسار تسجيل الدخول باستخدام حزمة تطوير البرامج (SDK) لمنصّة Firebase

على أجهزة Android، تتمثّل أسهل طريقة لمصادقة المستخدمين باستخدام Firebase من خلال حسابات Apple الخاصة بهم في معالجة عملية تسجيل الدخول بالكامل باستخدام حزمة تطوير البرامج (SDK) لمنصّة Firebase على Android.

لمعالجة عملية تسجيل الدخول باستخدام حزمة تطوير البرامج (SDK) لنظام التشغيل Android من Firebase، اتّبِع الخطوات التالية:

  1. أنشئ مثيلًا من OAuthProvider باستخدام أداة الإنشاء مع معرّف موفِّر الخدمة apple.com:

    Kotlin

    val provider = OAuthProvider.newBuilder("apple.com")
    

    Java

    OAuthProvider.Builder provider = OAuthProvider.newBuilder("apple.com");
    
  2. اختياري: حدِّد نطاقات OAuth 2.0 إضافية غير النطاق التلقائي الذي تريده من موفِّر المصادقة.

    Kotlin

    provider.setScopes(arrayOf("email", "name"))
    

    Java

    List<String> scopes =
        new ArrayList<String>() {
          {
            add("email");
            add("name");
          }
        };
    provider.setScopes(scopes);
    

    بشكلٍ تلقائي، عند تفعيل حساب واحد لكل عنوان بريد إلكتروني، تطلب Firebase نطاقات البريد الإلكتروني والاسم. في حال تغيير هذا الإعداد إلى حسابات متعدّدة لكل عنوان بريد إلكتروني، لن تطلب Firebase أي نطاقات من Apple ما لم تحدّدها.

  3. اختياري: إذا كنت تريد عرض شاشة تسجيل الدخول في Apple بلغة غير الإنجليزية، اضبط المَعلمة locale. اطّلِع على تسجيل الدخول باستخدام مستندات Apple لمعرفة اللغات المتاحة.

    Kotlin

    // Localize the Apple authentication screen in French.
    provider.addCustomParameter("locale", "fr")
    

    Java

    // Localize the Apple authentication screen in French.
    provider.addCustomParameter("locale", "fr");
    
  4. يمكنك المصادقة باستخدام Firebase باستخدام كائن مزوّد OAuth. يُرجى العلم أنّه على عكس عمليات FirebaseAuth الأخرى، سيؤدي هذا الإجراء إلى التحكّم في واجهة المستخدم من خلال فتح علامة تبويب مخصّصة في Chrome. نتيجةً لذلك، لا تُشير إلى نشاطك في OnSuccessListener وOnFailureListener اللذَين يتم إرفاقهما لأنّه سيتم فصلهما على الفور عندما تبدأ العملية واجهة المستخدم.

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

    للتحقّق مما إذا كانت هناك نتيجة في انتظار المراجعة، يُرجى الاتصال على getPendingAuthResult():

    Kotlin

    val pending = auth.pendingAuthResult
    if (pending != null) {
        pending.addOnSuccessListener { authResult ->
            Log.d(TAG, "checkPending:onSuccess:$authResult")
            // Get the user profile with authResult.getUser() and
            // authResult.getAdditionalUserInfo(), and the ID
            // token from Apple with authResult.getCredential().
        }.addOnFailureListener { e ->
            Log.w(TAG, "checkPending:onFailure", e)
        }
    } else {
        Log.d(TAG, "pending: null")
    }
    

    Java

    mAuth = FirebaseAuth.getInstance();
    Task<AuthResult> pending = mAuth.getPendingAuthResult();
    if (pending != null) {
        pending.addOnSuccessListener(new OnSuccessListener<AuthResult>() {
            @Override
            public void onSuccess(AuthResult authResult) {
                Log.d(TAG, "checkPending:onSuccess:" + authResult);
                // Get the user profile with authResult.getUser() and
                // authResult.getAdditionalUserInfo(), and the ID
                // token from Apple with authResult.getCredential().
            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                Log.w(TAG, "checkPending:onFailure", e);
            }
        });
    } else {
        Log.d(TAG, "pending: null");
    }
    

    إذا لم تكن هناك نتيجة في انتظار المراجعة، ابدأ عملية تسجيل الدخول من خلال الاتصال بالرقم startActivityForSignInWithProvider():

    Kotlin

    auth.startActivityForSignInWithProvider(this, provider.build())
            .addOnSuccessListener { authResult ->
                // Sign-in successful!
                Log.d(TAG, "activitySignIn:onSuccess:${authResult.user}")
                val user = authResult.user
                // ...
            }
            .addOnFailureListener { e ->
                Log.w(TAG, "activitySignIn:onFailure", e)
            }
    

    Java

    mAuth.startActivityForSignInWithProvider(this, provider.build())
            .addOnSuccessListener(
                    new OnSuccessListener<AuthResult>() {
                        @Override
                        public void onSuccess(AuthResult authResult) {
                            // Sign-in successful!
                            Log.d(TAG, "activitySignIn:onSuccess:" + authResult.getUser());
                            FirebaseUser user = authResult.getUser();
                            // ...
                        }
                    })
            .addOnFailureListener(
                    new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            Log.w(TAG, "activitySignIn:onFailure", e);
                        }
                    });
    

    على عكس موفّري الخدمات الآخرين المتوافقين مع Firebase Auth، لا يوفّر Apple عنوان URL للصورة.

    بالإضافة إلى ذلك، عندما يختار المستخدم عدم مشاركة عنوان بريده الإلكتروني مع التطبيق، تهيئ Apple عنوان بريد إلكتروني فريدًا لذلك المستخدم (على شكل xyz@privaterelay.appleid.com)، وتشاركه مع تطبيقك. إذا أعددت خدمة إعادة توجيه البريد الإلكتروني الخاص، ستعيد Apple توجيه الرسائل الإلكترونية المُرسَلة إلى العنوان المجهول الهوية إلى عنوان البريد الإلكتروني الحقيقي للمستخدم.

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

إعادة المصادقة وربط الحساب

يمكن استخدام النمط نفسه مع startActivityForReauthenticateWithProvider() الذي يمكنك استخدامه لاسترداد بيانات اعتماد جديدة للعمليات الحسّاسة التي تتطلّب تسجيل الدخول مؤخرًا:

Kotlin

// The user is already signed-in.
val firebaseUser = auth.getCurrentUser()

firebaseUser
    .startActivityForReauthenticateWithProvider(/* activity= */ this, provider.build())
    .addOnSuccessListener( authResult -> {
        // User is re-authenticated with fresh tokens and
        // should be able to perform sensitive operations
        // like account deletion and email or password
        // update.
    })
    .addOnFailureListener( e -> {
        // Handle failure.
    })

Java

// The user is already signed-in.
FirebaseUser firebaseUser = mAuth.getCurrentUser();

firebaseUser
    .startActivityForReauthenticateWithProvider(/* activity= */ this, provider.build())
    .addOnSuccessListener(
        new OnSuccessListener<AuthResult>() {
          @Override
          public void onSuccess(AuthResult authResult) {
            // User is re-authenticated with fresh tokens and
            // should be able to perform sensitive operations
            // like account deletion and email or password
            // update.
          }
        })
    .addOnFailureListener(
        new OnFailureListener() {
          @Override
          public void onFailure(@NonNull Exception e) {
            // Handle failure.
          }
        });

ويمكنك استخدام linkWithCredential() لربط مقدّمي هوية مختلفين ب الحسابات الحالية.

تشترط Apple الحصول على موافقة صريحة من المستخدمين قبل ربط حساباتهم على Apple ببيانات أخرى.

على سبيل المثال، لربط حساب Facebook بحساب Firebase الحالي، استخدِم رمز هجمة الوصول الذي حصلت عليه من تسجيل المستخدِم الدخول إلى Facebook:

Kotlin

// Initialize a Facebook credential with a Facebook access token.
val credential = FacebookAuthProvider.getCredential(token.getToken())

// Assuming the current user is an Apple user linking a Facebook provider.
mAuth.getCurrentUser().linkWithCredential(credential)
    .addOnCompleteListener(this, task -> {
        if (task.isSuccessful()) {
          // Facebook credential is linked to the current Apple user.
          // The user can now sign in to the same account
          // with either Apple or Facebook.
        }
      });

Java

// Initialize a Facebook credential with a Facebook access token.
AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());

// Assuming the current user is an Apple user linking a Facebook provider.
mAuth.getCurrentUser().linkWithCredential(credential)
    .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
      @Override
      public void onComplete(@NonNull Task<AuthResult> task) {
        if (task.isSuccessful()) {
          // Facebook credential is linked to the current Apple user.
          // The user can now sign in to the same account
          // with either Apple or Facebook.
        }
      }
    });

إجراء متقدّم: التعامل مع مسار تسجيل الدخول يدويًا

يمكنك أيضًا المصادقة باستخدام Firebase باستخدام حساب Apple من خلال معالجة مسار تسجيل الولوج إما باستخدام حزمة تطوير البرامج (SDK) لتسجيل الدخول باستخدام JavaScript من Apple أو من خلال إنشاء مسار OAuth يدويًا أو باستخدام مكتبة OAuth مثل AppAuth.

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

    يمكنك إنشاء عدد عشوائي آمن من الناحية التشفيرية على Android باستخدام ‎ SecureRandom، كما هو موضّح في المثال التالي:

    Kotlin

    private fun generateNonce(length: Int): String {
        val generator = SecureRandom()
    
        val charsetDecoder = StandardCharsets.US_ASCII.newDecoder()
        charsetDecoder.onUnmappableCharacter(CodingErrorAction.IGNORE)
        charsetDecoder.onMalformedInput(CodingErrorAction.IGNORE)
    
        val bytes = ByteArray(length)
        val inBuffer = ByteBuffer.wrap(bytes)
        val outBuffer = CharBuffer.allocate(length)
        while (outBuffer.hasRemaining()) {
            generator.nextBytes(bytes)
            inBuffer.rewind()
            charsetDecoder.reset()
            charsetDecoder.decode(inBuffer, outBuffer, false)
        }
        outBuffer.flip()
        return outBuffer.toString()
    }
    

    Java

    private String generateNonce(int length) {
        SecureRandom generator = new SecureRandom();
    
        CharsetDecoder charsetDecoder = StandardCharsets.US_ASCII.newDecoder();
        charsetDecoder.onUnmappableCharacter(CodingErrorAction.IGNORE);
        charsetDecoder.onMalformedInput(CodingErrorAction.IGNORE);
    
        byte[] bytes = new byte[length];
        ByteBuffer inBuffer = ByteBuffer.wrap(bytes);
        CharBuffer outBuffer = CharBuffer.allocate(length);
        while (outBuffer.hasRemaining()) {
            generator.nextBytes(bytes);
            inBuffer.rewind();
            charsetDecoder.reset();
            charsetDecoder.decode(inBuffer, outBuffer, false);
        }
        outBuffer.flip();
        return outBuffer.toString();
    }
    

    بعد ذلك، احصل على تجزئة SHA246 للقيمة العشوائية بصفتها سلسلة سداسية عشرية:

    Kotlin

    private fun sha256(s: String): String {
        val md = MessageDigest.getInstance("SHA-256")
        val digest = md.digest(s.toByteArray())
        val hash = StringBuilder()
        for (c in digest) {
            hash.append(String.format("%02x", c))
        }
        return hash.toString()
    }
    

    Java

    private String sha256(String s) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] digest = md.digest(s.getBytes());
        StringBuilder hash = new StringBuilder();
        for (byte c: digest) {
            hash.append(String.format("%02x", c));
        }
        return hash.toString();
    }
    

    سترسل تجزئة SHA256 للرمز المؤقت مع طلب تسجيل الدخول، وستحول Apple هذا الرمز بدون تغيير في الاستجابة. تتحقّق Firebase من الاستجابة من خلال تجزئة المفتاح المؤقت الأصلي ومقارنته بالقيمة التي أرسلتها Apple.

  2. ابدأ مسار تسجيل الدخول في Apple باستخدام مكتبة OAuth أو طريقة أخرى. احرص على تضمين المفتاح المؤقت المجزّأ كمَعلمة في طلبك.

  3. بعد تلقّي ردّ من Apple، احصل على رمز التعريف من الردّ واستخدِمه مع المفتاح المؤقت غير المحوَّل إلى تجزئة لإنشاء AuthCredential:

    Kotlin

    val credential =  OAuthProvider.newCredentialBuilder("apple.com")
        .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce)
        .build()
    

    Java

    AuthCredential credential =  OAuthProvider.newCredentialBuilder("apple.com")
        .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce)
        .build();
    
  4. يمكنك المصادقة باستخدام Firebase باستخدام بيانات اعتماد Firebase:

    Kotlin

    auth.signInWithCredential(credential)
          .addOnCompleteListener(this) { task ->
              if (task.isSuccessful) {
                // User successfully signed in with Apple ID token.
                // ...
              }
          }
    

    Java

    mAuth.signInWithCredential(credential)
        .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
          @Override
          public void onComplete(@NonNull Task<AuthResult> task) {
            if (task.isSuccessful()) {
              // User successfully signed in with Apple ID token.
              // ...
            }
          }
        });
    

في حال نجاح طلب signInWithCredential، يمكنك استخدام الطريقة getCurrentUser للحصول على بيانات حساب المستخدم.

إبطال الرمز المميّز

تشترط شركة Apple أن تتيح التطبيقات التي تتيح إنشاء الحسابات للمستخدمين بدء عملية حذف حساباتهم من داخل التطبيق، كما هو موضّح في إرشادات مراجعة التطبيقات في App Store.

بالإضافة إلى ذلك، على التطبيقات التي تتيح ميزة "تسجيل الدخول باستخدام حساب Apple" استخدام ميزة "تسجيل الدخول باستخدام حساب Apple" REST لإبطال الرموز المميّزة للمستخدم.

لاستيفاء هذا الشرط، عليك تنفيذ الخطوات التالية:

  1. استخدِم طريقة startActivityForSignInWithProvider() لتسجيل الدخول باستخدام Apple والحصول على AuthResult.

  2. الحصول على رمز دخول موفِّر Apple

    Kotlin

    val oauthCredential: OAuthCredential =  authResult.credential
    val accessToken = oauthCredential.accessToken
    

    Java

    OAuthCredential oauthCredential = (OAuthCredential) authResult.getCredential();
    String accessToken = oauthCredential.getAccessToken();
    
  3. يمكنك إلغاء الرمز المميّز باستخدام واجهة برمجة التطبيقات revokeAccessToken.

    Kotlin

    mAuth.revokeAccessToken(accessToken)
      .addOnCompleteListener(this) { task ->
        if (task.isSuccessful) {
          // Access token successfully revoked
          // for the user ...
        }
    }
    

    Java

    mAuth.revokeAccessToken(accessToken)
        .addOnCompleteListener(this, new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
              if (task.isSuccessful()) {
                // Access token successfully revoked
                // for the user ...
              }
            }
      });
    
  1. أخيرًا، احذِف حساب المستخدم (وجميع البيانات المرتبطة به).

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

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

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

    • في Firebase Realtime Database وCloud Storage قواعد الأمان، يمكنك الحصول على معرّف المستخدم الفريد للمستخدم الذي سجّل الدخول من متغيّر auth، واستخدامه للتحكّم في البيانات التي يمكن للمستخدم الوصول إليها.

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

    لتسجيل خروج مستخدم، اتصل بالرقم signOut:

    Kotlin

    Firebase.auth.signOut()

    Java

    FirebaseAuth.getInstance().signOut();