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

> Supabase × GitHub OAuthをChrome拡張機能で実装する方法を、詰まりポイントと対処法を含めて丁寧に解説。

- 公開日: 2025-03-27
- 著者: 村井 謙太
- タグ: Supabase, OAuth, Chrome拡張
- URL: https://tech.anycloud.co.jp/articles/supabase_github_oauth_in_extension

---

今回は「**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 を行うためには、以下の流れを取りました：

1.  `chrome.identity.launchWebAuthFlow()` で Supabase の `/auth/v1/authorize` にアクセス
2.  Supabase が GitHub OAuth を開始
3.  認証成功後、 Supabaseの `/auth/v1/callback` にリダイレクト
4.  Supabaseから拡張機能側 `https://<extension-id>.chromiumapp.org/#access_token=...` にリダイレクト
5.  そのURLを Chrome がキャッチし、トークンを取得
6.  `supabase.auth.setSession()` によりセッション確立

## background.js で `launchWebAuthFlow()` を使う

launchWebAuthFlowを使うことで、拡張機能でも問題なくOAuthのプロセスを開始することができます。

```typescript
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

```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など）でもログイン状態を共有できます。

```typescript
// 保存
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 × 拡張機能**の組み合わせはまだ事例が少ないので、この記事が同じように詰まっている人の助けになれば嬉しいです 🙌
