Firebase (3) - Google Provider Authentication -


Firebase Typescript React Node.js

Firebase Authenticationを使って、GoogleアカウントでログインできるAPIを利用してみた

Firebaseの設定

いつもどおりFirbeaseのプロジェクトからマイアプリ(Web)を登録してfirebaseConfigを取得する。
んでAuthenticationを有効にしてから有効プロバイダーにGoogleをオンにしておく

これだけあとはがんがんコード書いてくだけ

src/lib/firebase.ts

import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';

const firebaseConfig = {
  // 省略
};

const app = initializeApp(firebaseConfig);

export const auth = getAuth(app);

getAuthでFirebase Authentication APIのインスタンスを作ってそれを利用できるようにする

src/contexts/AuthContexts.tsx

onAuthStateChangedを使ってFirebase Authenticationでログイン状態を監視して状態の取得を行えるようにする

import { createContext, useContext, useEffect, useState, ReactNode } from 'react';
import { onAuthStateChanged, User } from 'firebase/auth';
import { auth } from '../lib/firebase';

interface AuthContextType {
  currentUser: User | null;
  loading: boolean;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

export const AuthProvider = ({ children }: { children: ReactNode }) => {
  const [ currentUser, setCurrentUser ] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setCurrentUser(user);
      setLoading(false);
    });

    return () => unsubscribe();
  }, []);

  return (
    <AuthContext.Provider value={{ currentUser, loading }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => {
  const context = useContext(AuthContext);

  if (context === undefined) {
    throw new Error('useAuth must be used within an AuthProvider');
  }

  return context;
};

useAuthを使ってログインしてるかの状態を取得できる

src/index.tsx

import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
import { AuthProvider } from './contexts/AuthContexts';

const container = createRoot(document.getElementById('root')!);
container.render(
  <React.StrictMode>
    <AuthProvider>
      <App />
    </AuthProvider>
  </React.StrictMode>
);

src/App.tsx

import { HashRouter, Route, Routes } from 'react-router-dom';
import Home from './components/Home';
import Login from './components/Login';

const App = (): React.JSX.Element => {
  return (
    <HashRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/login" element={<Login />} />
      </Routes>
    </HashRouter>
  );
};

export default App;

src/components/Login.tsx

import { useCallback } from 'react';
import { Navigate } from 'react-router-dom';
import { signInWithPopup, GoogleAuthProvider } from 'firebase/auth';
import { useAuth } from '../contexts/AuthContexts';
import { auth } from '../lib/firebase';

const Login = (): React.JSX.Element => {
  const { currentUser, loading } = useAuth();
  const handleClick = useCallback(async () => {
    try {
      await signInWithPopup(auth, new GoogleAuthProvider());
      // userCredential.userからログインしたユーザー情報を取りそれを元にユーザーテーブルとかに入れるなり
      // const userCredential = await signInWithPopup(auth, new GoogleAuthProvider());
    } catch (error) {
      console.error("Login failed:", error);
    }
  }, []);

  if (loading) {
    return <div>loading...</div>;
  }

  if (currentUser) {
    return <Navigate to="/" replace />;
  }

  return (
    <button type="button" style={{ backgroundColor: '#4285f4' }} onClick={handleClick}>Googleでログイン</button>
  );
};

export default Login;

signInWithPopupのAPIを使ってGoogleアカウントでログインする。他にもsignInWithRedirectなどのAPIがあるがここでは省略

データベースなどでユーザーテーブルなどにuidを登録する必要があるような場合、初回ログイン時だけプロフィール登録ページなどを用意してそこからuidも送信して登録するような処理が必要。そのため登録されてるかなどの検証ができるREST APIなどを設計しておく必要もあり

src/components/Home.tsx

import { useCallback } from 'react';
import { Navigate } from 'react-router-dom';
import { signOut } from 'firebase/auth';
import { useAuth } from '../contexts/AuthContexts';
import { auth } from '../lib/firebase';

const Home = (): React.JSX.Element => {
  const { currentUser, loading } = useAuth();
  const handleClick = useCallback(async () => {
    await signOut(auth);
  }, []);

  if (loading) {
    return <div>loading...</div>;
  }

  if (!currentUser) {
    return <Navigate to="/login" replace />;
  }

  return (
    <div>
      {!!currentUser && (
        <div style={{ position: 'absolute', top: 0, right: 0 }}>
          <button type="button" style={{ backgroundColor: '#dc3545' }} onClick={handleClick}>ログアウト</button>
        </div>
      )}
    </div>
  );
};

export default Home;

ログアウト処理はsignOutを使用する

というような感じでFirebase Authenticationを使ってGoogleアカウント認証を利用することもできる。

サーバー側でログインしたユーザーの処理を行う場合

Firebase Authentication APIを使ってログインしたユーザーからgetIdTokenを使ってトークンを取得する

const token = await currentUser.getIdToken(false);

こんな感じで取得してそれをヘッダーのAuthorizationにBearerトークンとして乗せてリクエストを送信する。それを以下のようにBearerトークンを取得してからfirebase-adminのライブラリを使ってトークンの検証をしつつデコードする

import { initializeApp, applicationDefault } from 'firebase-admin/app';
import { getAuth } from 'firebase-admin/auth';

// GOOGLE_APPLICATION_CREDENTIALSに設定したサービスアカウント秘密鍵(JSON)を自動的に読み込んで初期化する
// 前回のCloud Messagingでもこの件に関しては書いてるのでそこ参照
initializeApp({
  credential: applicationDefault()
});

const auth = getAuth();

async function verifyToken(req, res) {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).send('Unauthorized');
  }

  const token = authHeader.split(' ')[1];

  try {
    const decodedToken = await auth.verifyIdToken(token);
    const uid = decodedToken.uid;

    console.log(`認証成功。ユーザーID: ${uid}`);
  } catch (error) {
    console.error("error", error);
    res.status(401).send('Unauthorized');
  }
}
Table of Contents
Firebaseの設定src/lib/firebase.tssrc/contexts/AuthContexts.tsxsrc/index.tsxsrc/App.tsxsrc/components/Login.tsxsrc/components/Home.tsxサーバー側でログインしたユーザーの処理を行う場合
関連記事