Autenticando em múltiplos projetos com Firebase Admin

Paulo Henrique Araujo Leite
bawilabs
Published in
4 min readJan 24, 2018

Certo dia você poderá se deparar com o seguinte problema: você possui vários projetos no Firebase, mas gostaria que seus usuários, como por exemplo o João, usassem o mesmo e-mail e senha em todos os projetos sem ter que criar o usuário novamente. Além disso, você gostaria que o João ao se autenticar em outro de seus projetos, mantivesse sempre o mesmo UID (ID de usuário) para que ele possa ser identificado facilmente.

Representação visual da solução apresentada

Criando os usuários

A solução do problema começa quando passamos a usar apenas um projeto para controlar as contas de usuário. Vamos chamá-lo aqui de auth-project. Todo novo usuário será criado neste projeto.

Até esse ponto ainda é opcional o uso do Firebase Admin. Você pode criar os usuários pelo console.

Tela de criação de usuário do console do Firebase

Entretanto, ao utilizar o Firebase Admin você terá a vantagem de inserir informações adicionais sobre usuários, como: nome, telefone e url de uma foto. Estes dados poderão ser recuperados após o usuário logar.

A desvantagem é que você deverá integrar o Firebase Admin com um servidor (back-end) próprio, mas de toda forma você não conseguirá fugir disso.

Exemplo da criação de usuário utilizando Firebase Admin (back-end) em Python:

from firebase_admin import authuser = auth.create_user(
email='user@example.com',
email_verified=False,
phone_number='+15555550100',
password='secretPassword',
display_name='John Doe',
photo_url='http://www.example.com/12345678/photo.png',
disabled=False)

Instanciando múltiplos apps (cliente: JavaScript)

Iremos partir do pressuposto que você tenha uma página web que necessita autenticar em mais de um projeto do Firebase simultaneamente.

Primeiramente, você deve copiar as configurações da web de todos os projetos (auth-project e todos os outros que desejar)

Configurações da web no console do Firebase

Exemplo da instanciação de múltiplos apps no cliente web em JavaScript:

var configAuthProject = { ... };
var configOtherProject = { ... };
var authApp = firebase.initializeApp(configAuthProject);
var otherApp = firebase.initializeApp(configOtherProject, "other");

Perceba que nosso app “Default” se tornou o authApp e que ao instanciar o otherApp passamos a string “other” como parâmetro adicional. Essa string serve para identificar os outros apps além do app padrão. Você pode instanciar quantos apps desejar com suas respectivas configurações, além de nomeá-los da maneira que lhe for mais conveniente.

O próximo passo é autenticar normalmente no auth-project:

authApp.auth().signInWithEmailAndPassword(email, password)

Porém, dessa forma ainda não conseguimos autenticar no other-project. Para isso vamos utilizar um custom token.

Instanciando múltiplos apps (back-end: Python)

Em nossa solução o custom token será gerado pelo back-end e retornado para cliente web quando este o solicitar através de uma requisição HTTP (o foco aqui não é explicar como implementar esse servidor back-end).

O custom token é gerado para um projeto específico, portanto de forma similar ao que fizemos no cliente web, devemos instanciar diferentes apps no back-end também. Para isso devemos gerar uma chave privada (json) para cada projeto.

Botão para gerar chave privada do console. Localizado em Configurações do Projeto > Contas de Serviço

Ficará assim a inicialização de múltiplos apps no nosso back-end (Python):

from firebase_admin import credentials
from firebase_admin import auth
authCred = credentials.Certificate('auth-project-key.json')
otherCred = credentials.Certificate('other-project-key.json')
firebase_admin.initialize_app(authCred)
firebase_admin.initialize_app(otherCred, name="other-project-id")
#obs: O segundo app foi nomeado com seu id de projeto.

Solicitando o Custom Token

Após autenticar no auth-project o cliente web faz uma requisição POST ao nosso back-end enviando seu recém adquirido idToken e o id do projeto no qual deseja se autenticar:

authApp.auth().onAuthStateChanged(user => {
if (user) {
user.getIdToken(true).then(idToken => {
let postData = {
idToken: idToken,
projectId: "other-project-id"
}
axios.post(BACK_END_URL, postData)

}
}

Gerando o custom token

O método (em Python) para gerar o custom token deve receber três parâmetros:

  • uid: id do usuário para qual o custom token está sendo gerado;
  • additional_claims: qualquer informação adicional que poderão ser lidas por suas regras de segurança.
  • app: instância do app equivalente ao projeto para qual se destina o custom token.

Para obter o uid do usuário, iremos decodificá-lo a partir do idToken que o back-end irá receber na requisição.

Verificando se o idToken recebido é válido e extraindo seu uid (Python):

decoded_token = auth.verify_id_token(id_token)
uid = decoded_token['uid']
#obs: o idToken deverá ser verificado no mesmo projeto em que foi solicitado pelo cliente web (nesse caso, no auth-project)

Caso o idToken seja válido, agora devemos gerar o custom token e retorná-lo ao cliente:

additional_claims = {
'premiumAccount': False
}

custom_token = auth.create_custom_token(uid, additional_claims, firebase_admin.get_app(project_id))
#Obs1: "project_id" representa o id do projeto da requisição.
#Obs2: Anteriormente o app foi nomeado utilizando seu id.

Autenticando no outro projeto com o custom token

Após o back-end gerar e retornar nosso custom token, é só usá-lo na autenticação:

authApp.auth().onAuthStateChanged(user => {
if (user) {
user.getIdToken(true).then(idToken => {
var postData = {
idToken: idToken,
projectId: "other-project-id"
};
axios.post(BACK_END_URL, postData).then(response => {
var customToken = response.data;
otherApp.auth().signInWithCustomToken(customToken);
}

}
}
}

Pronto! Agora nosso cliente web está autenticado em dois projetos diferentes (auth-project e other-project). O uid do usuário é o mesmo em ambos os projetos, pois o custom token o garante, já que foi gerado utilizando o uid extraido do idToken forenecido pelo auth-project.

--

--