Fazer login de usuários com as credenciais salvas

Use o cliente de login com um toque para solicitar permissão do usuário para recuperar uma das credenciais usadas anteriormente para fazer login no app. Essas credenciais podem ser uma Conta do Google ou uma combinação de nome de usuário e senha salva com o 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 de maneira simples no seu app.

Se o usuário não tiver salvo nenhuma credencial, nenhuma IU será apresentada, e você poderá fornecer sua experiência normal sem fazer login.

Onde devo usar o login com um toque?

Se o app exigir que os usuários façam login, exiba a IU com um toque na tela de login. Isso pode ser útil mesmo que você já tenha um botão de "Login com o Google" porque a IU com um toque pode ser configurada para mostrar apenas as credenciais que o usuário usou anteriormente para fazer login, pode ser um lembrete para os usuários que fazem login com pouca frequência na última vez e impedir que eles criem acidentalmente contas novas com o app.

Se o login for opcional para seu app, considere usar o login com um toque em qualquer tela com uma experiência aprimorada ao fazer login. Por exemplo, se os usuários puderem navegar pelo conteúdo com seu app enquanto estiverem desconectados, mas puderem postar comentários ou adicionar itens a um carrinho de compras depois de fazer login, isso seria um contexto sensato para o login com um toque.

Os apps opcionais de login também precisam usar o login com um toque nas telas de login, pelos motivos mencionados acima.

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, Contas do Google salvas ou ambos. É recomendável oferecer suporte aos dois 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 retornantes.

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 as solicitações de token de ID do Google:

  • Defina o ID do cliente do servidor como o ID que você criou no console de APIs do Google. Este é o ID do cliente do seu servidor, não o 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 seu app com as Contas do Google que já usaram antes. Isso pode ajudar os usuários a fazer login com êxito quando não tiverem certeza se já têm uma conta ou qual Conta do Google eles usaram, além de evitar que os usuários criem novas contas acidentalmente com seu app.

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

    • O usuário tem exatamente uma credencial salva para o app, ou seja, uma senha ou uma Conta do Google salva.
    • O usuário não desativou o login automático nas configurações da Conta do Google.
  • Embora opcional, recomendamos usar um valor de uso único para melhorar a segurança de login e evitar ataques repetidos. Use setNonce para incluir um valor de uso único em cada solicitação. Consulte a seção Receber um valor de uso único da SafetyNet para ver sugestões e outros 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 um usuário fez login

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

Você também precisa acompanhar se o usuário já recusou o login com um toque fechando a solicitação ou tocando fora dela. Ela pode ser tão simples quanto uma propriedade booleana da atividade. Consulte Parar de exibir a IU com um toque abaixo.

3. Exibir a IU de login com um toque

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

O cliente de um toque chamará o listener de êxito se o usuário tiver alguma credencial salva para o app. No listener de êxito, receba a intent pendente do resultado Task e transmita-a para startIntentSenderForResult() para iniciar a IU de login de um toque.

Se o usuário não tiver credenciais salvas, o cliente de um toque chamará o listener de falha. Nesse caso, nenhuma ação é necessária: basta continuar apresentando a experiência sem fazer login no app. No entanto, se oferecer suporte para a inscrição com um toque, você poderá iniciar esse fluxo aqui e, assim, ter uma experiência perfeita de criação de conta. 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 tiver recusado o login, seja fechando a IU com um toque ou tocando fora dela, o resultado retornará com o código RESULT_CANCELED. Seu app precisa lidar com as duas possibilidades.

Fazer login com as credenciais recuperadas

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

Use a credencial para fazer a autenticação com o back-end do seu app.

  • Se um par de nome de usuário e senha tiver sido recuperado, use-o para fazer login da mesma forma que faria se o usuário tivesse fornecido esses pares 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 ajudar a evitar ataques de repetição, verifique o valor da resposta no seu 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 com um toque

Se o usuário não fizer 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 incomodar os usuários com solicitações repetidas. O exemplo a seguir faz isso configurando uma propriedade na atividade, que ela usa para determinar se oferecer ao usuário um login com um toque. 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 nas 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 de um toque não solicitará a permissão para o usuário nas 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 o logout

Quando um usuário sair do seu app, chame o método signOut() do cliente de um toque. 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 os usuários saiam do app quando o estado de autenticação de qualquer API Play Services usada também for redefinido.

Próximas etapas

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

Se você oferece suporte ao Login do Google, também pode usar o cliente de um toque para adicionar fluxos de criação de contas simples ao seu app.