اصالت‌سنجی بااستفاده از Apple در Android

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

قبل از شروع

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

به «برنامه توسعه‌دهندگان Apple» بپیوندید

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

پیکربندی «ورود به سیستم با Apple»

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

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

    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 خود اضافه کنید.
  2. اگر قبلاً اثر انگشت SHA-1 برنامه‌تان را مشخص نکرده‌اید، آن را مشخص کنید.
    1. در کنسول Firebase، به تنظیمات > زبانه کلی بروید.
    2. به کارت برنامه‌های شما پیمایش کنید، برنامه Android خود را انتخاب کنید، و اثر انگشت SHA-1 خود را در فیلد اثر انگشت گواهینامه SHA اضافه کنید.
  3. در کنسول Firebase، به امنیت > اصالت‌سنجی بروید.
  4. در زبانه روش ورود به سیستم، ارائه‌دهنده ورود به سیستم Apple را فعال کنید. «شناسه سرویس» را که در بخش قبلی ایجاد کرده‌اید مشخص کنید. همچنین، در بخش پیکربندی جریان کد OAuth، «شناسه تیم Apple» و کلید خصوصی و شناسه کلیدی را که در بخش قبلی ایجاد کردید مشخص کنید.

از الزامات داده‌های ناشناس‌سازی‌شده Apple پیروی کنید

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

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

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

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

مدیریت جریان ورود به سیستم با «کیت توسعه نرم‌افزار Firebase»

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

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

  1. بااستفاده از «سازنده» آن با شناسه ارائه‌دهنده apple.com، نمونه‌ای از OAuthProvider بسازید:

    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. بااستفاده از شیء ارائه‌دهنده OAuth، با Firebase اصالت‌سنجی کنید. توجه داشته باشید که برخلاف دیگر عملیات 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 نشانی وب عکس ارائه نمی‌دهد.

    همچنین، وقتی کاربر انتخاب می‌کند که ایمیلش را با برنامه هم‌رسانی نکند، 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.
        }
      }
    });

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

همچنین می‌توانید بااستفاده از «حساب Apple» با Firebase اصالت‌سنجی کنید. برای این کار، جریان ورود به سیستم را بااستفاده از «کیت توسعه نرم‌افزار JS ورود به سیستم با 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» پشتیبانی می‌کنند باید از «میانای برنامه کاربردی REST ورود به سیستم با Apple» برای باطل کردن نشان‌های کاربر استفاده کنند.

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

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

  2. کد دسترسی را برای ارائه‌دهنده Apple دریافت کنید.

    Kotlin

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

    Java

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

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