Logowanie użytkowników przy użyciu zapisanych danych logowania

Poproś klienta logowania jednym dotknięciem o pozwolenie na pobranie od użytkownika jednej z danych logowania używanych wcześniej do logowania się do Twojej aplikacji. Dane te mogą być kontem Google lub kombinacją nazwy użytkownika i hasła zapisanych w Google przy użyciu funkcji autouzupełniania w Chrome lub na urządzeniu z Androidem lub Smart Lock na hasła.

Interfejs logowania jednym kliknięciem

Po pobraniu danych logowania możesz użyć ich do bezproblemowego logowania użytkownika w aplikacji.

Jeśli użytkownik nie zapisał żadnych danych logowania, nie wyświetli się interfejs użytkownika, a użytkownik może zalogować się w zwykły sposób.

Gdzie mam używać logowania jednym dotknięciem?

Jeśli w Twojej aplikacji użytkownicy muszą się logować, wyświetl na ekranie logowania interfejs One Tap. Może to być przydatne, nawet jeśli masz już przycisk „Zaloguj się przez Google”: interfejs One Tap możesz skonfigurować tak, aby wyświetlał tylko dane logowania używane przez użytkownika wcześniej. Przypomni to użytkownikom, którzy rzadko logowali się podczas ostatniego logowania, i uniknij przypadkowego utworzenia nowego konta przy użyciu Twojej aplikacji.

Jeśli logowanie się w Twojej aplikacji jest opcjonalne, pomyśl o logowaniu jednokrotnym na dowolnym ekranie, na którym będzie można korzystać z tej funkcji po zalogowaniu się. Jeśli na przykład użytkownicy mogą przeglądać treści w Twojej aplikacji po wylogowaniu się, ale po zalogowaniu się dodawać komentarze lub dodawać produkty do koszyka, może to być sensowny kontekst.

Z wymienionych wyżej powodów aplikacje opcjonalne powinny też logować się na ekranach logowania.

Zanim zaczniesz

1. Skonfiguruj klienta logowania jednym dotknięciem

Możesz skonfigurować klienta logowania One Tap tak, aby logował użytkowników przy użyciu zapisanych haseł, zapisanych kont Google lub obu tych elementów. Zalecamy korzystanie z obu ustawień, aby umożliwić tworzenie kont jednym kliknięciem dla nowych użytkowników i automatyczne logowanie lub logowanie jednym kliknięciem dla jak największej liczby powracających użytkowników.

Jeśli Twoja aplikacja używa logowania opartego na hasłach, użyj setPasswordRequestOptions() do włączenia żądań danych logowania haseł.

Jeśli aplikacja wykorzystuje Logowanie przez Google, użyj setGoogleIdTokenRequestOptions(), aby włączyć i skonfigurować żądania tokenów Google ID:

  • Ustaw identyfikator klienta serwera na identyfikator utworzony w konsoli interfejsów API Google. Pamiętaj, że to identyfikator klienta Twojego serwera, a nie identyfikatora klienta Androida.

  • Skonfiguruj klienta, aby filtrować według autoryzowanych kont. Gdy włączysz tę opcję, klient One Tap poprosi użytkownika o zalogowanie się w Twojej aplikacji tylko za pomocą kont Google, które już wcześniej były używane. Dzięki temu użytkownicy będą mogli zalogować się, jeśli nie mają pewności, czy mają już konto, lub którego konta Google używają, i nie dopuścić do przypadkowego utworzenia nowego konta w Twojej aplikacji.

  • Jeśli chcesz automatycznie logować użytkowników, włącz tę funkcję za pomocą setAutoSelectEnabled(). Automatyczne logowanie jest możliwe po spełnieniu tych kryteriów:

    • Użytkownik ma dokładnie 1 zapisane dane logowania do aplikacji, 1 zapisane hasło lub 1 zapisane konto Google.
    • Użytkownik nie wyłączył automatycznego logowania w ustawieniach konta Google.
  • Choć jest to opcjonalne, zdecydowanie zalecamy stosowanie wartości jednorazowej, by poprawić bezpieczeństwo logowania i uniknąć ataków ponownego odtwarzania. Używaj właściwości setNonce, by w każdym żądaniu uwzględnić wartość jednorazową. Więcej informacji o generowaniu wartości jednorazowej znajdziesz w sekcji Uzyskiwanie wartości jednorazowej w sekcji 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. Sprawdzanie zalogowanego użytkownika

Jeśli Twoja aktywność może być wykorzystywana przez niezalogowanego użytkownika, przed wyświetleniem interfejsu logowania jednym dotknięciem sprawdź stan tego użytkownika.

Sprawdzaj, czy użytkownik odmówił już logowania jednym kliknięciem, zamykając potwierdzenie lub dotykając go. Może to być po prostu zasada logiczna aktywności. Patrz sekcja Przestań wyświetlać interfejs One Tap poniżej.

3. Wyświetlanie interfejsu logowania jednym dotknięciem

Jeśli użytkownik nie jest zalogowany, ale nie odmówił jeszcze korzystania z logowania jednym dotknięciem, wywołaj metodę beginSignIn() obiektu klienckiego i dołącz detektory do zwróconego obiektu Task. Aplikacje zwykle korzystają z tej metody w metodzie onCreate() aktywności lub po zakończeniu przenoszenia ekranu, gdy używasz architektury z pojedynczą aktywnością.

Klient One Tap wywoła metodę wykrywania powodzenia, jeśli użytkownik ma zapisane dane logowania w aplikacji. W detektorze udanych żądań pobierz intencję z wyniku Task i przekaż ją do startIntentSenderForResult(), aby uruchomić interfejs logowania One Tap.

Jeśli użytkownik nie ma żadnych zapisanych danych logowania, klient One Tap wywoła metodę wykrywania błędów. W takim przypadku nie musisz nic robić: możesz po prostu kontynuować prezentację aplikacji po wylogowaniu się. Jeśli jednak zajmujesz się rejestracją jednym dotknięciem, możesz rozpocząć tutaj proces tworzenia konta bez problemów. Zobacz Tworzenie nowych kont jednym kliknięciem.

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. Obsługa odpowiedzi użytkownika

Odpowiedź użytkownika na prośbę o zalogowanie się jednym kliknięciem zostanie zgłoszona w Twojej aplikacji za pomocą metody onActivityResult() związana z aktywnością. Jeśli użytkownik się zaloguje, zostaną zapisane zapisane dane logowania. Jeśli użytkownik odmówi logowania się, zamykając interfejs One Tap lub dotykając go poza tym kodem, wyświetli się kod z kodem RESULT_CANCELED. Aplikacja musi obsługiwać obie te możliwości.

Logowanie się przy użyciu pobranych danych logowania

Jeśli użytkownik zdecyduje się udostępnić dane logowania Twojej aplikacji, możesz je uzyskać, przekazując dane intencji z metody onActivityResult() do klienta One Tap getSignInCredentialFromIntent(). Dane logowania będą miały niezerową właściwość googleIdToken, jeśli użytkownik udostępnił dane logowania do konta Google Twojej aplikacji, lub inną wartość password, jeśli użytkownik udostępnił zapisane hasło.

Dane logowania służą do uwierzytelniania w backendzie aplikacji.

  • Po pobraniu pary nazwy użytkownika i hasła użyj ich do logowania w taki sam sposób, jak w przypadku ręcznego podania ich przez użytkownika.
  • Jeśli dane logowania do konta Google zostały pobrane, użyj tokena identyfikatora, aby uwierzytelnić się w backendzie. Jeśli zdecydujesz się na używanie wartości jednorazowej, aby uniknąć ponownego odtwarzania ataków, sprawdź wartość odpowiedzi na serwerze backendu. Zobacz Uwierzytelnianie za pomocą backendu przy użyciu tokenów identyfikatora.

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) {
                    // ...
                }
            }
        }
    }
    // ...
}

Wyłączanie wyświetlania interfejsu One One

Jeśli użytkownik odmówił zalogowania, wywołanie metody getSignInCredentialFromIntent() spowoduje wyświetlenie ciągu ApiException z kodem stanu CommonStatusCodes.CANCELED. W takim przypadku należy tymczasowo wyłączyć interfejs logowania jednym dotknięciem, aby nie denerwować użytkowników powtarzających się prośbami. Zgodnie z poniższym przykładem możesz skonfigurować właściwość w sekcji Aktywność, która służy do określania, czy zaoferować użytkownikowi logowanie jednym dotknięciem. Możesz też zapisać wartość w tagu SharedPreferences lub użyć innej metody.

Ważne jest, aby wdrożyć własne ograniczenie liczby żądań logowania jednym dotknięciem. Jeśli użytkownik się nie anuluje, a użytkownik anuluje kilka próśb z rzędu, prośba nie pojawi się w ciągu najbliższych 24 godzin.

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. Uchwyć wylogowanie

Gdy użytkownik wyloguje się z Twojej aplikacji, wywołaj metodę klienta One Tap signOut(). Wywoływanie signOut() spowoduje wyłączenie automatycznego logowania, dopóki użytkownik nie zaloguje się ponownie.

Nawet jeśli nie korzystasz z automatycznego logowania, ten krok jest ważny, ponieważ gwarantuje, że gdy użytkownik wyloguje się z Twojej aplikacji, stan uwierzytelniania wszystkich używanych przez Ciebie interfejsów API usług Google Play również zostanie zresetowany.

Dalsze kroki

Jeśli skonfigurujesz klienta One Tap tak, by pobierał dane logowania Google, Twoja aplikacja będzie mogła pobierać tokeny identyfikatora Google reprezentujące użytkowników i konta Google. Dowiedz się, jak używać tych tokenów w backendzie.

Jeśli obsługujesz Logowanie przez Google, możesz też użyć klienta One Tap, aby dodać proces tworzenia konta bezproblemowy do Twojej aplikacji.