De nombreux utilisateurs gèrent encore leurs propres identifiants lorsqu'ils configurent un nouvel appareil Android. Ce processus manuel peut devenir difficile et nuit souvent à l'expérience utilisateur. L'API Block Store, une bibliothèque fournie par les services Google Play, vise à résoudre ce problème en permettant aux applications d'enregistrer les identifiants des utilisateurs sans la complexité ni les risques pour la sécurité liés à l'enregistrement des mots de passe.
L'API Block Store permet à votre application de stocker des données qu'elle peut ensuite récupérer pour réauthentifier les utilisateurs sur un nouvel appareil. Cela permet d'offrir une expérience plus fluide à l'utilisateur, car il n'a pas besoin de voir un écran de connexion lorsqu'il lance votre application pour la première fois sur le nouvel appareil.
Voici les avantages de l'utilisation de Block Store :
- Solution de stockage chiffré des identifiants pour les développeurs. Les identifiants sont chiffrés de bout en bout lorsque cela est possible.
- Enregistrez des jetons au lieu des noms d'utilisateur et des mots de passe.
- Éliminez les frictions dans les parcours de connexion.
- Épargnez aux utilisateurs la gestion de mots de passe complexes.
- Google vérifie l'identité de l'utilisateur.
Avant de commencer
Pour préparer votre application, procédez comme indiqué dans les sections suivantes.
Configurer votre application
Dans le fichier build.gradle
au niveau du projet, incluez le dépôt Maven de Google dans les sections buildscript
et allprojects
:
buildscript {
repositories {
google()
mavenCentral()
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
Ajoutez la dépendance des services Google Play pour l'API Block Store au fichier de compilation Gradle de votre module, qui est généralement app/build.gradle
:
dependencies {
implementation 'com.google.android.gms:play-services-auth-blockstore:16.4.0'
}
Fonctionnement
Block Store permet aux développeurs d'enregistrer et de restaurer jusqu'à 16 tableaux d'octets. Cela vous permet d'enregistrer des informations importantes concernant la session utilisateur actuelle et vous offre la possibilité de les enregistrer comme vous le souhaitez. Ces données peuvent être chiffrées de bout en bout. L'infrastructure qui prend en charge Block Store est basée sur l'infrastructure de sauvegarde et de restauration.
Ce guide couvre le cas d'utilisation de l'enregistrement du jeton d'un utilisateur dans Block Store. Voici comment fonctionne une application utilisant Block Store :
- Pendant le flux d'authentification de votre application ou à tout moment par la suite, vous pouvez stocker le jeton d'authentification de l'utilisateur dans Block Store pour le récupérer ultérieurement.
- Le jeton sera stocké localement et pourra également être sauvegardé dans le cloud, avec un chiffrement de bout en bout si possible.
- Les données sont transférées lorsque l'utilisateur lance un processus de restauration sur un nouvel appareil.
- Si l'utilisateur restaure votre application pendant le processus de restauration, votre application peut ensuite récupérer le jeton enregistré dans Block Store sur le nouvel appareil.
Enregistrer le jeton
Lorsqu'un utilisateur se connecte à votre application, vous pouvez enregistrer le jeton d'authentification que vous générez pour cet utilisateur dans Block Store. Vous pouvez stocker ce jeton à l'aide d'une paire clé/valeur unique, avec une taille maximale de 4 ko par entrée. Pour stocker le jeton, appelez setBytes()
et setKey()
sur une instance de StoreBytesData.Builder
pour stocker les identifiants de l'utilisateur sur l'appareil source. Une fois que vous avez enregistré le jeton avec Block Store, il est chiffré et stocké localement sur l'appareil.
L'exemple suivant montre comment enregistrer le jeton d'authentification sur l'appareil local :
Java
BlockstoreClient client = Blockstore.getClient(this); byte[] bytes1 = new byte[] { 1, 2, 3, 4 }; // Store one data block. String key1 = "com.example.app.key1"; StoreBytesData storeRequest1 = StoreBytesData.Builder() .setBytes(bytes1) // Call this method to set the key value pair the data should be associated with. .setKeys(Arrays.asList(key1)) .build(); client.storeBytes(storeRequest1) .addOnSuccessListener(result -> Log.d(TAG, "stored " + result + " bytes")) .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));
Kotlin
val client = Blockstore.getClient(this) val bytes1 = byteArrayOf(1, 2, 3, 4) // Store one data block. val key1 = "com.example.app.key1" val storeRequest1 = StoreBytesData.Builder() .setBytes(bytes1) // Call this method to set the key value with which the data should be associated with. .setKeys(Arrays.asList(key1)) .build() client.storeBytes(storeRequest1) .addOnSuccessListener { result: Int -> Log.d(TAG, "Stored $result bytes") } .addOnFailureListener { e -> Log.e(TAG, "Failed to store bytes", e) }
Utiliser le jeton par défaut
Les données enregistrées à l'aide de StoreBytes sans clé utilisent la clé par défaut BlockstoreClient.DEFAULT_BYTES_DATA_KEY
.
Java
BlockstoreClient client = Blockstore.getClient(this); // The default key BlockstoreClient.DEFAULT_BYTES_DATA_KEY. byte[] bytes = new byte[] { 9, 10 }; StoreBytesData storeRequest = StoreBytesData.Builder() .setBytes(bytes) .build(); client.storeBytes(storeRequest) .addOnSuccessListener(result -> Log.d(TAG, "stored " + result + " bytes")) .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));
Kotlin
val client = Blockstore.getClient(this); // the default key BlockstoreClient.DEFAULT_BYTES_DATA_KEY. val bytes = byteArrayOf(1, 2, 3, 4) val storeRequest = StoreBytesData.Builder() .setBytes(bytes) .build(); client.storeBytes(storeRequest) .addOnSuccessListener { result: Int -> Log.d(TAG, "stored $result bytes") } .addOnFailureListener { e -> Log.e(TAG, "Failed to store bytes", e) }
Récupérer le jeton
Plus tard, lorsqu'un utilisateur suit la procédure de restauration sur un nouvel appareil, les services Google Play vérifient d'abord l'identité de l'utilisateur, puis récupèrent les données de votre Block Store. L'utilisateur a déjà accepté de restaurer les données de votre application dans le cadre du processus de restauration. Aucun consentement supplémentaire n'est donc requis. Lorsque l'utilisateur ouvre votre application, vous pouvez demander votre jeton à Block Store en appelant retrieveBytes()
. Le jeton récupéré peut ensuite être utilisé pour maintenir l'utilisateur connecté sur le nouvel appareil.
L'exemple suivant montre comment récupérer plusieurs jetons en fonction de clés spécifiques.
Java
BlockstoreClient client = Blockstore.getClient(this); // Retrieve data associated with certain keys. String key1 = "com.example.app.key1"; String key2 = "com.example.app.key2"; String key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY; // Used to retrieve data stored without a key ListrequestedKeys = Arrays.asList(key1, key2, key3); // Add keys to array RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder() .setKeys(requestedKeys) .build(); client.retrieveBytes(retrieveRequest) .addOnSuccessListener( result -> { Map<String, BlockstoreData> blockstoreDataMap = result.getBlockstoreDataMap(); for (Map.Entry<String, BlockstoreData> entry : blockstoreDataMap.entrySet()) { Log.d(TAG, String.format( "Retrieved bytes %s associated with key %s.", new String(entry.getValue().getBytes()), entry.getKey())); } }) .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));
Kotlin
val client = Blockstore.getClient(this) // Retrieve data associated with certain keys. val key1 = "com.example.app.key1" val key2 = "com.example.app.key2" val key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY // Used to retrieve data stored without a key val requestedKeys = Arrays.asList(key1, key2, key3) // Add keys to array val retrieveRequest = RetrieveBytesRequest.Builder() .setKeys(requestedKeys) .build() client.retrieveBytes(retrieveRequest) .addOnSuccessListener { result: RetrieveBytesResponse -> val blockstoreDataMap = result.blockstoreDataMap for ((key, value) in blockstoreDataMap) { Log.d(ContentValues.TAG, String.format( "Retrieved bytes %s associated with key %s.", String(value.bytes), key)) } } .addOnFailureListener { e: Exception? -> Log.e(ContentValues.TAG, "Failed to store bytes", e) }
Récupérer tous les jetons.
Vous trouverez ci-dessous un exemple de récupération de tous les jetons enregistrés dans BlockStore.
Java
BlockstoreClient client = Blockstore.getClient(this) // Retrieve all data. RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder() .setRetrieveAll(true) .build(); client.retrieveBytes(retrieveRequest) .addOnSuccessListener( result -> { Map<String, BlockstoreData> blockstoreDataMap = result.getBlockstoreDataMap(); for (Map.Entry<String, BlockstoreData> entry : blockstoreDataMap.entrySet()) { Log.d(TAG, String.format( "Retrieved bytes %s associated with key %s.", new String(entry.getValue().getBytes()), entry.getKey())); } }) .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));
Kotlin
val client = Blockstore.getClient(this) val retrieveRequest = RetrieveBytesRequest.Builder() .setRetrieveAll(true) .build() client.retrieveBytes(retrieveRequest) .addOnSuccessListener { result: RetrieveBytesResponse -> val blockstoreDataMap = result.blockstoreDataMap for ((key, value) in blockstoreDataMap) { Log.d(ContentValues.TAG, String.format( "Retrieved bytes %s associated with key %s.", String(value.bytes), key)) } } .addOnFailureListener { e: Exception? -> Log.e(ContentValues.TAG, "Failed to store bytes", e) }
Vous trouverez ci-dessous un exemple de récupération de la clé par défaut.
Java
BlockStoreClient client = Blockstore.getClient(this); RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder() .setKeys(Arrays.asList(BlockstoreClient.DEFAULT_BYTES_DATA_KEY)) .build(); client.retrieveBytes(retrieveRequest);
Kotlin
val client = Blockstore.getClient(this) val retrieveRequest = RetrieveBytesRequest.Builder() .setKeys(Arrays.asList(BlockstoreClient.DEFAULT_BYTES_DATA_KEY)) .build() client.retrieveBytes(retrieveRequest)
Supprimer des jetons
La suppression de jetons de BlockStore peut être nécessaire pour les raisons suivantes :
- L'utilisateur suit le flux de déconnexion.
- Le jeton a été révoqué ou n'est pas valide.
Comme pour la récupération de jetons, vous pouvez spécifier les jetons à supprimer en définissant un tableau de clés à supprimer.
L'exemple suivant montre comment supprimer certaines clés :
Java
BlockstoreClient client = Blockstore.getClient(this); // Delete data associated with certain keys. String key1 = "com.example.app.key1"; String key2 = "com.example.app.key2"; String key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY; // Used to delete data stored without key ListrequestedKeys = Arrays.asList(key1, key2, key3) // Add keys to array DeleteBytesRequest deleteRequest = new DeleteBytesRequest.Builder() .setKeys(requestedKeys) .build(); client.deleteBytes(deleteRequest)
Kotlin
val client = Blockstore.getClient(this) // Retrieve data associated with certain keys. val key1 = "com.example.app.key1" val key2 = "com.example.app.key2" val key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY // Used to retrieve data stored without a key val requestedKeys = Arrays.asList(key1, key2, key3) // Add keys to array val retrieveRequest = DeleteBytesRequest.Builder() .setKeys(requestedKeys) .build() client.deleteBytes(retrieveRequest)
Supprimer tous les jetons
L'exemple suivant montre comment supprimer tous les jetons actuellement enregistrés dans BlockStore :
Java
// Delete all data. DeleteBytesRequest deleteAllRequest = new DeleteBytesRequest.Builder() .setDeleteAll(true) .build(); client.deleteBytes(deleteAllRequest) .addOnSuccessListener(result -> Log.d(TAG, "Any data found and deleted? " + result));
Kotlin
val deleteAllRequest = DeleteBytesRequest.Builder() .setDeleteAll(true) .build() retrieve bytes, the keyBlockstoreClient.DEFAULT_BYTES_DATA_KEY
can be used in theRetrieveBytesRequest
instance in order to get your saved data
The following example shows how to retrieve the default key.
Java
End-to-end encryption
In order for end-to-end encryption to be made available, the device must be
running Android 9 or higher, and the user must have set a screen lock
(PIN, pattern, or password) for their device. You can verify if encryption will
be available on the device by calling isEndToEndEncryptionAvailable()
.
The following sample shows how to verify if encryption will be available during cloud backup:
client.isEndToEndEncryptionAvailable()
.addOnSuccessListener { result ->
Log.d(TAG, "Will Block Store cloud backup be end-to-end encrypted? $result")
}
Activer la sauvegarde dans le cloud
Pour activer la sauvegarde cloud, ajoutez la méthode setShouldBackupToCloud()
à votre objet StoreBytesData
. Block Store effectue régulièrement des sauvegardes dans le cloud des octets stockés lorsque setShouldBackupToCloud()
est défini sur "true".
L'exemple suivant montre comment activer la sauvegarde dans le cloud uniquement lorsque la sauvegarde dans le cloud est chiffrée de bout en bout :
val client = Blockstore.getClient(this)
val storeBytesDataBuilder = StoreBytesData.Builder()
.setBytes(/* BYTE_ARRAY */)
client.isEndToEndEncryptionAvailable()
.addOnSuccessListener { isE2EEAvailable ->
if (isE2EEAvailable) {
storeBytesDataBuilder.setShouldBackupToCloud(true)
Log.d(TAG, "E2EE is available, enable backing up bytes to the cloud.")
client.storeBytes(storeBytesDataBuilder.build())
.addOnSuccessListener { result ->
Log.d(TAG, "stored: ${result.getBytesStored()}")
}.addOnFailureListener { e ->
Log.e(TAG, “Failed to store bytes”, e)
}
} else {
Log.d(TAG, "E2EE is not available, only store bytes for D2D restore.")
}
}
Comment tester
Utilisez les méthodes suivantes pendant le développement pour tester les flux de restauration.
Désinstaller/réinstaller sur le même appareil
Si l'utilisateur active les services de sauvegarde (vous pouvez le vérifier en accédant à Paramètres > Google > Sauvegarde), les données Block Store sont conservées lors de la désinstallation/réinstallation de l'application.
Pour effectuer un test, procédez comme suit :
- Intégrez l'API Block Store à votre application de test.
- Utilisez l'application de test pour appeler l'API Block Store afin de stocker vos données.
- Désinstallez votre application de test, puis réinstallez votre application sur le même appareil.
- Utilisez l'application de test pour appeler l'API Block Store et récupérer vos données.
- Vérifiez que les octets récupérés sont identiques à ceux qui ont été stockés avant la désinstallation.
D'appareil à appareil
Dans la plupart des cas, vous devrez rétablir la configuration d'usine de l'appareil cible. Vous pouvez ensuite suivre la procédure de restauration sans fil Android ou la procédure de restauration avec câble Google (pour les appareils compatibles).
Restauration à partir du cloud
- Intégrez l'API Block Store à votre application de test. L'application de test doit être envoyée au Play Store.
- Sur l'appareil source, utilisez l'application de test pour appeler l'API Block Store afin de stocker vos données, avec
shouldBackUpToCloud
défini surtrue
. - Pour les appareils équipés d'Android O ou version ultérieure, vous pouvez déclencher manuellement une sauvegarde cloud Block Store : accédez à Paramètres > Google > Sauvegarde, puis cliquez sur le bouton "Sauvegarder maintenant".
- Pour vérifier que la sauvegarde cloud Block Store a réussi, vous pouvez :
- Une fois la sauvegarde terminée, recherchez les lignes de journaux avec le tag "CloudSyncBpTkSvc".
- Des lignes semblables à celles-ci devraient s'afficher : "......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., uploaded size: XXX bytes ..." (......, CloudSyncBpTkSvc: résultat de la synchronisation : SUCCÈS, ..., taille importée : XXX octets ...).
- Après une sauvegarde cloud Block Store, une période de refroidissement de cinq minutes est appliquée. Pendant ces cinq minutes, si vous cliquez sur le bouton "Sauvegarder maintenant", aucune autre sauvegarde cloud Block Store ne sera déclenchée.
- Pour vérifier que la sauvegarde cloud Block Store a réussi, vous pouvez :
- Rétablissez la configuration d'usine de l'appareil cible et suivez la procédure de restauration à partir du cloud. Sélectionnez pour restaurer votre application de test pendant le processus de restauration. Pour en savoir plus sur les flux de restauration à partir du cloud, consultez Flux de restauration à partir du cloud compatibles.
- Sur l'appareil cible, utilisez l'application de test pour appeler l'API Block Store et récupérer vos données.
- Vérifiez que les octets récupérés sont identiques à ceux stockés dans l'appareil source.
Configuration requise pour l'appareil
Chiffrement de bout en bout
- Le chiffrement de bout en bout est compatible avec les appareils équipés d'Android 9 (API 29) ou version ultérieure.
- L'appareil doit être équipé d'un verrouillage de l'écran avec un code, un schéma ou un mot de passe pour que le chiffrement de bout en bout soit activé et chiffre correctement les données de l'utilisateur.
Processus de restauration d'un appareil à un autre
Pour effectuer une restauration d'appareil à appareil, vous devez disposer d'un appareil source et d'un appareil cible. Il s'agit des deux appareils qui transfèrent des données.
Les appareils sources doivent être équipés d'Android 6 (API 23) ou version ultérieure pour effectuer une sauvegarde.
Ciblez les appareils exécutant Android 9 (API 29) ou version ultérieure pour pouvoir effectuer la restauration.
Pour en savoir plus sur le processus de restauration d'appareil à appareil, cliquez ici.
Flux de sauvegarde et de restauration dans le cloud
La sauvegarde dans le cloud et la restauration nécessitent un appareil source et un appareil cible.
Les appareils sources doivent être équipés d'Android 6 (API 23) ou version ultérieure pour effectuer une sauvegarde.
Les appareils cibles sont compatibles en fonction de leurs fournisseurs. Les appareils Pixel peuvent utiliser cette fonctionnalité à partir d'Android 9 (API 29), et tous les autres appareils doivent exécuter Android 12 (API 31) ou version ultérieure.