Permite que los usuarios accedan con sus credenciales guardadas

Usa el cliente de acceso con One Tap a fin de solicitar permiso al usuario a fin de recuperar una de las credenciales que usó antes para acceder a tu app. Estas credenciales pueden ser una Cuenta de Google o una combinación de nombre de usuario y contraseña que guardaron con Google mediante Chrome, el autocompletado de Android o Smart Lock para contraseñas.

IU de acceso con un toque

Cuando las credenciales se recuperan correctamente, puedes usarlas para que el usuario acceda a tu app sin inconvenientes.

Si el usuario no guardó ninguna credencial, no se presentará ninguna IU y podrás brindar una experiencia de acceso normal sin acceder a tu cuenta.

¿Dónde debo usar el acceso con One Tap?

Si tu app requiere que los usuarios accedan, muestra la IU de One Tap en la pantalla de acceso. Esto puede ser útil incluso si ya tienes un botón "Acceder con Google": como la IU de One Tap se puede configurar para que muestre solo las credenciales que el usuario usó antes para acceder, puede ser un recordatorio para los usuarios que no suelen acceder a su cuenta la última vez y evitar que creen nuevas cuentas con tu app por accidente.

Si el acceso es opcional para tu app, considera usar el acceso con One Tap en cualquier pantalla que tenga una experiencia mejorada. Por ejemplo, si los usuarios pueden explorar contenido con la app sin acceder a la cuenta, pero solo pueden publicar comentarios o agregar artículos a un carrito de compras después de acceder, se trata de un contexto razonable para el acceso con One Tap.

Las apps opcionales de acceso también deben usar el acceso con One Tap en sus pantallas de acceso, por las razones mencionadas anteriormente.

Antes de comenzar

1. Configura el cliente de acceso con One Tap

Puedes configurar el cliente de acceso con One Tap para que los usuarios accedan con contraseñas guardadas, Cuentas de Google guardadas o cualquiera de las dos. (se recomienda admitir ambos a fin de habilitar la creación de cuentas con un toque para usuarios nuevos y el acceso automático o con un toque a tantos usuarios recurrentes como sea posible).

Si tu app usa el acceso basado en contraseñas, usa setPasswordRequestOptions() para habilitar las solicitudes de credenciales de contraseña.

Si tu app usa el Acceso con Google, usa setGoogleIdTokenRequestOptions() para habilitar y configurar las solicitudes de token de ID de Google:

  • Configura el ID de cliente del servidor como el ID que creaste en la consola de las API de Google. Ten en cuenta que este es el ID de cliente de tu servidor, no tu ID de cliente de Android.

  • Configurar el cliente para filtrar por cuentas autorizadas Cuando habilitas esta opción, el cliente de One Tap solo solicita a los usuarios que accedan a tu app con Cuentas de Google que ya hayan usado. Esto puede ayudar a los usuarios a acceder con éxito cuando no están seguros de si ya tienen una cuenta o qué Cuenta de Google usaron, y evita que los usuarios creen nuevas cuentas con tu app por accidente.

  • Si deseas que los usuarios accedan automáticamente, cuando sea posible, habilita la función con setAutoSelectEnabled(). El acceso automático es posible cuando se cumplen los siguientes criterios:

    • El usuario tiene exactamente una credencial guardada para tu app. Es decir, una contraseña guardada o una Cuenta de Google guardada.
    • El usuario no inhabilitó el acceso automático en la configuración de la Cuenta de Google.
  • Si bien es opcional, te recomendamos que consideres usar un nonce para mejorar la seguridad de acceso y evitar ataques de repetición. Usa setNonce para incluir un nonce en cada solicitud. Consulta la sección Cómo obtener un nonce de SafetyNet para obtener sugerencias y detalles adicionales sobre cómo generar un 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. Verifica si un usuario accedió

Si un usuario que accedió a su cuenta o uno que no accedió a su cuenta pueden usar tu actividad, verifica su estado antes de mostrar la IU de acceso con One Tap.

También debes realizar un seguimiento de si el usuario ya rechazó el acceso con One Tap. Para ello, cierra el mensaje o presiona fuera de él. Esto puede ser tan simple como una propiedad booleana de tu actividad. (Consulta Cómo dejar de mostrar la IU de One Tap a continuación).

3. Cómo mostrar la IU de acceso con One Tap

Si el usuario no accedió y aún no rechazó el acceso con One Tap, llama al método beginSignIn() del objeto cliente y adjunta los objetos de escucha al objeto Task que muestra. Por lo general, las apps hacen esto en el método onCreate() de Activity o después de las transiciones de pantalla cuando se usa una arquitectura de actividad única.

El cliente de One Tap llamará al objeto de escucha que detecta el resultado correcto si el usuario tiene credenciales guardadas para tu app. En el objeto de escucha que detecta el resultado correcto, obtén el intent pendiente del resultado Task y pásalo a startIntentSenderForResult() para iniciar la IU de acceso con One Tap.

Si el usuario no tiene credenciales guardadas, el cliente de One Tap llamará al objeto de escucha que falla. En este caso, no es necesario realizar ninguna acción: simplemente puedes seguir presentando la experiencia de cierre de sesión de la app. Sin embargo, si admites el registro en One Tap, puedes iniciar ese flujo aquí para una experiencia fluida de creación de cuentas. Consulta Crear cuentas nuevas con solo presionar una vez.

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. Manejar la respuesta del usuario

La respuesta del usuario al mensaje de acceso con One Tap se informará a la app mediante el método onActivityResult() de tu actividad. Si el usuario decidió acceder, el resultado será una credencial guardada. Si el usuario rechazó el acceso, ya sea al cerrar la IU de One Tap o al presionar fuera de ella, el resultado mostrará el código RESULT_CANCELED. Tu app debe manejar ambas posibilidades.

Acceder con credenciales recuperadas

Si el usuario eligió compartir credenciales con tu app, puedes recuperarlas si pasas los datos del intent de onActivityResult() al método getSignInCredentialFromIntent() del cliente de One Tap. La credencial tendrá una propiedad googleIdToken no nula si el usuario compartió una credencial de Cuenta de Google con tu app o una propiedad no nula password si el usuario compartió una contraseña guardada.

Usa la credencial para autenticar con el backend de tu app.

  • Si se recuperó un par de nombre de usuario y contraseña, úsalos para acceder de la misma manera en que lo harías si el usuario los hubiera suministrado manualmente.
  • Si se recuperaron las credenciales de la Cuenta de Google, usa el token de ID para autenticar con el backend. Si decidiste usar un nonce para ayudar a evitar ataques de repetición, verifica el valor de respuesta en tu servidor de backend. Consulta Autentica con un backend mediante 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) {
                    // ...
                }
            }
        }
    }
    // ...
}

Cómo dejar de mostrar la IU de One Tap

Si el usuario rechazó el acceso, la llamada a getSignInCredentialFromIntent() mostrará un ApiException con un código de estado CommonStatusCodes.CANCELED. Cuando esto sucede, debes inhabilitar temporalmente la IU de acceso con One Tap para no molestar a los usuarios con mensajes repetidos. En el siguiente ejemplo, esto se logra mediante la configuración de una propiedad en la actividad, que utiliza para determinar si se debe ofrecer al usuario el acceso con One Tap. Sin embargo, también puedes guardar un valor en SharedPreferences o usar otro método.

Es importante que implementes tu propio límite de frecuencia de mensajes de acceso con One Tap. Si no lo haces y un usuario cancela varios mensajes seguidos, el cliente de One Tap no le solicitará que lo haga durante las siguientes 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. Controla el cierre de sesión

Cuando un usuario salga de tu app, llama al método signOut() del cliente de One Tap. Si llamas a signOut(), se inhabilita el acceso automático hasta que el usuario vuelve a acceder.

Incluso si no usas el acceso automático, este paso es importante porque garantiza que, cuando los usuarios salgan de la app, también se restablezca el estado de autenticación de las API de los Servicios de Play que uses.

Próximos pasos

Si configuraste el cliente de One Tap para recuperar credenciales de Google, tu app ahora puede obtener tokens de ID de Google que representan tus Cuentas de Google. Obtén más información sobre cómo usar estos tokens en el backend.

Si admites el Acceso con Google, también puedes usar el cliente de One Tap para agregar flujos de creación de cuentas sin inconvenientes a tu app.