כניסה של משתמשים באמצעות פרטי הכניסה השמורים שלהם

משתמשים בלקוח הכניסה בהקשה אחת כדי לבקש הרשאה מהמשתמש לאחזר את אחד מפרטי הכניסה שבהם הוא השתמש כדי להיכנס לאפליקציה בעבר. פרטי הכניסה האלה יכולים להיות חשבון Google או שילוב של שם משתמש וסיסמה, שהם שמרו באופן אוטומטי ב-Google באמצעות Chrome, Android או Smart Lock לסיסמאות.

ממשק משתמש לכניסה בהקשה אחת

אחרי שמאחזרים את פרטי הכניסה בקלות, אפשר להשתמש בהם כדי להכניס את המשתמשים לאפליקציה בקלות.

אם המשתמש לא שמר פרטי כניסה, לא יוצג ממשק משתמש, ותוכלו לספק את החוויה הרגילה שלכם ללא חיבור לחשבון.

איפה כדאי להיכנס לחשבון בהקשה אחת?

אם האפליקציה מחייבת כניסה של משתמשים, יש להציג את ממשק המשתמש בהקשה אחת במסך הכניסה. האפשרות הזו יכולה להיות שימושית גם אם כבר יש לך לחצן 'כניסה באמצעות חשבון Google': מכיוון שאפשר להגדיר בממשק המשתמש של One Tap רק את פרטי הכניסה שהמשתמש השתמש בו בעבר כדי להיכנס, זו יכולה להיות תזכורת למשתמשים שנכנסים לחשבון בפעם האחרונה באופן נדיר, ולמנוע מהם ליצור בטעות חשבונות חדשים באפליקציה שלך.

אם הכניסה לאפליקציה היא אופציונלית, כדאי להשתמש בכניסה בהקשה אחת בכל מסך שחוויית השימוש בו שופרה על ידי כניסה. לדוגמה, אם משתמשים יכולים לגלוש בתוכן באפליקציה כשהם לא מחוברים לחשבון, אך הם יכולים רק לפרסם תגובות או להוסיף פריטים לעגלת הקניות לאחר הכניסה לחשבון, זה יהיה הקשר הגיוני לכניסה באמצעות One Tap.

באפליקציות שבהן אפשר להשתמש כדי להיכנס לחשבון צריך להשתמש בכניסה באמצעות הקשה אחת גם במסכי הכניסה, מהסיבות שצוינו למעלה.

לפני שמתחילים

‫1. הגדרת לקוח לכניסה בהקשה אחת

תוכלו להגדיר את לקוח הכניסה בהקשה אחת כדי להיכנס למשתמשים באמצעות סיסמאות שמורות, חשבונות Google שמורים או אחד מהם. (מומלץ לתמוך בשניהם, כדי לאפשר יצירת חשבון בהקשה אחת למשתמשים חדשים, וכניסה אוטומטית או בהקשה אחת לכמה שיותר משתמשים חוזרים).

אם האפליקציה שלכם משתמשת בכניסה מבוססת סיסמה, השתמשו ב-setPasswordRequestOptions() כדי להפעיל בקשות לפרטי כניסה באמצעות סיסמה.

אם האפליקציה שלכם משתמשת בכניסה באמצעות חשבון Google, השתמשו ב-setGoogleIdTokenRequestOptions() כדי להפעיל ולהגדיר בקשות לאסימון Google ID:

  • מגדירים את מזהה הלקוח שיצרתם במסוף Google APIs. שימו לב שזהו מזהה הלקוח של השרת ולא מזהה הלקוח ב-Android.

  • צריך להגדיר את הלקוח לסינון לפי חשבונות מורשים. כשמפעילים את האפשרות הזו, הלקוח 'הקשה אחת' מבקש מהמשתמשים להיכנס לאפליקציה שלך רק באמצעות חשבונות Google שבהם הם השתמשו בעבר. זה יעזור למשתמשים להיכנס לחשבון בהצלחה כשהם לא בטוחים אם כבר יש להם חשבון או באיזה חשבון Google הם השתמשו, ולמנוע ממשתמשים ליצור בטעות חשבונות חדשים באפליקציה.

  • אם אתם רוצים לאפשר למשתמשים להיכנס באופן אוטומטי, צריך להפעיל את התכונה באמצעות setAutoSelectEnabled(). כניסה אוטומטית מתאפשרת כאשר מתקיימים הקריטריונים הבאים:

    • למשתמש יש רק פרטי כניסה אחד שמורים לאפליקציה, כלומר סיסמה שמורה אחת או חשבון Google שמור אחד.
    • המשתמש לא השבית את הכניסה האוטומטית בהגדרות של חשבון Google.
  • זה אמנם לא חובה, אבל מומלץ מאוד להשתמש בצופן חד-פעמי (nonce) כדי לשפר את האבטחה בכניסה לחשבון ולהימנע מהתקפות שליחה מחדש. משתמשים ב-setNonce כדי לכלול צופן חד-פעמי בכל בקשה. בקטע קבלת צופן חד-פעמי (nonce) של SafetyNet תוכלו למצוא הצעות ופרטים נוספים על יצירת צופן חד-פעמי (nonce).

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. איך בודקים אם יש משתמש מחובר

אם משתמשים מחוברים או משתמשים שלא מחוברים לחשבון יכולים להשתמש בפעילות שלכם, חשוב לבדוק את הסטטוס של המשתמש לפני הצגת ממשק המשתמש לכניסה בהקשה אחת.

כדאי גם לבדוק אם המשתמש כבר סירב להשתמש בכניסה באמצעות One Tap – פשוט סוגרים את ההודעה או מקישים מחוץ לה. זה יכול להיות פשוט כמו נכס בוליאני של הפעילות שלכם. (פרטים נוספים זמינים בקטע הפסקת ההצגה של ממשק המשתמש של הקשה אחת בהמשך).

3. הצגת ממשק המשתמש לכניסה בהקשה אחת

במקרה שהמשתמש לא מחובר ולא סירב להשתמש בכניסה בהקשה אחת, קוראים לשיטה beginSignIn() של אובייקט הלקוח ומצרפים מאזינים ל-Task שמוחזר. אפליקציות בדרך כלל עושות זאת בשיטת onCreate() של הפעילות או אחרי מעברים בין מסכים, כשמשתמשים בארכיטקטורה של פעילות יחידה.

אם יש למשתמש פרטי כניסה שמורים לאפליקציה, הלקוח One Tap יקרא ל-listener. ב-listener, מקבלים את ה-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() ל-method getSignInCredentialFromIntent() של הלקוח ב-One Tap. אם המשתמש שיתף פרטי כניסה לחשבון Google עם האפליקציה, יהיה בפרטי הכניסה נכס googleIdToken שאינו null, או נכס password שהוא לא null אם המשתמש שיתף סיסמה שמורה.

משתמשים בפרטי הכניסה כדי לבצע אימות בקצה העורפי של האפליקציה.

  • אם אוחזרו צמד של שם משתמש וסיסמה, השתמשו בהם כדי להיכנס באותו אופן שבו הייתם עושים זאת אם המשתמש היה מספק אותם באופן ידני.
  • אם פרטי הכניסה לחשבון 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 או להשתמש בשיטה אחרת.

חשוב להטמיע הגבלת קצב משלכם של הודעות כניסה לחשבון בהקשה אחת. אם לא תעשו זאת, ומשתמש יבטל מספר בקשות ברצף, הלקוח ב-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. ניהול היציאה מהחשבון

כשמשתמש יוצא מהאפליקציה, צריך לבצע קריאה ל-method signOut() של הלקוח בהקשה אחת. קריאה ל-signOut() משביתה את הכניסה האוטומטית עד שהמשתמש נכנס שוב.

גם אם אתם לא משתמשים בכניסה אוטומטית, השלב הזה חשוב כי הוא מבטיח שכאשר משתמשים יצאו מהאפליקציה, גם מצב האימות של ממשקי ה-API של Play Services שבהם אתם משתמשים יאופס.

השלבים הבאים

אם הגדרתם ללקוח One Tap לאחזר את פרטי הכניסה של Google, האפליקציה יכולה לקבל עכשיו אסימונים מזהים של Google שמייצגים את חשבונות Google של המשתמשים. איך אפשר להשתמש באסימונים האלה בקצה העורפי

אם אתם תומכים בכניסה באמצעות חשבון Google, תוכלו גם להשתמש בלקוח One Tap כדי להוסיף לאפליקציה תהליכי יצירה של חשבון בצורה חלקה.