Molti utenti continuano a gestire le proprie credenziali durante la configurazione di un nuovo dispositivo Android. Questa procedura manuale può diventare complessa e spesso comporta un'esperienza utente scadente. L'API Block Store, una libreria basata su Google Play Services, cerca di risolvere il problema offrendo alle app un modo per salvare le credenziali utente senza la complessità o il rischio per la sicurezza associati al salvataggio delle password utente.
L'API Block Store consente alla tua app di memorizzare dati che può recuperare in un secondo momento per autenticare nuovamente gli utenti su un nuovo dispositivo. In questo modo, l'utente può usufruire di un'esperienza più fluida, in quanto non deve visualizzare una schermata di accesso quando avvia l'app per la prima volta sul nuovo dispositivo.
I vantaggi dell'utilizzo di Block Store includono:
- Soluzione di archiviazione delle credenziali criptata per gli sviluppatori. Le credenziali sono protette con crittografia end-to-end, se possibile.
- Salva i token anziché i nomi utente e le password.
- Eliminare le difficoltà nei flussi di accesso.
- Libera gli utenti dall'oneroso compito di gestire password complesse.
- Google verifica l'identità dell'utente.
Prima di iniziare
Per preparare l'app, completa i passaggi descritti nelle sezioni seguenti.
Configurare l'app
Nel file build.gradle
a livello di progetto, includi il repository Maven di Google sia nelle sezioni buildscript
sia in allprojects
:
buildscript {
repositories {
google()
mavenCentral()
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
Aggiungi la dipendenza da Google Play Services per l'API Block Store al file di compilazione Gradle del modulo, che in genere è app/build.gradle
:
dependencies {
implementation 'com.google.android.gms:play-services-auth-blockstore:16.4.0'
}
Come funziona
Block Store consente agli sviluppatori di salvare e ripristinare fino a 16 array di byte. In questo modo, puoi salvare informazioni importanti relative alla sessione utente corrente e hai la flessibilità di salvarle come preferisci. Questi dati possono essere criptati end-to-end e l'infrastruttura che supporta Block Store è basata sull'infrastruttura di Backup e ripristino.
Questa guida illustra il caso d'uso del salvataggio del token di un utente in Block Store. I passaggi che seguono descrivono il funzionamento di un'app che utilizza Block Store:
- Durante il flusso di autenticazione dell'app o in qualsiasi momento successivo, puoi memorizzare il token di autenticazione dell'utente in Block Store per recuperarlo in un secondo momento.
- Il token verrà archiviato localmente e di esso è possibile eseguire il backup sul cloud, con crittografia end-to-end, se possibile.
- I dati vengono trasferiti quando l'utente avvia un flusso di ripristino su un nuovo dispositivo.
- Se l'utente ripristina la tua app durante il flusso di ripristino, l'app può recuperare il token salvato dal Block Store sul nuovo dispositivo.
Salvataggio del token
Quando un utente accede alla tua app, puoi salvare il token di autenticazione che hai generato per quell'utente in Block Store. Puoi memorizzare questo token utilizzando un valore della coppia di chiavi univoco con un massimo di 4 KB per voce. Per memorizzare il token, chiama
setBytes()
e setKey()
su un'istanza di
StoreBytesData.Builder
per memorizzare le credenziali dell'utente sul
dispositivo di origine. Dopo aver salvato il token con Block Store, il token viene criptato e memorizzato localmente sul dispositivo.
L'esempio riportato di seguito mostra come salvare il token di autenticazione sul dispositivo locale:
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) }
Utilizza il token predefinito
I dati salvati utilizzando StoreBytes senza una chiave utilizzano la chiave predefinitaBlockstoreClient.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) }
Recupero del token
In seguito, quando un utente esegue il flusso di ripristino su un nuovo dispositivo, i servizi Google Play prima verificano l'utente e poi recuperano i dati di Block Store. L'utente
ha già accettato di ripristinare i dati dell'app nell'ambito della procedura di ripristino, pertanto non sono necessari consensi aggiuntivi. Quando l'utente apre la tua app, puoi richiedere il tuo token da Block Store chiamando retrieveBytes()
. Il token recuperato può essere utilizzato per mantenere l'utente connesso al nuovo dispositivo.
L'esempio seguente mostra come recuperare più token in base a chiavi specifiche.
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) }
Recupero di tutti i token.
Di seguito è riportato un esempio di come recuperare tutti i token salvati in 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) }
Di seguito è riportato un esempio di come recuperare la chiave predefinita.
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)
Eliminazione dei token
L'eliminazione dei token da BlockStore potrebbe essere necessaria per i seguenti motivi:
- L'utente completa il flusso di disconnessione.
- Il token è stato revocato o non è valido.
Come per il recupero dei token, puoi specificare quali token devono essere eliminati impostando un array di chiavi che richiedono l'eliminazione.
L'esempio seguente mostra come eliminare determinate chiavi:
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)
Elimina tutti i token
L'esempio seguente mostra come eliminare tutti i token attualmente salvati in 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")
}
Attiva il backup sul cloud
Per attivare il backup nel cloud, aggiungi il metodo setShouldBackupToCloud()
all'oggetto
StoreBytesData
. Block Store eseguirà periodicamente il backup sul cloud dei
byte archiviati quando setShouldBackupToCloud()
è impostato su true.
L'esempio seguente mostra come attivare il backup nel cloud solo se è criptato end-to-end:
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.")
}
}
Come eseguire il test
Utilizza i seguenti metodi durante lo sviluppo per testare i flussi di ripristino.
Disinstallazione/reinstallazione sullo stesso dispositivo
Se l'utente attiva i servizi di backup (l'opzione è disponibile in Impostazioni > Google > Backup), i dati di Block Store vengono mantenuti durante la disinstallazione/reinstallazione dell'app.
Per eseguire il test, segui questi passaggi:
- Integra l'API Block Store nella tua app di test.
- Utilizza l'app di test per invocare l'API Block Store per archiviare i dati.
- Disinstalla l'app di test e reinstallala sullo stesso dispositivo.
- Utilizza l'app di test per richiamare l'API Block Store e recuperare i dati.
- Verifica che i byte recuperati siano gli stessi memorizzati prima della disinstallazione.
Dispositivo a dispositivo
Nella maggior parte dei casi, sarà necessario ripristinare i dati di fabbrica del dispositivo di destinazione. Puoi quindi seguire il percorso di ripristino wireless Android o il ripristino tramite cavo Google (per i dispositivi supportati).
Ripristino dal cloud
- Integra l'API Block Store nell'app di test. L'app di test deve essere inviata al Play Store.
- Sul dispositivo di origine, utilizza l'app di test per invocare l'API Block Store per archiviare i tuoi dati, con
shouldBackUpToCloud
impostato sutrue
. - Per i dispositivi O e versioni successive, puoi attivare manualmente un backup sul cloud di Block Store:
vai a Impostazioni > Google > Backup, fai clic sul pulsante "Esegui backup ora".
- Per verificare che il backup nel cloud di Block Store sia andato a buon fine, puoi:
- Al termine del backup, cerca le righe di log con il tag "CloudSyncBpTkSvc".
- Dovresti vedere righe come questa: "......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., uploaded size: XXX bytes ...”
- Dopo un backup sul cloud di Block Store, è previsto un periodo di attesa di 5 minuti. Entro questi 5 minuti, fare clic sul pulsante "Esegui il backup ora" non attiverà un altro backup sul cloud di Block Store.
- Per verificare che il backup nel cloud di Block Store sia andato a buon fine, puoi:
- Ripristina i dati di fabbrica del dispositivo di destinazione ed esegui un flusso di ripristino dal cloud. Seleziona per ripristinare l'app di test durante il flusso di ripristino. Per ulteriori informazioni sui flussi di ripristino del cloud, consulta Flusso di ripristino del cloud supportato.
- Sul dispositivo di destinazione, utilizza l'app di test per invocare l'API Block Store al fine di recuperare i dati.
- Verifica che i byte recuperati siano gli stessi memorizzati nel dispositivo di origine.
Requisiti del dispositivo
Crittografia end-to-end
- La crittografia end-to-end è supportata sui dispositivi con Android 9 (API 29) e versioni successive.
- Affinché la crittografia end-to-end venga attivata e criptata correttamente i dati dell'utente, sul dispositivo deve essere impostato un blocco schermo con PIN, sequenza o password.
Flusso di ripristino da dispositivo a dispositivo
Per il ripristino da dispositivo a dispositivo, devi disporre di un dispositivo di origine e di un dispositivo di destinazione. Questi saranno i due dispositivi che trasferiscono i dati.
Per eseguire il backup, i dispositivi di origine devono avere Android 6 (API 23) o versioni successive.
I dispositivi target con Android 9 (API 29) e versioni successive devono avere la possibilità di eseguire il ripristino.
Puoi trovare ulteriori informazioni sulla procedura di ripristino da dispositivo a dispositivo qui.
Flusso di backup e ripristino sul cloud
Il backup e il ripristino sul cloud richiedono un dispositivo di origine e un dispositivo di destinazione.
Per eseguire il backup, i dispositivi di origine devono avere Android 6 (API 23) o versioni successive.
I dispositivi target sono supportati in base ai relativi fornitori. I Pixel possono usare questa funzionalità da Android 9 (API 29) e tutti gli altri dispositivi devono avere Android 12 (API 31) o versioni successive.