Molti utenti continuano a gestire le proprie credenziali quando configurano un nuovo dispositivo Android. Questa procedura manuale può diventare impegnativa e spesso determina un'esperienza utente scadente. L'API Block Store, una libreria basata su Google Play Services, cerca di risolvere questo problema offrendo alle app un modo per salvare le credenziali degli utenti senza i rischi per la sicurezza o la complessità associati al salvataggio delle password degli utenti.
L'API Block Store consente alla tua app di archiviare dati che può recuperare in un secondo momento per autenticare nuovamente gli utenti su un nuovo dispositivo. Ciò contribuisce a offrire un'esperienza più fluida all'utente, in quanto non è necessario vedere una schermata di accesso quando l'app viene lanciata per la prima volta sul nuovo dispositivo.
I vantaggi dell'utilizzo di Block Store includono quanto segue:
- Soluzione di archiviazione delle credenziali criptata per gli sviluppatori. Le credenziali vengono protette con crittografia end-to-end, se possibile.
- Salva i token anziché i nomi utente e le password.
- Eliminare le difficoltà dei flussi di accesso.
- Risparmia agli utenti l'onere di gestire password complesse.
- Google verifica l'identità dell'utente.
Prima di iniziare
Per preparare l'app, completa i passaggi nelle sezioni seguenti.
Configura la tua app
Nel file build.gradle
a livello di progetto, includi il repository Maven di Google in entrambe le sezioni buildscript
e allprojects
:
buildscript {
repositories {
google()
mavenCentral()
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
Aggiungi la dipendenza da Google Play Services
per l'API Block Store al
file di build Gradle del tuo modulo,
che solitamente è app/build.gradle
:
dependencies {
implementation 'com.google.android.gms:play-services-auth-blockstore:16.2.0'
}
Come funziona
L'archivio a blocchi consente agli sviluppatori di salvare e ripristinare array fino a 16 byte. In questo modo puoi salvare informazioni importanti relative alla sessione utente corrente e avere la flessibilità di salvarle come preferisci. Questi dati possono essere protetti con crittografia end-to-end e l'infrastruttura che supporta l'archiviazione a blocchi si basa sull'infrastruttura di backup e ripristino.
Questa guida riguarda il caso d'uso del salvataggio del token di un utente nell'archivio a blocchi. I passaggi seguenti spiegano come funziona un'app che utilizza Block Store:
- Durante il flusso di autenticazione della tua app o in qualsiasi momento successivo, puoi archiviare il token di autenticazione dell'utente in Block Store per recuperarlo in un secondo momento.
- Il token verrà archiviato localmente e, se possibile, può anche essere sottoposto a backup nel cloud con crittografia end-to-end.
- I dati vengono trasferiti quando l'utente avvia un flusso di ripristino su un nuovo dispositivo.
- Se l'utente ripristina l'app durante il flusso di ripristino, l'app può recuperare il token salvato da Block Store sul nuovo dispositivo.
Salvataggio del token
Quando un utente accede alla tua app, puoi salvare nell'archivio bloccato il token di autenticazione che generi per quell'utente. Puoi memorizzare questo token utilizzando un valore di coppia di chiavi univoco con un massimo di 4 kB per voce.
Per archiviare il token, chiama setBytes()
e setKey()
su un'istanza di
StoreBytesData.Builder
per archiviare le credenziali dell'utente sul dispositivo di origine. Dopo averlo salvato con Block Store, il token viene criptato e archiviato localmente sul dispositivo.
Il seguente esempio 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) }
Usa token predefinito
I dati salvati utilizzando StoreBytes senza una chiave utilizzano la chiave predefinita 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) }
Recupero del token
In seguito, quando un utente esegue il flusso di ripristino su un nuovo
dispositivo, Google Play Services verifica innanzitutto l'utente, quindi recupera i dati del tuo
archivio a blocchi. L'utente ha già accettato di ripristinare i dati dell'app nell'ambito del flusso di ripristino, pertanto non sono necessari ulteriori consensi. Quando l'utente apre la tua app, puoi richiedere il token da Block Store chiamando retrieveBytes()
.
Il token recuperato può quindi essere utilizzato per mantenere l'accesso dell'utente sul nuovo dispositivo.
Il seguente esempio 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 blockstoreDataMap = result.getBlockstoreDataMap(); for (Map.Entry 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 -> { MapblockstoreDataMap = result.getBlockstoreDataMap(); for (Map.Entry 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 esegue il flusso utente per uscire.
- 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.
Di seguito è riportato un esempio per l'eliminazione di 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 elimina 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() client.deleteBytes(deleteAllRequest) .addOnSuccessListener { result: Boolean -> Log.d(TAG, "Any data found and deleted? $result") }
Crittografia end-to-end
Per rendere disponibile la crittografia end-to-end, sul dispositivo deve essere installato Android 9 o versioni successive e l'utente deve impostare un blocco schermo (PIN, sequenza o password) per il dispositivo. Puoi verificare se la crittografia sarà disponibile sul dispositivo chiamando il numero isEndToEndEncryptionAvailable()
.
Il seguente esempio mostra come verificare se la crittografia sarà disponibile durante il backup sul cloud:
client.isEndToEndEncryptionAvailable()
.addOnSuccessListener { result ->
Log.d(TAG, "Will Block Store cloud backup be end-to-end encrypted? $result")
}
Abilita backup sul cloud
Per abilitare il backup sul cloud, aggiungi il metodo setShouldBackupToCloud()
all'oggetto StoreBytesData
. Block Store eseguirà periodicamente il backup per cloud dei byte archiviati quando setShouldBackupToCloud()
è impostato su true.
Il seguente esempio mostra come abilitare il backup sul cloud solo quando il backup sul cloud è protetto con crittografia 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
Per testare i flussi di ripristino, utilizza i seguenti metodi durante lo sviluppo.
Disinstallazione/reinstallazione dello stesso dispositivo
Se l'utente attiva i servizi di backup (può essere controllato in Impostazioni > Google > Backup), i dati di Block Store vengono mantenuti durante la disinstallazione/reinstallazione dell'app.
Per verificare, puoi procedere nel seguente modo:
- Integra l'API BlockStore nella tua app di test.
- Utilizza l'app di prova per richiamare l'API BlockStore e archiviare i dati.
- Disinstalla l'app di prova e reinstalla l'app sullo stesso dispositivo.
- Utilizza l'app di prova per richiamare l'API BlockStore e recuperare i dati.
- Verifica che i byte recuperati corrispondano a quelli archiviati prima della disinstallazione.
Da dispositivo a dispositivo
Nella maggior parte dei casi, sarà necessario ripristinare i dati di fabbrica del dispositivo di destinazione. Dopodiché puoi inserire il flusso di ripristino wireless di Android o il ripristino di cavi Google (per i dispositivi supportati).
Ripristino nel cloud
- Integra l'API Blockstore nella tua app di test. L'app di test deve essere inviata al Play Store.
- Sul dispositivo di origine, utilizza l'app di test per richiamare l'API Blockstore in modo da archiviare i dati, con il valore dovrebbeBackUpToCloud impostato su true.
- Per i dispositivi O e versioni successive, puoi attivare manualmente un backup sul cloud nell'archivio a blocchi:
vai a Impostazioni > Google > Backup, fai clic sul pulsante "Esegui ora il backup".
- Per verificare che il backup sul cloud di Block Store sia riuscito, puoi:
- Al termine del backup, cerca le righe di log con il tag "CloudSyncBpTkSvc".
- Dovresti vedere righe come questa: "......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., dimensioni caricate: XXX byte ..."
- Dopo un backup sul cloud da Block Store, c'è un periodo di "raffreddamento" di 5 minuti. Entro questi 5 minuti, se fai clic sul pulsante "Esegui backup ora", non verrà attivato un altro backup sul cloud di Block Store.
- Per verificare che il backup sul cloud di Block Store sia riuscito, puoi:
- Ripristina i dati di fabbrica del dispositivo di destinazione ed esegui un flusso di ripristino nel cloud. Seleziona per ripristinare l'app di test durante il flusso di ripristino. Per ulteriori informazioni sui flussi di ripristino del cloud, consulta Flussi di ripristino nel cloud supportati.
- Sul dispositivo di destinazione, utilizza l'app di test per richiamare l'API Blockstore e recuperare i dati.
- Verifica che i byte recuperati corrispondano a quelli archiviati 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.
- Per attivare e criptare correttamente i dati dell'utente, sul dispositivo deve essere impostato un blocco schermo con un PIN, una sequenza o una password per la crittografia end-to-end.
Flusso di ripristino da dispositivo a dispositivo
Il ripristino da dispositivo a dispositivo richiede un dispositivo di origine e un dispositivo di destinazione. Questi sono i due dispositivi che trasferiscono i dati.
Per eseguire il backup, i dispositivi di origine devono eseguire il sistema operativo Android 6 (API 23) o versioni successive.
Scegli come target i dispositivi con Android 9 (API 29) e versioni successive per poter eseguire il ripristino.
Ulteriori informazioni sul flusso di ripristino da dispositivo a dispositivo sono disponibili qui.
Flusso di backup e ripristino sul cloud
Per il backup e il ripristino sul cloud saranno necessari un dispositivo di origine e un dispositivo di destinazione.
Per eseguire il backup, i dispositivi di origine devono eseguire il sistema operativo Android 6 (API 23) o versioni successive.
I dispositivi di destinazione sono supportati in base ai relativi fornitori. I dispositivi Pixel possono utilizzare questa funzionalità a partire da Android 9 (API 29) e tutti gli altri dispositivi devono avere il sistema operativo Android 12 (API 31) o versioni successive.