Firebase Authentication (Firebase Kimlik Doğrulaması) ile Rollere Göre API Oluşturma

Fatih Küçükkarakurt

Fatih Küçükkarakurt

18 min read

Bu makalede, Firebase ve Node.js kullanarak kullanıcıları ve rolleri yönetmek için bir REST API oluşturacağız. Ek olarak, hangi kullanıcıların belirli kaynaklara erişebileceğini yetkilendirmek için bu API’yi nasıl kullanacağımızı göstermeye çalışacağım.

İçerik

Başlangıç

Hemen hemen her uygulama, belirli bir düzeyde yetkilendirme sistemi gerektirir. Bazı durumlarda, önceden ayarlanmış bir kullanıcı adı ve parolayı doğrulamak yeterlidir, ancak çoğu zaman, belirli kullanıcıların belirli kaynaklara erişmesine ve bunları diğerlerinden kısıtlamasına izin vermek için daha ayrıntılı bir izin modeline ihtiyacımız vardır. İkincisini desteklemek için bir sistem oluşturmak oldukça önemlidir ve gerçekten fazla zaman alabilir. Bu makalede, hızlı bir şekilde çalışmaya başlamamıza yardımcı olacak Firebase Auth. kullanacak rol tabanlı bir kimlik doğrulama API‘sinin nasıl oluşturulacağını öğreneceğiz.

Rol tabanlı Kimlik Doğrulama

Bu yetkilendirme modelinde, belirli kullanıcılar yerine rollere erişim verilir ve izin modelinizi nasıl tasarladığınıza bağlı olarak bir kullanıcı bir veya daha fazlasına sahip olabilir. Öte yandan kaynaklar, bir kullanıcının uygulamasına izin vermek için belirli roller gerektirir.

Firebase Kimlik Tabanlı Kimlik Doğrulama

Firebase

Firebase Authentication (Firebase Kimlik Doğrulama)

Firebase Authentication, genişletilebilir token tabanlı bir kimlik doğrulama sistemidir ve diğerlerinin yanı sıra Google, Facebook ve Twitter gibi en yaygın sağlayıcılarla kullanıma hazır entegrasyonlar sağlar. Ayrıca esnek bir rol tabanlı API oluşturmak için yararlanacağımız özel talepleri kullanmamızı sağlar. Bu taleplere herhangi bir JSON değeri koyabiliriz. Örnek vermek gerekirse;

{ role: 'admin' }
{ role: 'manager' }

Gerekli ayarlar yapıldıktan sonra, özel hak talepleri Firebase’in oluşturduğu token’e dahil edilir ve erişimi kontrol etmek için değeri okuyabiliriz. Ayrıca, çoğu durumda fazlasıyla yeterli olacak çok cömert bir ücretsiz kota ile birlikte gelir.

Firebase İşlevleri

Firebase İşlevleri, tamamen yönetilen sunucusuz bir platform hizmetidir. Kodumuzu Node.js’ee yazmamız ve dağıtmamız gerekiyor. Firebase, altyapının isteğe bağlı olarak ölçeklendirilmesi, sunucu yapılandırması ve daha fazlasıyla ilgilenir. Bizim ise, API’mizi oluşturmak ve HTTP aracılığıyla web’e sunmak için kullanacağız.

Firebase, express.js uygulamalarını farklı yollar için, işleyici olarak ayarlamamıza olanak tanır. Örneğin, bir Express uygulaması oluşturabilir ve onu /mypath‘e bağlayabilirsiniz. Böylece bu rotaya gelen tüm istekler app tarafından işlenir.

Yönetici SDK‘sını (Admin SDK) kullanarak bir işlev bağlamından tüm Firebase Authentication API‘ye erişebilirsiniz.

Ne İnşa Edeceğiz?

Başlamadan önce, ne inşa edeceğimize bir göz atalım. Adım adım gitmek, konunun çok daha iyi anlaşılmasını sağlayacaktır. Aşağıdaki uç noktalara sahip bir REST API oluşturacağız:

HTTPPathAçıklamaDoğrulama
GET/usersTüm kullanıcıları listelerYalnızca yöneticiler erişebilir
POST/usersYeni kullanıcı yaratırYalnızca yöneticiler erişebilir
GET/users/:id:id kullanıcısını alırYöneticiler ve aynı :id kullanıcıları erişebilir
PATCH/users/:id:id kullanıcısını güncellerYöneticiler ve aynı :id kullanıcıları erişebilir
DELETE/users/:id:id kullanıcısını silerYöneticiler ve aynı :id kullanıcıları erişebilir

Bu uç noktaların her biri kimlik doğrulamasını ele alacak, yetkilendirmeyi doğrulayacak, karşılık gelen işlemi gerçekleştirecek ve son olarak anlamlı bir HTTP kodu döndürecektir. Token’i doğrulamak için gerekli kimlik doğrulama ve yetkilendirme işlevlerini oluşturacağız ve taleplerin işlemi yürütmek için gerekli rolü içerip içermediğini kontrol edeceğiz.

API’yi Oluşturmaya Başlayalım

API’yi oluşturmak için şunlara ihtiyacımız var:

  • Bir Firebase projesi ve
  • firebase-tools ‘un sisteminizde yüklenmiş olması gerekiyor.

Şimdi ise Firebase’e giriş yapın: firebase login

Ardından, bir functions projesi başlatın. Bunun için aşağıdaki kodları takip edebilirsiniz:

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: {ornek-projemiz}

? 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

Buradan sonra, bir Functions klasörünüz olması gerekir.

**src/index.ts** adresinde, İşlevlerinizin çalıştığını doğrulamak için açıklamayı kaldırabileceğiniz bir helloWorld örneği olacaktır. Ayrıca bu aşamadan sonra cd functions ve npm run serve komutlarını çalıştırabilirsiniz. Bu komut, kodu aktaracak ve yerel sunucuyu başlatacaktır.

Sonucunuza http://localhost:5000/{ornek-projemiz}/us-central1/helloWorld adresinden göz atabilirsiniz.

'index.ts: 'helloWorld' : işlevin adının olarak tanımlanan yolda gösterildiğine dikkat edin.

Firebase HTTP İşlevi Oluşturma

Şimdi API’mızı kodlayalım. Bir HTTP Firebase işlevi oluşturacağız ve onu /apiyoluna bağlayacağız.

İlk önce npm install express‘i kurun.

src/index.ts içerisinde bazı işlemler yapacağız.

  • Firebase-Admin SDK modülünü şununla başlatın: admin.initializeApp();
  • Bir Express uygulamasını api https uç noktamızın işleyicisi olarak ayarlayın.

Bu işlemleri aşağıdaki gibi yapabilirsiniz:

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

Şimdi, /api‘ye giden tüm istekler app tarafından ele alınacak.

Yapacağımız bir sonraki şey, app örneğini CORS’u destekleyecek şekilde yapılandırmak ve JSON gövde ayrıştırıcı ara yazılımını eklemek. Bu şekilde herhangi bir URL’den istekte bulunabilir ve JSON formatlı istekleri ayrıştırabiliriz.

İlk önce gerekli bağımlılıkları kuralım.

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

Sonra ise;

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

Son olarak, app‘in işleyeceği rotaları yapılandıracağız.

//...
import { routesConfig } from './users/routes-config';

//…
app.use(cors({ origin: true }));
routesConfig(app)

export const api = functions.https.onRequest(app);

Firebase Functions, bir Express uygulamasını işleyici olarak ayarlamamıza olanak tanır ve sizin kurduğunuz uygulamadan sonraki herhangi bir yol (ki biz burada functions.https.onRequest(app); kullandık), app tarafından da ele alınacaktır.

Bu, api/users gibi belirli uç noktalar yazmamıza ve daha sonra yapacağımız her HTTP fiili için bir işleyici ayarlamamıza olanak tanıyacaktır.

Şimdi ise, src/users/routes-config.ts dosyasını oluşturalım.

Burada POST '/users' için bir create işleyici belirleyeceğiz.

import { Application } from "express";
import { create} from "./controller";

export function routesConfig(app: Application) {

 app.post('/users',
 create
 );
}

Şimdi de src/users/controller.ts dosyasını oluşturacağız.

Bu işlevde, önce tüm alanların gövde isteğinde olduğunu doğrularız ve kullanıcıyı oluşturup özel istekleri belirleriz.

Burada { role } işlevini geçiyoruz. Çünkü bu zaten setCustomUserClaims içerisinde Firebase tarafından belirlenmiş olacak.

Eğer her hangi bir hata oluşmazsa, oluşturulan kullanıcının uid kimliğiyle bir 201 kodu döndürürüz.

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

Şimdi, yetkilendirme ekleyerek işleyiciyi güvenli hale getirelim. Bunu yapmak için, create uç noktamıza birkaç işleyici ekleyeceğiz. express.js ile, sırayla yürütülecek bir işleyici zinciri ayarlayabilirsiniz. Bir işleyicide kodu çalıştırabilir ve next() işleyiciye iletebilir veya bir yanıt döndürebilirsiniz. Yapacağımız şey, önce kullanıcının kimliğini doğrulamak ve ardından yürütme yetkisine sahip olup olmadığını doğrulamaktır.

src/users/routes-config.tsdosyamızda aşağıdaki güncellemeleri yapmaya başlayabiliriz.

//...
import { isAuthenticated } from "../auth/authenticated";
import { isAuthorized } from "../auth/authorized";

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

Şimdi ise src/auth/authenticated.ts dosyasını oluşturalım.

Bu işlevde,authorization belirtecinin varlığını doğrulayacağız. Sonra bunu admin.auth().verifyidToken() ile deşifre edeceğiz ve kullanıcının uid, role ve email bilgilerini res.localsüzerinde saklayacağız. Elbette daha sonra bu bilgileri yetkilendirmeyi doğrulamak için kullanacağız.

Token’in geçersiz olması durumunda, kullanıcıya bir 401 yanıtı döndüreceğiz:

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

Şimdi ise bir src/auth/authorized.ts dosyası oluşturalım.

Bu işleyicide, res.locals, daha önce belirlediğimiz kullanıcının bilgilerini çıkarır ve işlemi yürütmek için gerekli role sahip olup olmadığını kontrol eder. Bunun yanında, işlemin aynı kullanıcının yürütmesine izin vermesi durumunda, istek parametrelerindeki kimliğin aynı olduğunu doğrular.

Eğer kullanıcı gerekli role sahip değilse 403 yanıtı döndüreceğiz.

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

Bu iki yöntemle, isteklerin kimliğini doğrulayabilir ve gelen role bilgisine göre bunları yetkilendirebiliriz. Bu harika, ancak Firebase proje konsolundan özel talepler belirlememize izin vermediğinden , bu uç noktalardan hiçbirini yürütemeyeceğiz. Bunu atlamak için Firebase Authentication Console‘dan bir root kullanıcı oluşturabiliriz.

Firebase Authentication Console’dan bir root kullanıcı oluşturabiliriz.
//...
 const { role, email, uid } = res.locals
 const { id } = req.params

 if (email === 'your-root-user-email@domain.com')
     return next();
//...

Şimdi geri kalan CRUD işlemlerini src/users/routes-config.ts dosyasına ekleyelim.

Tek bir kullanıcıyı getirme veya güncelleme işlemleri için id parametresinin gönderildiği yerde, aynı kullanıcının işlemi yürütmesine de izin veriyoruz.

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

Ve src/users/controller.ts dosyası üzerinden devam ediyoruz. Bu işlemlerde, Firebase Authentication ile etkileşim kurmak ve ilgili işlemleri gerçekleştirmek için Admin SDK’sından (Yönetici SDK) yararlanıyoruz. Daha önce create operasyonda yaptığımız gibi, her işlemde anlamlı bir HTTP kodu döndürüyoruz.

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

Şimdi işlevi yerel olarak çalıştırabiliriz. Bunu yapmak için, önce yerel olarak kimlik doğrulama API’ına bağlanabilmek için hesap anahtarını ayarlamanız gerekir.

npm run serve

API Dağıtımı

Buraya kadar her şey güzeldi! Artık rol tabanlı bir API yazdığımıza göre, onu web’e dağıtabilir ve kullanmaya başlayabiliriz. Firebase ile dağıtım yapmak çok kolaydır, sadece firebase deploy komutunu çalıştırmamız gerekiyor. Dağıtım tamamlandığında, API’ye yayınlanan URL’den erişebiliriz.

Firebase Deploy

API URL’nizi buradaki linkten kontrol edebilirsiniz: https://console.firebase.google.com/u/0/project/{ornek-projemiz}/functions/list

API Kullanımı

API’miz dağıtıldıktan sonra, onu kullanmanın birkaç yolu var. Ben bu makalede, Postman veya bir Angular uygulamasını ele alacağım.

Herhangi bir tarayıcıya Tüm Kullanıcıları Listele URL’sini (/api/users) girersek, aşağıdaki sonucu alırız:

{"message":"Unauthorized"}

Bunun nedeni, bir tarayıcıdan istek gönderirken, yetkilendirme başlıkları olmadan bir GET isteği gerçekleştirmemizdir. Bu, API’mizin aslında beklendiği gibi çalıştığı anlamına gelir! Yani tebrikler!

Angular ile Deneyelim!

Bu makalede, bir Angular uygulaması ile API’yi kullanmak için önemli olan kısımların üzerinden geçeceğim.

Oturum açmaya geri döndüğümüzde, kullanıcının bir kullanıcı adı ve şifre girmesine izin veren bir <form> içeren SignInComponent‘imiz olacak.

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

AngularFireAuth hizmetini kullanarak signInWithEmailAndPassword ‘u kullanıyoruz.

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

Artık Firebase projemize giriş yapabilmeliyiz.

DevTools’taki ağ isteklerini incelediğimizde, Firebase’in kullanıcı ve şifremizi doğruladıktan sonra bir token döndürdüğünü görebiliriz. Bu token, başlığımızın isteğini oluşturduğumuz API’ye göndermek için kullanacağımız token’dir. Belirteci tüm isteklere eklemenin bir yolu, bir HttpInterceptor kullanmaktır.

Bu dosya, token’in AngularFireAuth‘tan nasıl alınacağını ve başlığın isteğine nasıl ekleneceğini gösterir.

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 { }

interceptor ayarlandıktan sonra, httpClient‘ten API’mize isteklerde bulunabiliriz. Örneğin UsersService, listeye tüm kullanıcılar adını verdiğimiz, kullanıcıyı kimliğine göre aldığımız, bir kullanıcı oluşturduğumuz ve bir kullanıcıyı güncellediğimiz yerdir.

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

Şimdi, oturum açmış kullanıcıyı kimliğine göre getirmek için API’yi çağırabilir ve aşağıdaki gibi bir bileşende tüm kullanıcıları listeleyebiliriz:

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

Bir role=user kullanıcısıyla oturum açarsak, yalnızca “Me” bölümünün işleneceğine dikkat edin.

Firebase Role User

Ve ağ denetçisinden bir 403 cevabı alacağız. Bunun nedeni, yalnızca “Yöneticilerin” tüm kullanıcıları listelemesine izin vermek için API üzerinde daha önce belirlediğimiz kısıtlamadır.

Şimdi “create user” ve “edit user” işlevlerini ekleyelim. Bunu yapmak için önce UserFormComponent ve UserFormService oluşturalım.

<ng-container *ngIf="user$ | async"></ng-container>
<div class="modal-header">
     <h4 class="modal-title"
         id="modal-title">{{ title$ | async}}</h4>
     <button type="button"
             class="close"
             aria-describedby="modal-title"
             (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)
         );
     }
 }

Ana bileşene geri dönelim, bu eylemleri çağırmak için butonları ekleyelim. Böylece, “Edit User” yalnızca oturum açmış kullanıcı için kullanılabilir olacaktır. İhtiyaç duyduğunuzda diğer kullanıcıları da düzenleme işlevini ekleyebilirsiniz.

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

Postman ile Deneyelim

Postman, API’ler oluşturmak ve bunlara istekte bulunmak için kullanılan bir araçtır. Bu şekilde, API’mizi herhangi bir istemci uygulamasından veya farklı bir hizmetten çağırdığımızı simüle edebiliriz. Deneme yapacağımız şey, tüm kullanıcıları listelemek için nasıl istek gönderileceğidir.

Aracı açtıktan sonra, URL’yi şu şekilde girebilirsiniz: https://us-central1-{ornek-projemiz}.cloudfunctions.net/api/users

Bunun ardından, sekme yetkilendirmesinde Bearer Token‘ı seçiyoruz ve daha önce Dev Tools’tan çıkardığımız değeri belirliyoruz. İşte bu kadar!

Sonuç Olarak

Firebase’de kullanıcı rolü tabanlı API oluşturma konusunda kafanızda en azından bir şeylerin şekillendiğine inanıyorum. Ayrıca Angular ve Postman uygulamaları ile nasıl kullanılacağının üzerinde de durmuş olduk. Ama yine de önemli kısımların üzerinden geçmemiz gerektiğini düşünüyorum:

  1. Firebase, daha sonra genişletebileceğiniz, kurumsal düzeyde bir kimlik doğrulama API’si ile hızla çalışmaya başlamanıza olanak tanır.
  2. Hemen hemen her proje için yetkilendirme gerekir. Rol tabanlı bir model kullanarak erişimi kontrol etmeniz gerekiyorsa, Firebase Authentication çok hızlı bir şekilde başlamanıza olanak tanır.
  3. Rol tabanlı model, belirli kullanıcılara karşı belirli rollere sahip kullanıcılardan istenen kaynakların doğrulanmasına dayanır.
  4. Firebase Function üzerinde bir Express.js uygulaması kullanarak bir REST API oluşturabilir ve istekleri doğrulamak ve yetkilendirmek için işleyiciler ayarlayabiliriz.
  5. Yerleşik özel isteklerde yararlanarak, rol tabanlı bir kimlik doğrulama API’si oluşturabilir ve uygulamanızı güvenli hale getirebilirsiniz.
  • Firebase Auth bir REST API midir? Firebase Auth, uygulamanızın (Google, Facebook, Twitter, GitHub vb.) birden çok sağlayıcıya kaydolmasına ve bir kullanıcının kimliğini doğrulamasına olanak tanıyan bir hizmettir. Firebase Auth, web, Android ve iOS ile kolayca entegre edebileceğiniz SDK’lar sağlar. Firebase Auth ayrıca bir REST API olarak da kullanılabilir.
  • Firebase ne için kullanılır? Firebase, sunucusuz bir mobil veya web uygulamasını çok hızlı bir şekilde oluşturmanıza yardımcı olan bir bulut ürünleri paketidir. Her uygulamada yer alan yaygın hizmetlerin çoğunu sağlar (veritabanı, yetkilendirme, depolama, barındırma).
  • Firebase Auth API'yi nasıl edinebilirim?Firebase adresinde Google hesabınızla bir proje oluşturabilirsiniz. Proje oluşturulduktan sonra Firebase Auth’u açabilir ve uygulamanızda kullanmaya başlayabilirsiniz.
  • Firebase mi AWS mi? Firebase, Google destekli bir üründür ve Google’ın büyümeye ve daha fazla özellik eklemeye çalıştığı bir üründür. AWS Amplify, çoğunlukla mobil uygulamaları hedefleyen benzer bir üründür. Firebase daha fazla özelliğe sahip daha eski bir ürün olduğu için her ikisi de harika ürünlerdir.
  • Firebase'in kullanımı kolay mı? Firebase, çok kolay bir şekilde başlayabileceğiniz ve büyütmeniz gerektiğinde altyapı konusunda endişelenmenize gerek kalmayan, tamamen yönetilen bir hizmettir. Nasıl çalıştığını hızlıca öğrenmek için birçok harika belge, örnekler ve bloglar var.
  • Firebase büyük veritabanları için önerilir mi? Firebase’in iki veritabanı vardır: Realtime Database ve Firestore. Her ikisi de benzer özelliklere ve farklı fiyatlandırma modellerine sahip NoSQL veritabanlarıdır. Firestore, daha iyi sorgulama özelliklerini destekler ve her iki veritabanı da, sorgu gecikmesinin veritabanı boyutundan etkilenmemesi için tasarlanmıştır.

Buraya kadar bana eşlik ettiğiniz için teşekkür ederim. Umarım aklınızda belirli bir şeyleri canlandırmayı başarabilmişimdir.

Keyifli Kodlamalar…

Kendinize İyi Bakın, Sağlıcakla Kalın!

Anatoliacode Makale Aboneliği

Bize abone olarak tüm makaleleri ilk siz okuyabilirsiniz. Ayrıca asla reklam veya spam yapmıyoruz.