当サイトは、一部記事に広告を含みます

Next.js(App Router)×TypeScriptでログイン機能を実装する(Google認証)|Firebase Authentication

Next.js(App Router)×TypeScriptで
FirebaseのAuthenticationを使ってGoogleログイン認証を実装した時の手順。

ちなみに「メール認証」編はこちら👇

あわせて読みたい

Next.js(App Router)×TypeScriptでログイン機能を実装する(Google認証)|Firebase Authentication

前提

  • Next.js:プロジェクト作成済み
  • Firebase : プロジェクト作成済み
  • フォームのUI:実装済み

Firebase AuthenticationでGoogle認証を実装する方法

アプリ追加〜初期化まですでに実施済みの場合は、ログインとログアウト機能を実装するまでスキップ🏃‍♀️💨

Firebaseでアプリを追加

プロジェクトの設定>全般タブ から「アプリの追加」をクリック。
下記は左から3つ目を選択。(今回はWebアプリなので)

  • Firebase SDK の追加
  • Firebase CLI のインストール

手順通りに進め、上記を実施。

※うろ覚えなので次回実施時に修正するかも。firebase init含めそれ以降は実施不要。

.env.localに必要情報を記述

上記を行うと下記の値が表示されるので.env.localに記述する

NEXT_PUBLIC_FIREBASE_API_KEY="xxxxxxx"
FIREBASE_AUTH_DOMAIN="xxxxxxx.firebaseapp.com"
FIREBASE_PROJECT_ID="xxxxxxx"
FIREBASE_STORAGE_BUCKET="xxxxxxx.firebasestorage.app"
FIREBASE_MEASUREMENT_ID="xxxxxxx"
FIREBASE_SENDER_ID="xxxxxxx"
FIREBASE_APP_ID="xxxxxxx"

FIREBASE_API_KEYだけ”NEXT_PUBLIC_”をつけている理由

クライアントサイドでAPI KEYの値が参照できず、下記エラーとなるため。

FirebaseError: Firebase: Error (auth/invalid-api-key).

✍️その他の変数も、クライアントサイドで利用する場合は適宜NEXT_PUBLIC_を付与する。

✍️ただしNEXT_PUBLIC_ をつけた環境変数はクライアント (ブラウザ) にも送信され、誰でも見れる状態になる。機密情報 (APIキーやDB接続情報など) を含む環境変数には使わないこと。

Firebase を初期化する

service/firebase.js

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

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_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
  measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);

ログインとログアウト機能を実装する

service/firebase.js に下記を追記

export const auth = getAuth(app);
export const db = getFirestore(app);
export const provider = new GoogleAuthProvider();

export const signInWithGoogle = () => {
  signInWithPopup(auth, provider).then((result) => {
    console.log(result);
  });
};
export const signOutWithGoogle = () => {
  auth.signOut();
};

ログインボタンを実装する

components/auth/GoogleLogin.tsx に下記を記述
router.push("/");でログイン後のリダイレクト先を設定

"use client";
import Image from 'next/image'
import { signInWithGoogle } from '../../service/firebase'
import { useRouter } from 'next/navigation';

function GoogleLogin() {
    const router = useRouter(); 
    const handleSignIn = async () => {
        await signInWithGoogle();
        router.push("/");
    };
    
  return (
    <button onClick={handleSignIn}>
        <Image src={"/img/web_light_rd_SU@2x.png"} alt={"Googleでログインする"} width={358} height={80} />
    </button>
  )
}

export default GoogleLogin

ログアウトボタンを実装する

components/auth/Logout.tsx に下記を記述
router.push("/login");でログアウト後のリダイレクト先を設定

"use client";
import { signOutWithGoogle } from '@/service/firebase';
import { useRouter } from 'next/navigation';
import React from 'react'

function Logout() {
    const router = useRouter(); 
    const handleSignOut = async () => {
        await signOutWithGoogle();
        router.push("/login");
    };
    
  return (
    <button onClick={handleSignOut}>ログアウト</button>
  )
}
export default Logout

ログイン・ログアウト状態を判定し、表示分けをする※

react-firebase-hooks をインストール

npm i react-firebase-hooks

ログインしている時だけフッターコンポーネントを表示するコードはこんな感じになる。
useAuthState はクライアントサイドでしか使えない

"use client"
import Footer from '@/components/layout/Footer';
import { auth } from '@/service/firebase';
import React, { ReactNode } from 'react'
import { useAuthState } from 'react-firebase-hooks/auth';

function AsyncLayout({ children }: { children: ReactNode }) {
    const [user] = useAuthState(auth); // ログイン判定
    const containerClassName = user ? "login" : "logout"

  return (
    <>
    <div className={containerClassName}>
      {children}
      { user ? <Footer /> : "" }
    </div>
    </>
  )
}
export default AsyncLayout

※【追記】この方法だとクライアントサイドでしか使えないようなので、サーバサイドでもログイン管理するためには他の手段の検討が必要そう・・・なので、このやり方は却下になるかも?
まだそこまで実装が進んでいないので、のちほど検討したいと思います・・・!

以上です!

---

記事が気に入ったら、ぜひ応援いただけると更新がんばれます☕️

Buy Me A Coffee