Muitos usuários ainda gerenciam as próprias credenciais ao configurar um novo dispositivo Android. Esse processo manual pode se tornar desafiador e, muitas vezes, resulta em uma experiência ruim para o usuário. A API Block Store, uma biblioteca com tecnologia do Google Play Services, busca resolver esse problema oferecendo uma maneira de os apps salvarem as credenciais do usuário sem a complexidade ou o risco de segurança associado ao salvamento de senhas.
A API Block Store permite que o app armazene dados que podem ser recuperados posteriormente para autenticar novamente os usuários em um novo dispositivo. Isso ajuda a proporcionar uma experiência mais integrada para o usuário, já que ele não precisa ver uma tela de login ao iniciar o app pela primeira vez no novo dispositivo.
Os benefícios de usar o Block Store incluem:
- Solução de armazenamento de credenciais criptografadas para desenvolvedores. As credenciais são criptografadas de ponta a ponta quando possível.
- Salve tokens em vez de nomes de usuário e senhas.
- Elimine o atrito dos fluxos de login.
- Evite que os usuários tenham que gerenciar senhas complexas.
- O Google verifica a identidade do usuário.
Antes de começar
Para preparar o app, siga as etapas nas seções a seguir.
Configurar o app
No arquivo build.gradle para envolvidos no projeto, inclua o repositório Maven do Google nas seções buildscript e allprojects:
buildscript {
repositories {
google()
mavenCentral()
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
Adicione a dependência do Google Play Services para a API Block Store
ao arquivo de build do Gradle do seu módulo, que geralmente é app/build.gradle:
dependencies {
implementation 'com.google.android.gms:play-services-auth-blockstore:16.4.0'
}
Como funciona
O Block Store permite que os desenvolvedores salvem e restaurem até 16 matrizes de bytes. Isso permite salvar informações importantes sobre a sessão do usuário atual e oferece a flexibilidade de salvar essas informações como quiser. Esses dados podem ser criptografados de ponta a ponta, e a infraestrutura que oferece suporte ao Block Store é criada com base na infraestrutura de backup e restauração.
Este guia aborda o caso de uso de salvar o token de um usuário no Block Store. As etapas a seguir descrevem como um app que usa o Block Store funciona:
- Durante o fluxo de autenticação do app ou a qualquer momento depois disso, é possível armazenar o token de autenticação do usuário no Block Store para recuperação posterior.
- O token será armazenado localmente e também poderá ser armazenado em backup na nuvem, com criptografia de ponta a ponta, quando possível.
- Os dados são transferidos quando o usuário inicia um fluxo de restauração em um novo dispositivo.
- Se o usuário restaurar o app durante o fluxo de restauração, o app poderá recuperar o token salvo do Block Store no novo dispositivo.
Como salvar o token
Quando um usuário faz login no seu app, é possível salvar o token de autenticação
gerado para esse usuário no Block Store. É possível armazenar esse token usando um valor de par de chaves exclusivo que tenha no máximo 4 KB por entrada. Para armazenar o token, chame
setBytes() e setKey() em uma instância de
StoreBytesData.Builder para armazenar as credenciais do usuário no dispositivo
de origem. Depois que você salva o token com a Block Store, ele é criptografado e
armazenado localmente no dispositivo.
O exemplo a seguir mostra como salvar o token de autenticação no dispositivo 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) }
Usar token padrão
Os dados salvos usando StoreBytes sem uma chave usam a chave padrão
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) }
Como recuperar o token
Depois, quando um usuário passa pelo fluxo de restauração em um novo dispositivo, o Google Play
Services primeiro verifica o usuário e, em seguida, recupera os dados do Block Store. O usuário
já concordou em restaurar os dados do app como parte do fluxo de restauração, então não
é necessário dar mais consentimentos. Quando o usuário abrir o app, você poderá solicitar
seu token do Block Store chamando retrieveBytes(). O token recuperado
pode ser usado para manter o usuário conectado no novo dispositivo.
O exemplo a seguir mostra como recuperar vários tokens com base em chaves específicas.
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) }
Recuperando todos os tokens.
Veja abaixo um exemplo de como recuperar todos os tokens salvos no 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) }
Veja abaixo um exemplo de como recuperar a chave padrão.
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)
Como excluir tokens
A exclusão de tokens do BlockStore pode ser necessária pelos seguintes motivos:
- O usuário passa pelo fluxo de saída.
- O token foi revogado ou é inválido.
Assim como na recuperação de tokens, é possível especificar quais tokens precisam ser excluídos definindo uma matriz de chaves que exigem exclusão.
O exemplo a seguir demonstra como excluir determinadas chaves:
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)
Excluir todos os tokens
O exemplo a seguir mostra como excluir todos os tokens salvos no 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_KEYcan be used in theRetrieveBytesRequestinstance 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")
}
Ativar o backup na nuvem
Para ativar o backup na nuvem, adicione o método setShouldBackupToCloud() ao objeto
StoreBytesData. O Block Store vai fazer backup periódico na nuvem dos
bytes armazenados quando setShouldBackupToCloud() for definido como verdadeiro.
O exemplo a seguir mostra como ativar o backup na nuvem somente quando ele estiver criptografado de ponta a ponta:
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.")
}
}
Como testar
Use os métodos a seguir durante o desenvolvimento para testar os fluxos de restauração.
Desinstalação/reinstalação no mesmo dispositivo
Se o usuário ativar os serviços de backup (isso pode ser verificado em Configurações > Google > Backup), os dados da Block Store serão mantidos durante a desinstalação/reinstalação do app.
Siga estas etapas para testar:
- Integre a API Block Store ao seu app de teste.
- Use o app de teste para invocar a API Block Store e armazenar seus dados.
- Desinstale o app de teste e reinstale-o no mesmo dispositivo.
- Use o app de teste para invocar a API Block Store e recuperar seus dados.
- Verifique se os bytes recuperados são os mesmos que foram armazenados antes da desinstalação.
De dispositivo para dispositivo
Na maioria dos casos, isso exige uma redefinição de fábrica do dispositivo de destino. Em seguida, você pode entrar no fluxo de restauração sem fio do Android ou na restauração por cabo do Google (para dispositivos compatíveis).
Restauração na nuvem
- Integre a API Block Store ao seu app de teste, que precisa ser enviado à Google Play Store.
- No dispositivo de origem, use o app de teste para invocar a API Block Store e armazenar
seus dados, com
shouldBackUpToClouddefinido comotrue. - Para dispositivos com o Android O e versões mais recentes, é possível acionar manualmente um backup em nuvem do Block Store:
acesse Configurações > Google > Backup e clique no botão "Fazer backup agora".
- Para verificar se o backup em nuvem do Block Store foi concluído, você pode:
- Depois que o backup for concluído, pesquise linhas de registro com a tag "CloudSyncBpTkSvc".
- Você verá linhas como esta: "......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., tamanho do upload: XXX bytes ..."
- Depois de um backup em nuvem do Block Store, há um período de "resfriamento" de cinco minutos. Durante esses cinco minutos, clicar no botão "Fazer backup agora" não vai acionar outro backup na nuvem do Block Store.
- Para verificar se o backup em nuvem do Block Store foi concluído, você pode:
- Faça a redefinição de fábrica no dispositivo de destino e siga um fluxo de restauração na nuvem. Selecione para restaurar o app de teste durante o fluxo de restauração. Para mais informações sobre fluxos de restauração na nuvem, consulte Fluxos de restauração na nuvem compatíveis.
- No dispositivo de destino, use o app de teste para invocar a API Block Store e recuperar seus dados.
- Verifique se os bytes recuperados são os mesmos que foram armazenados no dispositivo de origem.
Requisitos do dispositivo
Criptografia de ponta a ponta
- A criptografia de ponta a ponta é compatível com dispositivos que executam o Android 9 (API 29) e versões mais recentes.
- O dispositivo precisa ter um bloqueio de tela definido com um PIN, padrão ou senha para que a criptografia de ponta a ponta seja ativada e criptografe corretamente os dados do usuário.
Fluxo de restauração entre dispositivos
A restauração de dispositivo para dispositivo exige que você tenha um dispositivo de origem e um de destino. Esses serão os dois dispositivos que vão transferir dados.
Os dispositivos de origem precisam ter o Android 6 (API 23) ou mais recente para fazer backup.
Segmente dispositivos com o Android 9 (API 29) e versões mais recentes para ter a capacidade de restauração.
Saiba mais sobre o fluxo de restauração entre dispositivos neste link.
Fluxo de backup e restauração na nuvem
O backup e a restauração na nuvem exigem um dispositivo de origem e um de destino.
Os dispositivos de origem precisam ter o Android 6 (API 23) ou mais recente para fazer backup.
Os dispositivos de destino são compatíveis com base nos fornecedores. Os dispositivos Pixel podem usar esse recurso no Android 9 (API 29), e todos os outros dispositivos precisam ter o Android 12 (API 31) ou mais recente.