Son zamanlarda VueJS ve Firebase odaklı mini projeler geliştiriyorum. Firebase ile basit bir Chat Uygulaması yaptıktan sonra kendim için Authentication içeren ve middleware işlemlerini de yapabileceğim bir boilerplate hazırlamak isterken Firebase Storage özelliğini de dahil edip mini bir galeri uygulaması yaptım. Bu yazıda Firebase ile Auth işlemlerini Pinia ile nasıl yapabiliriz ondan bahsedeceğim.
Firebase, Google tarafından mobil ve web uygulamaları oluşturmak için geliştirilmiş bir platform. Birçok konuda çok hızlı bir şekilde kullanmak istediğimiz özelliklerini projenize dahil edebiliyoruz.
Doküman gayet yeterli. Ben Authentication olarak GitHub Provider'ını kullandım, dilerseniz farklı yöntemleri de sisteminize entegre edebilirsiniz.
Auth kısmında state management olarak Pinia kullandım.
Firebase yapılandırması
Öncelikle Firebase entegrasyonunu yapıyoruz,
// plugins/firebase.js
import {initializeApp} from "firebase/app";
import { getAuth }from "firebase/auth";
const firebaseConfig = initializeApp({
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: "",
});
const auth = getAuth(firebaseConfig);
export {auth}
Buradaki firebaseConfig değişkeni Firebase'den aldığımız API Key'lerini içeriyor. Daha sonra bu değişkenleri getAuth fonksiyonuna parametre olarak veriyoruz.
Router ve middleware kurulumu
Şimdi Auth işlemlerini yapalım. Router kısmında middleware oluşturacağımız için plugins klasörü altında router.js adında bir dosya oluşturuyoruz. Bunu daha sonra main.js dosyasında projeye register ediyoruz.
// plugins/router.js
import {createRouter, createWebHistory} from 'vue-router';
const routes: [];
const router = createRouter({
history: createWebHistory(),
routes: routes,
});
export default router;
Aşağıda oluşturduğumuz fonksiyonda eğer ilgili route'a hiçbir middleware tanımlanmamışsa next() fonksiyonu ile request'e herhangi bir işlem yapmadan devam ettiriyoruz. Eğer middleware tanımlanmışsa ilgili middleware fonksiyonunu çalıştırıyoruz.
// plugins/router.js
function nextFactory(context, middleware, index) {
const subsequentMiddleware = middleware[index];
if (!subsequentMiddleware) return context.next;
return (...parameters) => {
context.next(...parameters);
const nextMiddleware = nextFactory(context, middleware, index + 1);
subsequentMiddleware({...context, next: nextMiddleware});
};
}
Aşağıdaki fonksiyonda ise middleware'lerimizi beforeEach fonksiyonu ile register ediyoruz.
// plugins/router.js
router.beforeEach((to, from, next) => {
if (to.meta.middleware) {
const middleware = Array.isArray(to.meta.middleware) ? to.meta.middleware : [to.meta.middleware];
const context = {from, next, router, to};
const nextMiddleware = nextFactory(context, middleware, 1);
return middleware[0]({...context, next: nextMiddleware});
}
return next();
});
Pinia kimlik doğrulama deposu
Şimdi sıra Pinia ile state management işlemlerine geldi. stores klasörü altında authStore.js adında bir dosya oluşturuyoruz. Bu dosyada defineStore fonksiyonunu kullanarak bir store oluşturuyoruz. Bu store içerisinde state ile değişkenleri, getters ile değişkenlerin değerlerini okuyabileceğimiz fonksiyonları, actions ile değişkenlerin değerlerini değiştirebileceğimiz ve veri işlemlerini yapabileceğimiz fonksiyonları tanımlıyoruz.
import { defineStore } from 'pinia'
import {
onAuthStateChanged,
GithubAuthProvider,
signInWithPopup,
signOut,
} from "firebase/auth";
import {auth} from "@/plugins/firebase";
import router from "@/plugins/router.js";
export const useAuthStore = defineStore('authStore', {
state: () => ({
userData: null,
loadingUser: false,
loadingSession: false,
}),
actions: {
async login() {
this.loadingUser = true;
try {
const provider = new GithubAuthProvider();
const { user } = await signInWithPopup(auth, provider);
this.userData = user;
await router.push("/");
}catch (e) {
console.log(e)
}finally {
this.loadingUser = false;
}
},
async logout() {
try {
await signOut(auth);
this.userData = null;
await router.push("/login");
}catch (e) {
console.log(e)
}
},
async checkSession() {
return new Promise((resolve, reject) => {
const unsubscribe = onAuthStateChanged(
auth,
(user) => {
if (user) {
this.userData = user;
} else {
this.userData = null;
}
resolve(user);
},
(e) => reject(e)
);
unsubscribe();
});
}
}
})
Burada login fonksiyonunda Github ile giriş işlemini yapıyoruz. logout fonksiyonunda ise Firebase Auth ile çıkış işlemini yapıyoruz. checkSession fonksiyonunda ise Firebase Auth ile kullanıcının oturum durumunu onAuthStateChanced fonksiyonu ile kontrol ediyoruz.
Store'u sayfalarda kullanmak
Daha sonra bu store'u kullanmak istediğimiz yerde import edip kullanıyoruz. Örneğin bir sayfada kullanmak istediğimizde,
import { useAuthStore } from '@/stores/authStore'
const authStore = useAuthStore()
şeklinde import ediyoruz. Bu şekilde authStore değişkeni ile store'daki değişkenlere ve fonksiyonlara erişebiliyoruz.
Şimdi sıra sayfalara gelince. pages klasörü altında Login.vue ve Home.vue adında iki dosya oluşturuyoruz. Bu dosyalarda da setup fonksiyonu içerisinde store'daki değişkenleri ve fonksiyonları kullanarak işlemlerimizi yapıyoruz.
<script setup>
// pages/Login.vue
import { useAuthStore } from '@/stores/authStore'
const authStore = useAuthStore()
const login = () => {
authStore.login()
}
</script>
<template>
<div>
<h1>Login</h1>
<button @click="login">Login</button>
</div>
</template>
Kimliği doğrulanmış rotaları korumak
Sıra middleware'e geldi. middleware klasörü altında auth.js adında bir dosya oluşturuyoruz. Bu dosyada da useAuthStore ile oluşturduğumuz checkSession fonksiyonu ile kullanıcının oturum durumunu kontrol ediyoruz. Eğer kullanıcı oturum açmamışsa login sayfasına yönlendiriyoruz.
// middleware/auth.js
import {useAuthStore} from "@/stores/authStore.js";
export default async function auth({next, router}) {
const userStore = useAuthStore();
userStore.loadingSession = true;
const user = await userStore.checkSession();
if (user) {
next();
} else {
next("/login");
}
userStore.loadingSession = false;
}
Şimdi sıra router.js dosyasına gelince. Bu dosyada da meta kısmına middleware olarak oluşturduğumuz auth middleware'ini ekliyoruz.
// plugins/router.js
import auth from "@/middleware/auth.js";
const routes = [
{
path: "/",
name: "Home",
component: () => import("@/pages/Home.vue"),
meta: {
middleware: auth,
},
},
{
path: "/login",
name: "Login",
component: () => import("@/pages/Login.vue"),
},
];
Misafir rotalarını korumak
Şimdi bu işlemin tersini yapalım. Yani Login.vue sayfasına giriş yapılmış ise erişilemesin. Bunun için yine bir middleware oluşturuyoruz. middleware klasörü altında guest.js adında bir dosya oluşturuyoruz. Bu dosyada da useAuthStore ile oluşturduğumuz checkSession fonksiyonu ile kullanıcının oturum durumunu kontrol ediyoruz. Eğer kullanıcı oturum açmışsa Home sayfasına yönlendiriyoruz.
// middleware/guest.js
import {useAuthStore} from "@/stores/authStore.js";
export default async function authCheck({next, router}) {
const userStore = useAuthStore();
userStore.loadingSession = true;
const user = await userStore.checkSession();
if (user) {
next("/");
} else {
next();
}
userStore.loadingSession = false;
}
Fark ettiğiniz üzere tam tersi bir işlem yapıyoruz. Şimdi sıra router.js dosyasına gelince. Bu dosyada da meta kısmına middleware olarak oluşturduğumuz guest middleware'ini ekliyoruz.
// plugins/router.js
import auth from "@/middleware/auth.js";
import guest from "@/middleware/guest.js";
const routes = [
{
path: "/",
name: "Home",
component: () => import("@/pages/Home.vue"),
meta: {
middleware: auth,
},
},
{
path: "/login",
name: "Login",
component: () => import("@/pages/Login.vue"),
meta: {
middleware: guest,
},
},
];
Böylelikle en basit haliyle Firebase Auth kütüphanesini kullanarak oturum yönetim işlemlerini yapmış olduk, projeyi aşağıdaki bağlantıya tıklayarak inceleyebilirsiniz.
Şimdilik bu kadar, sonraki yazıda görüşmek üzere. 👋🏼
Proje Linki