Chrome拡張でSupabase × GitHub OAuthを実装するときのポイント

今回は「Supabase + GitHub OAuth 認証を Chrome拡張でどう実装するか?」をテーマに、
実際にハマったポイントと、それをどう乗り越えたかをまとめます。
やりたかったこと
- Chrome拡張内に GitHub ログインボタンを設置
- Supabase の Auth 機能でログインセッション管理したい
- GitHub の
provider_token
も取得して PRレビューに活かしたい - 出来るだけシンプル&セキュアな構成でやりたい
なぜ supabase.auth.signInWithOAuth()
は使えないのか?
signInWithOAuth()
は Web 向けに設計されており、内部で window.location
によるリダイレクトが発生します。
でも Chrome拡張の popup や background では window.location
の動作が制限されているため、使えません。
さらに chrome-extension://
のURLは OAuth における redirect_uri
として使えないため、GitHub 側で弾かれてしまいます。
採用した構成(Implicit Flow パターン)
Chrome拡張で OAuth を行うためには、以下の流れを取りました:
chrome.identity.launchWebAuthFlow()
で Supabase の/auth/v1/authorize
にアクセス- Supabase が GitHub OAuth を開始
- 認証成功後、 Supabaseの
/auth/v1/callback
にリダイレクト - Supabaseから拡張機能側
https://<extension-id>.chromiumapp.org/#access_token=...
にリダイレクト - そのURLを Chrome がキャッチし、トークンを取得
supabase.auth.setSession()
によりセッション確立
background.js で launchWebAuthFlow()
を使う
launchWebAuthFlowを使うことで、拡張機能でも問題なくOAuthのプロセスを開始することができます。
const redirectUri = chrome.identity.getRedirectURL();
const authUrl = `https://<supabase-project>.supabase.co/auth/v1/authorize` +
`?provider=github&redirect_to=${encodeURIComponent(redirectUri)}`;
chrome.identity.launchWebAuthFlow({ url: authUrl, interactive: true }, async (redirectUrl) => {
const params = new URLSearchParams(new URL(redirectUrl).hash.slice(1));
const access_token = params.get("access_token");
const refresh_token = params.get("refresh_token");
await supabase.auth.setSession({ access_token, refresh_token });
});
URL周りの設定
URL周りの設定を間違いやすいので注意です。
Supabaseのconfig.toml
[auth]
site_url = "https://<extension-id>.chromiumapp.org/"
additional_redirect_urls = [
"https://<extension-id>.chromiumapp.org/"
]
Github 側の設定
GitHub OAuth App の Callback URL には以下を登録します。
https://<your-project>.supabase.co/auth/v1/callback
このURLは Supabase 側でGitHub OAuthの結果を受け取るための固定のコールバックです。
ハマったポイントとその回避法
この構成で特にハマったのは以下のような点です。
- state の付与エラー:最初、
/auth/v1/authorize
のリクエストにstate=xxx
を自分で付けていたのですが、Supabase 側が内部でstate
を生成・検証しているため、自前の値と一致せずにエラー(invalid state)になっていました。解決策は、自分で state を付けず Supabase に任せることでした。 - 拡張機能のURL設定ミス:Supabaseの site_url, additional_redirect_urlsで
chrome-extension://<extension-id>/
のフォーマットにしていたところ、Authorization page could not be loaded
というエラーが発生。https://<extension-id>.chromiumapp.org/
のフォーマットに変更して解決しました。
セキュリティ観点でどうか
今回の構成では、GitHub認証後のリダイレクトURLに #access_token=...
が含まれるため、トークンがURLフラグメントに露出します。これはセキュリティ的に少し注意が必要です。
ただし、個人開発・社内利用・PoC用途であれば十分許容できる範囲だと考えています。
セッションが popup 側に伝わらない問題
background.js でログイン処理を行っても、popup 側では Supabase のセッションが認識されませんでした。これは拡張機能内でコンテキストが分かれているためで、解決策はトークンを chrome.storage.local に保存して、popup 側で読み出して setSession() し直すことでした。
以下のようにして chrome.storage.local
に保存・復元することで、拡張の別コンテキスト(popupやcontent scriptなど)でもログイン状態を共有できます。
// 保存
chrome.storage.local.set({ access_token, refresh_token });
// 復元
chrome.storage.local.get(['access_token', 'refresh_token'], async (items) => {
if (items.access_token) {
await supabase.auth.setSession({
access_token: items.access_token,
refresh_token: items.refresh_token
});
}
});
おわりに
Supabase × GitHub OAuth × 拡張機能の組み合わせはまだ事例が少ないので、この記事が同じように詰まっている人の助けになれば嬉しいです 🙌