En esta página, se describe cómo crear, acceder y borrar una clave de restablecimiento.
Habilidades de Android
Ver en GitHubRestablecer credenciales
android skills add restore-credentialsCompatibilidad de versiones
La función Restore Credentials de Credential Manager funciona en dispositivos que ejecutan Android 9 (nivel de API 28) y versiones posteriores, la versión principal 242200000 (v24.22) o posterior de los Servicios de Google Play (GMS), y la versión 1.5.0 o posterior de la biblioteca de androidx.credentials. En dispositivos sin los Servicios de Google Play o con una versión anterior de GMS, detecta CreateCredentialProviderConfigurationException y GetCredentialProviderConfigurationException para que tu app recurra correctamente al acceso estándar.
Requisitos previos
Configura un servidor de la parte de confianza de WebAuthn similar al servidor para las llaves de acceso. Si ya tienes un servidor configurado para controlar la autenticación con llaves de acceso, puedes volver a usar los mismos extremos de registro y aserción de WebAuthn para las claves de restablecimiento.
Dependencias
Agrega las siguientes dependencias al archivo build.gradle del módulo de tu app:
Kotlin
dependencies { implementation("androidx.credentials:credentials:1.7.0-alpha02") implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha02") }
Groovy
dependencies { implementation "androidx.credentials:credentials:1.7.0-alpha02" implementation "androidx.credentials:credentials-play-services-auth:1.7.0-alpha02" }
Restore Credentials se encuentra disponible en la versión 1.5.0 y posteriores de la biblioteca de androidx.credentials. Sin embargo, se recomienda usar las versiones estables más recientes de las dependencias siempre que sea posible.
Cómo afecta android:allowBackup a Restore Credentials
El atributo de manifiesto android:allowBackup controla la copia de seguridad de los datos de la app, que interactúa con Restore Credentials de tres maneras distintas:
- Transferencia de claves de restablecimiento (independiente de
allowBackup): Los Servicios de Google Play transfieren el par de clavesRestoreCredentialcon una copia de seguridad en la nube encriptada de extremo a extremo o una transferencia local de un dispositivo a otro (D2D), independientemente de siandroid:allowBackupestrueofalse. En el caso de las transferencias de D2D, la clave de restablecimiento siempre se transfiere. La única condición en la que no se transfiere es durante una copia de seguridad en la nube cuando el usuario inhabilitó la copia de seguridad en la nube en la configuración de Android. - Restablecimiento en segundo plano en
BackupAgent(requiereandroid:allowBackup="true"): El acceso silencioso en segundo plano antes de que el usuario abra la app en el dispositivo nuevo (BackupAgent.onRestoreFinished()) solo se ejecuta cuandoandroid:allowBackup="true"yandroid:backupAgentestán configurados en tu manifiesto. Si esandroid:allowBackup="false", restablece la sesión cuando el usuario inicie tuActivityprincipal por primera vez. También puedes implementar ambos enfoques para que la restauración sea más sólida. - Pruebas de copia de seguridad en la nube de Android Studio (requiere
android:allowBackup="true"): Para probar el tipo de copia de seguridad en la nube en la herramienta de copia de seguridad y restablecimiento de Android Studio, se requiereandroid:allowBackup="true", mientras que las pruebas de dispositivo a dispositivo funcionan independientemente deallowBackup.
Descripción general
Restablecer credenciales sigue un ciclo de vida de tres etapas en los dispositivos anteriores y nuevos del usuario:
- Crea una clave de restablecimiento en el dispositivo anterior: Después de que el usuario acceda (o cuando se inicie la app si el usuario ya accedió y no se registra ninguna clave de restablecimiento en el almacenamiento local), recupera las opciones de creación de tu servidor de la parte que confía y llama a
credentialManager.createCredential(). - Accede con la clave de restablecimiento en el dispositivo nuevo: Recupera la
RestoreCredentialtransferida con una estrategia de dos niveles: primero, de forma silenciosa enBackupAgent.onRestoreFinished()(cuandoandroid:allowBackup="true") para que la sincronización en segundo plano y las notificaciones push se reanuden de inmediato y, segundo, como resguardo en tuActivity.onCreate()de selector en el primer inicio de la app. - Borra la clave de restablecimiento cuando se salga de la cuenta: Cuando el usuario salga de su cuenta o el servidor invalide su sesión, llama a
credentialManager.clearCredentialState()conClearCredentialStateRequest(ClearCredentialStateRequest.TYPE_CLEAR_RESTORE_CREDENTIAL)y borra tu marca de sincronización local.
Crea una clave de restablecimiento
Tu app debe abarcar todos los casos de acceso de un usuario para garantizar que los usuarios activos tengan una clave de restablecimiento creada. Crea la clave de restablecimiento en las siguientes situaciones:
- Si el usuario accedió y aún no se creó una clave de restablecimiento (como en
el método
onCreate()de laActivityprincipal). - Cuando el usuario accede o completa un flujo de registro de cuenta nueva
Para evitar llamar a createCredential() cada vez que se inicia la app, almacena una marca de sincronización local, como has_synced_restore_credential, en el almacenamiento local. No almacenes
esta marca en la copia de seguridad predeterminada de SharedPreferences o DataStore, a menos que ese
archivo se excluya en data_extraction_rules.xml; de lo contrario, la Copia de seguridad automática copiará
has_synced_restore_credential = true en el dispositivo nuevo y evitará que este
genere o rote su propia clave de restablecimiento.
Crea una instancia del Administrador de credenciales
Usa el contexto de la actividad de tu app para crear una instancia de un objeto CredentialManager.
// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)
Obtén opciones de creación de credenciales del servidor de tu app
Usa una biblioteca compatible con FIDO en tu servidor de apps para enviar a tu app cliente el JSON de PublicKeyCredentialCreationOptions necesario para crear la credencial de restablecimiento. Para obtener más información sobre la implementación del servidor, consulta la guía del servidor.
El JSON de las opciones de creación sigue la misma estructura que el registro de llaves de acceso:
{
"challenge": "<base64url-encoded challenge>",
"rp": {
"name": "<relying party name>",
"id": "<relying party host name>"
},
"user": {
"id": "<base64url-encoded user ID>",
"name": "<user name>",
"displayName": "<user display name>"
},
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
}
],
"attestation": "none",
"excludeCredentials": [
{
"id": "<base64url-encoded credential ID to exclude>",
"type": "public-key"
}
],
"authenticatorSelection": {
"requireResidentKey": true,
"residentKey": "required",
"userVerification": "required"
}
}
Asegúrate de que el JSON de las opciones de creación cumpla con estos requisitos:
challengeyuser.id: Codifica ambos valores como cadenas Base64URL sin relleno (reemplaza+por-,/por_y quita el=final). Los caracteres Base64 estándar o los IDs de números enteros sin procesar causanCreateRestoreCredentialDomExceptionoIllegalArgumentException.rp.id: Se establece en el dominio de la parte que confía (por ejemplo,example.com).pubKeyCredParams: Incluye{"type": "public-key", "alg": -7}(ES256) y{"type": "public-key", "alg": -257}(RS256).
Crea la clave de restablecimiento
Después de recibir el JSON de opciones de creación de claves públicas de tu servidor, une el JSON en un objeto CreateRestoreCredentialRequest y llama a createCredential() en el objeto CredentialManager.
// requestJson contains the credential creation options sent by the server
val createRestoreRequest = CreateRestoreCredentialRequest(requestJson)
val response = try {
credentialManager.createCredential(context, createRestoreRequest)
} catch (e: E2eeUnavailableException) {
// Fall back to local device-to-device (D2D) restore if cloud E2EE is unavailable
credentialManager.createCredential(
context,
CreateRestoreCredentialRequest(requestJson, isCloudBackupEnabled = false),
)
}
Puntos clave sobre el código
El objeto
CreateRestoreCredentialRequestcontiene los siguientes campos:requestJson: Son las opciones de creación de credenciales que envía el servidor de la app en el formato de la API de Web Authentication paraPublicKeyCredentialCreationOptionsJSON.isCloudBackupEnabled: Es el campoBooleanpara determinar si se debe crear una copia de seguridad de la clave de restablecimiento en la nube. De forma predeterminada, esta marca estrue. Este campo tiene los siguientes valores:true: (Recomendado) Este valor habilita la copia de seguridad de las claves de restablecimiento en la nube si el usuario tiene habilitada la copia de seguridad de Google y la encriptación de extremo a extremo, como un bloqueo de pantalla.false: Este valor guarda la clave de forma local y no en la nube. La clave no está disponible en el dispositivo nuevo si el usuario elige restablecer desde la nube, pero se transfiere durante una configuración local de dispositivo a dispositivo (D2D) por cable o Wi-Fi.
Controla la respuesta de creación de credenciales
La API de Credential Manager devuelve una respuesta de tipo CreateRestoreCredentialResponse. Esta respuesta contiene la respuesta de registro de credenciales de clave pública en formato JSON.
Envía response.registrationResponseJson desde tu app al servidor de la parte que confía para verificar y almacenar la clave pública junto con la cuenta del usuario y, luego, registra el marcador de sincronización local en el almacenamiento local. Para obtener más información sobre la verificación del servidor, consulta la guía para llaves de acceso.
Durante el proceso de creación de la clave de restablecimiento, controla estas excepciones:
CreateRestoreCredentialDomException: Esta excepción se produce sirequestJsonno es válido y no sigue el formato de WebAuthn paraPublicKeyCredentialCreationOptionsJSON.E2eeUnavailableException: Esta excepción se produce siisCloudBackupEnabledestrue, pero el dispositivo del usuario no tiene copia de seguridad de los datos ni encriptación de extremo a extremo, como un bloqueo de pantalla.
Para garantizar que se creen credenciales de restablecimiento para las transferencias locales de dispositivo a dispositivo (D2D) incluso cuando la E2EE en la nube no esté disponible, detectaE2eeUnavailableExceptiony vuelve a intentarcreateCredential()de inmediato conCreateRestoreCredentialRequest(requestJson, isCloudBackupEnabled = false).IllegalArgumentException: Esta excepción se produce sirequestJsonestá vacío, no es un JSON válido o no tiene unuser.idcodificado en Base64URL válido que cumpla con las especificaciones de WebAuthn.
Cómo acceder con una clave de restablecimiento
Usa Restore Credentials para que el usuario acceda de forma silenciosa durante el proceso de configuración del dispositivo.
Obtén opciones de recuperación de credenciales del servidor de la app
Antes de llamar a getCredential() en el dispositivo nuevo, solicita un desafío JSON de WebAuthn PublicKeyCredentialRequestOptions desde el servidor de tu app. Como
la app en el dispositivo nuevo aún no sabe qué usuario está accediendo, este
extremo debe estar sin autenticar (detección sin usuario). Para obtener orientación relacionada con el servidor, consulta Accede con una llave de acceso y la guía de autenticación del servidor.
El JSON de las opciones de recuperación tiene la siguiente estructura:
{
"challenge": "<your app challenge>",
"allowCredentials": [],
"rpId": "<your app server domain>"
}
Configura el JSON de opciones de recuperación en tu servidor de la siguiente manera:
allowCredentials: Omite este campo o pasa un array vacío ([]) porque la cuenta del usuario no se conoce antes del restablecimiento. Después de que se devuelvagetCredential(), tu servidor identificará al usuario desde el camporesponse.userHandle(que coincide con eluser.idregistrado durante la creación) y buscará la clave pública porid(credentialId).userVerification: Se establece en"discouraged"(o"preferred"). Debido a que Restore Credentials se ejecuta de forma silenciosa sin la IU de bloqueo de pantalla o biométrica, Android anula"required"a"discouraged", y los datos del autenticador que se devuelven a tu servidor tienen la marca Usuario verificado (uv) establecida enfalse. Configura el verificador del servidor de WebAuthn para que acepteuv = falsepara las aserciones de claves de restablecimiento.
Obtén la clave de restablecimiento
Para obtener la clave de restablecimiento en el dispositivo nuevo, une el requestJson del servidor en un GetRestoreCredentialOption dentro de un GetCredentialRequest y llama a getCredential() en el objeto CredentialManager.
Implementa una estrategia de restablecimiento de dos niveles para que los usuarios accedan, independientemente de si se ejecuta o no el restablecimiento de datos de app en segundo plano:
- Nivel 1 (restablecimiento en segundo plano en
BackupAgent.onRestoreFinished()): Si la copia de seguridad de los datos de la app está habilitada (android:allowBackup="true"), crea una subclase deBackupAgenty anulaonRestoreFinished()(no usesonRestore(), que solo se llama para las copias de seguridad de pares clave-valor heredadas). ComoonRestoreFinished()es síncrona ygetCredential()es una funciónsuspendde Kotlin, une la llamada conrunBlocking(Dispatchers.IO). Se detectaNoCredentialExceptionde forma silenciosa cuando no hay una clave de restablecimiento. - Nivel 2 (resguardo en primer plano en el selector
Activity.onCreate()): En el primer inicio de la app, cuando no exista una sesión local, llama agetCredential()desdelifecycleScopeantes de mostrar la pantalla de acceso. Esto controla las apps conandroid:allowBackup="false"y se recupera si el dispositivo no tenía conexión cuando se ejecutóonRestoreFinished().
Si agregas un nuevo BackupAgent a una app que ya usa la Copia de seguridad automática con android:allowBackup="true", también establece android:fullBackupOnly="true" en el elemento <application> de AndroidManifest.xml para que Android siga usando la Copia de seguridad automática en lugar de cambiar a la copia de seguridad de par clave-valor heredada.
suspend fun getRestoreKey(
fetchAuthenticationJson: () -> String,
) {
// Fetch the options required to get the restore key
val authenticationJson = fetchAuthenticationJson()
// Create the GetRestoreCredentialOption object
val options = GetRestoreCredentialOption(authenticationJson)
val getRequest = GetCredentialRequest(listOf(options))
try {
val response = credentialManager.getCredential(context, getRequest)
// Type-check and extract the restore credential
val credential = response.credential
if (credential is RestoreCredential) {
val responseJson = credential.authenticationResponseJson
}
} catch (e: NoCredentialException) {
// No restore key is present on this device
}
}
class MyBackupAgent : BackupAgent() {
override fun onRestoreFinished() {
super.onRestoreFinished()
val credentialManager = CredentialManager.create(this)
val restoreFunctions = RestoreCredentialsFunctions(this, credentialManager)
runBlocking(Dispatchers.IO) {
restoreFunctions.getRestoreKey(
fetchAuthenticationJson = {
/* Fetch challenge JSON from server */ ""
}
)
}
}
}
La API de Credential Manager devuelve un GetCredentialResponse cuya propiedad credential es un RestoreCredential (no un PublicKeyCredential) que contiene authenticationResponseJson.
Controla la respuesta de acceso
Envía restoreCredential.authenticationResponseJson desde la app al servidor de la parte que confía para verificar la aserción criptográfica y emitir un nuevo token de sesión para el usuario. Para obtener detalles sobre la verificación del servidor, consulta Accede con una llave de acceso.
Cuando diseñes el almacenamiento de backend y las políticas de ciclo de vida para las claves de restablecimiento, sigue estos lineamientos:
- Diferencia las claves de restablecimiento de las llaves de acceso de los usuarios: Etiqueta las claves de restablecimiento en tu base de datos para que se oculten de las pantallas de administración de llaves de acceso para el usuario y estén exentas de las verificaciones de usuario (
uv = true). - Admite varios dispositivos activos por usuario: Un usuario puede tener varios dispositivos (como un teléfono y una tablet). Permite varias claves de restablecimiento activas por cuenta de usuario (por ejemplo, con clave por ID de dispositivo o de instalación) en lugar de reemplazar una sola fila.
- Establece un TTL adecuado y limpia las claves huérfanas: Cuando se desinstala una app o se borran los datos de app, se borra la clave de restablecimiento local sin notificar al servidor. Conserva las claves de restablecimiento durante el tiempo suficiente para que sobrevivan a los períodos de actualización del dispositivo (incluso si el usuario sale de su cuenta en el dispositivo anterior después de la transferencia) y descarta las claves inactivas según el TTL de inactividad o los límites de claves por usuario.
Borra la clave de restablecimiento
Credential Manager no tiene estado y no conoce la actividad del usuario, por lo que no borra automáticamente las claves de restablecimiento después de su uso. Para borrar una clave de restablecimiento, llama al
método clearCredentialState(). Por motivos de seguridad, borra la clave cada vez que un
usuario salga de su cuenta para que no vuelva a acceder de forma silenciosa la próxima vez que se inicie la app.
Las claves de restablecimiento solo se quitan en las siguientes situaciones:
- Acciones a nivel del sistema: Los usuarios desinstalan la app o borran sus datos.
- Llamadas a nivel de la app: Borra la clave de forma programática llamando a
clearCredentialState()cuando se controle la salida del usuario en el código de tu app.
Debido a que un ClearCredentialStateRequest() predeterminado solo borra el estado de la sesión estándar y deja intacta la clave de restablecimiento, realiza un borrado doble cuando se salga de la sesión:
- Llama a
credentialManager.clearCredentialState(ClearCredentialStateRequest())para borrar el estado de la sesión activa del proveedor de credenciales. - Llama a
credentialManager.clearCredentialState()conClearCredentialStateRequest(ClearCredentialStateRequest.TYPE_CLEAR_RESTORE_CREDENTIAL)para borrar elRestoreCredentialalmacenado. - Borra el marcador
has_synced_restore_credentiallocal del almacenamiento local.
// Clear active credential provider session state
credentialManager.clearCredentialState(ClearCredentialStateRequest())
// When the user logs out, delete the restore key
val clearRequest = ClearCredentialStateRequest(TYPE_CLEAR_RESTORE_CREDENTIAL)
val response = credentialManager.clearCredentialState(clearRequest)
Próximos pasos
- Prueba las credenciales de restablecimiento: Prueba los flujos de restablecimiento en la nube y de dispositivo a dispositivo (D2D) con Android Studio o
adb shell bmgr. - Pruebas automatizadas de copia de seguridad y restablecimiento: Escribe pruebas de host de JUnit 5 con
BackupRestoreControllerpara verificar los restablecimientos deCLOUD_ENCRYPTEDyDEVICE_TO_DEVICEen la CI.