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

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

قبل البدء

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

الانضمام إلى برنامج Apple Developer Program

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1. يمكنك إنشاء مثيل لـ OAuthProvider باستخدام أداة الإنشاء الخاصة به باستخدام معرّف الموفّر apple.com:

    Kotlin+KTX

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

    Java

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

    Kotlin+KTX

    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+KTX

    // 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+KTX

    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+KTX

    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، لا توفر Apple عنوان URL لصورة.

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

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

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

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

Kotlin+KTX

// 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+KTX

// 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+KTX

    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 من nonce كسلسلة سداسية:

    Kotlin+KTX

    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 من nonce مع طلب تسجيل الدخول، والتي لن تجرِها Apple بدون تغيير في الاستجابة. يتحقّق Firebase من صحة الردّ من خلال تجزئة رقم التعريف الأصلي الأصلي ومقارنتها بالقيمة التي حدّدتها Apple.

  2. يمكنك بدء عملية تسجيل الدخول إلى Apple باستخدام مكتبة OAuth أو طريقة أخرى. تأكد من تضمين nonce المجزأ كمعلمة في طلبك.

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

    Kotlin+KTX

    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+KTX

    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 API" لإبطال الرموز المميّزة للمستخدم.

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

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

  2. يمكنك الحصول على رمز الدخول لمزوّد خدمة Apple.

    Kotlin+KTX

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

    Java

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

    Kotlin+KTX

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

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

    لتسجيل خروج المستخدم، يمكنك الاتصال على signOut:

    Kotlin+KTX

    Firebase.auth.signOut()

    Java

    FirebaseAuth.getInstance().signOut();