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

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

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

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

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

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

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

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

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

قبل البدء

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

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

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

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

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

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

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

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

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 المستمع الناجح في حال كان لدى المستخدم أي بيانات اعتماد محفوظة لتطبيقك. وفي حال نجاح الاستماع، يمكنك الحصول على النية بالشراء المعلّقة من نتيجة Task وتمريرها إلى startIntentSenderForResult() لبدء واجهة المستخدم لتسجيل الدخول بنقرة واحدة.

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

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. يجب أن يعالج تطبيقك كلا الاحتمالين.

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

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

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

الخطوات اللاحقة

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

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