本記事の内容
本記事ではScript SetUp、FireBase Authenticationを用いたログイン機能の実装を行います。
実装する機能は以下になります。
- アカウント登録
- ログイン
- ログアウト
- 未ログイン時、中の情報を見れないようにする
事前準備
事前準備として、以下の二つがあります。
- axiosのインストール
- 型定義
axiosのインストール
手順は以下の2つです。
①axiosのインストール
②axiosのインポート
axiosのインストール
以下のコマンドを実行し、axiosをインストールしてください
npm i axios
axiosのインポート
完了したらaxiosをインポートします。
この際、必要なURLを省略できるよう、baseURLの設定も行なっておきます。
import axios from "axios";
export const axiosComments = axios.create({
//URL設定
baseURL:
'https://firestore.googleapis.com/v1/projects/vuejs-http-f75d6/databases/(default)/documents'
})
export const axiosAuth = axios.create({
baseURL:
'https://identitytoolkit.googleapis.com/v1'
})
export const axiosRefresh = axios.create({
baseURL:
'https://securetoken.googleapis.com/v1'
})
型定義
//vuexのstate用型定義
export type AuthState = {
idToken: null
};
//ログインの情報送信用型定義
export type AuthRequest = {
email: string;
password: string;
returnSecureToken: boolean;
};
//ログインのレスポンスデータ用型定義
export type AuthResponse = {
idToken: string;
refreshToken: string;
expiresIn: number;
};
//ログイン継続のレスポンスデータ用型定義
export type RefreshResponse = {
id_token: string;
refresh_token: string;
expires_in: number;
};
Firebase Authenticationを使用したログイン処理
前述の通り、実装したい機能は以下の4つです。
- アカウント登録
- ログイン
- ログアウト
- 未ログイン時、中の情報を見れないようにする
アカウント登録/ログイン/ログアウト
ここでは、タイトル通り、アカウント登録・ログイン・ログアウトの機能を実装します。
そのためには以下の4つの処理が必要です。
①登録情報をサーバーに渡し、必要なデータをローカルストレージに保存
②ログイン時データをサーバーに渡し、必要なデータをローカルストレージに保存、ページを遷移させる処理
③ログインに必要なidTokenの有効期限が切れた時に、必要なトークン情報を取得し、ローカルストレージに保存する処理。
④ログアウト時、ローカルストレージに保存したデータを全て削除し、ログイン画面に遷移する処理
それぞれより具体的には以下の処理をする必要があります。
①登録情報をサーバーに渡し、必要なデータをローカルストレージに保存
- データをサーバーに送信
- レスポンスデータを受け取り、必要なデータを保存する処理
②ログイン時データをサーバーに渡し、必要なデータをローカルストレージに保存、ページを遷移させる処理
- データをサーバーに送信
- レスポンスデータを受け取り、必要なデータを保存する処理
- ページ遷移
③ログインに必要なidTokenの有効期限が切れた時に、必要なトークン情報を取得し、ローカルストレージに保存する処理。
- 有効期限の計算
- 切れた際にidTokenの代わりとなるrefreshTokenを取得する処理
- ローカルストレージに必要なデータを保存する処理
④ログアウト時、ローカルストレージに保存したデータを全て削除し、ログイン画面に遷移する処理
- idTokenをなくす処理
- ローカルストレージに保存してあるデータを削除する処理
- ログイン画面に遷移する処理
ではコードを見ていきましょう。
ちなみに、axiosを使用する際に渡す、もしくは取得するデータに関してはこちらをご覧ください。
import { Module, ActionTree, MutationTree, GetterTree } from "vuex";
import { AuthState, Auth } from "../../types";
import { RootState } from "../../types/rootstate";
import { axiosAuth, axiosRefresh } from '../../axios';
import router from "../../router"
import { AxiosResponse } from "axios";
const state: AuthState = {
idToken : ''
};
const getters: GetterTree<AuthState, RootState> = {
idToken: state => state.idToken
};
const mutations: MutationTree<AuthState> = {
updateIdToken(state, idToken: string) {
state.idToken = idToken;
}
};
const actions: ActionTree<AuthState, RootState> = {
//③ログインに必要なidTokenの有効期限が切れた時に、必要なトークン情報を取得し、ローカルストレージに保存する処理。
async autoLogin({commit, dispatch}){
const idToken = localStorage.getItem('idToken')
//もしidTokenがなかったら何もしない
if (!idToken) return;
const now = new Date()
const expiryTimeMs = localStorage.getItem('expiryTimeMs')
// 有効期限が切れているとき値を定める
const isExpired = now.getTime() >= (+expiryTimeMs!);
const refreshToken = localStorage.getItem('refreshToken');
if (isExpired) {
await dispatch('refreshIdToken', refreshToken);
} else {
//有効期限の残り時間を計算して、その時間経ったらrefreshTokenを発行させる処理。
const expiresInMs = +expiryTimeMs! - now.getTime();
setTimeout(() => {
dispatch('refreshIdToken', refreshToken);
}, expiresInMs);
commit('updateIdToken', idToken);
}
},
//②ログイン時データをサーバーに渡し、必要なデータをローカルストレージに保存、ページを遷移させる処理
login({dispatch}, authData: AuthRequest){
//サーバーへ以下の情報を送信
axiosAuth
.post(
'/accounts:signInWithPassword?key=AIzaSyB_4rHWZuPdElvwvC3jSCsjZr54H1_0fPg',
{
email: authData.email,
password: authData.password,
returnSecureToken: true
}
)
//レスポンスを受け取る
.then((response: AxiosResponse<AuthResponse>)=>{
//コミットして、受け取った値を渡す。
dispatch('setAuthData', {
idToken: response.data.idToken,
expiresIn: response.data.expiresIn,
refreshToken: response.data.refreshToken
});
//ページ遷移
router.push('/')
})
},
//④ログアウト時、ローカルストレージに保存したデータを全て削除し、ログイン画面に遷移する処理
logout({ commit }) {
commit('updateIdToken', null);
localStorage.removeItem('idToken');
localStorage.removeItem('expiryTimeMs');
localStorage.removeItem('refreshToken');
router.replace('/login');
},
//リフレッシュトークンを返し、継続的に利用できるようにする関数
async refreshIdToken({dispatch}, refreshToken: string){
await axiosRefresh
.post('/token?key=AIzaSyB_4rHWZuPdElvwvC3jSCsjZr54H1_0fPg',{
grant_type: 'refresh_token',
refresh_token: refreshToken
})
.then((response: AxiosResponse<RefreshResponse>) =>{
dispatch('setAuthData', {
idToken: response.data.id_token,
expiresIn: response.data.expires_in,
refreshToken: response.data.refresh_token
});
})
},
//①登録情報をサーバーに渡し、必要なデータをローカルストレージに保存
register({dispatch}, authData: AuthRequest){
axiosAuth
.post(
'/accounts:signUp?key=AIzaSyB_4rHWZuPdElvwvC3jSCsjZr54H1_0fPg',
{
email: authData.email,
password: authData.password,
returnSecureToken: true
}
)
.then((response: AxiosResponse<AuthResponse>)=>{
dispatch('setAuthData', {
idToken: response.data.idToken,
expiresIn: response.data.expiresIn,
refreshToken: response.data.refreshToken
});
})
},
//ローカルステージにidTokenとexpiryTImeMsを保存することによって、永続的にログイン状態でいられる。
setAuthData({ commit, dispatch }, authData: AuthResponse) {
const now = new Date();
//有効期限が切れるときの秒数を定義
const expiryTimeMs = now.getTime() + authData.expiresIn * 1000;
commit('updateIdToken', authData.idToken);
//ローカルストレージに保存
localStorage.setItem('idToken', authData.idToken);
localStorage.setItem('expiryTimeMs', expiryTimeMs.toString());
localStorage.setItem('refreshToken', authData.refreshToken);
//トークンの有効期限後、リフレッシュトークンを返し、継続的に利用できるようにする処理
setTimeout(() => {
dispatch('refreshIdToken', authData.refreshToken);
}, authData.expiresIn * 1000);
}
}
export const AuthModule: Module<AuthState, RootState> = {
namespaced: true,
state,
getters,
actions,
mutations,
};
idTokenがある場合に、ログイン状態を保つため、以下のコードを追記します。
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { store, key } from './store'
//autoLoginを一番最初に実行するよう設定
store.dispatch('Auth/autoLogin').then(() => {
createApp(App).use(store, key).use(router).mount('#app')
})
未ログイン時、中の情報をブロックする処理
以下のコードをご覧ください。
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import TodoListView from '../views/TodoListView.vue'
import RegisterView from '../views/RegisterView.vue'
import LoginView from '../views/LoginView.vue'
import { store } from '../store'
const routes: Array<RouteRecordRaw> = [
{
path: '/register',
name: 'register',
component: RegisterView
},
{
path: '/login',
name: 'login',
component: LoginView
},
{
path: '/,
name: 'todo',
component: TodoListView,
//追記
beforeEnter(to, from, next) {
if (store.getters['Auth/idToken']) {
next();
} else {
next('/login');
}
}
},
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
ナビゲーションバーの変更
最後に、ログイン状態によってナビゲーションバーも変化する処理を行います。
<script setup lang="ts">
import { computed } from 'vue'
import { useStore } from './store'
const store = useStore()
//idTokenがあれば、認証されていると判断する算出プロパティ
const idAuthenticated = computed(()=>{
return store.getters['Auth/idToken']!= ''
})
const logout = () => {
store.dispatch('Auth/logout');
}
</script>
<template>
<nav>
<template v-if="!idAuthenticated" >
<router-link to="/register" class="header-item">Register</router-link> |
<router-link to="/login" class="header-item">Login</router-link> |
</template>
<template v-if="idAuthenticated" >
<router-link to="/">Kanban-Board</router-link> |
<span to="/" class="header-item" @click="logout" >Logout</span> |
</template>
</nav>
<router-view/>
</template>