Cómo crear una API basada en roles con Firebase Authentication

Publicado: 2022-03-11

En este tutorial, crearemos una API de REST para administrar usuarios y funciones mediante Firebase y Node.js. Además, veremos cómo usar la API para autorizar (o no) qué usuarios pueden acceder a recursos específicos.

Introducción

Casi todas las aplicaciones requieren algún nivel de sistema de autorización. En algunos casos, basta con validar un conjunto de nombre de usuario/contraseña con nuestra tabla de Usuarios, pero a menudo necesitamos un modelo de permisos más detallado para permitir que ciertos usuarios accedan a ciertos recursos y restringirlos de otros. Construir un sistema para soportar este último no es trivial y puede llevar mucho tiempo. En este tutorial, aprenderemos a crear una API de autenticación basada en roles con Firebase, lo que nos ayudará a ponernos en marcha rápidamente.

Autenticación basada en roles

En este modelo de autorización, el acceso se otorga a roles, en lugar de a usuarios específicos, y un usuario puede tener uno o más según cómo diseñe su modelo de permiso. Los recursos, por otro lado, requieren ciertos roles para permitir que un usuario los ejecute.

Autenticación basada en roles con ilustraciones

base de fuego

Autenticación de base de fuego

En pocas palabras, Firebase Authentication es un sistema de autenticación extensible basado en tokens y proporciona integraciones listas para usar con los proveedores más comunes, como Google, Facebook y Twitter, entre otros.

Nos permite usar notificaciones personalizadas que aprovecharemos para crear una API flexible basada en roles.

Podemos establecer cualquier valor JSON en las notificaciones (p. ej., { role: 'admin' } o { role: 'manager' } ).

Una vez configurados, los reclamos personalizados se incluirán en el token que genera Firebase y podemos leer el valor para controlar el acceso.

Además viene con una cuota gratuita muy generosa, que en la mayoría de los casos será más que suficiente.

Funciones de base de fuego

Las funciones son un servicio de plataforma sin servidor completamente administrado. Solo necesitamos escribir nuestro código en Node.js e implementarlo. Firebase se encarga de escalar la infraestructura a pedido, la configuración del servidor y más. En nuestro caso, lo usaremos para construir nuestra API y exponerla a través de HTTP en la web.

Firebase nos permite establecer aplicaciones express.js como controladores para diferentes rutas; por ejemplo, puede crear una aplicación Express y conectarla a /mypath , y todas las solicitudes que lleguen a esta ruta serán manejadas por la app configurada.

Desde el contexto de una función, tiene acceso a toda la API de autenticación de Firebase mediante el SDK de administrador.

Así es como crearemos la API de usuario.

Lo que construiremos

Entonces, antes de comenzar, echemos un vistazo a lo que construiremos. Vamos a crear una API REST con los siguientes puntos finales:

Verbo Http Sendero Descripción Autorización
OBTENER /usuarios Lista todos los usuarios Solo los administradores y gerentes tienen acceso
CORREO /usuarios Crea nuevo usuario Solo los administradores y gerentes tienen acceso
OBTENER /usuarios/:id Obtiene el :id del usuario Los administradores, gerentes y el mismo usuario que :id tienen acceso
PARCHE /usuarios/:id Actualiza el usuario :id Los administradores, gerentes y el mismo usuario que :id tienen acceso
ELIMINAR /usuarios/:id Elimina el usuario :id Los administradores, gerentes y el mismo usuario que :id tienen acceso

Cada uno de estos puntos finales manejará la autenticación, validará la autorización, realizará la operación correspondiente y finalmente devolverá un código HTTP significativo.

Crearemos las funciones de autenticación y autorización requeridas para validar el token y verificar si las notificaciones contienen el rol requerido para ejecutar la operación.

Construyendo la API

Para construir la API, necesitaremos:

  • Un proyecto de Firebase
  • firebase-tools instaladas

Primero, inicie sesión en Firebase:

 firebase login

A continuación, inicialice un proyecto de 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

En este punto, tendrá una carpeta de funciones, con una configuración mínima para crear funciones de Firebase.

En src/index.ts hay un ejemplo de helloWorld , que puede descomentar para validar que sus funciones funcionan. Luego puede cd functions y ejecutar npm run serve . Este comando transpilará el código e iniciará el servidor local.

Puede consultar los resultados en http://localhost:5000/{your-project}/us-central1/helloWorld

Una nueva aplicación de Firebase

Observe que la función está expuesta en la ruta definida como su nombre en 'index.ts: 'helloWorld' .

Creación de una función HTTP de Firebase

Ahora vamos a codificar nuestra API. Vamos a crear una función http Firebase y conectarla en la ruta /api .

Primero, instale npm install express .

En src/index.ts haremos lo siguiente:

  • Inicialice el módulo SDK firebase-admin con admin.initializeApp();
  • Establezca una aplicación Express como controlador de nuestro punto final https de 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);

Ahora, todas las solicitudes que van a /api serán manejadas por la instancia de la app .

Lo siguiente que haremos es configurar la instancia de la app para admitir CORS y agregar el middleware del analizador de cuerpo JSON. De esta manera, podemos realizar solicitudes desde cualquier URL y analizar solicitudes con formato JSON.

Primero instalaremos las dependencias requeridas.

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

Y luego:

 //... 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);

Finalmente, configuraremos las rutas que manejará la app .

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

Firebase Functions nos permite establecer una aplicación Express como controlador y cualquier ruta después de la que configuró en functions.https.onRequest(app); —en este caso, api también será manejada por la app . Esto nos permite escribir puntos finales específicos como api/users y establecer un controlador para cada verbo HTTP, lo que haremos a continuación.

Vamos a crear el archivo src/users/routes-config.ts

Aquí, configuraremos un controlador de create en POST '/users'

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

Ahora, crearemos el archivo src/users/controller.ts .

En esta función, primero validamos que todos los campos estén en el cuerpo de la solicitud y, a continuación, creamos el usuario y establecemos las notificaciones personalizadas.

Solo estamos pasando { role } en setCustomUserClaims ; los otros campos ya están configurados por Firebase.

Si no se producen errores, devolvemos un código 201 con el uid del usuario creado.

 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}` }); }

Ahora, aseguremos el controlador agregando autorización. Para hacer eso, agregaremos un par de controladores a nuestro punto final de create . Con express.js , puede establecer una cadena de controladores que se ejecutarán en orden. Dentro de un controlador, puede ejecutar código y pasarlo al controlador next() o devolver una respuesta. Lo que haremos será primero autenticar al usuario y luego validar si está autorizado para ejecutar.

En el archivo 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 a crear los archivos src/auth/authenticated.ts .

En esta función, validaremos la presencia del token del portador de authorization en el encabezado de la solicitud. Luego, lo decodificaremos con admin.auth().verifyidToken() y conservaremos el uid , el role y el email del usuario en la variable res.locals , que luego usaremos para validar la autorización.

En caso de que el token no sea válido, devolvemos una respuesta 401 al 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' }); } }

Ahora, creemos un archivo src/auth/authorized.ts .

En este controlador, extraemos la información del usuario de res.locals que configuramos previamente y validamos si tiene el rol requerido para ejecutar la operación o, en el caso de que la operación permita que el mismo usuario la ejecute, validamos que la ID en los parámetros de solicitud es el mismo que el del token de autenticación. Si el usuario no tiene el rol requerido, devolveremos un 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(); } }

Con estos dos métodos, podremos autenticar solicitudes y autorizarlas según el role en el token entrante. Eso es genial, pero dado que Firebase no nos permite establecer reclamos personalizados desde la consola del proyecto, no podremos ejecutar ninguno de estos puntos finales. Para evitar esto, podemos crear un usuario raíz desde Firebase Authentication Console.

Crear un usuario desde Firebase Authentication Console

Y establezca una comparación de correo electrónico en el código. Ahora, al disparar solicitudes de este usuario, podremos ejecutar todas las operaciones.

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

Ahora, agreguemos el resto de las operaciones CRUD a src/users/routes-config.ts .

Para operaciones para obtener o actualizar un solo usuario donde se envía :id param, también permitimos que el mismo usuario ejecute la operación.

 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 ]); }

Y en src/users/controller.ts . En estas operaciones, aprovechamos el SDK de administrador para interactuar con Firebase Authentication y realizar las operaciones respectivas. Como hicimos anteriormente en la operación de create , devolvemos un código HTTP significativo en cada operación.

Para la operación de actualización, validamos todos los campos presentes y customClaims con los enviados en la solicitud:

 //.. 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) } } //...

Ahora podemos ejecutar la función localmente. Para hacer eso, primero debe configurar la clave de la cuenta para poder conectarse con la API de autenticación localmente. Entonces corre:

 npm run serve

Implementar la API

¡Genial! Ahora que hemos escrito nuestra API basada en roles, podemos implementarla en la web y comenzar a usarla. Implementar con Firebase es muy fácil, solo necesitamos ejecutar firebase deploy . Una vez que se completa la implementación, podemos acceder a nuestra API en la URL publicada.

Ejecutando el comando de implementación de firebase

Puede consultar la URL de la API en https://console.firebase.google.com/u/0/project/{your-project}/functions/list.

La URL de la API en Firebase Console

En mi caso es [https://us-central1-joaq-lab.cloudfunctions.net/api].

Consumir la API

Una vez que se implementa nuestra API, tenemos varias formas de usarla; en este tutorial, cubriré cómo usarla a través de Postman o desde una aplicación Angular.

Si ingresamos la URL Listar todos los usuarios ( /api/users ) en cualquier navegador, obtendremos lo siguiente:

API de autenticación de Firebase

La razón de esto es que cuando enviamos la solicitud desde un navegador, estamos realizando una solicitud GET sin encabezados de autenticación. ¡Esto significa que nuestra API está funcionando como se esperaba!

Nuestra API está protegida a través de tokens; para generar dicho token, debemos llamar al SDK de cliente de Firebase e iniciar sesión con una credencial de usuario/contraseña válida. Cuando tenga éxito, Firebase devolverá un token en la respuesta que luego podemos agregar al encabezado de cualquier solicitud siguiente que queramos realizar.

Desde una aplicación angular

En este tutorial, solo repasaré las piezas importantes para consumir la API desde una aplicación Angular. Se puede acceder al repositorio completo aquí, y si necesita un tutorial paso a paso sobre cómo crear una aplicación Angular y configurar @angular/fire para usar, puede consultar esta publicación.

Entonces, volviendo al inicio de sesión, tendremos un SignInComponent con un <form> para permitir que el usuario ingrese un nombre de usuario y una contraseña.

 //... <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> //...

Y en la clase, iniciamos sesión con AngularFireAuth signInWithEmailAndPassword

 //... 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) } } //..

En este punto, podemos iniciar sesión en nuestro proyecto de Firebase.

Iniciar sesión a través de la aplicación Angular

La respuesta de la API al iniciar sesión desde la aplicación Angular

Y cuando inspeccionamos las solicitudes de red en DevTools, podemos ver que Firebase devuelve un token después de verificar nuestro usuario y contraseña.

Este token es el que usaremos para enviar la solicitud de nuestro encabezado a la API que hemos creado. Una forma de agregar el token a todas las solicitudes es usar un HttpInterceptor .

Este archivo muestra cómo obtener el token de AngularFireAuth y agregarlo a la solicitud del encabezado. Luego proporcionamos el archivo interceptor en 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 }

aplicación.módulo.ts

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

Una vez que se configura el interceptor, podemos realizar solicitudes a nuestra API desde httpClient . Por ejemplo, aquí hay un UsersService donde llamamos a la lista de todos los usuarios, obtenemos el usuario por su ID, creamos un usuario y actualizamos un usuario.

 //… 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) } }

Ahora, podemos llamar a la API para obtener el usuario que inició sesión por su ID y enumerar todos los usuarios de un 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)) ) } //...

Y aquí está el resultado.

Todos los usuarios en nuestra aplicación Angular

Tenga en cuenta que si iniciamos sesión con un usuario con role=user , solo se representará la sección Me.

La vista del recurso de usuario al que tiene acceso el usuario con el rol de usuario

Y obtendremos un 403 en el inspector de red. Esto se debe a la restricción que establecimos antes en la API para permitir que solo los "Administradores" enumeren a todos los usuarios.

Un error 403 en el inspector de red

Ahora, agreguemos la funcionalidad "crear usuario" y "editar usuario". Para hacer eso, vamos a crear primero un UserFormComponent y un 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) ); } }

Volviendo al componente principal, agreguemos los botones para llamar a esas acciones. En este caso, "Editar usuario" solo estará disponible para el usuario que haya iniciado sesión. ¡Puede continuar y agregar la funcionalidad para editar a otros usuarios si lo necesita!

 //... <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 => { }); }

del cartero

Postman es una herramienta para construir y realizar solicitudes a las API. De esta forma, podemos simular que estamos llamando a nuestra API desde cualquier aplicación cliente o un servicio diferente.

Lo que demostraremos es cómo enviar una solicitud para enumerar todos los usuarios.

Una vez que abrimos la herramienta, configuramos la URL https://us-central1-{your-project}.cloudfunctions.net/api/users:

La URL de API cargada en el campo Postman lista para activarse como una solicitud GET

A continuación, en la pestaña de autorización, elegimos Bearer Token y establecemos el valor que extrajimos de Dev Tools anteriormente.

Configuración del token del portador en Postman

El cuerpo de la respuesta que recibimos

Conclusión

¡Felicidades! Ha completado todo el tutorial y ahora ha aprendido a crear una API basada en funciones de usuario en Firebase.

También hemos cubierto cómo consumirlo desde una aplicación Angular y Postman.

Recapitulemos lo más importante:

  1. Firebase le permite ponerse en marcha rápidamente con una API de autenticación de nivel empresarial, que puede ampliar más adelante.
  2. Casi todos los proyectos requieren autorización. Si necesita controlar el acceso mediante un modelo basado en roles, Firebase Authentication le permite comenzar muy rápidamente.
  3. El modelo basado en roles se basa en la validación de los recursos que se solicitan de los usuarios con roles específicos frente a usuarios específicos.
  4. Usando una aplicación Express.js en Firebase Function, podemos crear una API REST y configurar controladores para autenticar y autorizar solicitudes.
  5. Al aprovechar las notificaciones personalizadas integradas, puede crear una API de autenticación basada en funciones y proteger su aplicación.

Puede leer más sobre la autenticación de Firebase aquí. Y si desea aprovechar los roles que hemos definido, puede usar los ayudantes de @angular/fire.