概要
現在実装を進めている個人開発のWebアプリプロジェクトについて、備忘を含めた記事を進めています。
今回の記事は本アプリで必須となるログイン管理機能について、「Firebase Authentication」を用いた実装方法を記載していきます。
開発環境
- Vue 2.x
- Vuex / Babel / Vue-CLI
- ESLint + Prettier
参考文献
- Firebase公式
- Firebase を JavaScript プロジェクトに追加する
- これからFirebaseでプロジェクトを始めようとする全ての人が知っておくべきこと v8→v9リリース
- https://qiita.com/suzuki-navi/items/a613bc47fccff091374f
事前のセットアップ
前提として、下記セットアップをしてあります。
- Firebase コンソールへの登録
- Firebaseプロジェクトを作成
- プロジェクト内でアプリを登録済み(Webアプリ)
Firebaseでの作業
プロジェクトを作った後、該当プロジェクトのダッシュボードページを参照します。
「Authentication」「Sign-in method」から各種プロバイダを使用したログイン方法の設定をする事ができます。今回はシンプルな「メール/パスワード」での設定方法を試してみます。
「メール/パスワード」を選択するとポップアップが表示されるので、「有効にする」を選択して保存します。
続いて「Users」タブでユーザーを追加します。
「ユーザーを追加」ボタンを押下して表示されるポップアップから、任意のメールアドレス+パスワードを入力して追加します。
メールアドレスがID、任意の文字列がユーザーUIDとして登録されます。
Vueプロジェクト側の設定(Firebase連携)
プロジェクトを作った後、該当プロジェクトのダッシュボードページを参照します。
まずFirebaseとの連携について独立ファイルで定義していきます。
import firebase from "firebase/compat/app";
import "firebase/compat/firestore";
import "firebase/compat/auth";
import "firebase/compat/storage";
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: "",
measurementId: "",
};
// init firebase
firebase.initializeApp(firebaseConfig); // バックエンドのfirebaseを初期化する
// init firestore service
const projectFirestore = firebase.firestore(); // firebaseのサービスも初期化する
const projectAuth = firebase.auth();
const projectStorage = firebase.storage();
const timestamp = firebase.firestore.FieldValue.serverTimestamp; //firebaseのtimestamp
export { projectFirestore, projectAuth, projectStorage, timestamp };
export default firebase;
公式サイトで記載されている方法をそのまま試したところエラーが発生していたのですが、これはバージョン違いによるものだと調べて判明したため、今後使う可能性のある他サービスも含め最終的に上記のような記述になっています。
なお、firebaseConfig
にはFirebase側で発行された各値をコピペします。
Vueプロジェクト側の設定(画面コンポーネント・共通コンポーネント)
次に画面側の記述です。
上記のFirebase連携ファイルを元に、必要に応じてインポートする流れになります。
<template>
<div class="login l-login__container u-height--full">
<h1>Logo</h1>
<div class="l-login__contents">
<div class="l-login__section u-mb20">
<v-container>
<v-row class="align-center">
<v-col cols="12" md="4"> ユーザーID </v-col>
<v-col cols="12" md="8">
<v-text-field
v-model="email"
:rules="emailRules"
label="email"
required
></v-text-field>
</v-col>
</v-row>
<v-row class="align-center">
<v-col cols="12" md="4"> パスワード </v-col>
<v-col cols="12" md="8">
<v-text-field
v-model="password"
:rules="passwordRules"
type="password"
label="password"
required
></v-text-field>
</v-col>
</v-row>
<v-row class="align-center">
<v-col cols="12" md="12">
<v-btn class="mx-1" color="primary" @click="userSingIn">
ログイン
</v-btn>
</v-col>
</v-row>
</v-container>
</div>
<div class="l-login__section">
<div><h2>他のアカウントでログイン</h2></div>
<div>Form</div>
</div>
</div>
</div>
</template>
<script>
import firebase from "@/firebase";
export default {
name: "Login",
data: () => ({
valid: false,
// firstname: "",
email: "",
emailRules: [
(v) => !!v || "email is required",
// (v) => v.length <= 10 || "email must be less than 10 characters",
],
password: "",
passwordRules: [
(v) => !!v || "password is required",
// (v) => /.+@.+/.test(v) || "password must be valid",
],
}),
methods: {
userSingIn() {
firebase
.auth()
.signInWithEmailAndPassword(this.email, this.password)
.then(() => {
console.log("login success!");
this.$router.push("/dashboard");
})
.catch(function (error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
console.log(error);
console.log(errorCode);
console.log(errorMessage);
});
},
},
};
</script>
まずログイン画面のソースです。
先ほど定義したfirebase
ファイルをscript内でインポートし、ログインボタンを押した際のuserSingIn
メソッドでfirebase側でのサインイン処理を行なった後、ダッシュボード画面へリダイレクトされる流れになります。
なお各フォーム項目へはVuetifyベースでのバリデーションを実装しています。
<template>
<div class="dashboard">
<Header />
<div class="l-container">
<SideMenu />
<!-- メインコンテンツ -->
</div>
</div>
</template>
<script>
// Firebase認証
import firebase from "@/firebase";
// コンポーネント
import Header from "@/components/Header";
import SideMenu from "@/components/SideMenu";
export default {
name: "Dashboard",
props: [],
components: {
Header,
SideMenu,
},
mounted() {
// ログインチェック
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
console.log("login");
console.log(user);
} else {
console.log("logout");
this.$router.push("/login");
}
});
},
};
</script>
ログイン後の基盤となるダッシュボード画面では、
- 共通コンポーネント(ヘッダー、サイドメニュー)をインポート
- ログイン状態の管理を
mounted
で管理
という状態にします。
これは基本的に他の画面コンポーネントでも同様です。
ログイン状態が継続している場合は処理をせず、何らかの原因でログイン状態が外れている場合はログインページへのリダイレクトを行うという処理になります。
<template>
<!-- ヘッダーメニュー -->
<header class="l-header">
<div class="l-header_contents">
<div class="l-header_contentsTitle">Logo</div>
<div class="l-header_contents_menuArea">
<ul class="l-header_contents_menu">
<li>[アカウントID]様</li>
<li>
<v-btn text to="/mypage">マイページ</v-btn>
</li>
<li>
<v-btn text to="/mypage/edit_profile"> プロフィール変更 </v-btn>
</li>
<li>
<v-btn outlined @click="logout">ログアウト</v-btn>
</li>
</ul>
</div>
</div>
</header>
</template>
<script>
import firebase from "@/firebase";
export default {
name: "Header",
props: [],
data() {
return {};
},
mounted() {
// ログインチェック
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
console.log("login");
console.log(user);
} else {
console.log("logout");
this.$router.push("/login");
}
});
},
methods: {
logout() {
firebase.auth().signOut();
console.log("logout complete");
this.$router.push("/login");
},
},
};
</script>
(サイドメニューではログイン機能を管理していないため省略します)
ログイン/ログアウト処理についてはヘッダコンポーネントで行っています。
ダッシュボードと同じくfirebase
ファイルをインポートし、mounted
についてもほぼ同様です。
ログアウトボタンを押した際に@click
メソッドを実装し、logout
メソッドを通じてFirebase側でのログアウト(サインアウト)を行います。
そのままログイン画面へリダイレクトされる流れになります。