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

React又はNext.jsでFirebaseを使ったユーザー認証(Authentication)をするためには、認証の後に、UseEffectとonAuthStateChangedを使ってサインアップ(及びログイン)状態を管理する方法が考えられますが、コードはやや複雑になります。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認証を追加する例です。

Email/Password認証は私のこちらの記事「メールとパスワードによるユーザー認証の実装方法 – Firebase + React」で詳しく紹介しています。


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行は不要です。このproviderの部分も、先ほどのauthと同様にコンフィギュレーションファイルで設定せず、各コンポーネントに直接記述する方法でも問題なく動きます。

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はグローバルかつリアルタイムにユーザーのログイン状態を保持します。useAuthStateをなんらかの事情で使えない(使いたくない)場合は、firebase/authからonAuthStateChangedをインポートし、useAuthStateとほぼ同じ機能のカスタムフックを作成することでも同じ機能を実装可能です(但し、loadingとerrorの機能はない。公式ドキュメントはこちら)。

サインアップの関数

google認証のサインアップではsignInWithPopupを使用しています。関数名にsignInとありますが本件サインアップ(初回のユーザー登録)にもサインイン又はログイン(2回目以降の認証)にも同じ関数を使います。

signInWithPopupでは文字通りポップアップが開いてユーザー認証を求める画面が開きます。関数の引数は2つあり、1番目はauthオブジェクト、2番目はGoogleAuthProvierオブジェクトのproviderです(これだけサインアップ完了)。.thenでエラーを処理します。ポップアップではなくgoogleのログインのページを経由するリダイレクトととしたい場合は、signInWithRedirectをインポートして同様に使うこともできます。

サインインに伴う処理が多い場合には、.thenと繋げずにasync + awaitの非同期処理のtry catchブロックでsignInWithinPopupを使用することも可能です。前述の私のこちらの記事「メールとパスワードによるユーザー認証の実装方法 – Firebase + React」では、try catchブロックを使用していますので参考にして頂ければ幸いです。

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

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

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

サインアウト機能

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

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

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