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があるがここでは省略
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');
}
}