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

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

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

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

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

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

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

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

يجب أن تستخدم التطبيقات التي لا تتطلّب تسجيل الدخول ميزة "تسجيل الدخول بنقرة واحدة" على شاشات تسجيل الدخول، للأسباب نفسها.

قبل البدء

1. ضبط عميل تسجيل الدخول باستخدام ميزة "نقرة واحدة"

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

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

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

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

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

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

    • لدى المستخدم بيانات اعتماد واحدة محفوظة لتطبيقك، أي كلمة مرور واحدة محفوظة أو حساب Google واحد محفوظ.
    • لم يوقف المستخدم ميزة تسجيل الدخول التلقائي في إعدادات حسابه على Google.
  • على الرغم من أنّ استخدام رقم خاص يُعدّ اختياريًا، ننصحك بشدة باستخدامه لتحسين أمان تسجيل الدخول وتجنُّب هجمات إعادة الإرسال. استخدِم 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() الخاصة بالنشاط أو بعد الانتقال بين الشاشات عند استخدام بنية نشاط واحد.

  • العثور على بيانات اعتماد محفوظة: سيستدعي برنامج One Tap مستمع النجاح إذا كان لدى المستخدم أي بيانات اعتماد محفوظة لتطبيقك. في مستمع النجاح، احصل على intent المؤجّلة من نتيجة Task ومرِّرها إلى startIntentSenderForResult() لبدء واجهة مستخدم تسجيل الدخول باستخدام ميزة "نقرة واحدة".
  • لم يتم العثور على بيانات اعتماد محفوظة: سيستدعي عميل One Tap مستمع الفشل. في هذه الحالة، ليس عليك اتّخاذ أي إجراء، ويمكنك مواصلة تقديم تجربة استخدام التطبيق بدون تسجيل الدخول. ومع ذلك، إذا كنت تتيح الاشتراك باستخدام ميزة "نقرة واحدة"، يمكنك بدء هذا المسار هنا لتوفير تجربة سلسة لإنشاء الحساب. يُرجى الاطّلاع على المقالة إنشاء حسابات جديدة بنقرة واحدة.

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() في نشاطك. إذا اختار المستخدم تسجيل الدخول، ستكون النتيجة بيانات اعتماد محفوظة. إذا رفض المستخدم تسجيل الدخول، إما عن طريق إغلاق واجهة مستخدم One Tap أو النقر خارجها، ستظهر النتيجة مع الرمز RESULT_CANCELED. ويجب أن يتعامل تطبيقك مع كلا الاحتمالين.

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

إذا اختار المستخدم مشاركة بيانات الاعتماد مع تطبيقك، يمكنك استردادها من خلال تمرير بيانات intent من onActivityResult() إلى طريقة getSignInCredentialFromIntent() لبرنامج One Tap. ستتضمّن بيانات الاعتماد سمة 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) {
                      // ...
                  }
              }
          }
      }
      // ...
  }

إيقاف عرض واجهة مستخدم One Tap

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

من المهم تنفيذ حدّ معدّل طلبات تسجيل الدخول باستخدام ميزة "نقرة واحدة". إذا لم تفعل ذلك، وألغى المستخدم عدة طلبات متتالية، لن يطلب عميل One Tap من المستخدم تسجيل الدخول خلال الـ 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() لعميل One Tap. يؤدي استدعاء signOut() إلى إيقاف ميزة "تسجيل الدخول تلقائيًا" إلى أن يسجّل المستخدم الدخول مرة أخرى.

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

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

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

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