Google Firestore com Autenticação customizada no Android.
Ao elaborar a arquitetura de aplicativos, devemos tomar certos cuidados com a segurança. É algo comum ao utilizar algum serviço de terceiros, o uso de tokens de autenticação, por exemplo. Devemos sempre ter cuidado com esses dados sensíveis, e sempre que possível, não embarcá-los no seu aplicativo.
O Cloud Firestore é um banco de dados flexível e escalonável para desenvolvimento de dispositivos móveis, web e back-end a partir do Firebase e do Google Cloud Platform. Com o Firebase Realtime Database, é possível manter seus dados em sincronia em aplicativos cliente por meio de listeners em tempo real. Além disso, oferece suporte off-line para dispositivos móveis e Web para que você possa criar aplicativos responsivos que funcionem independentemente da latência de rede ou da conectividade com a Internet. O Cloud Firestore também oferece integração perfeita com outros produtos do Firebase e do Google Cloud Platform, incluindo o Cloud Functions.
Existem algumas formas de se autenticar no Firestore em seu aplicativo Android, a mais comum é embarcar o google-service.json e importar em seu gradle file a depêndencia do google-services, que em tempo de compilação irá gerar todos os resources com as chaves necessárias para sua autenticação. Existe uma outra forma, que utilizaremos como exemplo,para gerarmos um custom token de autenticação. Esse token será gerado por uma Google Cloud Function , tirando assim a necessidade de embarcar dados sensíveis no aplicativo.
Arquitetura da solução
Iniciaremos com um diagrama da solução proposto no artigo:
Olhando o diagrama, fica fácil entender a arquitetura da solução que será apresentada.
Gerando o arquivo de autenticação
Para gerar o json com sua chave de autenticação é bem simples, basta acessar o console do Google Cloud Plataform, ir na aba IAM e Admin e escolher a opção Contas de serviço. Escolha sua conta que neste caso é a Firebase Admin SDK Service Agent e selecione a ação Criar chave. Depois selecione a opção json. Após isso o download do documento irá iniciar.
Iniciando um projeto de Cloud Functions
Vamos levar em consideração que o firebase-tools CLI esteja configurado em sua máquina, para iniciar um projeto Cloud Functions basta executar o seguinte comando:
firebase init functions
Várias perguntas quanto a configuração, serão mostradas em seu terminal, como por exemplo, qual projeto será atrelado ao serviço, a linguagem utilizada, se deseja utilizar o lint e se deseja instalar as dependências.
Para o exemplo a ser mostrado, usaremos o pacote firebase-admin, instalaremos via npm o seguinte comando :
npm install firebase-admin
Criando a Cloud Function de Autenticação
Para a autenticação do firebase e geração do token para o cliente, usaremos a seguinte função de exemplo :
Utilizaremos o arquivo de certificado que criamos logo no início, inserindo este dentro do diretório de nossa função e informando o path onde está sendo solicitado. Os dados presentes nesse arquivo, serão de grande importância para formar o certificado que usaremos para inicializar o firebase-admin.
Após o trecho de inicialização do firebase-admin, temos o escopo de nossa função auth. No início teremos a declaração de nosso uid, ele nada mais é que a identificação do nosso usuário, que será utilizada para a composição do custom token que será gerado pelo firebase. Vamos montar um exemplo simples, nossa API estará aberta para o acesso, mas a forma mais segura, seria validar o usuário , e a partir dessa validação, utilizando um token JWT por exemplo, poderíamos gerar um identificador de usuário para ser usado.
Ocorrendo um erro ao tentar gerar o token, iremos retornar 401 Unauthorized para o usuário, e em caso de sucesso retornaremos o objeto com os dados necessários para a autenticação do cliente.
Para descobrir seu apiKey é muito simples, basta acessar o Firebase console , escolher seu projeto e ir em configurações, lá estarão os dados referentes a seu projeto, como id do projeto, api key e outros.
Fazendo Deploy de sua Cloud Function
Nesse artigo não entraremos no mérito de CI/CD de Cloud functions (Isso ficará para um outro artigo).
Para fazer deploy a partir de sua máquina é bem simples, basta rodar o seguinte comando estando na raiz de seu projeto :
firebase deploy --only functions
Se você estiver autenticado em sua conta e tudo ocorrer certo ao final do processo você terá o seguinte log em seu terminal:
Ao acessar a url do console você poderá ver todas funções publicadas em seu projeto, gráfico de uso, log de erros entre outras features.
Criando a autenticação customizada em nosso aplicativo
Agora falaremos de como fazer com que nossa aplicação faça a autenticação do firestore utilizando da função que acabamos de criar anteriormente.
Criei um módulo de autenticação chamado auth, nele as seguintes dependências devem estar inclusas:
implementation 'com.google.firebase:firebase-auth:16.1.0'
implementation 'com.google.code.gson:gson:2.8.2'
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.8.0'
Inicialmente, criaremos uma classe que irá corresponder ao objeto para onde nossa função retornará em caso de sucesso:
Depois, criaremos uma interface de serviço do retrofit com a chamada a nossa função:
Criaremos uma interface de repositório, onde teremos um método getInfo que retornar[á um LiveData da classe AuthInfo que criamos anteriormente, com isso manteremos a implementação utilizando o retrofit encapsulada.
A implementação do repositório irá herdar da seguinte classe :
Onde BASE_URL, será o domínio de seu serviço. Essa classe basicamente abstrai a construção do retrofit, tornando mais fácil a chamada de um serviço, como veremos a seguir:
Como podemos ver, temos o método getInfo , onde retornamos um liveData que pode ser observado. É nesse método que fazemos uso da nossa cloud function e em caso de sucesso retornamos os dados para autenticação.
Agora criaremos a classe que fará a autenticação no firebase, utilizando o repositório que acabamos de criar para requisitar os dados necessários:
Pronto, já temos o componente responsável pela autenticação em nosso aplicativo, para testar fiz um pequeno teste na Activity:
Ao executar a aplicação, podemos acompanhar pelo logcat a chamada a nossa function, o login ao firestore e o retorno do registro de teste no firestore:
Como podemos ver, é fácil criar uma autenticação customizada para o Google Firestore, o exemplo utilizado é bem simples e podemos amadurecer a idéia, porém é uma prova de conceito funcional.
O projeto de exemplo está disponível no meu Github. Da uma conferida lá! :)