🌟

Next.js(TypeScript)で Firebase を利用し, Google ログインを

6 min read

はじめに

この記事では, Firebase Authentication を使って TypeScript を使用した Next アプリに Google ログインを実装する方法を記述します.
JavaScript を使用した Next アプリに Google ログインを実装する方法はこちらに記述しています.

この記事が他の人の参考になれば幸いです.
また, この記事の内容に間違った記載がありましたら, 指摘してもらえるとありがたいです.

環境

名前 バージョン
macOS Big Sur 11.4
Node.js 16.4.1
TypeScript 4.3.5
React 17.0.2
Next.js 11.0.1

適当な Next アプリの作成

以下のコマンドで auth-example という Next アプリを作成しました.
このアプリに Google ログインを実装していきます.

terminal
npx create-next-app --ts --use-npm auth-example

Firebase の設定

以下の流れで Firebase の設定を行っていきます.

  1. Firebase プロジェクトの作成
  2. Authentication の有効化
  3. Google プロバイダの有効化
  4. (任意)承認済みドメインの設定
  5. ウェブアプリの追加

Firebase console にアクセスし, Firebase プロジェクトを作成します. 適当なプロジェクト名を入力し, プロジェクトを作成します. Google アナリティクス の設定は任意です.

プロジェクトの作成後, 左側のナビゲーションから Authentication のページに飛び, [始める]ボタンで Authentication を有効にします.

その後, Authentication の Sign-in method タブで Google プロバイダを有効にします. プロジェクトの公開名, プロジェクトのサポートメールは適当なものを設定し, 保存します.

また, Sign-in method タブのページ下部で承認済みドメインの設定ができます.
今回は変更しません.

左側のナビゲーションからプロジェクトの概要に飛び, </> のボタンでウェブアプリを追加します. 適当なアプリのニックネームを入力し, アプリを登録してください.
その後, Firebase SDK の追加 で表示されるソースコードは, Next アプリで使用するので, コピーして保存しておきます.

Next アプリでの Google ログインの実装

以下の流れで Next アプリに Google ログインを実装します.

  1. 環境変数ファイル .env.local の作成
  2. Firebase ライブラリのセットアップ
  3. ログイン処理の実装

環境変数ファイル .env.local の作成

プロジェクトディレクトリに.env.local ファイルを作成し, 環境変数を設定します.
先ほどコピーしたソースコードの firebaseConfig 変数のプロパティ値を環境変数として読み込むために以下のように記述します.
NEXT_PUBLIC_ のプリフィックスはクライアントで動作する Next アプリが環境変数を読み込むために必要です. また, Google アナティクスの有効無効によって, 設定する環境変数の数が以下の場合と異なる可能性があります.

.env.local
NEXT_PUBLIC_FIREBASE_API_KEY=<apiKey>
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=<authDomain>
NEXT_PUBLIC_FIREBASE_PROJECT_ID=<projectId>
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=<storageBucket>
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=<messagingSenderId>
NEXT_PUBLIC_FIREBASE_APP_ID=<appId>

Firebase ライブラリのセットアップ

以下のコマンドで firebase ライブラリをインストールします.

terminal
npm i firebase

インストール後, 任意のファイル(この記事では lib/firebase.ts)で firebase ライブラリのセットアップを行います.

lib/firebase.ts は以下のように記述しました.
環境変数から Firebase の設定情報を読み込み, firebase ライブラリを初期化しています.
また, 再レンダリングなどの際に, 初期化が複数回行われないように if 文で条件分岐させています.

lib/firebase.ts
import firebase from "firebase/app";
import "firebase/auth";

const firebaseConfig = {
    apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
    authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
    projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
    storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
    messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
    appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};

if (firebase.apps.length === 0) {
    firebase.initializeApp(firebaseConfig);
}

export const auth = firebase.auth();
export default firebase;

ログイン処理の実装

実際のログイン処理を実装します.

この記事では, ユーザの認証関連の情報を useContext を使用し, グローバルで扱います.
そのため, lib/AuthContext に認証関連の情報を扱うコンテキストを, pages/_app.js にコンテキストを読み込む処理を, pages/index.js にログイン, ログアウト関数の呼び出しを記述します.

以下の流れで実装していきます.

  1. lib/AuthContext.tsxAuthContext の作成
  2. pages/_app.tsxAuthContext の読み込み
  3. pages/index.tsxAuthContext の使用

lib/AuthContext.tsxAuthContext の作成

lib/AuthContext.tsx を作成し, 以下のように記述しました.

createContext で ユーザ, ログインやログアウトを扱う関数を保持する AuthContext を作成しています. AuthContext はカスタム Hook useAuth を通して使用できます.
login 関数の auth.signInWithRedirect 関数でリダイレクトでログインする方法を使用していますが, ポップアップを表示し, ログインする方法も存在します.

lib/AuthContext.tsx
import { createContext, useState, useEffect, useContext } from "react";
import { User } from "@firebase/auth-types";

import firebase, { auth } from "../lib/firebase";

type AuthContextType = {
    currentUser: User | null;
    login?: () => Promise<void>;
    logout?: () => Promise<void>;
};

const AuthContext = createContext<AuthContextType>({ currentUser: null });

export const useAuth = () => {
    return useContext(AuthContext);
};

type Props = {
    children?: JSX.Element;
};

const AuthProvider = ({ children }: Props): JSX.Element => {
    const [currentUser, setCurrentUser] = useState<User | null>(null);
    const [isLoading, setIsLoading] = useState(true);

    const login = () => {
        const provider = new firebase.auth.GoogleAuthProvider();
        return auth.signInWithRedirect(provider);
    };

    const logout = () => {
        return auth.signOut();
    };

    useEffect(() => {
        return auth.onAuthStateChanged((user: User | null) => {
            setCurrentUser(user);
            setIsLoading(false);
        });
    }, []);

    const value: AuthContextType = {
        currentUser,
        login,
        logout,
    };

    return (
        <AuthContext.Provider value={value}>
            {isLoading ? <p>Loading...</p> : children}
        </AuthContext.Provider>
    );
};

export default AuthProvider;

pages/_app.tsxAuthContext の読み込み

pages/_app.tsx を以下のように変更しました.

<AuthProvider><Component> の親コンポーネントにすることで全てのコンポーネントから AuthContext の情報にアクセスできるようにしています.

pages/_app.tsx
import '../styles/globals.css'
import type { AppProps } from 'next/app'
import AuthProvider from "../lib/AuthContext";

function MyApp({ Component, pageProps }: AppProps) {
  return (
      <AuthProvider>
        <Component {...pageProps} />
      </AuthProvider>
  )
}

export default MyApp

pages/index.tsxAuthContext の使用

pages/index.tsx を以下のように変更しました.

これは useAuth を使って AuthContext を利用する一つの例です.
currentUser の値でユーザがログインしているかどうかの判別ができます.

pages/index.tsx
import styles from '../styles/Home.module.css'
import {useAuth} from "../lib/AuthContext";

export default function Home() {
const {currentUser, login, logout} = useAuth()

  return (
    <div className={styles.container}>
      <main className={styles.main}>
          {!currentUser && <button onClick={login}>ログイン</button>}
          {currentUser &&
          <div>
              <p>{currentUser.email} でログイン中</p>
              <button onClick={logout}>ログアウト</button>
          </div>}
      </main>
    </div>
  )
}