🔖
vite + firebase auth + pinia で認証フロー周りを作ってみた。
なぜ作ったのかと言われると困るのですが、firebaseがv9が出たってことと、vuex5までの繋ぎでpiniaを触っておいてみたかったというのが理由です。今回はその時作成した、認証周りについての備忘録です。
piniaとは
vuex5のrfcにより近い形の状態管理のライブラリです。
従来のvuexとは違い、mutationがなくなっていたり、よりtypesafeであったりします。
ReactHooksのuseContextに使い方としては近いのかな?って印象を個人的には持っています。
Vuex 5 RFC.
https://github.com/vuejs/rfcs/discussions/270
今回はこのpiniaにfirebase authを載せる形で認証フローを作成していけたらと思います。
firebaseの初期化
src/lib/firebase/index.ts
import {initializeApp} from 'firebase/app';
const firebaseConfig = {
apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
appId: import.meta.env.VITE_FIREBASE_APP_ID,
};
// Initialize Firebase
initializeApp(firebaseConfig);
viteがサポートする環境変数にfirebaseの各値を設定・初期化します。
初期化するためのtsファイルはentryファイルである、main.ts
の上部で読み込ませます。
src/main.ts
import 'sakura.css';
import '@/styles/global.scss';
+ import './lib/firebase';
import {createPinia} from 'pinia';
import {createApp} from 'vue';
import App from './App.vue';
import {router} from './lib/router';
createApp(App)
.use(router)
.use(createPinia())
.mount('#app');
authStoreの作成
src/lib/store/auth.store.ts
import {defineStore} from 'pinia';
import {useErrorStore} from './error.store';
import {
getAuth,
GoogleAuthProvider,
signInWithPopup,
onAuthStateChanged,
signOut,
User,
} from 'firebase/auth';
const auth = getAuth();
const provider = new GoogleAuthProvider();
export const useAuthStore = defineStore('auth', {
state: () => ({
currentUser: null as User | null,
}),
getters: {
isLoggedIn: (state) => state.currentUser !== null,
},
actions: {
async signIn() {
try {
const res = await signInWithPopup(auth, provider);
this.$patch({currentUser: res.user});
} catch (err) {
if (err instanceof Error) {
const errorStore = useErrorStore();
errorStore.setError(err);
return;
}
throw err;
}
},
async signOut() {
try {
await signOut(auth);
this.$reset();
} catch (err) {
if (err instanceof Error) {
const errorStore = useErrorStore();
errorStore.setError(err);
return;
}
throw err;
}
},
async getAuthState() {
return new Promise((resolve, reject) => {
onAuthStateChanged(auth,
(user) => {
this.$patch({currentUser: user});
resolve(user);
},
(error) => {
const errorStore = useErrorStore();
errorStore.setError(error);
reject(error);
});
});
},
},
});
前述した通り、piniaにはvuex4以前にあったmutationの概念はありません。
actionsでstateを更新します。
ですので、defineStore
の内部にはstate・getters・actionsの三種類のみとなります。
呼び出し側
src/pages/signIn.vue
<script lang="ts" setup>
import {routeNames} from '@/lib/router/routes';
import {useAuthStore} from '@/lib/store/auth.store';
import {useRouter} from 'vue-router';
const authStore = useAuthStore();
const router = useRouter();
const signIn = async () => {
try {
await authStore.signIn();
router.push({name: routeNames.About});
} catch (err) {
console.error(err);
}
};
</script>
<template>
<h1>SignIn</h1>
<button @click="signIn">Sign In</button>
</template>
最後に
久しぶりにvueを触ってみましたが、reactもいいけどvueも悪くないな〜って思いました。
Discussion