Consentire agli utenti di accedere con le credenziali salvate

Utilizza il client di accesso One Tap per richiedere all'utente l'autorizzazione a recuperare una delle credenziali che ha utilizzato in precedenza per accedere alla tua app. Queste credenziali possono essere un Account Google o una combinazione di nome utente e password che ha salvato con Google utilizzando la compilazione automatica di Chrome, Android o Smart Lock per le password.

Interfaccia utente di accesso con un tocco

Quando le credenziali vengono recuperate correttamente, puoi utilizzarle per consentire all'utente di accedere alla tua app senza problemi.

Se l'utente non ha salvato credenziali, non viene visualizzata alcuna UI e puoi fornire la normale esperienza di disconnessione.

Dove devo utilizzare l'accesso One Tap?

Se la tua app richiede agli utenti di accedere, mostra la UI di One Tap sulla schermata di accesso. Questa soluzione può essere utile anche se hai già un pulsante "Accedi con Google": poiché l'interfaccia utente di One Tap può essere configurata per mostrare solo le credenziali che l'utente ha utilizzato in precedenza per accedere, può ricordare agli utenti che accedono di rado come hanno eseguito l'accesso l'ultima volta e impedire loro di creare accidentalmente nuovi account con la tua app.

Se l'accesso è facoltativo per la tua app, valuta la possibilità di utilizzare l'accesso One Tap in qualsiasi schermata che offre un'esperienza migliorata con l'accesso. Ad esempio, se gli utenti possono sfogliare i contenuti con la tua app dopo aver eseguito la disconnessione, ma possono pubblicare commenti o aggiungere articoli a un carrello degli acquisti solo dopo aver eseguito l'accesso, questo sarebbe un contesto sensato per l'accesso One Tap.

Anche le app con accesso facoltativo devono utilizzare l'accesso One Tap nelle schermate di accesso, per i motivi indicati sopra.

Prima di iniziare

1. Configurare il client di accesso One Tap

Puoi configurare il client di accesso One Tap in modo che gli utenti accedano con le password salvate, gli Account Google salvati o entrambi. (È consigliabile supportare entrambi per attivare la creazione di account con un solo tocco per i nuovi utenti e l'accesso automatico o con un solo tocco per il maggior numero possibile di utenti di ritorno.)

Se la tua app utilizza l'accesso basato su password, utilizza setPasswordRequestOptions() per attivare le richieste di credenziali password.

Se la tua app utilizza Accedi con Google, usa setGoogleIdTokenRequestOptions() per attivare e configurare le richieste di token ID Google:

  • Imposta l'ID client server sull'ID che hai creato nella console API di Google. Tieni presente che si tratta dell'ID client del server, non dell'ID client Android.

  • Configura il client in modo che filtri in base agli account autorizzati. Quando attivi questa opzione, il client One Tap chiede agli utenti di accedere alla tua app solo con gli Account Google che hanno già utilizzato in passato. In questo modo, gli utenti possono accedere correttamente quando non sanno se hanno già un account o quale Account Google hanno utilizzato e impedisce loro di creare accidentalmente nuovi account con la tua app.

  • Se vuoi che gli utenti accedano automaticamente quando possibile, attiva la funzionalità con setAutoSelectEnabled(). L'accesso automatico è possibile quando vengono soddisfatti i seguenti criteri:

    • L'utente ha esattamente una credenziale salvata per la tua app, ovvero una password salvata o un Account Google salvato.
    • L'utente non ha disattivato l'accesso automatico nelle impostazioni dell'Account Google.
  • Sebbene sia facoltativo, ti consigliamo vivamente di utilizzare un nonce per migliorare la sicurezza dell'accesso ed evitare attacchi di replay. Utilizza setNonce per includere un nonce in ogni richiesta. Per suggerimenti e ulteriori dettagli sulla generazione di un nonce, consulta la sezione Ottenere un nonce di 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. Controllare se è presente un utente che ha eseguito l'accesso

Se la tua Activity potrebbe essere utilizzata da un utente che ha eseguito l'accesso o da un utente che non ha eseguito l'accesso, controlla lo stato dell'utente prima di visualizzare l'interfaccia utente di accesso One Tap.

Devi anche tenere traccia se l'utente ha già rifiutato di utilizzare l'accesso One Tap chiudendo la richiesta o toccando al di fuori di essa. Può essere semplice come una proprietà booleana dell'attività. (vedi Interrompere la visualizzazione della UI di One Tap di seguito).

3. Visualizzare la UI di accesso One Tap

Se l'utente non ha eseguito l'accesso e non ha ancora rifiutato di utilizzare l'accesso One Tap, chiama il metodo beginSignIn() dell'oggetto client e collega i listener all'oggetto Task restituito. In genere, le app lo fanno nel metodo onCreate() dell'Activity o dopo le transizioni dello schermo quando si utilizza un'architettura a singola Activity.

Il client One Tap chiamerà il listener di successo se l'utente ha credenziali salvate per la tua app. Nel listener di successo, recupera il pending intent dal risultato Task e passalo a startIntentSenderForResult() per avviare l'interfaccia utente di accesso One Tap.

Se l'utente non ha credenziali salvate, il client One Tap chiamerà il listener di errore. In questo caso, non è necessario alcun intervento: puoi semplicemente continuare a presentare l'esperienza dell'app senza accesso. Tuttavia, se supporti la registrazione con un solo tocco, puoi avviare il flusso qui per un'esperienza di creazione dell'account senza interruzioni. Vedi Creare nuovi account con un solo tocco.

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. Gestire la risposta dell'utente

La risposta dell'utente al prompt di accesso One Tap verrà comunicata alla tua app utilizzando il metodo onActivityResult() dell'attività. Se l'utente ha scelto di accedere, il risultato sarà una credenziale salvata. Se l'utente ha rifiutato di accedere chiudendo l'interfaccia utente One Tap o toccando al di fuori, il risultato verrà restituito con il codice RESULT_CANCELED. La tua app deve gestire entrambe le possibilità.

Accedere con le credenziali recuperate

Se l'utente ha scelto di condividere le credenziali con la tua app, puoi recuperarle passando i dati intent da onActivityResult() al metodo getSignInCredentialFromIntent() del client One Tap. La credenziale avrà una proprietà googleIdToken non nulla se l'utente ha condiviso una credenziale dell'Account Google con la tua app o una proprietà password non nulla se l'utente ha condiviso una password salvata.

Utilizza la credenziale per l'autenticazione con il backend della tua app.

  • Se è stata recuperata una coppia di nome utente e password, utilizzala per accedere come se l'utente l'avesse fornita manualmente.
  • Se sono state recuperate le credenziali dell'Account Google, utilizza il token ID per l'autenticazione con il backend. Se hai scelto di utilizzare un nonce per evitare attacchi di replay, controlla il valore di risposta sul server di backend. Consulta Autenticarsi con un backend utilizzando i token ID.

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

Interrompere la visualizzazione della UI di Accesso One Tap

Se l'utente ha rifiutato di accedere, la chiamata a getSignInCredentialFromIntent() genererà un ApiException con un codice di stato CommonStatusCodes.CANCELED. In questo caso, devi disattivare temporaneamente l'interfaccia utente di accesso One Tap per non infastidire gli utenti con richieste ripetute. Il seguente esempio lo fa impostando una proprietà sull'Activity, che utilizza per determinare se offrire all'utente l'accesso One Tap; tuttavia, potresti anche salvare un valore in SharedPreferences o utilizzare un altro metodo.

È importante implementare la limitazione della frequenza dei prompt di accesso One Tap. In caso contrario, se un utente annulla diverse richieste di seguito, il client One Tap non mostrerà richieste all'utente per le 24 ore successive.

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. Gestire l'uscita

Quando un utente esce dalla tua app, chiama il metodo signOut() del client One Tap. La chiamata a signOut() disattiva l'accesso automatico finché l'utente non accede di nuovo.

Anche se non utilizzi l'accesso automatico, questo passaggio è importante perché garantisce che quando gli utenti escono dalla tua app, venga reimpostato anche lo stato di autenticazione di tutte le API dei servizi Google Play che utilizzi.

Passaggi successivi

Se hai configurato il client One Tap per recuperare le credenziali Google, la tua app ora può ottenere token ID Google che rappresentano gli Account Google dei tuoi utenti. Scopri come utilizzare questi token nel backend.

Se supporti Accedi con Google, puoi anche utilizzare il client One Tap per aggiungere flussi di creazione di account senza problemi alla tua app.