Cum să construiți un API bazat pe roluri cu autentificarea Firebase

Publicat: 2022-03-11

În acest tutorial, vom construi un API REST pentru a gestiona utilizatorii și rolurile folosind Firebase și Node.js. În plus, vom vedea cum să folosim API-ul pentru a autoriza (sau nu) ce utilizatori pot accesa anumite resurse.

Introducere

Aproape fiecare aplicație necesită un anumit nivel de sistem de autorizare. În unele cazuri, validarea unui set de nume de utilizator/parolă cu tabelul nostru Utilizatori este suficientă, dar adesea, avem nevoie de un model de permisiuni mai precis pentru a permite anumitor utilizatori să acceseze anumite resurse și să le restricționeze de la alții. Construirea unui sistem care să sprijine acesta din urmă nu este banală și poate consuma foarte mult timp. În acest tutorial, vom învăța cum să construim un API de autentificare bazat pe roluri folosind Firebase, care ne va ajuta să începem și să rulăm rapid.

Autentificare bazată pe rol

În acest model de autorizare, accesul este acordat rolurilor, în loc de anumiți utilizatori, iar un utilizator poate avea unul sau mai mulți, în funcție de modul în care vă proiectați modelul de permisiune. Resursele, pe de altă parte, necesită anumite roluri pentru a permite unui utilizator să le execute.

Autorizare bazată pe roluri cu ilustrate

Firebase

Autentificare Firebase

Pe scurt, Firebase Authentication este un sistem de autentificare extensibil bazat pe jetoane și oferă integrări din nou cu cei mai obișnuiți furnizori, cum ar fi Google, Facebook și Twitter, printre alții.

Ne permite să folosim revendicări personalizate pe care le vom folosi pentru a construi un API flexibil bazat pe roluri.

Putem seta orice valoare JSON în revendicări (de exemplu, { role: 'admin' } sau { role: 'manager' } ).

Odată setate, revendicările personalizate vor fi incluse în simbolul pe care îl generează Firebase și putem citi valoarea pentru a controla accesul.

Vine și cu o cotă gratuită foarte generoasă, care în cele mai multe cazuri va fi mai mult decât suficientă.

Funcții Firebase

Funcțiile sunt un serviciu de platformă fără server complet gestionat. Trebuie doar să scriem codul nostru în Node.js și să îl implementăm. Firebase se ocupă de scalarea infrastructurii la cerere, de configurarea serverului și multe altele. În cazul nostru, îl vom folosi pentru a ne construi API-ul și îl vom expune pe web prin HTTP.

Firebase ne permite să setăm aplicațiile express.js ca handlere pentru diferite căi — de exemplu, puteți crea o aplicație Express și o puteți conecta la /mypath , iar toate solicitările care vin pe această rută vor fi gestionate de app configurată.

Din contextul unei funcții, aveți acces la întregul API Firebase Authentication, folosind SDK-ul de administrare.

Acesta este modul în care vom crea API-ul utilizatorului.

Ce vom construi

Deci, înainte de a începe, să aruncăm o privire la ceea ce vom construi. Vom crea un API REST cu următoarele puncte finale:

Http verb cale Descriere Autorizare
OBȚINE /utilizatori Listează toți utilizatorii Doar administratorii și managerii au acces
POST /utilizatori Creează un utilizator nou Doar administratorii și managerii au acces
OBȚINE /utilizatori/:id Obține utilizatorul :id Administratorii, managerii și același utilizator ca :id au acces
PLASTURE /utilizatori/:id Actualizează utilizatorul :id Administratorii, managerii și același utilizator ca :id au acces
ȘTERGE /utilizatori/:id Șterge utilizatorul :id Administratorii, managerii și același utilizator ca :id au acces

Fiecare dintre aceste puncte finale se va ocupa de autentificare, va valida autorizarea, va efectua operația corespunzătoare și, în final, va returna un cod HTTP semnificativ.

Vom crea funcțiile de autentificare și autorizare necesare pentru a valida token-ul și vom verifica dacă revendicările conțin rolul necesar pentru a executa operația.

Construirea API-ului

Pentru a construi API-ul, vom avea nevoie de:

  • Un proiect Firebase
  • firebase-tools instalate

Mai întâi, conectați-vă la Firebase:

 firebase login

Apoi, inițializați un proiect 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

În acest moment, veți avea un folder Functions, cu configurarea minimă pentru a crea Firebase Functions.

La src/index.ts există un exemplu helloWorld , pe care îl puteți decomenta pentru a valida funcțiile dumneavoastră. Apoi puteți să cd functions și să rulați npm run serve . Această comandă va transpila codul și va porni serverul local.

Puteți verifica rezultatele la http://localhost:5000/{your-project}/us-central1/helloWorld

O aplicație Firebase proaspătă

Observați că funcția este expusă pe calea definită ca numele acesteia la 'index.ts: 'helloWorld' .

Crearea unei funcții HTTP Firebase

Acum haideți să codificăm API-ul nostru. Vom crea o funcție http Firebase și o vom conecta pe calea /api .

Mai întâi, instalați npm install express .

Pe src/index.ts vom:

  • Inițializați modulul SDK firebase-admin cu admin.initializeApp();
  • Setați o aplicație Express ca handler al punctului nostru final 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);

Acum, toate cererile care ajung la /api vor fi gestionate de instanța app .

Următorul lucru pe care îl vom face este să configurați instanța app să accepte CORS și să adăugăm middleware JSON body parser. În acest fel, putem face cereri de la orice adresă URL și putem analiza cererile formatate JSON.

Mai întâi vom instala dependențele necesare.

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

Și apoi:

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

În cele din urmă, vom configura rutele pe care le va gestiona app .

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

Firebase Functions ne permite să setăm o aplicație Express ca handler și orice cale după cea pe care ați configurat-o la functions.https.onRequest(app); — în acest caz, api — va fi gestionat și de app . Acest lucru ne permite să scriem puncte finale specifice, cum ar fi api/users și să setăm un handler pentru fiecare verb HTTP, ceea ce vom face în continuare.

Să creăm fișierul src/users/routes-config.ts

Aici, vom seta un handler de create la POST '/users'

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

Acum, vom crea fișierul src/users/controller.ts .

În această funcție, validăm mai întâi că toate câmpurile sunt în cererea corporală, iar apoi, creăm utilizatorul și setăm revendicările personalizate.

Doar trecem { role } în setCustomUserClaims — celelalte câmpuri sunt deja setate de Firebase.

Dacă nu apar erori, returnăm un cod 201 cu uid -ul utilizatorului creat.

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

Acum, să securizăm handlerul adăugând autorizație. Pentru a face asta, vom adăuga câțiva handlere la punctul final de create . Cu express.js , puteți seta un lanț de handlere care vor fi executate în ordine. În cadrul unui handler, puteți să executați cod și să-l transmiteți handler-ului next() sau să returnați un răspuns. Ceea ce vom face este mai întâi să autentificăm utilizatorul și apoi să validăm dacă este autorizat să execute.

Pe fișierul 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 ); }

Să creăm fișierele src/auth/authenticated.ts .

Pe această funcție, vom valida prezența jetonului purtătorului de authorization în antetul cererii. Apoi îl vom decoda cu admin.auth().verifyidToken() și vom păstra uid -ul utilizatorului, role și e- email în variabila res.locals , pe care o vom folosi ulterior pentru a valida autorizarea.

În cazul în care simbolul este invalid, returnăm clientului un răspuns 401:

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

Acum, să creăm un fișier src/auth/authorized.ts .

În acest handler, extragem informațiile utilizatorului din res.locals pe care le-am setat anterior și validăm dacă are rolul necesar pentru a executa operația sau în cazul în care operația permite aceluiași utilizator să execute, validăm că ID-ul de pe parametrii de solicitare. este același cu cel din simbolul de autorizare. Dacă utilizatorul nu are rolul necesar, vom returna 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(); } }

Cu aceste două metode, vom putea autentifica cererile și le vom autoriza, având în vedere role în jetonul primit. Este grozav, dar, deoarece Firebase nu ne permite să setăm revendicări personalizate din consola de proiect, nu vom putea executa niciunul dintre aceste puncte finale. Pentru a ocoli acest lucru, putem crea un utilizator root din Firebase Authentication Console

Crearea unui utilizator din Consola de autentificare Firebase

Și setați o comparație de e-mail în cod. Acum, când lansăm solicitări de la acest utilizator, vom putea executa toate operațiunile.

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

Acum, să adăugăm restul operațiunilor CRUD la src/users/routes-config.ts .

Pentru operațiunile de obținere sau actualizare a unui singur utilizator unde este trimis :id param, permitem, de asemenea, aceluiași utilizator să execute operația.

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

Și pe src/users/controller.ts . În aceste operațiuni, folosim SDK-ul de administrare pentru a interacționa cu Firebase Authentication și pentru a efectua operațiunile respective. Așa cum am făcut anterior la operația de create , returnăm un cod HTTP semnificativ pentru fiecare operație.

Pentru operațiunea de actualizare, validăm toate câmpurile prezente și customClaims cu cele trimise în cerere:

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

Acum putem rula funcția local. Pentru a face asta, mai întâi trebuie să configurați cheia de cont pentru a vă putea conecta la API-ul de autentificare local. Apoi rulați:

 npm run serve

Implementați API-ul

Grozav! Acum că am scris API-ul bazat pe roluri, îl putem implementa pe web și începe să îl folosim. Implementarea cu Firebase este super ușoară, trebuie doar să rulăm firebase deploy . Odată ce implementarea este finalizată, putem accesa API-ul nostru la adresa URL publicată.

Rularea comenzii firebase deploy

Puteți verifica adresa URL API la https://console.firebase.google.com/u/0/project/{your-project}/functions/list.

Adresa URL API din Consola Firebase

În cazul meu, este [https://us-central1-joaq-lab.cloudfunctions.net/api].

Utilizarea API-ului

Odată ce API-ul nostru este implementat, avem mai multe moduri de a-l folosi — în acest tutorial, voi vorbi despre cum să îl folosim prin Postman sau dintr-o aplicație Angular.

Dacă introducem URL-ul List All Users ( /api/users ) în orice browser, vom obține următoarele:

API-ul Firebase Authentication

Motivul pentru aceasta este că atunci când trimitem cererea dintr-un browser, executăm o solicitare GET fără anteturi de autentificare. Aceasta înseamnă că API-ul nostru funcționează de fapt conform așteptărilor!

API-ul nostru este securizat prin simboluri — pentru a genera un astfel de simbol, trebuie să apelăm SDK-ul pentru client al Firebase și să ne conectăm cu un utilizator/parolă validă. Când are succes, Firebase va trimite un token înapoi în răspuns pe care îl putem adăuga apoi la antetul oricărei solicitări următoare pe care dorim să o realizăm.

Dintr-o aplicație Angular

În acest tutorial, voi trece doar peste piesele importante pentru a consuma API-ul dintr-o aplicație Angular. Depozitul complet poate fi accesat aici, iar dacă aveți nevoie de un tutorial pas cu pas despre cum să creați o aplicație Angular și să configurați @angular/fire pentru utilizare, puteți verifica această postare.

Deci, revenind la conectare, vom avea un SignInComponent cu un <form> pentru a permite utilizatorului să introducă un nume de utilizator și o parolă.

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

Și în clasă, ne signInWithEmailAndPassword cu e-mail și parolă folosind serviciul 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) } } //..

În acest moment, ne putem conecta la proiectul nostru Firebase.

Conectarea prin aplicația Angular

Răspunsul API la conectarea din aplicația Angular

Și când inspectăm solicitările de rețea în DevTools, putem vedea că Firebase returnează un token după ce ne verifică utilizatorul și parola.

Acest simbol este cel pe care îl vom folosi pentru a trimite cererea antetului către API-ul pe care l-am creat. O modalitate de a adăuga simbolul la toate solicitările este utilizarea unui HttpInterceptor .

Acest fișier arată cum să obțineți jetonul de la AngularFireAuth și să îl adăugați la cererea antetului. Apoi oferim fișierul interceptor în 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 }

aplicație.modul.ts

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

Odată ce interceptorul este setat, putem face solicitări către API-ul nostru de la httpClient . De exemplu, aici este un UsersService în care numim lista toți utilizatorii, obținem utilizatorul după ID-ul său, creăm un utilizator și actualizăm un utilizator.

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

Acum, putem apela API-ul pentru a obține utilizatorul conectat după ID-ul său și a enumera toți utilizatorii dintr-o componentă ca aceasta:

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

Și iată rezultatul.

Toți utilizatorii din aplicația noastră Angular

Observați că dacă ne conectăm cu un utilizator cu role=user , va fi redată doar secțiunea Eu.

Vizualizarea resursei utilizator la care are acces utilizatorul cu rol

Și vom primi un 403 la inspectorul de rețea. Acest lucru se datorează restricției pe care am stabilit-o anterior pe API pentru a permite doar „administratorilor” să listeze toți utilizatorii.

O eroare 403 în inspectorul de rețea

Acum, să adăugăm funcționalitatea „creare utilizator” și „editare utilizator”. Pentru a face asta, să creăm mai întâi un UserFormComponent și 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) ); } }

Înapoi în componenta principală, să adăugăm butoanele pentru a apela acele acțiuni. În acest caz, „Editare utilizator” va fi disponibil numai pentru utilizatorul conectat. Puteți continua și adăuga funcționalitatea pentru a edita alți utilizatori dacă aveți nevoie!

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

De la Poștaș

Postman este un instrument pentru a construi și a face cereri către API-uri. În acest fel, putem simula că ne apelăm API-ul din orice aplicație client sau alt serviciu.

Ceea ce vom demonstra este cum să trimiteți o solicitare pentru a lista toți utilizatorii.

Odată ce deschidem instrumentul, setăm adresa URL https://us-central1-{your-project}.cloudfunctions.net/api/users:

Adresa URL API încărcată în câmpul Poștaș gata să se declanșeze ca solicitare GET

Apoi, pe fila Autorizare, alegem Bearer Token și setăm valoarea pe care am extras-o anterior din Dev Tools.

Setarea jetonului purtător în Postman

Corpul răspunsului pe care îl primim

Concluzie

Felicitări! Ați trecut prin tot tutorialul și acum ați învățat să creați un API bazat pe rol de utilizator pe Firebase.

De asemenea, am descris cum să-l consumăm dintr-o aplicație Angular și Postman.

Să recapitulăm cele mai importante lucruri:

  1. Firebase vă permite să începeți rapid și să rulați cu un API de autentificare la nivel de întreprindere, pe care îl puteți extinde ulterior.
  2. Aproape fiecare proiect necesită autorizare — dacă trebuie să controlați accesul folosind un model bazat pe roluri, Firebase Authentication vă permite să începeți foarte repede.
  3. Modelul bazat pe roluri se bazează pe validarea resurselor care sunt solicitate de la utilizatorii cu roluri specifice față de utilizatori anumiți.
  4. Folosind o aplicație Express.js pe Firebase Function, putem crea un API REST și putem seta handlere pentru a autentifica și autoriza solicitările.
  5. Folosind revendicările personalizate încorporate, puteți crea un API de autentificare bazat pe roluri și vă puteți asigura aplicația.

Puteți citi mai multe despre autentificarea Firebase aici. Și dacă doriți să folosiți rolurile pe care le-am definit, puteți folosi @angular/fire helpers.