Fazer login dos usuários com as credenciais salvas

Use o cliente de login com um toque para pedir permissão ao usuário para recuperar uma das credenciais que ele usou anteriormente para fazer login no seu app. Essas credenciais podem ser uma Conta do Google ou uma combinação de nome de usuário e senha salva no Google usando o Chrome, o preenchimento automático do Android ou o Smart Lock para senhas.

IU de login com um toque

Quando as credenciais forem recuperadas, você poderá usá-las para fazer login do usuário no seu app sem complicações.

Se o usuário não tiver salvado nenhuma credencial, nenhuma interface será apresentada, e você poderá oferecer a experiência normal de usuário desconectado.

Onde devo usar o Login com um toque?

Se o app exigir que os usuários façam login, mostre a interface do One Tap na tela de login. Isso pode ser útil mesmo se você já tiver um botão "Fazer login com o Google": como a IU do One Tap pode ser configurada para mostrar apenas as credenciais que o usuário usou anteriormente para fazer login, ela pode ser um lembrete para os usuários que não fazem login com frequência de como fizeram login da última vez e evitar que criem acidentalmente novas contas com seu app.

Se o login for opcional no seu app, use o login com um toque em qualquer tela que tenha uma experiência aprimorada pelo login. Por exemplo, se os usuários podem procurar conteúdo no app sem fazer login, mas só podem postar comentários ou adicionar itens a um carrinho de compras depois de fazer login, esse seria um contexto adequado para o login com um toque.

Os apps com login opcional também precisam usar o login com um toque nas telas de login, pelos mesmos motivos.

Antes de começar

1. Configurar o cliente de login com um toque

É possível configurar o cliente de login com um toque para fazer login de usuários com senhas salvas, Contas do Google salvas ou ambas. É recomendável oferecer suporte a ambos para permitir a criação de contas com um toque para novos usuários e o login automático ou com um toque para o maior número possível de usuários recorrentes.

Se o app usa login baseado em senha, use setPasswordRequestOptions() para ativar solicitações de credenciais de senha.

Se o app usa o Login do Google, use setGoogleIdTokenRequestOptions() para ativar e configurar solicitações de token de ID do Google:

  • Defina o ID do cliente do servidor como o ID que você criou no console do Google Cloud. Esse é o ID do cliente do servidor, não o ID do cliente do Android.

  • Configure o cliente para filtrar por contas autorizadas. Quando você ativa essa opção, o cliente do One Tap só solicita que os usuários façam login no app com Contas do Google que já foram usadas no passado. Isso pode ajudar os usuários a fazer login quando não tiverem certeza se já têm uma conta ou qual Conta do Google usaram, além de evitar que criem novas contas acidentalmente com seu app.

  • Se você quiser fazer login dos usuários automaticamente quando possível, ative o recurso com setAutoSelectEnabled(). O login automático é possível quando os critérios abaixo são atendidos:

    • O usuário tem exatamente uma credencial salva para o app. Ou seja, uma senha ou Conta do Google salva.
    • O usuário não desativou o login automático nas configurações da Conta do Google.
  • Embora seja opcional, recomendamos que você considere usar um valor de uso único para melhorar a segurança do login e evitar ataques de repetição. Use setNonce para incluir um valor de uso único em cada solicitação. Consulte a seção Obter um valor de uso único do SafetyNet para sugestões e mais detalhes sobre como gerar um valor de uso único.

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. Verificar se há um usuário conectado

Se a atividade puder ser usada por um usuário conectado ou desconectado, verifique o status do usuário antes de mostrar a interface de login com um toque.

Também é preciso monitorar se o usuário já recusou o uso do Login com um toque fechando a solicitação ou tocando fora dela. Pode ser uma propriedade booleana da sua atividade. Consulte Parar de mostrar a interface do One Tap abaixo.

3. Mostrar a interface de login com um toque

Se o usuário não estiver conectado e não tiver recusado o uso do login com um toque, chame o método beginSignIn() do objeto cliente e anexe listeners ao Task que ele retornar. Os apps geralmente fazem isso no método onCreate() da atividade ou após as transições de tela ao usar uma arquitetura de atividade única.

  • Credenciais salvas encontradas: o cliente do One Tap vai chamar o listener de sucesso se o usuário tiver credenciais salvas para seu app. No listener de sucesso, receba a intent pendente do resultado Task e transmita-a para startIntentSenderForResult() para iniciar a interface de login com um toque do One Tap.
  • Nenhuma credencial salva encontrada: o cliente do One Tap vai chamar o listener de falha. Nesse caso, nenhuma ação é necessária: você pode continuar apresentando a experiência de desconexão do app. No entanto, se você oferecer suporte à inscrição com um toque, poderá iniciar esse fluxo aqui para uma experiência de criação de conta integrada. Consulte Criar novas contas com um toque.

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. Processar a resposta do usuário

A resposta do usuário à solicitação de login com um toque será informada ao app usando o método onActivityResult() da atividade. Se o usuário optar por fazer login, o resultado será uma credencial salva. Se o usuário recusar o login, seja fechando a interface do One Tap ou tocando fora dela, o resultado vai retornar com o código RESULT_CANCELED. Seu app precisa lidar com as duas possibilidades.

Fazer login com credenciais recuperadas

Se o usuário optar por compartilhar as credenciais com seu app, você poderá recuperá-las transmitindo os dados de intent de onActivityResult() para o método getSignInCredentialFromIntent() do cliente do One Tap. A credencial terá uma propriedade googleIdToken não nula se o usuário tiver compartilhado uma credencial da Conta do Google com seu app ou uma propriedade password não nula se o usuário tiver compartilhado uma senha salva.

Use a credencial para autenticar com o back-end do app.

  • Se um par de nome de usuário e senha for recuperado, use-o para fazer login da mesma forma que você faria se o usuário o tivesse fornecido manualmente.
  • Se as credenciais da Conta do Google foram recuperadas, use o token de ID para autenticar com seu back-end. Se você optou por usar um valor de uso único para evitar ataques de repetição, verifique o valor da resposta no servidor de back-end. Consulte Autenticar com um back-end usando tokens de 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) {
                      // ...
                  }
              }
          }
      }
      // ...
  }

Parar de mostrar a IU do One Tap

Se o usuário recusar o login, a chamada para getSignInCredentialFromIntent() vai gerar uma ApiException com um código de status CommonStatusCodes.CANCELED. Quando isso acontecer, desative temporariamente a IU de login com um toque para não irritar os usuários com solicitações repetidas. O exemplo a seguir faz isso definindo uma propriedade na atividade, que é usada para determinar se o login com um toque será oferecido ao usuário. No entanto, você também pode salvar um valor em SharedPreferences ou usar outro método.

É importante implementar sua própria limitação de taxa de solicitações de login com um toque. Se você não fizer isso e um usuário cancelar várias solicitações seguidas, o cliente do One Tap não vai solicitar o usuário pelas próximas 24 horas.

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. Processar a saída

Quando um usuário sair do app, chame o método signOut() do cliente do One Tap. Chamar signOut() desativa o login automático até que o usuário faça login novamente.

Mesmo que você não use o login automático, essa etapa é importante porque garante que, quando os usuários saírem do app, o estado de autenticação de todas as APIs do Google Play Services usadas também seja redefinido.

Próximas etapas

Se você configurou o cliente do One Tap para recuperar as credenciais do Google, seu app já pode receber tokens de ID do Google que representam as Contas do Google dos usuários. Saiba como usar esses tokens no back-end.

Se você oferece suporte ao Login do Google, também pode usar o cliente do One Tap para adicionar fluxos de criação de conta sem atrito ao seu app.