
Esse artigo trata de como fazer a autenticação usando o Firebase Authentication porém entre duas máquinas, entre um Client e API, por exemplo.
Recentemente, surgiu a necessidade de desenvolver uma aplicação na qual um cliente precisaria acessar determinados endpoints de uma API. A abordagem mais comum e prática para esse tipo de cenário seria utilizar um client ID e um client secret, enviados via header, validados no backend e utilizados para gerar um token JWT responsável pela autorização das requisições.
O desafio, porém, foi implementar esse fluxo utilizando usuários do Firebase Authentication. Embora o Google Cloud Platform ofereça mecanismos de autenticação para seus próprios recursos, esses não se aplicam diretamente a aplicações customizadas. Diante dessa limitação, optei por construir uma solução própria, utilizando o Firebase Authentication em conjunto com o Firebase Realtime Database para viabilizar a autenticação entre cliente e API.
Vamos aos detalhes técnicos

Primeiramente a Realtime Database, a escolha dela é por um detalhe bem simples, para esse uso ela vai ser praticamente gratuíta, e que o será inputado de dados nela será muito pequeno, e com uma busca igualmente simples, além da facilidade de criar mais de uma base de dados por projeto
O projeto precisa estar no Plano Blaze, pagamento por utilização:
Apesar de ser necessário um cartão de crédito internacional para isso, não serão efetuadas cobranças
Obviamente no que vamos propor nesse artigo 🙂

No Identity Platform dentro do GCP vamos habilitar a Multilocação
Esse recurso será usado para separar onde iremos armazenar os usuários


No backend vou utilizar Node com NestJs =D
Acredito que Nest irá facilitar muito o desenvolvimento
Mas vou mostrar isso tudo passo a passo logo em seguida.
Começando:
Como nunca sei se quem está lendo sabe como funciona o Firebase, sempre procuro começar com uma simples introdução.
Para usar/criar um projeto Firebase, é necessário apenas ter uma conta Google, um email @gmail ou qualquer email que seja também uma conta Google.
Dito isso, vamos acessar o console do Firebase, o link abaixo lhe levará diretamente a ele
https://console.firebase.google.com/
Você deve conseguir visualizar algo parecido com isso, bom, como as vezes muda, e desde a primeira vez que usei o Firebase mudou muito, no dia de hoje você de encontrar isso

No meu caso, o console já tem vários projetos criados, o que iremos fazer é criar um novo projeto,
para isso clique em “Criar um novo projeto do Firebase”

Algo importante a ser dito.
Um projeto do Firebase é um projeto do Cloud.
Quando você cria um projeto no console do Firebase, na verdade, está criando um projeto do Google Cloud
Clique aqui ou na imagem acima para maiores informações
Agora vamos dar um nome ao projeto
O nome do meu projeto irá ser client-and-secret

Clique em continuar

Clique novamente em continuar

na próxima tela, como escolhi ativar o Analytics, irá aparecer uma tela para escolher qual projeto de Analtyics quero usar, isso se dá pq tenho o meu site com um Analytics próprio dele

Para esse projeto irei escolher Defautl Account for Firebase, recomendo fazerem o mesmo.
Feito isso clique em continuar.

Projeto sendo criado

Pronto, projeto criado

Acredito que depois que você clicar em continuar, irá aparecer uma tela parecida com essa
No dia de hoje existe esse aviso sobre segurança no React or Next.js 🙂

Basic Auth:
Até esse momento é bem simples extrair um token do Firebase, simplesmente criando um usuário, dentro do repositório principal do Authentication, e passar o email + senha do usuário com Basic Auth e retornar um idToken, isso seria bem simples, porém iria expor tanto os emails, quanto as senhas dos usuários, e não é isso que queremos.
Realtime Database:
Agora vamos habilitar a Realtime Database
Para isso abra o menu lateral e clique em Realtime Database

Clique em “Criar Banco de dados”

Para o local, escolha Estados Unidos(us-central1)

Nas regras de segurança vamos deixá-las no modo bloqueado, sem poderem ser acessadas
{
"rules": {
".read": false,
".write": false
}
}

Após clicar em ativar, você deve poder visualizar sua base de dados

Identity Platform

A partir desse ponto, vamos precisar ativar o Identity Platform, e para isso vamos ter que atualizar o plano, que está no Spark, o plano de entrada de cada projeto Firebase/GCP para o plano Blaze
No menu lateral clique em “Fazer upgrade”

Selecione o Plano Blaze

Caso tenha um conta, selecione um, caso contrário crie uma

Caso precise criar uma nova conta, um popup com essas informações deve aparecer

Siga os próximos passos e você conseguirá atualizar seu plano do Firebase

Voltando ao Identity Platform
Para ativar o Identity Platform, use o link acima, apenas mude para o id do seu projeto

Clique em Ativar o Identity Platform
Dando tudo certo, essa tela deve ser mostrada

Para ativar a multilocação clique no menu lateral em configurações
Provavelmente esse popup de alerta irá aparecer, mas não se preocupe, ele apenas está avisando que as Cloud Functions não estão habilitadas, mas não as usaremos para o que estamos fazendo, pode fechar e continuar.

Na aba segurança, em Multilocação, clique em Permitir Locatários

Acredito que essa tela, ou bem parecida com essa tenha sido mostrada a você .

Vamos adicionar um locatário, como meu objetivo é validar clientes externos, o nome desse locatário será external. Para isso clique em “Adicionar um locatário”

Salvando o locatário é listado

Você excluir ou editar o locatário, porém o editar não seria editar o nome e sim os métodos de login, e provedores
Logins:

Provedores:

O mais interessante disso, é que não precisaremos ativar método algum, nem ao menos o login por email e senha.
Código backend
Eu não vou mostrar como criar o projeto ou até mesmo codificar as partes básicas do projeto que não são relacionadas ao Firebase, pq acho que cada um sabe como melhor deve ser seu código. Fique a vontade para usar o que e como acha mais confortável 🙂
Sobre versionamento, hoje dia 01/01/2026
WebStorm 2025.2.3
nest --version
10.4.5
node --version
v23.9.0
Conectando o backend:
A primeira coisa que precisamos fazer é conectar o nosso backend ao Firebase, para isso precisamos das credenciais de accesso, que não são as mesmas usadas no front end, para isso vamos voltar ao console do Firebase https://console.firebase.google.com/ entre no seu projeto
No canto superior esquerdo, procure pela engranagem, logo abaixo do logo, clique e selecione Configurações do projeto

Na aba Conta de serviço clique em Gerar nova chave privada

Esse aviso é bem importante, não envie essas chaves para Github, por exemplo, pois elas dão acesso administrativo ao Firebase.
Clique em Gerar Chave

Um arquivo .json será baixado com as credenciais do seu projeto.
Sugiro criar um arquivo .env e colocar esses dados dentro dele e adicionar esse .env ao .gitignore
Na arquitetura de backend que estou montando, eu criei uma lib que vai gerir a comunicação com o Firebasebase a firebase-admin, nela coloquei as credenciais dentro do .env
Obs: Novamente, essa arquitetura é baseada no que o NextJs oferece, os conceitos básicos são os mesmos, portanto fique a vontade para criar a sua 🙂
.env:

Código .env
# Firebase Admin SDK configuration
FIREBASE_TYPE=service_account
FIREBASE_PROJECT_ID=project-id
FIREBASE_PRIVATE_KEY_ID=00000000000
FIREBASE_PRIVATE_KEY='-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----\n'
FIREBASE_CLIENT_EMAIL=firebase-adminsdk-fbsvc@project-id.iam.gserviceaccount.com
FIREBASE_CLIENT_ID=000000000000
FIREBASE_AUTH_URI=https://accounts.google.com/o/oauth2/auth
FIREBASE_TOKEN_URI=https://oauth2.googleapis.com/token
FIREBASE_AUTH_PROVIDER_X509_CERT_URL=https://www.googleapis.com/oauth2/v1/certs
FIREBASE_CLIENT_X509_CERT_URL=https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-fbsvc%40project-id.iam.gserviceaccount.com
FIREBASE_UNIVERSE_DOMAIN=googleapis.com
Obs: Essas informações em produção devem ficar em algum gerenciador de secrets, não em arquivos .env
Também criei um arquivo firebase.config.ts que irá remontar esse objeto para uso posterior
firebase.config.ts:

Código firebase.config.ts:
export class FirebaseConfig {
static getConfig() {
return {
type: process.env.FIREBASE_TYPE,
project_id: process.env.FIREBASE_PROJECT_ID,
private_key_id: process.env.FIREBASE_PRIVATE_KEY_ID,
private_key: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, '\n'),
client_email: process.env.FIREBASE_CLIENT_EMAIL,
client_id: process.env.FIREBASE_CLIENT_ID,
auth_uri: process.env.FIREBASE_AUTH_URI,
token_uri: process.env.FIREBASE_TOKEN_URI,
auth_provider_x509_cert_url:
process.env.FIREBASE_AUTH_PROVIDER_X509_CERT_URL,
client_x509_cert_url: process.env.FIREBASE_CLIENT_X509_CERT_URL,
universe_domain: process.env.FIREBASE_UNIVERSE_DOMAIN,
};
}
}
Agora o código para conectar, para isso precisaremos de uma dependência, o firebase-admin
https://www.npmjs.com/package/firebase-admin
npm i --save firebase-admin
Feito isso, agora vamos colocar esse código para funcionar dentro do FirebaseAdminService

Código firebase-admin.service.ts
import { Injectable, Logger } from '@nestjs/common';
import { App, cert, initializeApp, ServiceAccount } from 'firebase-admin/app';
import { FirebaseConfig } from '@/libs/firebase-admin/src/firebase.config';
@Injectable()
export class FirebaseAdminService {
defaultApp: App;
constructor() {
this.defaultApp = initializeApp({
credential: cert(FirebaseConfig.getConfig() as ServiceAccount),
});
Logger.log('Firebase Admin initialized', 'FirebaseAdminService');
}
}
Testando:
Para testar essa conexão é simples, apenas inclua o FirebaseAdminModule no AppModule

Tudo ok a mensagem de log [FirebaseAdminService] Firebase Admin initialized deve ser mostrada
[Nest] 88458 - 01/02/2026, 1:35:43 PM LOG [FirebaseAdminService] Firebase Admin initialized
Código AppModule.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { FirebaseAdminModule } from '@/libs/firebase-admin/src';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [ConfigModule.forRoot(), FirebaseAdminModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Agora vamos ao módulo quevai gerir a conexão ao Firebase Authentication
Crie uma outra lib firebase-auth

Código firebase-auth.service.ts
import { Injectable, Logger } from '@nestjs/common';
import { FirebaseAdminService } from '@/libs/firebase-admin/src';
import { Auth, getAuth } from 'firebase-admin/auth';
@Injectable()
export class FirebaseAuthService {
auth: Auth;
constructor(private readonly firebaseAdminService: FirebaseAdminService) {
this.auth = getAuth(this.firebaseAdminService.defaultApp);
Logger.log('Firebase Auth initialized', 'FirebaseAuthService');
}
}
E importei o FirebaseAdminModule para dentro dessa nova lib

Código firebase-auth.module.ts
import { Module } from '@nestjs/common';
import { FirebaseAuthService } from './firebase-auth.service';
import { FirebaseAdminModule } from '@/libs/firebase-admin/src';
@Module({
imports: [FirebaseAdminModule],
providers: [FirebaseAuthService],
exports: [FirebaseAuthService],
})
export class FirebaseAuthModule {}
Testando:
Para testar é simples, remova o FirebaseAdminModule do AppModule e inclua o FirebaseAuthModule

Tudo ok você deve poder ver as seguintes mensagens de log.
[Nest] 91575 - 01/02/2026, 2:50:26 PM LOG [FirebaseAdminService] Firebase Admin initialized
[Nest] 91575 - 01/02/2026, 2:50:26 PM LOG [FirebaseAuthService] Firebase Auth initialized
Apesar de parecer tudo ok, existe uma configuração necessária
Dessa forma, mesmo que consigamos conectar no Firebase no Authentication, estamos apenas fazendo isso diretamente no repositório principal de usuários, não no tenant/locatário que criamos.
Para isso vamos pegar o id do locatário que criamos
O id é o Código do locatário, como pode ser visualizado abaixo: external-9zuoi

Pegue esse id e adicione a uma variável no .env
E vamos adicionar o código que irá conectar nesse locatário, para isso vamos usar o TenantAwareAuth
Você pode ver esse código abaixo, em vez de usarmos o repositório principal, usaremos o locatário external passando o seu id.

Código firebase-auth.service.ts:
import { Injectable, Logger } from '@nestjs/common';
import { FirebaseAdminService } from '@/libs/firebase-admin/src';
import { Auth, getAuth, TenantAwareAuth } from 'firebase-admin/auth';
@Injectable()
export class FirebaseAuthService {
auth: Auth;
authExternal: TenantAwareAuth;
tenantId = process.env.FIREBASE_EXTERNAL_TENANT_ID;
constructor(private readonly firebaseAdminService: FirebaseAdminService) {
this.auth = getAuth(this.firebaseAdminService.defaultApp);
this.authExternal = this.auth.tenantManager().authForTenant(this.tenantId!);
Logger.log(
`Firebase Auth External ${this.authExternal.tenantId} initialized`,
'FirebaseAuthService',
);
}
}
Acredito que estejas vendo corretamente o log de inicialização do locatário 🙂
Vamos criar a primeira função, que vai criar um usuário nesse locatário.
Ela será bem simples, apenas irá incluir um user qualquer e retornar as informações desse mesmo usuário, no formato UserRecord. Ela não será usada mais tarde, portando não se preocupe muito com essa função.

Código da função:
import { Injectable, Logger } from '@nestjs/common';
import { FirebaseAdminService } from '@/libs/firebase-admin/src';
import {
Auth,
getAuth,
TenantAwareAuth,
UserRecord,
} from 'firebase-admin/auth';
@Injectable()
export class FirebaseAuthService {
auth: Auth;
authExternal: TenantAwareAuth;
tenantId = process.env.FIREBASE_EXTERNAL_TENANT_ID;
constructor(private readonly firebaseAdminService: FirebaseAdminService) {
this.auth = getAuth(this.firebaseAdminService.defaultApp);
this.authExternal = this.auth.tenantManager().authForTenant(this.tenantId!);
Logger.log(
`Firebase Auth External ${this.authExternal.tenantId} initialized`,
'FirebaseAuthService',
);
}
async createUserInExternalTenant(
email: string,
displayName: string,
): Promise<UserRecord> {
return await this.authExternal.createUser({
email,
displayName,
});
}
}
Dentro do serviço da controller vamos incluir a chamada dessa função

Código do app.service.ts
import { Injectable } from '@nestjs/common';
import { FirebaseAuthService } from '@/libs/firebase-auth/src';
@Injectable()
export class AppService {
constructor(private readonly firebaseAuthService: FirebaseAuthService) {}
async createUserInExternalTenant(email: string, displayName: string) {
return this.firebaseAuthService.createUserInExternalTenant(
email,
displayName,
);
}
}
E claro, criar o endpoint para chamar essa função dentro do serviço

Código do endpoint
import { Body, Controller, Post } from '@nestjs/common';
import { AppService } from './app.service';
@Controller('auth')
export class AppController {
constructor(private readonly appService: AppService) {}
@Post('external/create-user')
async createUser(@Body() body: { email: string; displayName: string }) {
const { email, displayName } = body;
return this.appService.createUserInExternalTenant(email, displayName);
}
}
Testando:
Agora nos resta testar
Para isso vamos usar o Postman, passado a rota mapeada pelo NestJs
Você pode verificar as rotas mapeadas na inicialização, pois o Nest loga das elas durante esse processo
[Nest] 98350 - 01/02/2026, 5:40:45 PM LOG [InstanceLoader] AppModule dependencies initialized +0ms
[Nest] 98350 - 01/02/2026, 5:40:45 PM LOG [RoutesResolver] AppController {/auth}: +5ms
[Nest] 98350 - 01/02/2026, 5:40:45 PM LOG [RouterExplorer] Mapped {/auth/external/create-user, POST} route +5ms
[Nest] 98350 - 01/02/2026, 5:40:45 PM LOG [NestApplication] Nest application successfully started +8ms
Vamos usar a rota /auth/external/create-user, POST
Como no caso estou em localhost, o endereço completo será um POST em
http://localhost:3000/auth/external/create-user

Dando tudo certo ao enviar a requisição, você deve receber uma reposta parecida com essa

Voltando ao Google Cloud, você poderá ver o usuário criado

Você deve estar se perguntando, como foi criado um usuário apenas com email sem senha?
Pois então, é algo que pode ser feito, até pq quem vai definir a senha, que na realidade será um secret seremos nós mais tarde
Mas tanto o Firebase quanto o GCP permitem usuários desse nível de serem criados sem uma senha.
Até pq em ambos, temos o usuário anônimo, que cria um user sem ao menos email, apenas gera um uid de referência para posteriormente linkar a um user quando ele se registrar.
Vou continuar esse artigo em uma segunda parte, pois acredito que está ficando muito grande.
Assim que estiver pronto insiro o link dele aqui
Te espero lá 🙂
Muito obrigada pela leitura.
0





