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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

אם למשתמש יש פרטי כניסה שמורים לאפליקציה, לקוח One Tap יקרא ל-event listener אם הוא נשמר באפליקציה. ב-event listener, אפשר לקבל את ה-Intent בהמתנה מהתוצאה של Task ולהעביר אותו אל startIntentSenderForResult() כדי להפעיל את ממשק הכניסה בהקשה אחת.

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

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

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

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

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

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

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

השלבים הבאים

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

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