今日学んだこと:Google認証システムの実装に関して
2025年9月15日
概要
今日はGoogle認証システムの実装について学んだ。
全体の流れ
1. ユーザーがログインボタンをクリック
2. Googleの認証画面に移動
3. Googleで認証完了後、アプリに戻る
4. セッション情報を保存
5. ダッシュボードページを表示
ログインボタンがクリックされた時
// AuthStore内のsignInWithGoogle関数
signInWithGoogle: async () => {
const { error } = await supabase.auth.signInWithOAuth({
provider: "google",
options: {
redirectTo: `${window.location.origin}/auth/callback`,
queryParams: {
response_type: 'code',
flow_type: 'pkce',
},
},
});
}
何が起こるか:
Supabaseライブラリが、GoogleのOAuth認証URLを作成 ブラウザが自動的にGoogleの認証画面にリダイレクト redirectToで、認証後に戻ってくるURL(/auth/callback)を指定
Googleでの認証処理
ユーザーがGoogleアカウントでログインすると、Googleは以下のような情報を返す。
ユーザー情報:
- ID: "⚪︎⚪︎"
- Email: "⚪︎⚪︎@gmail.com"
- Name: "Test User"
- Avatar: プロフィール画像のURL
認証後のコールバック処理
Googleから戻ってきたとき、/auth/callbackページで処理される。
// app/auth/callback/route.ts
export async function GET(request: NextRequest) {
const code = searchParams.get('code'); // Googleからの認証コード
if (code) {
// Authorization Code Flow(推奨される方法)
const { data, error } = await supabase.auth.exchangeCodeForSession(code);
// 認証コードをセッション情報に変換
} else {
// Implicit Flow(代替方法)
// クライアントサイドで処理
}
}
重要なポイント:
認証コード: Googleから受け取る一時的なコード セッション: 実際にアプリで使用する永続的な認証情報
セッション情報の保存
認証が成功すると、セッション情報がブラウザのクッキーに保存される。
// app/api/auth/session/route.ts
const { data, error } = await supabase.auth.setSession({
access_token: accessToken, // アクセストークン
refresh_token: refreshToken, // リフレッシュトークン
});
保存される情報:
アクセストークン: APIにアクセスするための認証情報 リフレッシュトークン: アクセストークンを更新するための情報 有効期限: 通常1時間程度
ユーザープロフィールの作成
初回ログイン時は、データベースにユーザー情報を保存する。
await supabase.from('profiles').insert({
id: session.user.id,
email: session.user.email,
name: session.user.user_metadata?.full_name || 'ユーザー',
provider: 'google',
avatar_url: session.user.user_metadata?.avatar_url,
});
認証状態の管理
アプリ全体で認証状態を管理するため、Zustandストアを使用。
// AuthStore
const useAuthStore = create((set) => ({
user: null, // 現在のユーザー情報
isAuthenticated: false, // ログイン状態
isLoading: false, // 読み込み状態
// 初期化時にセッション確認
initialize: async () => {
// サーバーAPIでセッション確認
const response = await fetch('/api/auth/verify');
const serverSession = await response.json();
if (serverSession.authenticated) {
// 認証済みならユーザー情報を設定
set({ user: userData, isAuthenticated: true });
}
}
}));
ページでの認証確認
各ページで認証状態をチェックする。
// ダッシュボードページ
export default function DashboardPage() {
const { user, isLoading } = useAuth();
// 認証されていない場合はログインページにリダイレクト
useEffect(() => {
if (!isLoading && !user) {
router.push('/auth');
}
}, [user, isLoading]);
if (!user) return <div>読み込み中...</div>;
return <div>ようこそ、{user.name}さん</div>;
}
データの流れ
ブラウザ → Google → Supabase → アプリ → データベース