🏁 忙しい人のための結論
-
問題:FirebaseでGoogleログインした後、1時間後にGoogle Calendar APIが401エラーで失敗する
→ Firebaseの認証は有効なのに、Google側の認証が切れてしまう -
原因:FirebaseのIDトークンとGoogleのOAuthトークンは別物
→ FirebaseのIDトークンは自アプリ向け、Google APIはOAuth2トークンで別認証が必要 -
解決:
1. Googleログイン時に Blocking Function を使い、GoogleのOAuthトークンを取得してFirestoreに保存
2. フロントはFirebase IDトークンでFunctionsを呼び出し
3. FunctionsがFirestoreからGoogleトークンを取得し、OAuth2Client
でAPIを実行
🔹1. はじめに
個人開発で、FirebaseからGoogle Calendar APIを呼び出して
Googleカレンダーの内容を分析するWebアプリを作っていました。
Firebase Authenticationを使ってGoogleログインを実装し、
そこから取得した予定データを分析・可視化する構成です。
🧩 使用技術・構成概要
コンポーネント | 役割 |
---|---|
Firebase Authentication | Googleログイン+Firebase IDトークン発行 |
Next.js(フロント) | Firebaseトークンを保持し、直接Google Calendar APIを呼び出し |
Google Calendar API | カレンダー情報を返却(ただし1時間後に401エラー) |
🔹 2. 想定フローと実装方針
初期実装では以下のようなフローを想定していました。
- Googleでログイン
- Firebaseの認証トークンを取得し、フロントで保持
- そのFirebaseトークンを使ってGoogle Calendar APIを実行
- Googleカレンダーの情報を取得して表示
実際に実装したコード上は問題なく動作し、最初はカレンダー情報が取得できていました。
🔹 3. はまった現象
ところが、1時間後に再度APIを叩くと認証エラーが発生。
Firebase上ではログイン状態が維持されているにもかかわらず、
Google Calendar APIの呼び出しだけが401エラーで失敗していました。
「Firebaseではログインしているのに、なぜGoogleカレンダーは認証されないのか?」
この状態に1か月ほどハマっていました。
🔹 4. 原因の正体
原因は、Firebaseの認証トークンとGoogle Calendar APIの認証トークンは別物だったことです。
Firebaseの認証トークン(Firebase ID Token)はFirebaseサービスへの認可専用。
Google Calendar APIはOAuth2のAccess Tokenによって別途認証される仕組みです。
つまり以下のような違いがあります。
種類 | 用途 | 有効期限 |
---|---|---|
Firebase ID Token | Firebaseサービス用 | 約1時間 |
Google OAuth Access Token | Google API用 | 約1時間(Refresh可能) |
🔹 5. 解決方法
最終的には以下のようなフローに変更することで解決しました。
- Googleログインを実行(Firebase Authentication)
- ブロッキング関数でGoogleのOAuthトークンを取得し、Firestoreに保存
- フロントはFirebase IDトークンを使ってCloud Functionsを呼び出す
- FunctionsがFirestoreからGoogleトークンを取得し、Calendar APIを呼び出す
- 必要に応じてRefresh TokenでAccess Tokenを再発行
コンポーネント | 役割 |
---|---|
Firebase Authentication | Googleログインによる認証(IDトークン発行) |
Firebase Blocking Function | Google OAuthトークンを取得し、Firestoreへ安全に保存 |
Firestore | Googleトークンやユーザー設定を保持 |
Cloud Functions | FirestoreからGoogleトークンを取得し、OAuth2Client でCalendar APIを実行 |
Google Calendar API | ユーザーの予定データを取得 |
Next.js(フロント) | FirebaseトークンでFunctionsを呼び出し、取得データを表示 |
🧠 なぜブロッキング関数を使うのか
Firebaseの通常のGoogleログインでは、GoogleのOAuth認証情報(Access Token / Refresh Token)を取得できません。
Google APIを利用するには、Blocking Functionをかませてトークンをサーバー側で取得する必要があります。
Blocking Functionは、ログインリクエストの途中(beforeSignIn)で GoogleのOAuth認証情報にアクセスできるタイミングを提供します。
🔹 6. ブロッキング関数の作成
Firebase Authentication の Blocking Functions(ブロッキング関数) には、主に次の2種類があります。
-
beforeCreate
- ユーザーが 新規登録 する直前に実行される
- 初期データ登録や、初回のみ必要な処理(例:Firestoreへのユーザープロファイル作成)に利用
-
beforeSignIn
- 既存ユーザーが ログイン する直前に実行される
- 毎回ログイン時に行いたい処理(例:Googleトークンの更新・再取得)に利用
今回のケースでは、Google Calendar APIを利用するためのOAuthトークンをログイン時に取得・更新する必要があるため、beforeSignIn のみを実装しています。
import {beforeUserSignedIn} from 'firebase-functions/v2/identity';
import { AuthService } from '../services/auth/service';
export const beforesignedin = beforeUserSignedIn((event) => {
// firestoreにGoogle OAuthトークンを保存
AuthService.saveGoogleTokens(
event.data.uid,
event.credential.accessToken,
event.credential.refreshToken
);
});
上記関数をデプロイしてください。
🔹 7. ブロッキング関数の設定
以下画面のログイン前に先ほど作成した関数を設定してください。
ブロッキング関数で取得したい情報にチェックするのを忘れずに。
🔹 8. まとめ
今回1か月もこの事象にはまったのは、まったくの初見のfirebase、google apiをろくに調べずに実装をほぼAIに任せていたからです。
誰かが言っていた「バイブコーディングは9割を形にするのは速いが、残り1割に時間がかかる」というのを、身をもってい体験しました。
とほほ。。。