🐡

Next.jsとFirebase(SDK v9)authで匿名ログイン -> Googleログイン永続化のExample

2021/10/24に公開

要約

  • Next.jsを使って、Firebase Authの認証機能を実装
  • 状態管理はrecoilを使った
  • アプリ起動時に匿名ログイン、Googleログインを選択後アカウント移行
  • FirebaseWeb SDK v9(v8と結構違ったので最初びっくりしました)

コード

こちらです。
https://github.com/dl10yr/nextjs-firebase

動作

  1. アプリ起動時に匿名ログインが自動的に走る(isAnnonymusがtrue)

  2. Googleログインボタン押すと、ログインフローへ

  3. ログイン完了後、uidは変化せずにisAnnonymusがtrueからfalseに変化(匿名ログインの永続化)

解説

このコミット見てください!
https://github.com/dl10yr/nextjs-firebase/commit/e1658a473581970dbf5211ae43a3f2a2054bab7e

firebaseClient.ts
import { initializeApp } from 'firebase/app'
import { getFirestore } from 'firebase/firestore/lite'
import { getAuth, GoogleAuthProvider } from 'firebase/auth'

const CLIENT_CONFIG = {
  apiKey: 'XXXXX',
  authDomain: 'XXXXX',
  projectId: 'XXXX',
  storageBucket: 'XXXXX',
  messagingSenderId: 'XXXXXX',
  appId: 'XXXXX',
}

const app = initializeApp(CLIENT_CONFIG)
const firebaseAuth = getAuth(app)

const db = getFirestore(app)
const googleProvider = new GoogleAuthProvider()

export { app, firebaseAuth, db, googleProvider }
_app.tsx
import '../styles/globals.css'
import { useEffect } from 'react'
import { RecoilRoot, useSetRecoilState } from 'recoil'
import { currentUserState } from '../lib/atoms/currentUser'
import { signInAnonymously, onAuthStateChanged } from 'firebase/auth'
import { firebaseAuth } from '../lib/firebase/firebaseClient'

const AppInit = () => {
  const setCurrentUser = useSetRecoilState(currentUserState)
  const fetchSetUser = async () => {
    try {
      onAuthStateChanged(firebaseAuth, async (user) => {
        if (!user) {
          signInAnonymously(firebaseAuth)
            .then(async (e) => {
              if (e.user) {
                setCurrentUser({
                  uid: e.user.uid,
                  displayName: e.user.displayName,
                  isAnonymus: e.user.isAnonymous,
                })
              }
              // eslint-disable-next-line no-console
            })
            .catch(() => {
              // console.log(error)
            })
        } else {
          setCurrentUser({
            uid: user.uid,
            displayName: user.displayName,
            isAnonymus: user.isAnonymous,
          })
        }
      })
    } catch {
      setCurrentUser(null)
    }
  }
  useEffect(() => {
    fetchSetUser()
  }, [])
  return null
}

function MyApp({ Component, pageProps }) {
  return (
    <RecoilRoot>
      <AppInit />
      <Component {...pageProps} />
    </RecoilRoot>
  )
}

export default MyApp
index.tsx
import styles from '../styles/Home.module.css'
import { useCurrentUser } from '../lib/hooks/useCurrentUser'
import { firebaseAuth, googleProvider } from '../lib/firebase/firebaseClient'
import { linkWithPopup } from 'firebase/auth'
import { currentUserState } from '../lib/atoms/currentUser'
import { useSetRecoilState } from 'recoil'

export default function Home() {
  const { currentUser } = useCurrentUser()
  const setCurrentUser = useSetRecoilState(currentUserState)

  const loginWithGoogle = () => {
    linkWithPopup(firebaseAuth.currentUser, googleProvider)
      .then((result) => {
        const user = result.user
        setCurrentUser({
          uid: user.uid,
          displayName: user.displayName,
          isAnonymus: user.isAnonymous,
        })
      })
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      .catch((error) => {
        // Handle Errors here.
      })
  }
  return (
    <div className={styles.container}>
      <main className={styles.main}>
        <button className="bg-gray-100 p-5" onClick={() => loginWithGoogle()}>
          Googleログイン
        </button>
        <div className="w-full break-all">{JSON.stringify(currentUser)}</div>
      </main>
    </div>
  )
}

Discussion