Come creare un'API basata sui ruoli con l'autenticazione Firebase

Pubblicato: 2022-03-11

In questo tutorial creeremo un'API REST per gestire utenti e ruoli utilizzando Firebase e Node.js. Inoltre, vedremo come utilizzare l'API per autorizzare (o meno) quali utenti possono accedere a risorse specifiche.

introduzione

Quasi tutte le app richiedono un certo livello di sistema di autorizzazione. In alcuni casi, è sufficiente convalidare un nome utente/password impostato con la nostra tabella Utenti, ma spesso è necessario un modello di autorizzazioni più dettagliato per consentire a determinati utenti di accedere a determinate risorse e limitarle ad altri. Costruire un sistema per supportare quest'ultimo non è banale e può richiedere molto tempo. In questo tutorial impareremo come creare un'API di autenticazione basata sui ruoli utilizzando Firebase, che ci aiuterà a diventare rapidamente operativi.

Aut. basato sul ruolo

In questo modello di autorizzazione, l'accesso è concesso a ruoli, anziché a utenti specifici, e un utente può averne uno o più a seconda di come si progetta il modello di autorizzazione. Le risorse, d'altra parte, richiedono determinati ruoli per consentire a un utente di eseguirlo.

Auth basato sul ruolo con illustrato

Base di fuoco

Autenticazione Firebase

In poche parole, Firebase Authentication è un sistema di autenticazione estensibile basato su token e fornisce integrazioni pronte all'uso con i provider più comuni come Google, Facebook e Twitter, tra gli altri.

Ci consente di utilizzare attestazioni personalizzate che sfrutteremo per creare un'API flessibile basata sui ruoli.

Possiamo impostare qualsiasi valore JSON nelle attestazioni (ad esempio, { role: 'admin' } o { role: 'manager' } ).

Una volta impostate, le attestazioni personalizzate verranno incluse nel token generato da Firebase e potremo leggere il valore per controllare l'accesso.

Inoltre viene fornito con una quota gratuita molto generosa, che nella maggior parte dei casi sarà più che sufficiente.

Funzioni Firebase

Le funzioni sono un servizio di piattaforma serverless completamente gestito. Dobbiamo solo scrivere il nostro codice in Node.js e distribuirlo. Firebase si occupa della scalabilità dell'infrastruttura su richiesta, della configurazione del server e altro ancora. Nel nostro caso, lo utilizzeremo per creare la nostra API e per esporla tramite HTTP al Web.

Firebase ci consente di impostare le app express.js come gestori per percorsi diversi, ad esempio puoi creare un'app Express e collegarla a /mypath e tutte le richieste che arrivano a questa route verranno gestite app configurata.

Dal contesto di una funzione, hai accesso all'intera API di autenticazione di Firebase, utilizzando l'Admin SDK.

Questo è il modo in cui creeremo l'API utente.

Cosa costruiremo

Quindi, prima di iniziare, diamo un'occhiata a cosa costruiremo. Creeremo un'API REST con i seguenti endpoint:

Verbo HTTP Sentiero Descrizione Autorizzazione
OTTENERE /utenti Elenca tutti gli utenti Solo gli amministratori e i gestori hanno accesso
INVIARE /utenti Crea un nuovo utente Solo gli amministratori e i gestori hanno accesso
OTTENERE /utenti/:id Ottiene l'utente :id Gli amministratori, i gestori e lo stesso utente di :id hanno accesso
TOPPA /utenti/:id Aggiorna l'utente :id Gli amministratori, i gestori e lo stesso utente di :id hanno accesso
ELIMINARE /utenti/:id Elimina l'utente :id Gli amministratori, i gestori e lo stesso utente di :id hanno accesso

Ciascuno di questi endpoint gestirà l'autenticazione, convaliderà l'autorizzazione, eseguirà l'operazione corrispondente e infine restituirà un codice HTTP significativo.

Creeremo le funzioni di autenticazione e autorizzazione necessarie per convalidare il token e verificheremo se le attestazioni contengono il ruolo richiesto per eseguire l'operazione.

Costruire l'API

Per creare l'API, avremo bisogno di:

  • Un progetto Firebase
  • firebase-tools installati

Innanzitutto, accedi a Firebase:

 firebase login

Quindi, inizializza un progetto 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

A questo punto, avrai una cartella Funzioni, con una configurazione minima per creare le funzioni Firebase.

In src/index.ts c'è un esempio helloWorld , che puoi rimuovere dal commento per convalidare che le tue funzioni funzionino. Quindi puoi eseguire cd functions ed eseguire npm run serve . Questo comando trasporterà il codice e avvierà il server locale.

Puoi controllare i risultati su http://localhost:5000/{your-project}/us-central1/helloWorld

Una nuova app Firebase

Si noti che la funzione è esposta nel percorso definito come il suo nome in 'index.ts: 'helloWorld' .

Creazione di una funzione HTTP Firebase

Ora codifichiamo la nostra API. Creeremo una funzione http Firebase e la collegheremo al percorso /api .

Innanzitutto, installa npm install express .

Su src/index.ts faremo:

  • Inizializza il modulo Firebase-admin SDK con admin.initializeApp();
  • Imposta un'app Express come gestore del nostro endpoint https 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);

Ora, tutte le richieste che vanno a /api verranno gestite dall'istanza app .

La prossima cosa che faremo è configurare l'istanza app per supportare CORS e aggiungere il middleware del parser del corpo JSON. In questo modo possiamo effettuare richieste da qualsiasi URL e analizzare le richieste formattate in JSON.

Per prima cosa installeremo le dipendenze richieste.

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

Poi:

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

Infine, configureremo i percorsi che gestirà l' app .

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

Firebase Functions ci consente di impostare un'app Express come gestore e qualsiasi percorso successivo a quello impostato su functions.https.onRequest(app); —in questo caso, api —sarà gestito anche app . Questo ci consente di scrivere endpoint specifici come api/users e impostare un gestore per ogni verbo HTTP, cosa che faremo in seguito.

Creiamo il file src/users/routes-config.ts

Qui imposteremo un gestore di create su POST '/users'

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

Ora creeremo il file src/users/controller.ts .

In questa funzione, convalidiamo prima che tutti i campi siano nel corpo della richiesta, quindi creiamo l'utente e impostiamo le attestazioni personalizzate.

Stiamo solo passando { role } in setCustomUserClaims gli altri campi sono già impostati da Firebase.

Se non si verificano errori, restituiamo un codice 201 con l' uid dell'utente creato.

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

Ora proteggiamo il gestore aggiungendo l'autorizzazione. Per fare ciò, aggiungeremo un paio di gestori al nostro endpoint di create . Con express.js , puoi impostare una catena di gestori che verranno eseguiti in ordine. All'interno di un gestore, puoi eseguire codice e passarlo al gestore next() o restituire una risposta. Quello che faremo è prima autenticare l'utente e quindi convalidare se è autorizzato all'esecuzione.

Nel file 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 ); }

Creiamo i file src/auth/authenticated.ts .

Su questa funzione convalideremo la presenza del token di authorization nell'intestazione della richiesta. Quindi lo decodificheremo con admin.auth().verifyidToken() e persisteremo uid , role e email dell'utente nella variabile res.locals , che utilizzeremo in seguito per convalidare l'autorizzazione.

Nel caso in cui il token non sia valido, restituiamo una risposta 401 al client:

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

Ora creiamo un file src/auth/authorized.ts .

In questo gestore, estraiamo le informazioni dell'utente da res.locals che abbiamo impostato in precedenza e convalidiamo se ha il ruolo richiesto per eseguire l'operazione o nel caso in cui l'operazione consenta allo stesso utente di eseguire, convalidiamo che l'ID sui parametri della richiesta è lo stesso di quello nel token di autenticazione. Se l'utente non ha il ruolo richiesto, restituiremo 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 questi due metodi, saremo in grado di autenticare le richieste e autorizzarle in base al role nel token in entrata. È fantastico, ma poiché Firebase non ci consente di impostare attestazioni personalizzate dalla console del progetto, non saremo in grado di eseguire nessuno di questi endpoint. Per aggirare questo, possiamo creare un utente root dalla Console di autenticazione Firebase

Creazione di un utente dalla console di autenticazione Firebase

E imposta un confronto e-mail nel codice. Ora, quando attiveremo richieste da questo utente, saremo in grado di eseguire tutte le operazioni.

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

Ora aggiungiamo il resto delle operazioni CRUD a src/users/routes-config.ts .

Per le operazioni per ottenere o aggiornare un singolo utente in cui viene inviato :id param, consentiamo anche allo stesso utente di eseguire l'operazione.

 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 su src/users/controller.ts . In queste operazioni, sfruttiamo l'SDK di amministrazione per interagire con l'autenticazione Firebase ed eseguire le rispettive operazioni. Come abbiamo fatto in precedenza per l'operazione di create , restituiamo un codice HTTP significativo su ogni operazione.

Per l'operazione di aggiornamento convalidiamo tutti i campi presenti e sovrascriviamo customClaims con quelli inviati nella richiesta:

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

Ora possiamo eseguire la funzione localmente. Per fare ciò, devi prima configurare la chiave dell'account per poterti connettere con l'API di autenticazione in locale. Quindi esegui:

 npm run serve

Distribuisci l'API

Grande! Ora che abbiamo scritto l'API basata sui ruoli, possiamo distribuirla sul Web e iniziare a usarla. La distribuzione con Firebase è semplicissima, dobbiamo solo eseguire firebase deploy . Una volta completata la distribuzione, possiamo accedere alla nostra API all'URL pubblicato.

Esecuzione del comando firebase deploy

Puoi controllare l'URL dell'API su https://console.firebase.google.com/u/0/project/{your-project}/functions/list.

L'URL dell'API nella console Firebase

Nel mio caso, è [https://us-central1-joaq-lab.cloudfunctions.net/api].

Consumare l'API

Una volta che la nostra API è stata distribuita, abbiamo diversi modi per usarla: in questo tutorial tratterò come usarla tramite Postman o da un'app Angular.

Se inseriamo l'URL dell'elenco di tutti gli utenti ( /api/users ) su qualsiasi browser, otterremo quanto segue:

API di autenticazione Firebase

Il motivo è che quando inviamo la richiesta da un browser, stiamo eseguendo una richiesta GET senza intestazioni di autenticazione. Ciò significa che la nostra API funziona effettivamente come previsto!

La nostra API è protetta tramite token: per generare tale token, dobbiamo chiamare l'SDK client di Firebase e accedere con una credenziale utente/password valida. In caso di successo, Firebase invierà un token nella risposta che possiamo quindi aggiungere all'intestazione di qualsiasi richiesta successiva che desideriamo eseguire.

Da un'app angolare

In questo tutorial, esaminerò solo i pezzi importanti per consumare l'API da un'app Angular. È possibile accedere al repository completo qui e se hai bisogno di un tutorial passo-passo su come creare un'app Angular e configurare @angular/fire da utilizzare, puoi controllare questo post.

Quindi, tornando all'accesso, avremo un SignInComponent con un <form> per consentire all'utente di inserire un nome utente e una password.

 //... <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 sulla classe, accediamo con e- 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) } } //..

A questo punto, possiamo accedere al nostro progetto Firebase.

Accesso tramite l'app Angular

La risposta dell'API durante l'accesso dall'app Angular

E quando ispezioniamo le richieste di rete in DevTools, possiamo vedere che Firebase restituisce un token dopo aver verificato il nostro utente e password.

Questo token è quello che useremo per inviare la nostra richiesta di intestazione all'API che abbiamo creato. Un modo per aggiungere il token a tutte le richieste consiste nell'usare un HttpInterceptor .

Questo file mostra come ottenere il token da AngularFireAuth e aggiungerlo alla richiesta dell'intestazione. Quindi forniamo il file interceptor nell'AppModule.

http-interceptor/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 { }

Una volta impostato l'intercettore, possiamo effettuare richieste alla nostra API da httpClient . Ad esempio, ecco un UsersService in cui chiamiamo l'elenco tutti gli utenti, otteniamo l'utente tramite il suo ID, creiamo un utente e aggiorniamo un utente.

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

Ora possiamo chiamare l'API per ottenere l'utente che ha effettuato l'accesso in base al suo ID ed elencare tutti gli utenti da un componente come questo:

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

Ed ecco il risultato.

Tutti gli utenti nella nostra app Angular

Si noti che se accediamo con un utente con role=user , verrà visualizzata solo la sezione Me.

La visualizzazione della risorsa utente a cui ha accesso l'utente con il ruolo utente

E avremo un 403 sull'ispettore di rete. Ciò è dovuto alla restrizione che abbiamo impostato in precedenza sull'API per consentire solo agli "Amministratori" di elencare tutti gli utenti.

Un errore 403 nell'ispettore di rete

Ora aggiungiamo le funzionalità "crea utente" e "modifica utente". Per fare ciò, creiamo prima un UserFormComponent e 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) ); } }

Tornando al componente principale, aggiungiamo i pulsanti per chiamare quelle azioni. In questo caso, "Modifica utente" sarà disponibile solo per l'utente che ha effettuato l'accesso. Puoi andare avanti e aggiungere la funzionalità per modificare altri utenti, se necessario!

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

Dal postino

Postman è uno strumento per creare e fare richieste alle API. In questo modo, possiamo simulare che stiamo chiamando la nostra API da qualsiasi app client o da un servizio diverso.

Quello che mostreremo è come inviare una richiesta per elencare tutti gli utenti.

Una volta aperto lo strumento, impostiamo l'URL https://us-central1-{your-project}.cloudfunctions.net/api/users:

L'URL dell'API caricato nel campo Postman pronto per essere attivato come richiesta GET

Successivamente, nella scheda autorizzazione, scegliamo Bearer Token e impostiamo il valore che abbiamo estratto in precedenza da Dev Tools.

Impostazione del token al portatore in Postino

Il corpo della risposta che riceviamo

Conclusione

Congratulazioni! Hai superato l'intero tutorial e ora hai imparato a creare un'API basata sui ruoli utente su Firebase.

Abbiamo anche spiegato come consumarlo da un'app Angular e Postman.

Ricapitoliamo le cose più importanti:

  1. Firebase ti consente di diventare rapidamente operativo con un'API di autenticazione di livello aziendale, che puoi estendere in un secondo momento.
  2. Quasi tutti i progetti richiedono l'autorizzazione: se devi controllare l'accesso utilizzando un modello basato sui ruoli, Firebase Authentication ti consente di iniziare molto rapidamente.
  3. Il modello basato sui ruoli si basa sulla convalida delle risorse richieste da utenti con ruoli specifici rispetto a utenti specifici.
  4. Utilizzando un'app Express.js su Firebase Function, possiamo creare un'API REST e impostare gestori per autenticare e autorizzare le richieste.
  5. Sfruttando le attestazioni personalizzate integrate, puoi creare un'API di autenticazione basata sui ruoli e proteggere la tua app.

Puoi leggere ulteriori informazioni sull'autenticazione di Firebase qui. E se vuoi sfruttare i ruoli che abbiamo definito, puoi usare @angular/fire helpers.