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

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

قبل از اینکه شروع کنی

برای ورود کاربران با استفاده از اپل، ابتدا ورود با اپل را در سایت توسعه‌دهندگان اپل پیکربندی کنید، سپس اپل را به عنوان ارائه‌دهنده ورود به سیستم برای پروژه Firebase خود فعال کنید.

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

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

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

در سایت توسعه‌دهندگان اپل ، مراحل زیر را انجام دهید:

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

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

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

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

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

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

  1. فایربیس را به پروژه اندروید خود اضافه کنید . هنگام تنظیم برنامه خود در کنسول Firebase ، حتماً امضای SHA-1 برنامه خود را ثبت کنید.
  2. در کنسول Firebase ، بخش Auth را باز کنید. در تب Sign in method ، گزینه Apple provider را فعال کنید. شناسه سرویس (Service ID) که در بخش قبل ایجاد کرده‌اید را مشخص کنید. همچنین، در بخش OAuth code flow configuration ، شناسه تیم اپل (Apple Team ID) و کلید خصوصی و شناسه کلید (key ID) که در بخش قبل ایجاد کرده‌اید را مشخص کنید.

مطابق با الزامات داده‌های ناشناس اپل

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

این شامل دریافت هرگونه رضایت کاربر قبل از مرتبط کردن هرگونه اطلاعات شخصی که مستقیماً هویت شما را مشخص می‌کند با یک Apple ID ناشناس می‌شود. هنگام استفاده از احراز هویت Firebase، این ممکن است شامل اقدامات زیر باشد:

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

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

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

در اندروید، ساده‌ترین راه برای احراز هویت کاربران با فایربیس با استفاده از حساب‌های اپل آنها، مدیریت کل جریان ورود به سیستم با کیت توسعه نرم‌افزار اندروید فایربیس است.

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

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

    به طور پیش‌فرض، وقتی گزینه‌ی «یک حساب کاربری برای هر آدرس ایمیل» فعال باشد، فایربیس محدوده‌های ایمیل و نام را درخواست می‌کند. اگر این تنظیم را به «چندین حساب کاربری برای هر آدرس ایمیل» تغییر دهید، فایربیس هیچ محدوده‌ای را از اپل درخواست نمی‌کند، مگر اینکه شما آنها را مشخص کنید.

  3. اختیاری: اگر می‌خواهید صفحه ورود به سیستم اپل را به زبانی غیر از انگلیسی نمایش دهید، پارامتر locale را تنظیم کنید. برای اطلاع از زبان‌های پشتیبانی‌شده، به اسناد Sign In with 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. با استفاده از شیء ارائه دهنده OAuth، با Firebase احراز هویت کنید. توجه داشته باشید که برخلاف سایر عملیات FirebaseAuth ، این عملیات با باز کردن یک تب سفارشی کروم، کنترل رابط کاربری شما را به دست می‌گیرد. در نتیجه، به Activity خود در OnSuccessListener و OnFailureListener که پیوست می‌کنید، ارجاع ندهید زیرا آنها بلافاصله پس از شروع عملیات رابط کاربری جدا می‌شوند.

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

    برای بررسی اینکه آیا نتیجه‌ای در انتظار است یا خیر، تابع 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، اپل آدرس اینترنتی عکس ارائه نمی‌دهد.

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

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

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

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

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

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

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

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

    شما می‌توانید با استفاده از SecureRandom ، مانند مثال زیر، یک nonce امن از نظر رمزنگاری در اندروید ایجاد کنید:

    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 مربوط به nonce را به صورت یک رشته هگز دریافت کنید:

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

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

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

ابطال توکن

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

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

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

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

  2. توکن دسترسی را برای ارائه دهنده اپل دریافت کنید.

    Kotlin

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

    Java

    OAuthCredential oauthCredential = (OAuthCredential) authResult.getCredential();
    String accessToken = oauthCredential.getAccessToken();
    
  3. با استفاده از API مربوط به 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 دریافت کنید. به مدیریت کاربران مراجعه کنید.

    • در قوانین امنیتی پایگاه داده و Cloud Storage Firebase Realtime Database ، می‌توانید شناسه کاربری منحصر به فرد کاربر وارد شده را از متغیر auth دریافت کنید و از آن برای کنترل داده‌هایی که کاربر می‌تواند به آنها دسترسی داشته باشد، استفاده کنید.

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

    برای خروج کاربر، signOut را فراخوانی کنید:

    Kotlin

    Firebase.auth.signOut()

    Java

    FirebaseAuth.getInstance().signOut();