Cómo crear un tipo de cuenta personalizado

Hablamos sobre cómo acceder a las API de Google, que utilizan cuentas y usuarios definidos por Google. Sin embargo, si tienes tu propio servicio en línea, no tendrá cuentas ni usuarios de Google. En ese caso, ¿qué debes hacer? Es muy sencillo instalar nuevos tipos de cuentas en el dispositivo de un usuario. En esta lección, se explica cómo crear un tipo de cuenta personalizado que funcione de la misma manera que las cuentas integradas.

Cómo implementar un código de cuenta personalizado

Lo primero que necesitarás es una forma de obtener las credenciales del usuario. Se puede tratar de un diálogo que solicite un nombre y una contraseña. O puede ser un procedimiento más inusual, como una contraseña de un solo uso o un escaneo biométrico. De cualquier manera, es tu responsabilidad implementar un código que realice lo siguiente:

  1. Recopilar credenciales del usuario
  2. Autenticar las credenciales con el servidor
  3. Almacenar las credenciales en el dispositivo

Por lo general, una sola actividad puede encargarse de los tres requisitos. A esta la llamaremos actividad del autenticador.

Debido a que necesitan interactuar con el sistema de AccountManager, las actividades de autenticación tienen ciertos requisitos que las actividades normales no poseen. Para simplificar el proceso, el marco de trabajo de Android proporciona una clase base, AccountAuthenticatorActivity, que puedes extender para crear tu propio autenticador personalizado.

La forma en que abordes los dos primeros requisitos de una actividad de autenticación (la recopilación de credenciales y la autenticación) dependerá completamente de ti. (Después de todo, si hubiera una sola forma de hacerlo, no habría necesidad de tipos de cuenta "personalizados"). El tercer requisito tiene una implementación canónica y bastante simple:

Kotlin

    Account(username, your_account_type).also { account ->
        accountManager.addAccountExplicitly(account, password, null)
    }
    

Java

    final Account account = new Account(username, your_account_type);
    accountManager.addAccountExplicitly(account, password, null);
    

Aborda la seguridad con inteligencia

Es importante comprender que AccountManager no es un servicio de encriptación ni un llavero. Almacena las credenciales de la cuenta como las pasas, en texto sin formato. En la mayoría de los dispositivos, esto no es una preocupación particular, ya que las guarda en una base de datos a la que solo se puede acceder con derechos de administrador. Sin embargo, en un dispositivo con derechos de administrador, cualquier persona con acceso adb al dispositivo puede leer las credenciales.

Ten esto en cuenta y no pases la contraseña real del usuario a AccountManager.addAccountExplicitly(). En cambio, debes almacenar criptográficamente un token seguro que sea de uso limitado para un atacante. Si tus credenciales de usuario están protegiendo datos valiosos, procura hacer algo similar de manera cuidadosa.

Recuerda: Cuando se trata del código de seguridad, sigue la regla "Mythbusters": ¡no pruebes esto en casa! Consulta a un profesional de seguridad antes de implementar cualquier código de cuenta personalizado.

Ahora que ya no hay advertencias de seguridad, es hora de volver al trabajo. Ya implementaste la mayor parte de tu código de cuenta personalizado; lo que queda son los detalles.

Cómo extender AbstractAccountAuthenticator

Para que AccountManager funcione con el código de tu cuenta personalizada, necesitas una clase que implemente las interfaces que AccountManager espera. Se trata de la clase de autenticador.

La forma más simple de crear una clase de autenticador es extender AbstractAccountAuthenticator e implementar sus métodos abstractos. Si hiciste las lecciones anteriores, los métodos abstractos de AbstractAccountAuthenticator deberían resultarte familiares: son el opuesto de los métodos que invocaste en la lección anterior para obtener información de cuentas y tokens de autorización.

Implementar correctamente una clase de autenticador requiere una serie de fragmentos de código separados. En primer lugar, AbstractAccountAuthenticator tiene siete métodos abstractos que debes anular. En segundo lugar, debes agregar un filtro de intents para "android.accounts.AccountAuthenticator" al manifiesto de tu aplicación (se muestra en la siguiente sección). Finalmente, debes proporcionar dos recursos XML que definan, entre otros aspectos, el nombre del tipo de cuenta personalizado y el ícono que el sistema mostrará junto a las cuentas de este tipo.

En la documentación de AbstractAccountAuthenticator, encontrarás una guía paso a paso para implementar de manera correcta una clase de autenticador y los archivos XML. También hay una implementación de muestra en la app de ejemplo de SampleSyncAdapter.

A medida que leas el código de SampleSyncAdapter, notarás que varios de los métodos muestran un intent en un paquete. Se trata del mismo intent que se utilizará para iniciar tu actividad de autenticador personalizado. Si tu actividad de autenticación necesita algún parámetro de inicialización especial, puedes adjuntarlo al intent mediante Intent.putExtra().

Cómo crear un servicio de autenticador

Ahora que tienes una clase de autenticador, necesitas un lugar donde usarla. Los autenticadores de cuentas deben estar disponibles para varias aplicaciones y funcionar en segundo plano, por lo que deben ejecutarse dentro de un Service. A este lo llamaremos servicio de autenticador.

Tu servicio de autenticador puede ser muy simple. Todo lo que necesitas hacer es crear una instancia de tu clase de autenticador en onCreate() y llamar a getIBinder() en onBind(). SampleSyncAdapter incluye un buen ejemplo de un servicio de autenticador.

No olvides agregar una etiqueta <service> a tu archivo de manifiesto y agregar un filtro de intents para el intent AccountAuthenticator y declarar el autenticador de cuenta:

    <service ...>
       <intent-filter>
          <action android:name="android.accounts.AccountAuthenticator" />
       </intent-filter>
       <meta-data android:name="android.accounts.AccountAuthenticator"
                 android:resource="@xml/authenticator" />
    </service>
    

Cómo distribuir el servicio

¡Listo! Ahora, el sistema reconoce tu tipo de cuenta, junto con todos los tipos de cuentas conocidas, como "Google" y "Corporate". Puedes usar la opción de configuración Cuentas y sincronización para agregar una cuenta, y las apps que soliciten cuentas de tu tipo personalizado podrán enumerar y autenticar como lo harían con cualquier otro tipo de cuenta.

Por supuesto, todo esto supone que el servicio de tu cuenta esté instalado en el dispositivo. Si solo una app va a acceder al servicio, esto no representa un gran problema: solo debes empaquetar el servicio en ella. Sin embargo, si deseas que más de una app utilice el servicio de tu cuenta, el panorama es más complicado. No es conveniente empaquetar el servicio con todas tus apps y tener varias copias de él ocupando espacio en el dispositivo del usuario.

Una solución es colocar el servicio en un pequeño APK especial. Cuando una app desea usar tu tipo de cuenta personalizado, puede verificar el dispositivo para ver si está disponible tu servicio. De lo contrario, puede dirigir al usuario a Google Play para descargarlo. Esto puede parecer un gran problema al principio, pero en comparación con la alternativa de volver a ingresar las credenciales para cada app que utiliza tu cuenta personalizada, es muy fácil.