احراز هویت با استفاده از اپل در اندروید

می‌توانید با استفاده از Firebase SDK به کاربران خود اجازه دهید با Firebase با استفاده از Apple ID خود احراز هویت کنند تا جریان ورود به سیستم OAuth 2.0 را انجام دهند.

قبل از شروع

برای ورود کاربرانی که از اپل استفاده می کنند، ابتدا Sign In with Apple را در سایت برنامه نویس اپل پیکربندی کنید، سپس Apple را به عنوان ارائه دهنده ورود به سیستم برای پروژه Firebase خود فعال کنید.

به برنامه توسعه دهندگان اپل بپیوندید

ورود به سیستم با Apple فقط توسط اعضای برنامه توسعه دهنده Apple قابل پیکربندی است.

پیکربندی ورود با اپل

در سایت Apple Developer موارد زیر را انجام دهید:

  1. همانطور که در بخش اول پیکربندی ورود با اپل برای وب توضیح داده شده است، وب سایت خود را با برنامه خود مرتبط کنید. وقتی از شما خواسته شد، URL زیر را به عنوان URL بازگشتی ثبت کنید:

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

    می‌توانید ID پروژه Firebase خود را در صفحه تنظیمات کنسول Firebase دریافت کنید.

    وقتی کارتان تمام شد، شناسه سرویس جدید خود را که در بخش بعدی به آن نیاز دارید، یادداشت کنید.

  2. با کلید خصوصی اپل یک ورود به سیستم ایجاد کنید . در بخش بعدی به کلید خصوصی و شناسه کلید جدید خود نیاز دارید.
  3. اگر از یکی از ویژگی‌های Firebase Authentication که برای کاربران ایمیل ارسال می‌کند، از جمله ورود به سیستم پیوند ایمیل، تأیید آدرس ایمیل، لغو تغییر حساب و موارد دیگر، استفاده می‌کنید، سرویس رله ایمیل خصوصی Apple را پیکربندی کنید و noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com را ثبت کنید. (یا دامنه قالب ایمیل سفارشی شده شما) تا اپل بتواند ایمیل های ارسال شده توسط Firebase Authentication را به آدرس های ایمیل ناشناس اپل ارسال کند.

اپل را به عنوان ارائه دهنده ورود فعال کنید

  1. Firebase را به پروژه اندروید خود اضافه کنید . هنگام تنظیم برنامه خود در کنسول Firebase ، حتماً امضای SHA-1 برنامه خود را ثبت کنید.
  2. در کنسول Firebase ، بخش Auth را باز کنید. در زبانه روش ورود ، ارائه دهنده اپل را فعال کنید. شناسه سرویسی که در قسمت قبل ایجاد کردید را مشخص کنید. همچنین در قسمت پیکربندی جریان کد OAuth ، شناسه تیم Apple خود و شناسه کلید خصوصی و کلیدی که در قسمت قبل ایجاد کرده اید را مشخص کنید.

الزامات داده های ناشناس اپل را رعایت کنید

ورود به سیستم با اپل به کاربران این امکان را می دهد که هنگام ورود به سیستم، داده های خود، از جمله آدرس ایمیل خود را ناشناس کنند. کاربرانی که این گزینه را انتخاب می کنند، آدرس های ایمیل با دامنه privaterelay.appleid.com دارند. هنگامی که از ورود به سیستم با Apple در برنامه خود استفاده می کنید، باید از هر گونه خط مشی یا شرایط توسعه دهنده قابل اجرا از طرف Apple در مورد این شناسه های اپل ناشناس پیروی کنید.

این شامل دریافت هرگونه رضایت کاربر مورد نیاز قبل از مرتبط کردن هر گونه اطلاعات شخصی شناسایی مستقیم با یک شناسه ناشناس اپل است. هنگام استفاده از Firebase Authentication، این ممکن است شامل اقدامات زیر باشد:

  • یک آدرس ایمیل را به یک Apple ID ناشناس پیوند دهید یا برعکس.
  • یک شماره تلفن را به یک Apple ID ناشناس پیوند دهید یا برعکس
  • یک اعتبار اجتماعی غیر ناشناس (فیس بوک، گوگل و غیره) را به یک شناسه اپل ناشناس پیوند دهید یا برعکس.

فهرست فوق کامل نیست. به توافقنامه مجوز برنامه توسعه‌دهنده اپل در بخش عضویت حساب توسعه‌دهنده خود مراجعه کنید تا مطمئن شوید برنامه شما با الزامات اپل مطابقت دارد.

جریان ورود به سیستم را با Firebase SDK مدیریت کنید

در Android، ساده‌ترین راه برای احراز هویت کاربران با Firebase با استفاده از حساب‌های Apple آنها، مدیریت کل جریان ورود به سیستم با Firebase Android SDK است.

برای مدیریت جریان ورود به سیستم با Firebase Android SDK، این مراحل را دنبال کنید:

  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 هیچ محدوده‌ای از اپل درخواست نمی‌کند مگر اینکه آنها را مشخص کنید.

  3. اختیاری: اگر می خواهید صفحه ورود اپل به زبانی غیر از انگلیسی نمایش داده شود، پارامتر locale را تنظیم کنید. برای مناطق پشتیبانی شده، به اسناد ورود با اپل مراجعه کنید.

    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. با استفاده از شی ارائه دهنده OAuth با Firebase احراز هویت کنید. توجه داشته باشید که برخلاف سایر عملیات FirebaseAuth ، با باز کردن یک برگه سفارشی Chrome، کنترل رابط کاربری شما را در دست می‌گیرد. در نتیجه، فعالیت خود را در OnSuccessListener و OnFailureListener که پیوست می‌کنید ارجاع ندهید، زیرا با شروع عملیات رابط کاربری، فوراً جدا می‌شوند.

    ابتدا باید بررسی کنید که آیا قبلاً پاسخی دریافت کرده اید یا خیر. ورود به سیستم با این روش، Activity شما را در پس‌زمینه قرار می‌دهد، به این معنی که سیستم می‌تواند آن را در طول ورود به سیستم بازیابی کند. برای اینکه مطمئن شوید در صورت وقوع این اتفاق، کاربر را مجبور به امتحان مجدد نمی‌کنید، باید بررسی کنید که آیا نتیجه از قبل وجود دارد یا خیر.

    برای بررسی اینکه آیا یک نتیجه معلق وجود دارد، 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 Auth، اپل URL عکس ارائه نمی کند.

    همچنین، وقتی کاربر تصمیم می‌گیرد ایمیل خود را با برنامه به اشتراک نگذارد، اپل یک آدرس ایمیل منحصربه‌فرد برای آن کاربر ارائه می‌کند (به شکل xyz@privaterelay.appleid.com )، که آن را با برنامه شما به اشتراک می‌گذارد. اگر سرویس ارسال ایمیل خصوصی را پیکربندی کرده باشید، اپل ایمیل های ارسال شده به آدرس ناشناس را به آدرس ایمیل واقعی کاربر فوروارد می کند.

    اپل تنها اولین باری که کاربر وارد سیستم می‌شود، اطلاعات کاربر مانند نام نمایشی را با برنامه‌ها به اشتراک می‌گذارد. معمولاً، Firebase نام نمایشی را در اولین باری که کاربر با اپل وارد می‌شود، ذخیره می‌کند، که می‌توانید با getCurrentUser().getDisplayName() دریافت کنید. با این حال، اگر قبلاً از اپل برای ورود کاربر به برنامه بدون استفاده از Firebase استفاده کرده‌اید، اپل نام نمایشی کاربر را به 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() برای پیوند دادن ارائه دهندگان هویت مختلف به حساب های موجود استفاده کنید.

توجه داشته باشید که اپل از شما می‌خواهد قبل از اینکه حساب‌های اپل آن‌ها را به داده‌های دیگر پیوند دهید، رضایت صریح از کاربران دریافت کنید.

به عنوان مثال، برای پیوند دادن یک حساب فیس بوک به حساب فعلی Firebase، از نشانه دسترسی که از ورود کاربر به فیس بوک دریافت کرده اید استفاده کنید:

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.
        }
      }
    });

پیشرفته: جریان ورود به سیستم را به صورت دستی مدیریت کنید

همچنین می‌توانید با استفاده از حساب Apple با Firebase احراز هویت را انجام دهید، یا با استفاده از Apple Sign-In JS SDK، ساخت دستی جریان OAuth یا با استفاده از یک کتابخانه OAuth مانند AppAuth .

  1. برای هر درخواست ورود به سیستم، یک رشته تصادفی ایجاد کنید - یک "nonce" - که از آن استفاده می کنید تا مطمئن شوید که رمز شناسه دریافتی به طور خاص در پاسخ به درخواست احراز هویت برنامه شما اعطا شده است. این مرحله برای جلوگیری از حملات تکراری مهم است.

    می‌توانید با SecureRandom یک nonce امن رمزنگاری در Android ایجاد کنید، مانند مثال زیر:

    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 را همراه با درخواست ورود خود ارسال می‌کنید که اپل در پاسخ آن را بدون تغییر ارسال می‌کند. Firebase پاسخ را با هش کردن nonce اصلی و مقایسه آن با مقدار ارسال شده توسط Apple تأیید می کند.

  2. با استفاده از کتابخانه OAuth یا روش دیگر، جریان ورود به سیستم اپل را آغاز کنید. حتماً nonce هش شده را به عنوان پارامتر در درخواست خود لحاظ کنید.

  3. بعد از اینکه پاسخ اپل را دریافت کردید، کد ID را از پاسخ دریافت کنید و از آن و nonce هش نشده برای ایجاد یک 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 برای دریافت اطلاعات حساب کاربر استفاده کنید.

ابطال توکن

اپل می‌خواهد برنامه‌هایی که از ایجاد حساب پشتیبانی می‌کنند باید به کاربران اجازه دهند تا حساب خود را در برنامه حذف کنند، همانطور که در دستورالعمل‌های بررسی App Store توضیح داده شده است.

علاوه بر این، برنامه‌هایی که از Sign in with Apple پشتیبانی می‌کنند باید از Sign in with Apple REST API برای لغو توکن‌های کاربر استفاده کنند.

برای برآوردن این نیاز، مراحل زیر را اجرا کنید:

  1. از متد startActivityForSignInWithProvider() برای ورود با اپل و دریافت AuthResult استفاده کنید.

  2. رمز دسترسی ارائه دهنده اپل را دریافت کنید.

    Kotlin+KTX

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

    Java

    OAuthCredential oauthCredential = (OAuthCredential) authResult.getCredential();
    String accessToken = oauthCredential.getAccessToken();
    
  3. با استفاده از revokeAccessToken API رمز را باطل کنید.

    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 Realtime Database و Cloud Storage خود، می‌توانید شناسه کاربری منحصر به فرد کاربر واردشده به سیستم را از متغیر auth دریافت کنید و از آن برای کنترل داده‌هایی که کاربر می‌تواند به آن دسترسی داشته باشد استفاده کنید.

    می‌توانید به کاربران اجازه دهید با استفاده از چندین ارائه‌دهنده احراز هویت، با پیوند دادن اعتبار ارائه‌دهنده تأیید اعتبار به یک حساب کاربری موجود، به برنامه شما وارد شوند.

    برای خروج از سیستم کاربر، signOut تماس بگیرید:

    Kotlin+KTX

    Firebase.auth.signOut()

    Java

    FirebaseAuth.getInstance().signOut();