🔥

Next.jsとTypeScriptでFirebaseのGoogle authを実装する

commits5 min read 2

備忘録としてまとめます。

完成したものは下の URL においています。

https://github.com/mochi-sann/nextjs-and-Firebase-auth-with-google

Nextjs のセットアップ

yarn create next-app my-app --example with-typescript

ファイルを移動

mkdir src
mv  pages src
mv  components src
mkdir src/lib

あとtsconfig.json

tsconfig.json
"baseUrl": ".",
"paths": {
  "~/*": ["./*"]
},

を追加しておく

Firebase で新規プロジェクトをつくる

https://firebase.google.com

で新規プロジェクトを作って

プロジェクト設定 > 全般から API key などを.env.local に下のようにコピーします。
************のところはそれぞれ対応する値に置き換えてください。

.env.local
NEXT_PUBLIC_FIREBASE_API_KEY=************
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=************
NEXT_PUBLIC_FIREBASE_DETABASE_URL=************
NEXT_PUBLIC_FIREBASE_PROJECT_ID=************
NEXT_PUBLIC_FIREBASE_STORAGE_BAKET=************
NEXT_PUBLIC_FIREBASE_MESSAGE_SENDER_ID=************
NEXT_PUBLIC_FIREBASE_APP_ID=************
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=************

それができたら

yarn add firebase

で firebase 関連のパッケージを追加

Next.js のファイルにコードを追加

つぎに、以下の Nextjs ファイルにコードを追加していきます。

src/lib/firebase.ts
import firebase from "firebase/app";
import "firebase/auth"; // If you need it

export const config = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL,
  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,
};

!firebase.apps.length ? firebase.initializeApp(config) : firebase.app();

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

export const Login = () => {
  const provider = new firebase.auth.GoogleAuthProvider();
  firebase
    .auth()
    .signInWithPopup(provider)
    .then(function (result: any) {
      return result;
    })
    .catch(function (error) {
      console.log(error);
      const errorCode = error.code;
      console.log(errorCode);
      const errorMessage = error.message;
      console.log(errorMessage);
    });
};

// ログイン状態の検知
export const listenAuthState = (dispatch: any) => {
  return firebase.auth().onAuthStateChanged(function (user) {
    if (user) {
      // User is signed in.
      dispatch({
        type: "login",
        payload: {
          user,
        },
      });
    } else {
      // User is signed out.
      // ...
      dispatch({
        type: "logout",
      });
    }
  });
};

export const firebaseUser = () => {
  return firebase.auth().currentUser;
};

// Logout
export const Logout = () => {
  auth.signOut().then(() => {
    window.location.reload();
  });
};
src/lib/AuthContext.ts
import React from "react"
const AuthContext = React.createContext({})
export default AuthContext
src/pages/_app.tsx
import React, { useReducer, useEffect } from "react";

import { AppProps } from "next/app";

import AuthContext from "~/src/lib/AuthContext";
import authReducer from "~/src/lib/authReducer.ts";
import { listenAuthState } from "~/src/lib/firebase";

function MyApp({ Component, pageProps }: AppProps) {
  const [state, dispatch] = useReducer(
    authReducer.reducer,
    authReducer.initialState
  );
  useEffect(() => {
    return listenAuthState(dispatch);
  }, []);
  return (
    <AuthContext.Provider value={state}>
      <Component {...pageProps} />
    </AuthContext.Provider>
  );
}

export default MyApp;

src/lib/authReducer.ts
const initialState = {}

const reducer = (state: any, action: any) => {
  switch (action.type) {
    case "login":
      return action.payload.user
    case "logout":
      return initialState
    default:
      return state
  }
}

export default {
  initialState,
  reducer,
}

最後にsrc/pages/index.tsxを修正すれば閑静です

src/pages/index.tsx
// import Link from "next/link";
import Layout from "../components/Layout";
import { Login, Logout, auth } from "~/src/lib/firebase";

const IndexPage = () => (
  <Layout title="Home | Next.js + TypeScript Example">
    <h1>Hello Next.js 👋</h1>
    <div>
      <button onClick={() => Login()}>ログイン</button>
      <button onClick={() => Logout()}>ログアウト</button>
    </div>
    <div>
      <pre>
        {auth.currentUser
          ? auth.currentUser.displayName + "でログインしています"
          : "ログインしていません"}
      </pre>
    </div>
  </Layout>
);

export default IndexPage;

完成

ログインしているときはこのように

未ログイン時はこのように表示されると思います