تسجيل دخول المستخدمين باستخدام بيانات الاعتماد المحفوظة

استخدِم برنامج تسجيل الدخول لميزة "نقرة واحدة" لطلب إذن من المستخدم لاسترداد إحدى بيانات الاعتماد التي استخدمها سابقًا لتسجيل الدخول إلى تطبيقك. ويمكن أن تكون بيانات الاعتماد هذه إمّا حساب Google أو مجموعة من اسم المستخدم وكلمة المرور تم حفظها لدى Google باستخدام Chrome أو ميزة الملء التلقائي في Android أو Smart Lock لكلمات المرور.

واجهة مستخدم لتسجيل الدخول بنقرة واحدة

عند استرداد بيانات الاعتماد بنجاح، يمكنك استخدامها لتسجيل دخول المستخدم إلى تطبيقك بكل سلاسة.

إذا لم يحفظ المستخدم أي بيانات اعتماد، لن تظهر أي واجهة مستخدم، ويمكنك تقديم تجربة تسجيل الخروج المعتادة.

أين يجب استخدام ميزة تسجيل الدخول بنقرة واحدة؟

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

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

يجب أن تستخدم التطبيقات الاختيارية لتسجيل الدخول أيضًا ميزة "تسجيل الدخول بنقرة واحدة" على شاشات تسجيل الدخول للأسباب المذكورة أعلاه.

قبل البدء

  • يمكنك إعداد مشروع وحدة تحكُّم Google APIs ومشروع Android كما هو موضَّح في بدء تسجيل الدخول بنقرة واحدة.
  • إذا كنت تتيح تسجيل الدخول استنادًا إلى كلمة المرور، يمكنك تحسين تطبيقك للملء التلقائي (أو استخدام Smart Lock لكلمات المرور) ليتمكّن المستخدمون من حفظ بيانات اعتماد كلمة المرور بعد تسجيل الدخول.

1- إعداد برنامج تسجيل الدخول بنقرة واحدة

يمكنك ضبط برنامج تسجيل الدخول لميزة "نقرة واحدة" لتسجيل دخول المستخدمين باستخدام كلمات المرور المحفوظة أو حسابات Google المحفوظة أو باستخدام أي منهما. (ننصح باستخدام كليهما لإتاحة إنشاء الحساب بنقرة واحدة للمستخدمين الجُدد وتسجيل الدخول تلقائيًا أو بنقرة واحدة لأكبر عدد ممكن من المستخدمين المكرّري الزيارة).

إذا كان تطبيقك يستخدم ميزة تسجيل الدخول استنادًا إلى كلمة المرور، استخدِم setPasswordRequestOptions() لتفعيل طلبات بيانات اعتماد كلمة المرور.

إذا كان تطبيقك يستخدم ميزة "تسجيل الدخول بحساب Google"، استخدِم setGoogleIdTokenRequestOptions() لتفعيل وضبط طلبات الرموز المميّزة لمعرّف Google:

  • اضبط معرِّف العميل للخادم على المعرّف الذي أنشأته في وحدة تحكُّم Google APIs. لاحظ أن هذا هو معرف العميل لخادمك، وليس معرف عميل Android.

  • اضبط العميل للفلترة حسب الحسابات المُصرَّح بها. عند تفعيل هذا الخيار، يطلب برنامج "نقرة واحدة" من المستخدمين فقط تسجيل الدخول إلى التطبيق باستخدام حساباتهم على Google التي سبق لهم استخدامها من قبل. يساعد ذلك المستخدمين في تسجيل الدخول بنجاح عندما لا يكونوا متأكدين مما إذا كان لديهم حساب من قبل أو حساب Google الذي يستخدمونه، كما يمنع المستخدمين من إنشاء حسابات جديدة عن طريق الخطأ باستخدام تطبيقك.

  • إذا أردت تسجيل دخول المستخدمين تلقائيًا إن أمكن، فعِّل الميزة باستخدام setAutoSelectEnabled(). يمكن تسجيل الدخول تلقائيًا عند استيفاء المعايير التالية:

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

Java

public class YourActivity extends AppCompatActivity {
  // ...

  private SignInClient oneTapClient;
  private BeginSignInRequest signInRequest;

  @Override
  public void onCreate(@Nullable Bundle savedInstanceState,
                       @Nullable PersistableBundle persistentState) {
      super.onCreate(savedInstanceState, persistentState);

      oneTapClient = Identity.getSignInClient(this);
      signInRequest = BeginSignInRequest.builder()
              .setPasswordRequestOptions(PasswordRequestOptions.builder()
                      .setSupported(true)
                      .build())
              .setGoogleIdTokenRequestOptions(GoogleIdTokenRequestOptions.builder()
                      .setSupported(true)
                      // Your server's client ID, not your Android client ID.
                      .setServerClientId(getString(R.string.default_web_client_id))
                      // Only show accounts previously used to sign in.
                      .setFilterByAuthorizedAccounts(true)
                      .build())
              // Automatically sign in when exactly one credential is retrieved.
              .setAutoSelectEnabled(true)
              .build();
      // ...
  }
  // ...
}

Kotlin

class YourActivity : AppCompatActivity() {
    // ...

    private lateinit var oneTapClient: SignInClient
    private lateinit var signInRequest: BeginSignInRequest

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        oneTapClient = Identity.getSignInClient(this)
        signInRequest = BeginSignInRequest.builder()
            .setPasswordRequestOptions(BeginSignInRequest.PasswordRequestOptions.builder()
                .setSupported(true)
                .build())
            .setGoogleIdTokenRequestOptions(
                BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
                    .setSupported(true)
                    // Your server's client ID, not your Android client ID.
                    .setServerClientId(getString(R.string.your_web_client_id))
                    // Only show accounts previously used to sign in.
                    .setFilterByAuthorizedAccounts(true)
                    .build())
            // Automatically sign in when exactly one credential is retrieved.
            .setAutoSelectEnabled(true)
            .build()
        // ...
    }
    // ...
}

2. التحقُّق من وجود مستخدم مسجّل الدخول

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

عليك أيضًا تتبُّع ما إذا كان المستخدم قد رفض استخدام ميزة "تسجيل الدخول بنقرة واحدة" إما من خلال إغلاق رسالة الطلب أو النقر خارجها. يمكن أن يكون هذا بسيطًا مثل خاصية منطقية لنشاطك. (يمكنك الاطّلاع أدناه على خيار إيقاف عرض واجهة المستخدم بنقرة واحدة أدناه).

3- عرض واجهة المستخدم لتسجيل الدخول بنقرة واحدة

إذا لم يسجّل المستخدم الدخول ولم يرفض استخدام ميزة "تسجيل الدخول بنقرة واحدة"، عليك استدعاء طريقة beginSignIn() لعنصر العميل وإرفاق المستمعين بعملية تسجيل الدخول Task التي يعرضها. تُجري التطبيقات ذلك عادةً في طريقة onCreate() الخاصة بالنشاط أو بعد انتقالات الشاشة عند استخدام بنية "نشاط واحد".

سيطلب برنامج "نقرة واحدة" أداة معالجة النجاح إذا كان لدى المستخدم أي بيانات اعتماد محفوظة لتطبيقك. وفي أداة معالجة النجاح، يمكنك الحصول على الغرض المعلق من نتيجة Task وتمريره إلى startIntentSenderForResult() لبدء واجهة المستخدم لتسجيل الدخول بنقرة واحدة.

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

Java

oneTapClient.beginSignIn(signUpRequest)
        .addOnSuccessListener(this, new OnSuccessListener<BeginSignInResult>() {
            @Override
            public void onSuccess(BeginSignInResult result) {
                try {
                    startIntentSenderForResult(
                            result.getPendingIntent().getIntentSender(), REQ_ONE_TAP,
                            null, 0, 0, 0);
                } catch (IntentSender.SendIntentException e) {
                    Log.e(TAG, "Couldn't start One Tap UI: " + e.getLocalizedMessage());
                }
            }
        })
        .addOnFailureListener(this, new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                // No saved credentials found. Launch the One Tap sign-up flow, or
                // do nothing and continue presenting the signed-out UI.
                Log.d(TAG, e.getLocalizedMessage());
            }
        });

Kotlin

oneTapClient.beginSignIn(signInRequest)
    .addOnSuccessListener(this) { result ->
        try {
            startIntentSenderForResult(
                result.pendingIntent.intentSender, REQ_ONE_TAP,
                null, 0, 0, 0, null)
        } catch (e: IntentSender.SendIntentException) {
            Log.e(TAG, "Couldn't start One Tap UI: ${e.localizedMessage}")
        }
    }
    .addOnFailureListener(this) { e ->
        // No saved credentials found. Launch the One Tap sign-up flow, or
        // do nothing and continue presenting the signed-out UI.
        Log.d(TAG, e.localizedMessage)
    }

4. التعامل مع ردّ المستخدم

سيتم إبلاغ تطبيقك بردّ المستخدم على طلب تسجيل الدخول بنقرة واحدة من خلال طريقة onActivityResult() في "نشاطك". إذا اختار المستخدم تسجيل الدخول، ستكون النتيجة بيانات اعتماد محفوظة. إذا رفض المستخدم تسجيل الدخول، إما عن طريق إغلاق واجهة المستخدم من خلال ميزة "نقرة واحدة" أو النقر خارجها، سيتم عرض النتيجة بالرمز RESULT_CANCELED. يجب أن يتعامل تطبيقك مع كلا الاحتمالين.

تسجيل الدخول باستخدام بيانات الاعتماد التي تم استردادها

إذا اختار المستخدم مشاركة بيانات الاعتماد مع تطبيقك، يمكنك استردادها من خلال تمرير بيانات الغرض من onActivityResult() إلى طريقة getSignInCredentialFromIntent() في برنامج "نقرة واحدة". ستحتوي بيانات الاعتماد على سمة googleIdToken غير فارغة إذا شارك المستخدم بيانات اعتماد حساب Google مع تطبيقك، أو موقع password غير فارغ إذا شارك المستخدم كلمة مرور محفوظة.

استخدِم بيانات الاعتماد للمصادقة باستخدام الواجهة الخلفية لتطبيقك.

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

Java

public class YourActivity extends AppCompatActivity {

  // ...
  private static final int REQ_ONE_TAP = 2;  // Can be any integer unique to the Activity.
  private boolean showOneTapUI = true;
  // ...

  @Override
  protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
      super.onActivityResult(requestCode, resultCode, data);

      switch (requestCode) {
          case REQ_ONE_TAP:
              try {
                  SignInCredential credential = oneTapClient.getSignInCredentialFromIntent(data);
                  String idToken = credential.getGoogleIdToken();
                  String username = credential.getId();
                  String password = credential.getPassword();
                  if (idToken !=  null) {
                      // Got an ID token from Google. Use it to authenticate
                      // with your backend.
                      Log.d(TAG, "Got ID token.");
                  } else if (password != null) {
                      // Got a saved username and password. Use them to authenticate
                      // with your backend.
                      Log.d(TAG, "Got password.");
                  }
              } catch (ApiException e) {
                  // ...
              }
              break;
      }
  }
}

Kotlin

class YourActivity : AppCompatActivity() {

    // ...
    private val REQ_ONE_TAP = 2  // Can be any integer unique to the Activity
    private var showOneTapUI = true
    // ...

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        when (requestCode) {
             REQ_ONE_TAP -> {
                try {
                    val credential = oneTapClient.getSignInCredentialFromIntent(data)
                    val idToken = credential.googleIdToken
                    val username = credential.id
                    val password = credential.password
                    when {
                        idToken != null -> {
                            // Got an ID token from Google. Use it to authenticate
                            // with your backend.
                            Log.d(TAG, "Got ID token.")
                        }
                        password != null -> {
                            // Got a saved username and password. Use them to authenticate
                            // with your backend.
                            Log.d(TAG, "Got password.")
                        }
                        else -> {
                            // Shouldn't happen.
                            Log.d(TAG, "No ID token or password!")
                        }
                    }
                } catch (e: ApiException) {
                    // ...
                }
            }
        }
    }
    // ...
}

إيقاف عرض واجهة المستخدم بنقرة واحدة

إذا رفض المستخدم تسجيل الدخول، ستعرض المكالمة getSignInCredentialFromIntent() الخطأ ApiException مع رمز الحالة CommonStatusCodes.CANCELED. وعند حدوث ذلك، يجب إيقاف واجهة المستخدم مؤقتًا لتسجيل الدخول من خلال ميزة "نقرة واحدة" حتى لا تزعج المستخدمين بالطلبات المتكرّرة. يتم تحقيق ذلك في المثال التالي من خلال ضبط سمة في النشاط يتم استخدامها لتحديد ما إذا كنت تريد منح المستخدم خيار تسجيل الدخول بنقرة واحدة، ومع ذلك، يمكنك أيضًا حفظ قيمة في النشاط SharedPreferences أو استخدام طريقة أخرى.

من المهم تطبيق حدود لمعدل طلبات تسجيل الدخول بنقرة واحدة. إذا لم تفعل ذلك، وألغى المستخدم عدة طلبات على التوالي، لن يطلب برنامج "نقرة واحدة" المستخدم خلال 24 ساعة من الآن.

Java

public class YourActivity extends AppCompatActivity {

  // ...
  private static final int REQ_ONE_TAP = 2;  // Can be any integer unique to the Activity.
  private boolean showOneTapUI = true;
  // ...

  @Override
  protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
      super.onActivityResult(requestCode, resultCode, data);

      switch (requestCode) {
          case REQ_ONE_TAP:
              try {
                  // ...
              } catch (ApiException e) {
                  switch (e.getStatusCode()) {
                      case CommonStatusCodes.CANCELED:
                          Log.d(TAG, "One-tap dialog was closed.");
                          // Don't re-prompt the user.
                          showOneTapUI = false;
                          break;
                      case CommonStatusCodes.NETWORK_ERROR:
                          Log.d(TAG, "One-tap encountered a network error.");
                          // Try again or just ignore.
                          break;
                      default:
                          Log.d(TAG, "Couldn't get credential from result."
                                  + e.getLocalizedMessage());
                          break;
                  }
              }
              break;
      }
  }
}

Kotlin

class YourActivity : AppCompatActivity() {

    // ...
    private val REQ_ONE_TAP = 2  // Can be any integer unique to the Activity
    private var showOneTapUI = true
    // ...

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        when (requestCode) {
            REQ_ONE_TAP -> {
                try {
                    // ...
                } catch (e: ApiException) {
                    when (e.statusCode) {
                        CommonStatusCodes.CANCELED -> {
                            Log.d(TAG, "One-tap dialog was closed.")
                            // Don't re-prompt the user.
                            showOneTapUI = false
                        }
                        CommonStatusCodes.NETWORK_ERROR -> {
                            Log.d(TAG, "One-tap encountered a network error.")
                            // Try again or just ignore.
                        }
                        else -> {
                            Log.d(TAG, "Couldn't get credential from result." +
                                " (${e.localizedMessage})")
                        }
                    }
                }
            }
        }
    }
    // ...
}

5- التعامل مع عملية تسجيل الخروج

عندما يسجِّل أحد المستخدمين خروجه من تطبيقك، يمكنك استدعاء طريقة signOut() في برنامج "نقرة واحدة". سيؤدي طلب الرقم signOut() إلى إيقاف تسجيل الدخول التلقائي إلى أن يسجِّل المستخدم الدخول مرة أخرى.

حتى إذا كنت لا تستخدم ميزة "تسجيل الدخول تلقائيًا"، تُعدّ هذه الخطوة مهمة لأنّها تضمن إعادة ضبط حالة المصادقة الخاصة بأي من واجهات برمجة التطبيقات لخدمات Play تستخدمها عند تسجيل خروج المستخدمين من التطبيق.

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

إذا ضبطت برنامج "نقرة واحدة" لاسترداد بيانات اعتماد Google، سيتمكّن تطبيقك الآن من الحصول على رموز تعريف Google المميَّزة التي تمثّل حسابات المستخدمين على Google. تعرَّف على كيفية استخدام هذه الرموز المميّزة في الخلفية.

إذا كنت تتيح ميزة "تسجيل الدخول باستخدام حساب Google"، يمكنك أيضًا استخدام برنامج "نقرة واحدة" لإضافة خطوات إنشاء حسابات سلسة إلى تطبيقك.