メールとパスワードによるユーザー認証の実装方法 – Firebase + React

Firebase上のAuthenticationには様々な方法が提供されていますが、本稿では比較的簡単なメールとパスワードによるユーザー認証(サインアップ・ログイン)の方法をご紹介します。ユーザー認証にFirebaseを使うメリットとしては、(1)バックエンドサーバとdbの構築が不要、(2)React側で特別なグローバルな状態管理をせずともユーザーのログイン状態をグローバルに保持できる、(3)ユーザーのパスワードの管理が不要(管理者でも見れない)、の3つがございます。

本稿での設定の前提として、1.Firebaseプロジェクト内でアプリの登録があることと、2.Firebase上でAutheticationの設定が済んでいることの2点が必要です。前段については私のこちらの記事「FirebaseでWebアプリをデプロイする方法」の1章「Firebaseのサイト上での初期設定」と8章「Firebaseプロジェクト内でのアプリの登録方法」をご覧ください(2章-7章はスキップ)。後者については私のこちらの記事「React + Firebaseを使ったユーザー認証の実装方法」の2章「Authenticationの設定」を参考に、emailとパスワードによる認証を有効にしてください。

Firebaseのconfigファイル

前述の私の別の記事にも記載しましたが、使用するconfigファイルは次のようなものです。こちらのファイルをReactプロジェクトのApp.jsと同じ階層に配置します。ファイル名はfirebase.js、またはfirebase.config.jsとすることが一般的です。

firebase.js

import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";

const firebaseConfig = {
apiKey: "adfasfavnaAJFDKCnjkasjlkASJLFNKSA",
authDomain: "myapp-d92bc.firebaseapp.com",
projectId: "myapp-d92bc",
storageBucket: "myapp-d92bc.appspot.com",
messagingSenderId: "15645156456156",
appId: "1:15645156456156:web:1516532e652c5459f5f6b44",
};

const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);

export { auth };
export default db;

configファイルの始めの3行で必要なライブラリーをインストールします。initializeAppは文字通りの役割です。2行目のgetAuthはログインユーザーのAuthではなく、クライアント側からfirebaseのAuthenticationサーバーと接続に使うAuthです。3行目のgetFirestoreはfirestoreとの接続に使用します。本稿でご紹介するユーザー認証にfirestoreは不要ですが、実際のアプリケーションではユーザー認証とfirestoreをセットで使うことが多いです。firebaseConfigオブジェクトの内側は、firebaseでアプリを登録したときに表示される値を貼り付けてください(本稿の値は架空のものです)。

下段のinitializeApp()関数にて、firebaseConfigを引数としてappオブジェクトを取得します。続く2行では、それぞれgetAuthとgetFirestoreのオブジェクトをauthとdbに格納します。公式ドキュメンテーションではconfigファイルでgetAuth()を呼び出さず、各コンポーネントで関連する関数を呼び出す都度、getAuth()を呼び出しています。

最後の2行でauthとdbをエクスポートしています。いずれがdefault exportでも問題ありません。

必要なライブラリーのインストール

続いて必要なライブラリーをnpm installします。1行目はfirebaseに不可欠です。2行目のreact-router-domは複数ページを作成するためのライブラリーです。ユーザー認証とは直接関係ないですが、本稿の事例ではユーザーのログイン後に画面を切り替える(リダイレクトする)ために使用します。3行目のfirebase-toolsはグローバルにインストールし、firebaseでのホスティングに使用します。グローバルなので一度インストールすれば不要、かつ、firebaseでホスティングしない場合は不要です。

npm install firebase
npm install react-router-dom
npm install -g firebase-tools

SignUpコンポーネント

サインアップ(ユーザーによる初回登録)のためのコンポーネントは次の通りです。ユーザーはユーザー名、email、パスワードを入力し、最後にサインアップのボタンを押すことでfirebaseに情報が保存されます。以下順に解説いたします。

SignUp.js

import { useState } from "react";
import { useNavigate } from "react-router-dom";
import {
updateProfile,
createUserWithEmailAndPassword,
} from "firebase/auth";
import { auth } from "../firebase.config";

function SignUp() {
const [formData, setFormData] = useState({
name: "",
email: "",
password: "",
});
const { name, email, password } = formData;
const navigate = useNavigate();

const onChange = (e) => {
setFormData({
...formData,
[e.target.id]: e.target.value,
});
};

const onSubmit = async (e) => {
e.preventDefault();
try {
const userCredential = await createUserWithEmailAndPassword(
auth,
email,
password
);
updateProfile(auth.currentUser, {
displayName: name,
});
navigate("/");
} catch (error) {
console.log(error);
}
};

return (
<div>
<form onSubmit={onSubmit}>
<input
type="text"
placeholder="Name"
id="name"
value={name}
required
onChange={onChange}
/>
<input
type="email"
placeholder="Email"
id="email"
value={email}
required
onChange={onChange}
/>
<input
type="password"
placeholder="Password"
id="password"
value={password}
required
onChange={onChange}
/>
<button type="submit">
Submit
</button>
</form>
</div>
);
}

export default SignUp;

useState

Firebaseにユーザー名、email、パスワードをアップロードする前提として、ローカルステート(ローカルの状態)にユーザーからの入力を保存します。それぞれに3本のuseStateを用意してもいいのですが、本事例では1つのuseStateに1つのオブジェクトとして3つの値を保持する方法をとっています。それぞれの初期値は空のテキストです。

const [formData, setFormData] = useState({
name: "",
email: "",
password: "",
});

1つのformDataオブジェクトから、name、email、passwordそれぞれの値を取り出して保存します。destructure(分割代入)と呼ばれます。

const { name, email, password } = formData;

useNavigate

ユーザー登録(サインアップ)が完了してログイン後、画面を遷移(リダイレクト)させるためにuseNavigateを使用します。useNavigateの詳細他、React routerの使い方については私のこちらの記事「Reactで複数のページを作る方法 – Router v6.0準拠」をご覧ください。

const navigate = useNavigate();

onChange

ユーザーからの入力をローカルステートであるuseStateに保存するためのonChange関数を定義します。onChangeはinputタグの属性名ですが、関数名と一致させることが一般的です。inputタグの現在の状態が保存されるeを引数とします。setFormDataは先ほど定義したuseStateの状態を更新させる関数です。formDataはオブジェクトなので、引数もオブジェクトとします。ドットが3つ続く…formData部分で、現在の状態をそのままコピーします。その上で次の行で現在入力中の要素(例えばユーザー名)の部分だけを上書きします。各inputタグのidには、formDataのキー名と同じname、email、passwordを付したので、[e.target.id]とすることid名、すなわちformDataのキー名を指定して、e.target.valueの部分でユーザーの入力をローカルステートに保存・更新できます。なおuseStateを1つにまとめず、3つに分割した場合は、onChangeもそれぞれに対応するために3本必要です。onChangeの注意すべき挙動については私のこちらの記事「FormでのuseRefの使い方」もご覧ください。

const onChange = (e) => {
setFormData({
...formData,
[e.target.id]: e.target.value,
});
};

onSubmit

formタグの属性としてonSubmitがあります。同じ名前の関数を定義し、ユーザーがボタンを押したときの処理を記述します。まず、firebaseへのアップロードの部分で非同期処理が発生するのでonSubmit全体をasync関数とします。e.preventDefault();の部分は、ユーザーがsubmitしたときの画面のリフレッシュを防ぐためのコードです。

firebaseへの登録が失敗する可能性も考慮して、try・catchの構造をとります。tryの内側がfirebaseにサインアップに係る情報(emailとpassword)をアップロードする部分です。

createUserWithEmailAndPasswordというのは本コンポーネントの上段でインポートしたfirebaseの関数です。第一引数のauthはfirebaseのconfigファイルで作成してエクスポート、本コンポーネントの上段でインポートしたものです。クライアントからfirebaseに接続するための情報が入っております。2番目の引数はemail、3番目の引数はパスワードと決まっています。

ユーザーにはユーザー名も入力頂きましたが、ユーザー名はupdateProfileから続く3行でdisplayName属性に別途保存します。displayName以外にもphotoUrlがユーザーに関連する情報としてfirebaseのAuthentication機能の範囲内で格納できます。それ以上のユーザーに関連する情報を保存するためにはfirestoreを使う必要があります。本稿では詳細を書きませんが、firestoreにユーザーに関連する情報を保存するときは、Authenticationへの登録を済ませた後、firebaseから自動で生成されるidをfirestore内でユーザー情報を保存するためのドキュメントのidとして利用すること(RDBでいう外部キーの設定をすること)がポイントです。firestore一般については私のこちらの記事「React + Firestore【入門から実装まで】」を参考にしてください。

以上の処理が無事に完了した場合には、navigate(“/”);の部分で、ユーザーをルートディレクトリー(ホーム画面)にリダイレクトさせます。

catchブロックではエラーを処理します。本事例ではコンソル画面にエラーの内容を表示させていますが、実際にはユーザーに表示するなど他の処理を記述します。

const onSubmit = async (e) => {
e.preventDefault();
try {
const userCredential = await createUserWithEmailAndPassword(
auth,
email,
password
);
updateProfile(auth.currentUser, {
displayName: name,
});
navigate("/");
} catch (error) {
console.log(error);
}
};

return – jsx

最後はJSXの部分です。全体をdivタグで包み、内側にはformを置きます。先ほど説明したようにformにはonSubmit関数を呼び出すonSubmit属性を付けます。inputタグは同じ構造をしたものを、name、email、passwordに対応して3つ並べます。inputタグのそれぞれの属性の意味は、本稿をご覧になっている方には説明不要だ思いますので割愛します。onChange属性では、先ほど定義した同じ名前のonChange関数を呼び出します。formタグの最後にボタンを配置し、formに入力された情報をsubmitする(onSubmit属性をトリガーする)機能を持たせます。

return (
<div>
<form onSubmit={onSubmit}>
<input
type="text"
placeholder="Name"
id="name"
value={name}
required
onChange={onChange}
/>
<input
type="email"
placeholder="Email"
id="email"
value={email}
required
onChange={onChange}
/>
<input
type="password"
placeholder="Password"
id="password"
value={password}
required
onChange={onChange}
/>
<button type="submit">
Submit
</button>
</form>
</div>
);

今日も最後まで読んで頂きありがとうございました。私のこちらの記事「React + Firebaseを使ったユーザー認証の実装方法」ではfirebaseの各Authenticationに共通の設定に加えて、gmailによるサインアップを、こちらの記事「Facebook認証の実装方法 – Firebase + React」ではfacebookによるサインアップを解説していますので、宜しければ併せてご覧になってみてください。