React + Firebaseを使ったユーザー認証の実装方法

React又はNext.jsでFirebaseを使ったユーザー認証(Authentication)をするためには、UseEffectとonAuthStateChangedを使ってサインイン(ログイン)機能を実装し、Reduxを使いユーザーのログイン状況(State)を管理する方法が一般的ですが、コードは長く複雑になります。Firebaseとreact-firebase-hooksというカスタムフックを使うことで、ユーザー認証機能をずっとシンプルに実装できますので、はその方法を具体的にご紹介します。

Firebase側の初期設定

React-firebase-hooksを使いユーザー認証機能を実装するためには、firebaseのサイト内とローカルのプロジェクトで下記6つの初期設定を要します。

  1. Firebaseに新規のプロジェクトを追加
  2. プロジェクト内でアプリを登録
  3. 登録したアプリに係る構成(configuration)を取得(コピー)
  4. ローカルのプロジェクト内にfirebase.jsを作成し、3の構成情報を張り付け
  5. Firebaseのプロジェクト内でAuthenticationアプリを設定
  6. Firebase AuthenticationでのAuthorizedドメインの設定

上記のうち1,2,3,4は、以前のこちらの記事「FirebaseでWebアプリをデプロイする方法」の、1「Firebaseのサイト上での初期設定」と8「Firebaseプロジェクト内でのアプリの登録方法」で具体的な方法をご紹介していますので、そちらをご参照頂ければ幸いです。ここでは5,6のステップについてご紹介します。

Authenticationの設定

Firebaseで前掲1のステップで作成したプロジェクトの中に入ると、下記のような画面になりますので、左側サイドバーにあるAuthenticationをクリック、続く画面でGet startedをクリックしてください。


次の画面ではユーザー認証の方法を選択します。上段のSign-in methodをクリックした後、右上のAdd new providerをクリックします。下記は、Email/Password認証が既にあるところに、Google認証を追加する例です。


Firebase authentication - Add new provider

Add new providerをクリックすると、次ようなモーダルウィンドウが開きますので、Googleをクリックします。



Googleを選択すると下記のような画面になります。右上のトグルでEnable(有効)を選択、中央下段に管理用メールアドレス(Project support email)を入力した後に、右下のSave(保存)を押します。Saveの後は自動で上記画面に戻り、Googleの部分がEnableとなっていれば完了です。


Authorizedドメインの設定

Firebaseでホスティングをする場合は本項目は自動で設定されますが、Firebase以外でホスティングをする場合、又は、独自ドメインを取得した場合などはAuthenticationに係るドメインの設定を要します。FirebaseのサイドバーからAuthenticationを選択、中央画面から右のsettingsを選択すると、左下にAuthorized domainsがあると思います。ここをクリックすると画面の中央に既に有効なドメインが表示されます。右上のAdd domainを押して具体的なドメインを入力します。firebase hostingのアドレスに加えて、開発用のlocalhostはデフォルトで設定されていると思いますので、追加不要です。


firebase authentication authorized domains

ローカルプロジェクト内での設定

次にローカルのプロジェクトフォルダに戻り、Firebaseと接続するための設定をします。前述3と4「登録したアプリに係る構成(configuration)を取得(コピー)」「ローカルのプロジェクト内にfirebase.jsを作成し、3の構成情報を張り付け」のステップは必須です。加えて、今回はgmailによるログインですので、4の工程で作るfirebase.jsの下段は次のようにします。

initializeAppの部分でfirebaseとの接続を確保し、その内容をappに格納しますが既に接続が確立されている場合に備えて、3項演算子のif文で処理を分岐させます。getApps().lengthがnullの場合は初期化し、getApps().lengthが存在する場合は既存の接続を改めてappに格納します。2021年前半以前はこの処理は必須でしたが、最近この分岐処理がなくても正常に動くように思います。その場合は、単にconst app = initializeApp(firebaseConfig);とします。

getAuthオブジェクトの引数にはappを格納しています。firebaseの公式サイトでは、appを引数として渡す記述と渡さない記述が混在しており、私が試した限りいずれでも動きます。また、getAuthを本件firebase.jsのようなコンフィギュレーションファイルで設定せず(appをエクスポート)、各コンポーネントでgetAuth()を呼び出す方法も公式ドキュメントでは紹介されています。

下段のconst providerの部分は、Google以外にFacebooklなどプロバイダーのログイン方法を実装する場合は、const GoogleproviderのようなGoogle認証とわかる名称にしてexportします。また、firestoreが不要であればdbに関する2行は不要です。

firebase.js

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

const firebaseConfig = {
// configの内容を貼り付け
};

const app = !getApps().length ? initializeApp(firebaseConfig) : getApps();
const auth = getAuth(app);
const db = getFirestore(app);
const provider = new GoogleAuthProvider();


export { auth, provider };
export default db;

続いてFirebaseをインストールします。1・2行目はプロジェクト毎に必要ですが、3行目はPCにグローバルにインストールするツールですので、1回インストールすれば以降不要です。

npm install firebase
npm install react-firebase-hooks
npm install -g firebase-tools

App.js本体のコード

次のコードが最上流コンポーネント(App.js)本体です。ここでユーザー認証機能を実装していますが、login.jsなど別のコンポーネントでも問題ありません。ログインしていないユーザーに対しては、ログインを求めるボタンが表示され、ログイン済みのユーザーに対しては別のコンポーネントとして用意したChildAppの内容が表示されます。ChildAppの名称は任意です。

App.js

import ChildApp from "./ChildApp";
import { auth, provider } from "./firebase";
import { useAuthState } from "react-firebase-hooks/auth";

function App() {
const [user, loading, error] = useAuthState(auth);

const signIn = () => {
signInWithPopup(auth, provider).catch((err) => alert(err.message));
};

return (
< div className="app">
{user ? (
< Childapp />
) : (
< button onClick={signIn}>Sign in< /button>
)}
< div>
);
}

App.jsは(1)インポート、(2)カスタムフックのuseAuthState()、(3)サイイン関数、(4)JSX内のユーザーの状態に応じた表示の4つの部分から構成されます。

インポート

1行目のChildAppは先ほど述べた通り、ログイン済みのユーザーに対して表示する内容(ページ)です。2行目では、firebase.jsで作成ししたfirebaseの認証オブジェクトauthと、同google認証オブジェクトproviderをインポートしています。3行目で今回最も活躍するカスタムフックのuseAuthState関数をインポートしています。

useAuthState()関数

useAuthState()関数を呼び出し、ユーザーの状態を保持するuserと、ロードの状態を保持するloading、ログイン時のエラーの状態を保持するerrorの3つを宣言します。引数にはfirebaseの認証オブジェクトauthを渡します。useAuthStateはグローバルかつリアルタイムにユーザーのログイン状態を保持するので、ユーザー認証にReduxが不要になります。

サインイン関数

サインインではsignInWithPopupを使用しています。文字通りポップアップが開いてユーザー認証を求める画面が開きます。authオブジェクトとuseAuthStateが全てを管理してくれるので、ここに具体的なサインイン時の処理(.then)を記述する必要がなく、エラーをキャッチするだけのシンプルな関数です。なお、ポップアップではなくgoogleのログインのページを経由するリダイレクトととしたい場合は、signInWithRedirectをインポートして同様に使うこともできます。

JSX内のユーザーの状態に応じた表示部分

JSXの部分ではif文と同じ働きをする3項演算子を使用しています。useAuthStateで状態管理されているuserが真の場合、すなわちログイン済みのときはChildAppコンポーネントを表示し、userが偽の場合、すなわちログインしていないときはChildAppの内容を非表示としログインを求めるボタンだけを表示させています。

{ 条件 ? 真の場合の処理 : 偽の場合の処理 }

サインアウト機能

最後にサインアウト機能をご紹介します。サインアウトはメインページ内(本稿の事例ではChildApp)に設けるということが多いと思います。ChildApp内で、firebase.jsで作成したauthをインポートし(前述インポートを参照)、下記のようなボタンを配置します。また、signOutも前述signInのインポートに準じて、”firebase/auth”からインポートします。サインアウトの実装には、onClickの部分でsignOut関数を呼び出し実行することで十分です(厳密にはsignOut関数を持つ無名関数)。Reduxを使わずしてユーザーのログイン/ログアウトの状態を保持・管理・変更することが可能です。

< button onClick={() => signOut(auth)}>Sign Out< /button>

React-firebase-hooksを使ったユーザー認証の実装方法をご紹介いたしました。今日も最後まで読んで頂きありがとうございました。