Cara Membangun API Berbasis Peran dengan Firebase Authentication
Diterbitkan: 2022-03-11Dalam tutorial ini, kita akan membuat REST API untuk mengelola pengguna dan peran menggunakan Firebase dan Node.js. Selain itu, kita akan melihat cara menggunakan API untuk mengotorisasi (atau tidak) pengguna mana yang dapat mengakses sumber daya tertentu.
pengantar
Hampir setiap aplikasi memerlukan beberapa tingkat sistem otorisasi. Dalam beberapa kasus, memvalidasi set nama pengguna/kata sandi dengan tabel Users kami sudah cukup, tetapi seringkali, kami membutuhkan model izin yang lebih halus untuk memungkinkan pengguna tertentu mengakses sumber daya tertentu dan membatasi mereka dari orang lain. Membangun sistem untuk mendukung yang terakhir bukanlah hal yang sepele dan bisa sangat memakan waktu. Dalam tutorial ini, kita akan belajar cara membangun API autentikasi berbasis peran menggunakan Firebase, yang akan membantu kita memulai dan menjalankannya dengan cepat.
Otentikasi berbasis peran
Dalam model otorisasi ini, akses diberikan ke peran, bukan pengguna tertentu, dan pengguna dapat memiliki satu atau lebih tergantung pada bagaimana Anda mendesain model izin Anda. Sumber daya, di sisi lain, memerlukan peran tertentu untuk memungkinkan pengguna menjalankannya.
Firebase
Otentikasi Firebase
Singkatnya, Firebase Authentication adalah sistem autentikasi berbasis token yang dapat diperluas dan menyediakan integrasi out-of-the-box dengan penyedia paling umum seperti Google, Facebook, dan Twitter, antara lain.
Ini memungkinkan kami menggunakan klaim khusus yang akan kami manfaatkan untuk membangun API berbasis peran yang fleksibel.
Kami dapat menetapkan nilai JSON apa pun ke dalam klaim (misalnya, { role: 'admin' }
atau { role: 'manager' }
).
Setelah disetel, klaim khusus akan disertakan dalam token yang dihasilkan Firebase, dan kita dapat membaca nilainya untuk mengontrol akses.
Itu juga dilengkapi dengan kuota gratis yang sangat murah hati, yang dalam banyak kasus akan lebih dari cukup.
Fungsi Firebase
Functions adalah layanan platform tanpa server yang terkelola sepenuhnya. Kita hanya perlu menulis kode kita di Node.js dan menerapkannya. Firebase menangani penskalaan infrastruktur sesuai permintaan, konfigurasi server, dan banyak lagi. Dalam kasus kami, kami akan menggunakannya untuk membangun API kami dan mengeksposnya melalui HTTP ke web.
Firebase memungkinkan kami menyetel aplikasi express.js
sebagai penangan untuk jalur yang berbeda—misalnya, Anda dapat membuat aplikasi Express dan menghubungkannya ke /mypath
, dan semua permintaan yang datang ke rute ini akan ditangani oleh app
yang dikonfigurasi.
Dari dalam konteks suatu fungsi, Anda memiliki akses ke seluruh Firebase Authentication API, menggunakan Admin SDK.
Ini adalah bagaimana kita akan membuat API pengguna.
Apa yang Akan Kami Bangun
Jadi sebelum kita mulai, mari kita lihat apa yang akan kita buat. Kami akan membuat REST API dengan titik akhir berikut:
Http Verb | Jalur | Keterangan | Otorisasi |
---|---|---|---|
DAPATKAN | /pengguna | Daftar semua pengguna | Hanya admin dan pengelola yang memiliki akses |
POS | /pengguna | Membuat pengguna baru | Hanya admin dan pengelola yang memiliki akses |
DAPATKAN | /pengguna/:id | Mendapatkan :id pengguna | Admin, pengelola, dan pengguna yang sama dengan :id memiliki akses |
PATCH | /pengguna/:id | Memperbarui :id pengguna | Admin, pengelola, dan pengguna yang sama dengan :id memiliki akses |
MENGHAPUS | /pengguna/:id | Menghapus :id pengguna | Admin, pengelola, dan pengguna yang sama dengan :id memiliki akses |
Masing-masing titik akhir ini akan menangani otentikasi, memvalidasi otorisasi, melakukan operasi koresponden, dan akhirnya mengembalikan kode HTTP yang bermakna.
Kami akan membuat fungsi otentikasi dan otorisasi yang diperlukan untuk memvalidasi token dan memeriksa apakah klaim berisi peran yang diperlukan untuk menjalankan operasi.
Membangun API
Untuk membangun API, kita membutuhkan:
- Proyek Firebase
-
firebase-tools
terpasang
Pertama, masuk ke Firebase:
firebase login
Selanjutnya, inisialisasi proyek Fungsi:
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
Pada titik ini, Anda akan memiliki folder Fungsi, dengan pengaturan minimum untuk membuat Fungsi Firebase.
Di src/index.ts
ada contoh helloWorld
, yang dapat Anda batalkan komentarnya untuk memvalidasi bahwa Fungsi Anda berfungsi. Kemudian Anda dapat menjalankan cd functions
dan menjalankan npm run serve
. Perintah ini akan mengubah kode dan memulai server lokal.
Anda dapat memeriksa hasilnya di http://localhost:5000/{your-project}/us-central1/helloWorld
Perhatikan bahwa fungsi tersebut diekspos pada jalur yang didefinisikan sebagai namanya di 'index.ts: 'helloWorld'
.
Membuat Fungsi HTTP Firebase
Sekarang mari kita mengkodekan API kita. Kita akan membuat fungsi http Firebase dan mengaitkannya di jalur /api
.
Pertama, instal npm install express
.
Di src/index.ts
kita akan:
- Inisialisasi modul SDK firebase-admin dengan
admin.initializeApp();
- Tetapkan aplikasi Express sebagai pengendali titik akhir https
api
kami
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);
Sekarang, semua permintaan yang masuk ke /api
akan ditangani oleh instance app
.
Hal berikutnya yang akan kita lakukan adalah mengonfigurasi instance app
untuk mendukung CORS dan menambahkan middleware parser body JSON. Dengan cara ini kami dapat membuat permintaan dari URL apa pun dan mem-parsing permintaan berformat JSON.
Kami akan menginstal dependensi yang diperlukan terlebih dahulu.
npm install --save cors body-parser
npm install --save-dev @types/cors
Dan kemudian:
//... 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);
Terakhir, kami akan mengonfigurasi rute yang akan ditangani app
.
//... import { routesConfig } from './users/routes-config'; //… app.use(cors({ origin: true })); routesConfig(app) export const api = functions.https.onRequest(app);
Firebase Functions memungkinkan kami menyetel aplikasi Express sebagai pengendali, dan jalur apa pun setelah yang Anda siapkan di functions.https.onRequest(app);
—dalam hal ini, api
—juga akan ditangani oleh app
. Hal ini memungkinkan kita untuk menulis endpoint tertentu seperti api/users
dan mengatur handler untuk setiap kata kerja HTTP, yang akan kita lakukan selanjutnya.
Mari kita buat file src/users/routes-config.ts
Di sini, kita akan menetapkan handler create
di POST '/users'
import { Application } from "express"; import { create} from "./controller"; export function routesConfig(app: Application) { app.post('/users', create ); }
Sekarang, kita akan membuat file src/users/controller.ts
.
Dalam fungsi ini, pertama-tama kami memvalidasi bahwa semua bidang ada dalam permintaan isi, dan selanjutnya, kami membuat pengguna dan menetapkan klaim khusus.
Kami baru saja meneruskan { role }
di setCustomUserClaims
—bidang lain sudah disetel oleh Firebase.
Jika tidak terjadi kesalahan, kami mengembalikan kode 201 dengan uid
yang dibuat pengguna.
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}` }); }
Sekarang, mari kita amankan handler dengan menambahkan otorisasi. Untuk melakukan itu, kita akan menambahkan beberapa handler ke titik akhir create
kita. Dengan express.js
, Anda dapat mengatur rantai penangan yang akan dieksekusi secara berurutan. Di dalam handler, Anda dapat mengeksekusi kode dan meneruskannya ke handler next()
atau mengembalikan respons. Apa yang akan kita lakukan adalah pertama-tama mengautentikasi pengguna dan kemudian memvalidasi jika diizinkan untuk dijalankan.
Pada 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 ); }
Mari kita buat file src/auth/authenticated.ts
.
Pada fungsi ini, kami akan memvalidasi keberadaan token pembawa authorization
di header permintaan. Kemudian kita akan mendekodekannya dengan admin.auth().verifyidToken()
dan mempertahankan uid
, role
, dan email
pengguna dalam variabel res.locals
, yang nantinya akan kita gunakan untuk memvalidasi otorisasi.
Jika token tidak valid, kami mengembalikan respons 401 ke klien:
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' }); } }
Sekarang, mari kita buat file src/auth/authorized.ts
.
Dalam handler ini, kami mengekstrak info pengguna dari res.locals
yang kami tetapkan sebelumnya dan memvalidasi jika memiliki peran yang diperlukan untuk menjalankan operasi atau jika operasi memungkinkan pengguna yang sama untuk mengeksekusi, kami memvalidasi bahwa ID pada parameter permintaan sama dengan yang ada di token auth. Jika pengguna tidak memiliki peran yang diperlukan, kami akan mengembalikan 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(); } }
Dengan dua metode ini, kami akan dapat mengautentikasi permintaan dan mengotorisasinya berdasarkan role
dalam token yang masuk. Itu bagus, tetapi karena Firebase tidak mengizinkan kami menetapkan klaim khusus dari konsol proyek, kami tidak akan dapat menjalankan salah satu dari titik akhir ini. Untuk melewati ini, kita dapat membuat pengguna root dari Firebase Authentication Console
Dan atur perbandingan email dalam kode. Sekarang, saat mengaktifkan permintaan dari pengguna ini, kami akan dapat menjalankan semua operasi.
//... const { role, email, uid } = res.locals const { id } = req.params if (email === '[email protected]') return next(); //...
Sekarang, mari tambahkan sisa operasi CRUD ke src/users/routes-config.ts
.
Untuk operasi untuk mendapatkan atau memperbarui satu pengguna di mana :id
param dikirim, kami juga mengizinkan pengguna yang sama untuk menjalankan operasi.
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 ]); }
Dan di src/users/controller.ts
. Dalam operasi ini, kami memanfaatkan SDK admin untuk berinteraksi dengan Firebase Authentication dan melakukan operasi masing-masing. Seperti yang kami lakukan sebelumnya pada operasi create
, kami mengembalikan kode HTTP yang berarti pada setiap operasi.
Untuk operasi pembaruan, kami memvalidasi semua bidang yang ada dan menimpa customClaims
dengan yang dikirim dalam permintaan:
//.. 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) } } //...
Sekarang kita dapat menjalankan fungsinya secara lokal. Untuk melakukannya, pertama-tama Anda perlu menyiapkan kunci akun agar dapat terhubung dengan API auth secara lokal. Lalu lari:

npm run serve
Terapkan API
Besar! Sekarang setelah kami menulis API berbasis peran, kami dapat menerapkannya ke web dan mulai menggunakannya. Menerapkan dengan Firebase sangat mudah, kita hanya perlu menjalankan firebase deploy
. Setelah penerapan selesai, kami dapat mengakses API kami di URL yang dipublikasikan.
Anda dapat memeriksa URL API di https://console.firebase.google.com/u/0/project/{your-project}/functions/list.
Dalam kasus saya, ini adalah [https://us-central1-joaq-lab.cloudfunctions.net/api].
Mengkonsumsi API
Setelah API kami di-deploy, kami memiliki beberapa cara untuk menggunakannya—dalam tutorial ini, saya akan membahas cara menggunakannya melalui Postman atau dari aplikasi Angular.
Jika kita memasukkan URL Daftar Semua Pengguna ( /api/users
) di browser apa pun, kita akan mendapatkan yang berikut:
Alasannya adalah saat mengirim permintaan dari browser, kami melakukan permintaan GET tanpa header auth. Ini berarti API kami benar-benar berfungsi seperti yang diharapkan!
API kami diamankan melalui token—untuk menghasilkan token seperti itu, kami perlu memanggil SDK Klien Firebase dan masuk dengan kredensial pengguna/kata sandi yang valid. Jika berhasil, Firebase akan mengirimkan token kembali sebagai respons yang kemudian dapat kita tambahkan ke header dari setiap permintaan berikut yang ingin kita lakukan.
Dari Aplikasi Sudut
Dalam tutorial ini, saya hanya akan membahas bagian penting untuk menggunakan API dari aplikasi Angular. Repositori lengkap dapat diakses di sini, dan jika Anda memerlukan tutorial langkah demi langkah tentang cara membuat aplikasi Angular dan mengkonfigurasi @angular/fire untuk digunakan, Anda dapat memeriksa posting ini.
Jadi, kembali ke login, kita akan memiliki SignInComponent
dengan <form>
untuk memungkinkan pengguna memasukkan nama pengguna dan kata sandi.
//... <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> //...
Dan di kelas, kami signInWithEmailAndPassword
menggunakan layanan 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) } } //..
Pada titik ini, kita dapat masuk ke proyek Firebase kita.
Dan ketika kami memeriksa permintaan jaringan di DevTools, kami dapat melihat bahwa Firebase mengembalikan token setelah memverifikasi pengguna dan kata sandi kami.
Token ini adalah yang akan kita gunakan untuk mengirim permintaan header kita ke API yang telah kita buat. Salah satu cara untuk menambahkan token ke semua permintaan adalah menggunakan HttpInterceptor
.
File ini menunjukkan cara mendapatkan token dari AngularFireAuth
dan menambahkannya ke permintaan header. Kami kemudian menyediakan file pencegat di 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 { }
Setelah pencegat disetel, kami dapat membuat permintaan ke API kami dari httpClient
. Misalnya, inilah UsersService
tempat kami memanggil daftar semua pengguna, mendapatkan pengguna dengan ID-nya, membuat pengguna, dan memperbarui pengguna.
//… 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) } }
Sekarang, kita dapat memanggil API untuk mendapatkan pengguna yang masuk dengan ID-nya dan mendaftar semua pengguna dari komponen seperti ini:
//... <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)) ) } //...
Dan inilah hasilnya.
Perhatikan bahwa jika kita masuk dengan pengguna dengan role=user
, hanya bagian Saya yang akan ditampilkan.
Dan kita akan mendapatkan 403 pada inspektur jaringan. Ini karena batasan yang kami tetapkan sebelumnya di API untuk hanya mengizinkan "Admin" untuk mencantumkan semua pengguna.
Sekarang, mari tambahkan fungsionalitas "buat pengguna" dan "edit pengguna". Untuk melakukan itu, mari buat dulu UserFormComponent
dan 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">×</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) ); } }
Kembali ke komponen utama, mari tambahkan tombol untuk memanggil tindakan tersebut. Dalam hal ini, "Edit Pengguna" hanya akan tersedia untuk pengguna yang masuk. Anda dapat melanjutkan dan menambahkan fungsionalitas untuk mengedit pengguna lain jika perlu!
//... <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 => { }); }
Dari tukang pos
Postman adalah alat untuk membangun dan membuat permintaan ke API. Dengan cara ini, kami dapat mensimulasikan bahwa kami memanggil API kami dari aplikasi klien mana pun atau layanan yang berbeda.
Apa yang akan kami demo adalah bagaimana mengirim permintaan ke daftar semua pengguna.
Setelah kami membuka alat, kami mengatur URL https://us-central1-{your-project}.cloudfunctions.net/api/users:
Selanjutnya, pada otorisasi tab, kami memilih Bearer Token dan kami menetapkan nilai yang kami ekstrak dari Dev Tools sebelumnya.
Kesimpulan
Selamat! Anda telah berhasil melewati seluruh tutorial dan sekarang Anda telah belajar membuat API berbasis peran pengguna di Firebase.
Kami juga telah membahas cara mengkonsumsinya dari aplikasi Angular dan Tukang Pos.
Mari kita rekap hal-hal yang paling penting:
- Firebase memungkinkan Anda untuk memulai dan menjalankan dengan cepat dengan API autentikasi tingkat perusahaan, yang dapat Anda perluas nanti.
- Hampir setiap proyek memerlukan otorisasi—jika Anda perlu mengontrol akses menggunakan model berbasis peran, Firebase Authentication memungkinkan Anda memulai dengan sangat cepat.
- Model berbasis peran bergantung pada validasi sumber daya yang diminta dari pengguna dengan peran tertentu vs. pengguna tertentu.
- Menggunakan aplikasi Express.js di Firebase Function, kita dapat membuat REST API dan mengatur penangan untuk mengautentikasi dan mengotorisasi permintaan.
- Memanfaatkan klaim kustom bawaan, Anda dapat membuat API autentikasi berbasis peran dan mengamankan aplikasi Anda.
Anda dapat membaca lebih lanjut tentang Firebase auth di sini. Dan jika Anda ingin memanfaatkan peran yang telah kami tetapkan, Anda dapat menggunakan @angular/fire helpers.