Como criar uma API baseada em papéis com o Firebase Authentication

Publicados: 2022-03-11

Neste tutorial, criaremos uma API REST para gerenciar usuários e funções usando Firebase e Node.js. Além disso, veremos como usar a API para autorizar (ou não) quais usuários podem acessar recursos específicos.

Introdução

Quase todos os aplicativos requerem algum nível de sistema de autorização. Em alguns casos, validar um conjunto de nome de usuário/senha com nossa tabela Users é suficiente, mas muitas vezes precisamos de um modelo de permissões mais refinado para permitir que determinados usuários acessem determinados recursos e os restrinjam de outros. Construir um sistema para suportar o último não é trivial e pode consumir muito tempo. Neste tutorial, aprenderemos a criar uma API de autenticação baseada em função usando o Firebase, o que nos ajudará a começar a usar rapidamente.

Autenticação baseada em função

Nesse modelo de autorização, o acesso é concedido a funções, em vez de usuários específicos, e um usuário pode ter uma ou mais, dependendo de como você projeta seu modelo de permissão. Os recursos, por outro lado, exigem certas funções para permitir que um usuário os execute.

Autenticação baseada em função com ilustração

Firebase

Autenticação do Firebase

Em poucas palavras, o Firebase Authentication é um sistema de autenticação extensível baseado em token e fornece integrações prontas para uso com os provedores mais comuns, como Google, Facebook e Twitter, entre outros.

Ele nos permite usar declarações personalizadas que usaremos para criar uma API flexível baseada em função.

Podemos definir qualquer valor JSON nas declarações (por exemplo, { role: 'admin' } ou { role: 'manager' } ).

Depois de definidas, as declarações personalizadas serão incluídas no token gerado pelo Firebase e podemos ler o valor para controlar o acesso.

Ele também vem com uma cota gratuita muito generosa, que na maioria dos casos será mais que suficiente.

Funções do Firebase

As funções são um serviço de plataforma sem servidor totalmente gerenciado. Só precisamos escrever nosso código em Node.js e implantá-lo. O Firebase cuida do dimensionamento da infraestrutura sob demanda, configuração do servidor e muito mais. No nosso caso, vamos usá-lo para construir nossa API e expô-la via HTTP para a web.

O Firebase nos permite definir aplicativos express.js como manipuladores para caminhos diferentes. Por exemplo, você pode criar um aplicativo Express e conectá-lo a /mypath , e todas as solicitações que chegarem a essa rota serão tratadas pelo app configurado.

No contexto de uma função, você tem acesso a toda a API Firebase Authentication, usando o SDK Admin.

É assim que vamos criar a API do usuário.

O que vamos construir

Então, antes de começarmos, vamos dar uma olhada no que vamos construir. Vamos criar uma API REST com os seguintes endpoints:

Verbo Http Caminho Descrição Autorização
PEGAR /Comercial Lista todos os usuários Somente administradores e gerentes têm acesso
PUBLICAR /Comercial Cria novo usuário Somente administradores e gerentes têm acesso
PEGAR /usuários/:id Obtém o usuário :id Administradores, gerentes e o mesmo usuário que :id têm acesso
CORREÇÃO /usuários/:id Atualiza o usuário :id Administradores, gerentes e o mesmo usuário que :id têm acesso
EXCLUIR /usuários/:id Exclui o usuário :id Administradores, gerentes e o mesmo usuário que :id têm acesso

Cada um desses terminais tratará da autenticação, validará a autorização, executará a operação correspondente e, finalmente, retornará um código HTTP significativo.

Criaremos as funções de autenticação e autorização necessárias para validar o token e verificar se as declarações contêm a função necessária para executar a operação.

Construindo a API

Para construir a API, vamos precisar de:

  • Um projeto do Firebase
  • firebase-tools instaladas

Primeiro, faça login no Firebase:

 firebase login

Em seguida, inicialize um projeto do Functions:

 firebase init ? Which Firebase CLI features do you want to set up for this folder? ... (O) Functions: Configure and deploy Cloud Functions ? Select a default Firebase project for this directory: {your-project} ? What language would you like to use to write Cloud Functions? TypeScript ? Do you want to use TSLint to catch probable bugs and enforce style? Yes ? Do you want to install dependencies with npm now? Yes

Neste ponto, você terá uma pasta Functions, com configuração mínima para criar Firebase Functions.

Em src/index.ts há um exemplo helloWorld , que você pode descomentar para validar que suas funções funcionam. Então você pode cd functions e executar npm run serve . Este comando irá transpilar o código e iniciar o servidor local.

Você pode verificar os resultados em http://localhost:5000/{your-project}/us-central1/helloWorld

Um novo aplicativo do Firebase

Observe que a função é exposta no caminho definido como o nome dela em 'index.ts: 'helloWorld' .

Como criar uma função HTTP do Firebase

Agora vamos codificar nossa API. Vamos criar uma função http Firebase e ligá-la no caminho /api .

Primeiro, instale npm install express .

No src/index.ts iremos:

  • Inicialize o módulo Firebase-admin SDK com admin.initializeApp();
  • Defina um aplicativo Express como o manipulador do nosso endpoint https da api
 import * as functions from 'firebase-functions'; import * as admin from 'firebase-admin'; import * as express from 'express'; admin.initializeApp(); const app = express(); export const api = functions.https.onRequest(app);

Agora, todas as solicitações que vão para /api serão tratadas pela instância do app .

A próxima coisa que faremos é configurar a instância do app para dar suporte ao CORS e adicionar o middleware do analisador de corpo JSON. Dessa forma, podemos fazer solicitações de qualquer URL e analisar solicitações formatadas em JSON.

Primeiro, instalaremos as dependências necessárias.

 npm install --save cors body-parser
 npm install --save-dev @types/cors

E então:

 //... import * as cors from 'cors'; import * as bodyParser from 'body-parser'; //... const app = express(); app.use(bodyParser.json()); app.use(cors({ origin: true })); export const api = functions.https.onRequest(app);

Por fim, configuraremos as rotas que o app manipulará.

 //... import { routesConfig } from './users/routes-config'; //… app.use(cors({ origin: true })); routesConfig(app) export const api = functions.https.onRequest(app);

O Firebase Functions nos permite definir um aplicativo Express como manipulador e qualquer caminho após o que você configurou em functions.https.onRequest(app); —neste caso, api — também será tratado pelo app . Isso nos permite escrever endpoints específicos, como api/users , e definir um manipulador para cada verbo HTTP, o que faremos a seguir.

Vamos criar o arquivo src/users/routes-config.ts

Aqui, vamos definir um manipulador de create em POST '/users'

 import { Application } from "express"; import { create} from "./controller"; export function routesConfig(app: Application) { app.post('/users', create ); }

Agora, vamos criar o arquivo src/users/controller.ts .

Nesta função, primeiro validamos que todos os campos estão no corpo da solicitação e, em seguida, criamos o usuário e definimos as declarações personalizadas.

Estamos apenas passando { role } no setCustomUserClaims — os outros campos já estão definidos pelo Firebase.

Caso não ocorra nenhum erro, retornamos um código 201 com o uid do usuário criado.

 import { Request, Response } from "express"; import * as admin from 'firebase-admin' export async function create(req: Request, res: Response) { try { const { displayName, password, email, role } = req.body if (!displayName || !password || !email || !role) { return res.status(400).send({ message: 'Missing fields' }) } const { uid } = await admin.auth().createUser({ displayName, password, email }) await admin.auth().setCustomUserClaims(uid, { role }) return res.status(201).send({ uid }) } catch (err) { return handleError(res, err) } } function handleError(res: Response, err: any) { return res.status(500).send({ message: `${err.code} - ${err.message}` }); }

Agora, vamos proteger o manipulador adicionando autorização. Para fazer isso, adicionaremos alguns manipuladores ao nosso endpoint de create . Com express.js , você pode definir uma cadeia de manipuladores que serão executados em ordem. Dentro de um manipulador, você pode executar o código e passá-lo para o manipulador next() ou retornar uma resposta. O que faremos é primeiro autenticar o usuário e depois validar se ele está autorizado a executar.

No arquivo src/users/routes-config.ts :

 //... import { isAuthenticated } from "../auth/authenticated"; import { isAuthorized } from "../auth/authorized"; export function routesConfig(app: Application) { app.post('/users', isAuthenticated, isAuthorized({ hasRole: ['admin', 'manager'] }), create ); }

Vamos criar os arquivos src/auth/authenticated.ts .

Nesta função, validaremos a presença do token do portador de authorization no cabeçalho da solicitação. Em seguida, vamos decodificá-lo com admin.auth().verifyidToken() e persistir o uid , a role e o email do usuário na variável res.locals , que usaremos posteriormente para validar a autorização.

Caso o token seja inválido, retornamos uma resposta 401 ao cliente:

 import { Request, Response } from "express"; import * as admin from 'firebase-admin' export async function isAuthenticated(req: Request, res: Response, next: Function) { const { authorization } = req.headers if (!authorization) return res.status(401).send({ message: 'Unauthorized' }); if (!authorization.startsWith('Bearer')) return res.status(401).send({ message: 'Unauthorized' }); const split = authorization.split('Bearer ') if (split.length !== 2) return res.status(401).send({ message: 'Unauthorized' }); const token = split[1] try { const decodedToken: admin.auth.DecodedIdToken = await admin.auth().verifyIdToken(token); console.log("decodedToken", JSON.stringify(decodedToken)) res.locals = { ...res.locals, uid: decodedToken.uid, role: decodedToken.role, email: decodedToken.email } return next(); } catch (err) { console.error(`${err.code} - ${err.message}`) return res.status(401).send({ message: 'Unauthorized' }); } }

Agora, vamos criar um arquivo src/auth/authorized.ts .

Neste handler, extraímos as informações do usuário de res.locals que definimos anteriormente e validamos se ele possui o papel necessário para executar a operação ou caso a operação permita que o mesmo usuário execute, validamos que o ID nos parâmetros de solicitação é igual ao do token de autenticação. Se o usuário não tiver a função necessária, retornaremos um 403.

 import { Request, Response } from "express"; export function isAuthorized(opts: { hasRole: Array<'admin' | 'manager' | 'user'>, allowSameUser?: boolean }) { return (req: Request, res: Response, next: Function) => { const { role, email, uid } = res.locals const { id } = req.params if (opts.allowSameUser && id && uid === id) return next(); if (!role) return res.status(403).send(); if (opts.hasRole.includes(role)) return next(); return res.status(403).send(); } }

Com esses dois métodos, poderemos autenticar solicitações e autorizá-las de acordo com a role no token de entrada. Isso é ótimo, mas como o Firebase não permite definir declarações personalizadas no console do projeto, não poderemos executar nenhum desses endpoints. Para contornar isso, podemos criar um usuário root no Firebase Authentication Console

Como criar um usuário no Console do Firebase Authentication

E defina uma comparação de e-mail no código. Agora, ao disparar requisições deste usuário, poderemos executar todas as operações.

 //... const { role, email, uid } = res.locals const { id } = req.params if (email === '[email protected]') return next(); //...

Agora, vamos adicionar o restante das operações CRUD a src/users/routes-config.ts .

Para operações para obter ou atualizar um único usuário onde o parâmetro :id é enviado, também permitimos que o mesmo usuário execute a operação.

 export function routesConfig(app: Application) { //.. // lists all users app.get('/users', [ isAuthenticated, isAuthorized({ hasRole: ['admin', 'manager'] }), all ]); // get :id user app.get('/users/:id', [ isAuthenticated, isAuthorized({ hasRole: ['admin', 'manager'], allowSameUser: true }), get ]); // updates :id user app.patch('/users/:id', [ isAuthenticated, isAuthorized({ hasRole: ['admin', 'manager'], allowSameUser: true }), patch ]); // deletes :id user app.delete('/users/:id', [ isAuthenticated, isAuthorized({ hasRole: ['admin', 'manager'] }), remove ]); }

E em src/users/controller.ts . Nessas operações, aproveitamos o SDK admin para interagir com o Firebase Authentication e realizar as respectivas operações. Como fizemos anteriormente na operação de create , retornamos um código HTTP significativo em cada operação.

Para a operação de atualização, validamos todos os campos presentes e substituímos customClaims pelos enviados na solicitação:

 //.. export async function all(req: Request, res: Response) { try { const listUsers = await admin.auth().listUsers() const users = listUsers.users.map(mapUser) return res.status(200).send({ users }) } catch (err) { return handleError(res, err) } } function mapUser(user: admin.auth.UserRecord) { const customClaims = (user.customClaims || { role: '' }) as { role?: string } const role = customClaims.role ? customClaims.role : '' return { uid: user.uid, email: user.email || '', displayName: user.displayName || '', role, lastSignInTime: user.metadata.lastSignInTime, creationTime: user.metadata.creationTime } } export async function get(req: Request, res: Response) { try { const { id } = req.params const user = await admin.auth().getUser(id) return res.status(200).send({ user: mapUser(user) }) } catch (err) { return handleError(res, err) } } export async function patch(req: Request, res: Response) { try { const { id } = req.params const { displayName, password, email, role } = req.body if (!id || !displayName || !password || !email || !role) { return res.status(400).send({ message: 'Missing fields' }) } await admin.auth().updateUser(id, { displayName, password, email }) await admin.auth().setCustomUserClaims(id, { role }) const user = await admin.auth().getUser(id) return res.status(204).send({ user: mapUser(user) }) } catch (err) { return handleError(res, err) } } export async function remove(req: Request, res: Response) { try { const { id } = req.params await admin.auth().deleteUser(id) return res.status(204).send({}) } catch (err) { return handleError(res, err) } } //...

Agora podemos executar a função localmente. Para fazer isso, primeiro você precisa configurar a chave da conta para poder se conectar à API de autenticação localmente. Então corra:

 npm run serve

Implantar a API

Excelente! Agora que escrevemos a API baseada em função, podemos implantá-la na Web e começar a usá-la. A implantação com o Firebase é super fácil, só precisamos executar firebase deploy . Depois que a implantação for concluída, podemos acessar nossa API na URL publicada.

Executando o comando de implantação do firebase

Você pode verificar o URL da API em https://console.firebase.google.com/u/0/project/{your-project}/functions/list.

O URL da API no Console do Firebase

No meu caso, é [https://us-central1-joaq-lab.cloudfunctions.net/api].

Consumindo a API

Depois que nossa API for implantada, temos várias maneiras de usá-la - neste tutorial, abordarei como usá-la via Postman ou de um aplicativo Angular.

Se inserirmos o URL da lista de todos os usuários ( /api/users ) em qualquer navegador, obteremos o seguinte:

API de autenticação do Firebase

A razão para isso é que ao enviar a solicitação de um navegador, estamos realizando uma solicitação GET sem cabeçalhos de autenticação. Isso significa que nossa API está realmente funcionando conforme o esperado!

Nossa API é protegida por tokens. Para gerar esse token, precisamos chamar o SDK do cliente do Firebase e fazer login com uma credencial de usuário/senha válida. Quando bem-sucedido, o Firebase enviará um token de volta na resposta que podemos adicionar ao cabeçalho de qualquer solicitação a seguir que desejamos executar.

De um aplicativo angular

Neste tutorial, vou apenas examinar as partes importantes para consumir a API de um aplicativo Angular. O repositório completo pode ser acessado aqui, e se você precisar de um tutorial passo a passo sobre como criar um aplicativo Angular e configurar @angular/fire para usar, você pode conferir este post.

Então, de volta ao login, teremos um SignInComponent com um <form> para permitir que o usuário insira um nome de usuário e senha.

 //... <form [formGroup]="form"> <div class="form-group"> <label>Email address</label> <input type="email" formControlName="email" class="form-control" placeholder="Enter email"> </div> <div class="form-group"> <label>Password</label> <input type="password" formControlName="password" class="form-control" placeholder="Password"> </div> </form> //...

E na classe, signInWithEmailAndPassword usando o serviço AngularFireAuth .

 //... form: FormGroup = new FormGroup({ email: new FormControl(''), password: new FormControl('') }) constructor( private afAuth: AngularFireAuth ) { } async signIn() { try { const { email, password } = this.form.value await this.afAuth.auth.signInWithEmailAndPassword(email, password) } catch (err) { console.log(err) } } //..

Neste ponto, podemos fazer login em nosso projeto Firebase.

Fazendo login pelo aplicativo Angular

A resposta da API ao fazer login no aplicativo Angular

E quando inspecionamos as solicitações de rede no DevTools, podemos ver que o Firebase retorna um token após verificar nosso usuário e senha.

Esse token é o que usaremos para enviar a solicitação do nosso cabeçalho para a API que construímos. Uma maneira de adicionar o token a todas as solicitações é usando um HttpInterceptor .

Este arquivo mostra como obter o token do AngularFireAuth e adicioná-lo à solicitação do cabeçalho. Em seguida, fornecemos o arquivo interceptor no AppModule.

http-interceptors/auth-token.interceptor.ts

 @Injectable({ providedIn: 'root' }) export class AuthTokenHttpInterceptor implements HttpInterceptor { constructor( private auth: AngularFireAuth ) { } intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return this.auth.idToken.pipe( take(1), switchMap(idToken => { let clone = req.clone() if (idToken) { clone = clone.clone({ headers: req.headers.set('Authorization', 'Bearer ' + idToken) }); } return next.handle(clone) }) ) } } export const AuthTokenHttpInterceptorProvider = { provide: HTTP_INTERCEPTORS, useClass: AuthTokenHttpInterceptor, multi: true }

app.module.ts

 @NgModule({ //.. providers: [ AuthTokenHttpInterceptorProvider ] //... }) export class AppModule { }

Depois que o interceptor estiver definido, podemos fazer solicitações à nossa API de httpClient . Por exemplo, aqui está um UsersService onde chamamos a lista de todos os usuários, obtemos o usuário por seu ID, criamos um usuário e atualizamos um usuário.

 //… export type CreateUserRequest = { displayName: string, password: string, email: string, role: string } export type UpdateUserRequest = { uid: string } & CreateUserRequest @Injectable({ providedIn: 'root' }) export class UserService { private baseUrl = '{your-functions-url}/api/users' constructor( private http: HttpClient ) { } get users$(): Observable<User[]> { return this.http.get<{ users: User[] }>(`${this.baseUrl}`).pipe( map(result => { return result.users }) ) } user$(id: string): Observable<User> { return this.http.get<{ user: User }>(`${this.baseUrl}/${id}`).pipe( map(result => { return result.user }) ) } create(user: CreateUserRequest) { return this.http.post(`${this.baseUrl}`, user) } edit(user: UpdateUserRequest) { return this.http.patch(`${this.baseUrl}/${user.uid}`, user) } }

Agora, podemos chamar a API para obter o usuário logado por seu ID e listar todos os usuários de um componente como este:

 //... <div *ngIf="user$ | async; let user" class="col-12"> <div class="d-flex justify-content-between my-3"> <h4> Me </h4> </div> <ul class="list-group"> <li class="list-group-item d-flex justify-content-between align-items-center"> <div> <h5 class="mb-1">{{user.displayName}}</h5> <small>{{user.email}}</small> </div> <span class="badge badge-primary badge-pill">{{user.role?.toUpperCase()}}</span> </li> </ul> </div> <div class="col-12"> <div class="d-flex justify-content-between my-3"> <h4> All Users </h4> </div> <ul *ngIf="users$ | async; let users" class="list-group"> <li *ngFor="let user of users" class="list-group-item d-flex justify-content-between align-items-center"> <div> <h5 class="mb-1">{{user.displayName}}</h5> <small class="d-block">{{user.email}}</small> <small class="d-block">{{user.uid}}</small> </div> <span class="badge badge-primary badge-pill">{{user.role?.toUpperCase()}}</span> </li> </ul> //...
 //... users$: Observable<User[]> user$: Observable<User> constructor( private userService: UserService, private userForm: UserFormService, private modal: NgbModal, private afAuth: AngularFireAuth ) { } ngOnInit() { this.users$ = this.userService.users$ this.user$ = this.afAuth.user.pipe( filter(user => !!user), switchMap(user => this.userService.user$(user.uid)) ) } //...

E aqui está o resultado.

Todos os usuários em nosso aplicativo Angular

Observe que se entrarmos com um usuário com role=user , somente a seção Me será renderizada.

A visualização do recurso de usuário ao qual o usuário com a função user tem acesso

E teremos um 403 no inspetor de rede. Isso se deve à restrição que definimos anteriormente na API para permitir que apenas "Admins" listem todos os usuários.

Um erro 403 no inspetor de rede

Agora, vamos adicionar as funcionalidades “criar usuário” e “editar usuário”. Para isso, vamos criar primeiro um UserFormComponent e um UserFormService .

 <ng-container *ngIf="user$ | async"></ng-container> <div class="modal-header"> <h4 class="modal-title">{{ title$ | async}}</h4> <button type="button" class="close" (click)="dismiss()"> <span aria-hidden="true">&times;</span> </button> </div> <div class="modal-body"> <form [formGroup]="form" (ngSubmit)="save()"> <div class="form-group"> <label>Email address</label> <input type="email" formControlName="email" class="form-control" placeholder="Enter email"> </div> <div class="form-group"> <label>Password</label> <input type="password" formControlName="password" class="form-control" placeholder="Password"> </div> <div class="form-group"> <label>Display Name</label> <input type="string" formControlName="displayName" class="form-control" placeholder="Enter display name"> </div> <div class="form-group"> <label>Role</label> <select class="custom-select" formControlName="role"> <option value="admin">Admin</option> <option value="manager">Manager</option> <option value="user">User</option> </select> </div> </form> </div> <div class="modal-footer"> <button type="button" class="btn btn-outline-danger" (click)="dismiss()">Cancel</button> <button type="button" class="btn btn-primary" (click)="save()">Save</button> </div>
 @Component({ selector: 'app-user-form', templateUrl: './user-form.component.html', styleUrls: ['./user-form.component.scss'] }) export class UserFormComponent implements OnInit { form = new FormGroup({ uid: new FormControl(''), email: new FormControl(''), displayName: new FormControl(''), password: new FormControl(''), role: new FormControl(''), }); title$: Observable<string>; user$: Observable<{}>; constructor( public modal: NgbActiveModal, private userService: UserService, private userForm: UserFormService ) { } ngOnInit() { this.title$ = this.userForm.title$; this.user$ = this.userForm.user$.pipe( tap(user => { if (user) { this.form.patchValue(user); } else { this.form.reset({}); } }) ); } dismiss() { this.modal.dismiss('modal dismissed'); } save() { const { displayName, email, role, password, uid } = this.form.value; this.modal.close({ displayName, email, role, password, uid }); } }
 import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { map } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class UserFormService { _BS = new BehaviorSubject({ title: '', user: {} }); constructor() { } edit(user) { this._BS.next({ title: 'Edit User', user }); } create() { this._BS.next({ title: 'Create User', user: null }); } get title$() { return this._BS.asObservable().pipe( map(uf => uf.title) ); } get user$() { return this._BS.asObservable().pipe( map(uf => uf.user) ); } }

De volta ao componente principal, vamos adicionar os botões para chamar essas ações. Neste caso, “Editar usuário” estará disponível apenas para o usuário logado. Você pode ir em frente e adicionar a funcionalidade para editar outros usuários se precisar!

 //... <div class="d-flex justify-content-between my-3"> <h4> Me </h4> <button class="btn btn-primary" (click)="edit(user)"> Edit Profile </button> </div> //... <div class="d-flex justify-content-between my-3"> <h4> All Users </h4> <button class="btn btn-primary" (click)="create()"> New User </button> </div> //...
 //... create() { this.userForm.create(); const modalRef = this.modal.open(UserFormComponent); modalRef.result.then(user => { this.userService.create(user).subscribe(_ => { console.log('user created'); }); }).catch(err => { }); } edit(userToEdit) { this.userForm.edit(userToEdit); const modalRef = this.modal.open(UserFormComponent); modalRef.result.then(user => { this.userService.edit(user).subscribe(_ => { console.log('user edited'); }); }).catch(err => { }); }

Do carteiro

Postman é uma ferramenta para construir e fazer requisições a APIs. Dessa forma, podemos simular que estamos chamando nossa API de qualquer aplicativo cliente ou de um serviço diferente.

O que vamos demonstrar é como enviar uma solicitação para listar todos os usuários.

Depois de abrir a ferramenta, definimos a URL https://us-central1-{your-project}.cloudfunctions.net/api/users:

O URL da API carregado no campo Postman pronto para disparar como uma solicitação GET

Em seguida, na guia autorização, escolhemos Bearer Token e definimos o valor que extraímos do Dev Tools anteriormente.

Configurando o token do portador no Postman

O corpo da resposta que recebemos

Conclusão

Parabéns! Você passou por todo o tutorial e agora aprendeu a criar uma API baseada em função de usuário no Firebase.

Também abordamos como consumi-lo de um aplicativo Angular e Postman.

Vamos recapitular as coisas mais importantes:

  1. O Firebase permite que você comece a usar rapidamente uma API de autenticação de nível empresarial, que pode ser estendida posteriormente.
  2. Quase todos os projetos exigem autorização — se você precisar controlar o acesso usando um modelo baseado em função, o Firebase Authentication permite que você comece muito rapidamente.
  3. O modelo baseado em função depende da validação de recursos que são solicitados de usuários com funções específicas versus usuários específicos.
  4. Usando um aplicativo Express.js no Firebase Function, podemos criar uma API REST e definir manipuladores para autenticar e autorizar solicitações.
  5. Aproveitando as declarações personalizadas integradas, você pode criar uma API de autenticação baseada em função e proteger seu aplicativo.

Você pode ler mais sobre a autenticação do Firebase aqui. E se você quiser aproveitar as funções que definimos, você pode usar @angular/fire helpers.