clerk-chrome-extension-patterns について
Chrome 拡張機能に Clerk 認証を導入する開発者向けのパターン集です。ポップアップ、サイドパネル、サービスワーカー、コンテンツスクリプトと、ウェブアプリとの認証同期に関する制約を扱います。
主な機能
- 01ポップアップとサイドパネルの認証パターン
- 02syncHost によるウェブアプリセッション同期
- 03createClerkClient によるトークン取得
- 04メッセージ通信を使ったコンテンツスクリプトの認証状態要求
- 05安定した拡張機能 ID の設定
活用シーン
- 01Chrome 拡張機能へのメール認証追加
- 02OAuth 認証のウェブアプリへの委任
- 03サービスワーカーからのセッショントークン取得
- 04コンテンツスクリプトからバックグラウンドへの認証状態要求
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ clerk/ skills --skill clerk-chrome-extension-patternsインストール後、Claude Code で/clerk-chrome-extension-patternsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
evals/evals.jsonreferences/content-scripts.mdcreate-clerk-client.mdheadless-extension.mdsync-host.mdtemplates/chrome-ext-basic-auth/package.jsontsconfig.jsontemplates/chrome-ext-basic-auth/src/popup.tsx詳細
Chrome 拡張機能のパターン
重要なルール
- OAuth(Google、GitHub など)と SAML は、ポップアップやサイドパネルではサポートされていません。
syncHostを使用して、認証をウェブアプリに委任してください。 - メールリンク(マジックリンク)はポップアップでは機能しません。ユーザーが外側をクリックするとポップアップが閉じ、サインイン状態がリセットされます。
- サイドパネルでは認証状態が自動更新されません。ウェブアプリ経由でサインインした後、ユーザーはサイドパネルを閉じて再度開く必要があります。
- サービスワーカーとコンテンツスクリプトから Clerk の React フックにアクセスすることはできません。
createClerkClient()またはメッセージパッシングを使用してください。 - 拡張機能の URL では
http://ではなくchrome-extension://を使用します。すべてのリダイレクト URL でchrome.runtime.getURL('.')を使用してください。 - 安定した CRX ID がないと、ビルドのたびに認証が機能しなくなります。デプロイ前に、マニフェストで
keyを設定してください。 - コンテンツスクリプトはオリジンの制約により Clerk を直接使用できません。Clerk では許可するオリジンが厳格に制限されています。
- Clerk ダッシュボードでボット対策を無効にする必要があります。Cloudflare のボット検出は拡張機能の環境ではサポートされていません。
認証オプション
| 方法 | ポップアップ | サイドパネル | syncHost(ウェブアプリと連携) |
|---|---|---|---|
| メール + OTP | はい | はい | はい |
| メール + リンク | いいえ | いいえ | はい |
| メール + パスワード | はい | はい | はい |
| ユーザー名 + パスワード | はい | はい | はい |
| SMS + OTP | はい | はい | はい |
| OAuth(Google、GitHub など) | いいえ | いいえ | はい |
| SAML | いいえ | いいえ | はい |
| パスキー | はい | はい | はい |
| Google One Tap | いいえ | いいえ | はい |
| Web3 | いいえ | いいえ | はい |
クイックスタート(Plasmo)
npx create-plasmo --with-tailwindcss --with-src my-extension
cd my-extension
npm install @clerk/chrome-extension
Clerk ダッシュボードの「Native applications」で Native API を有効にします。すべての拡張機能との連携で必要です。
.env.development:
PLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_FRONTEND_API=https://your-app.clerk.accounts.dev
src/popup.tsx:
import { ClerkProvider, Show, SignInButton, SignUpButton, UserButton } from '@clerk/chrome-extension'
const PUBLISHABLE_KEY = process.env.PLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY
const EXTENSION_URL = chrome.runtime.getURL('.')
if (!PUBLISHABLE_KEY) {
throw new Error('Missing PLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY')
}
function IndexPopup() {
return (
<ClerkProvider
publishableKey={PUBLISHABLE_KEY}
afterSignOutUrl={`${EXTENSION_URL}/popup.html`}
signInFallbackRedirectUrl={`${EXTENSION_URL}/popup.html`}
signUpFallbackRedirectUrl={`${EXTENSION_URL}/popup.html`}
>
<Show when="signed-out">
<SignInButton mode="modal" />
<SignUpButton mode="modal" />
</Show>
<Show when="signed-in">
<UserButton />
</Show>
</ClerkProvider>
)
}
export default IndexPopup
SignInButton には mode="modal" を使用してください。別ページに移動すると、ポップアップのフローが機能しなくなります。
syncHost — ウェブアプリとの認証状態の同期
OAuth や SAML が必要な場合、またはウェブアプリでのサインインを拡張機能に反映させる場合に使用します。
仕組み:拡張機能は host_permissions を介して、ウェブアプリのドメインから Clerk のセッションクッキーを読み取ります。
ステップ 1 — 環境変数:
.env.development:
PLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_FRONTEND_API=https://your-app.clerk.accounts.dev
PLASMO_PUBLIC_CLERK_SYNC_HOST=http://localhost
.env.production:
PLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_...
CLERK_FRONTEND_API=https://clerk.your-domain.com
PLASMO_PUBLIC_CLERK_SYNC_HOST=https://clerk.your-domain.com
ステップ 2 — syncHost プロパティを追加:
const SYNC_HOST = process.env.PLASMO_PUBLIC_CLERK_SYNC_HOST
<ClerkProvider
publishableKey={PUBLISHABLE_KEY}
syncHost={SYNC_HOST}
afterSignOutUrl="/"
routerPush={(to) => navigate(to)}
routerReplace={(to) => navigate(to, { replace: true })}
>
ステップ 3 — package.json で host_permissions を設定:
{
"manifest": {
"key": "$CRX_PUBLIC_KEY",
"permissions": ["cookies", "storage"],
"host_permissions": [
"$PLASMO_PUBLIC_CLERK_SYNC_HOST/*",
"$CLERK_FRONTEND_API/*"
]
}
}
ステップ 4 — Clerk API を使って、ウェブアプリの許可済みオリジンに拡張機能の ID を追加:
curl -X PATCH https://api.clerk.com/v1/instance \
-H "Authorization: Bearer YOUR_SECRET_KEY" \
-H "Content-type: application/json" \
-d '{"allowed_origins": ["chrome-extension://YOUR_EXTENSION_ID"]}'
syncHost の使用時に、ポップアップでサポートされていない認証方法を非表示にする:
<SignIn
appearance={{
elements: {
socialButtonsRoot: 'plasmo-hidden',
dividerRow: 'plasmo-hidden',
},
}}
/>
完全なガイド:references/sync-host.md
Vanilla JS/サービスワーカーでの createClerkClient()
@clerk/chrome-extension ではなく、@clerk/chrome-extension/client からインポートします。
バックグラウンドサービスワーカー(src/background/index.ts):
import { createClerkClient } from '@clerk/chrome-extension/client'
const publishableKey = process.env.PLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY
async function getToken(): Promise<string | null> {
const clerk = await createClerkClient({
publishableKey,
background: true,
})
if (!clerk.session) return null
return await clerk.session.getToken()
}
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
getToken()
.then((token) => sendResponse({ token }))
.catch((error) => {
console.error('[Background] Error:', JSON.stringify(error))
sendResponse({ token: null })
})
return true
})
background: true フラグを設定すると、ポップアップやサイドパネルが閉じている間もセッションが最新の状態に保たれます。設定しない場合、トークンは 60 秒後に期限切れになります。
Vanilla JS を使ったポップアップ(src/popup.ts):
import { createClerkClient } from '@clerk/chrome-extension/client'
const EXTENSION_URL = chrome.runtime.getURL('.')
const POPUP_URL = `${EXTENSION_URL}popup.html`
const clerk = createClerkClient({ publishableKey })
clerk.load({
afterSignOutUrl: POPUP_URL,
signInForceRedirectUrl: POPUP_URL,
signUpForceRedirectUrl: POPUP_URL,
allowedRedirectProtocols: ['chrome-extension:'],
}).then(() => {
clerk.addListener(render)
render()
})
完全なガイド:references/create-clerk-client.md
ヘッドレス拡張機能(ポップアップもサイドパネルもなし)
完全にバックグラウンドで動作し、ウェブアプリと同期する拡張機能向けです。
syncHost と background: true を指定した createClerkClient を使用し、ウェブアプリのクッキーから認証状態を読み取ります。
import { createClerkClient } from '@clerk/chrome-extension/client'
const publishableKey = process.env.PLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY
const syncHost = process.env.PLASMO_PUBLIC_CLERK_SYNC_HOST
async function getAuthenticatedUser() {
const clerk = await createClerkClient({
publishableKey,
syncHost,
background: true,
})
return clerk.user
}
package.json で、同期ホストのドメインに対する host_permissions が必要です。
完全なガイド:references/headless-extension.md
コンテンツスクリプト
コンテンツスクリプトは、ウェブページに挿入された隔離された JavaScript 環境で実行されます。オリジンの制約により、Clerk を直接使用できません。
メッセージパッシングを使用して、バックグラウンドサービスワーカーに認証状態を問い合わせてください。
// content.ts
async function getToken(): Promise<string | null> {
return new Promise((resolve) => {
chrome.runtime.sendMessage({ type: 'GET_TOKEN' }, (response) => {
resolve(response?.token ?? null)
})
})
}
async function main() {
const token = await getToken()
if (!token) return
// use token for authenticated API calls
}
main()
完全なガイド:references/content-scripts.md
安定した CRX ID
固定されたキーがない場合、Chrome はビルド時にランダムなキーから CRX ID を生成します。再ビルドするたびに変わるため、許可されたオリジンが無効になります。
オプション A -- Plasmo Itero(推奨):
- Plasmo Itero キーペアの生成にアクセスします
- 「Generate KeyPairs」をクリックし、秘密鍵を安全に保存して、公開鍵と CRX ID をコピーします
オプション B -- OpenSSL:
openssl genrsa -out key.pem 2048
# Use Plasmo Itero to convert or extract the public key in correct format
.env.chrome:
CRX_PUBLIC_KEY="<PUBLIC KEY from Itero>"
package.json:
{
"manifest": {
"key": "$CRX_PUBLIC_KEY",
"permissions": ["cookies", "storage"],
"host_permissions": [
"http://localhost/*",
"$CLERK_FRONTEND_API/*"
]
}
}
Clerk Dashboard > Allowed Origins に chrome-extension://YOUR_STABLE_CRX_ID を追加します。
トークンキャッシュ(ポップアップを閉じても保持)
const tokenCache = {
async getToken(key: string) {
const result = await chrome.storage.local.get(key)
return result[key] ?? null
},
async saveToken(key: string, token: string) {
await chrome.storage.local.set({ [key]: token })
},
async clearToken(key: string) {
await chrome.storage.local.remove(key)
},
}
<ClerkProvider publishableKey={PUBLISHABLE_KEY} tokenCache={tokenCache}>
| ストレージの種類 | スコープ | 消去されるタイミング |
|---|---|---|
chrome.storage.local | デバイス | アンインストール時または手動で消去したとき |
chrome.storage.session | セッション | ブラウザーを閉じたとき |
chrome.storage.sync | すべてのデバイス | アンインストール時(サイズ制限あり、8KB) |
localStorage | ポップアップのみ | ポップアップを閉じたとき。認証には使用しないでください |
よくある落とし穴
| 症状 | 原因 | 対処方法 |
|---|---|---|
| サインイン時にリダイレクトがループする | ClerkProvider の props に CRX URL がない | afterSignOutUrl と signInFallbackRedirectUrl を設定します |
| OAuth ボタンが機能しない | ポップアップでは OAuth がサポートされていない | syncHost を使用してウェブアプリに処理を委任します |
| ウェブアプリへのサインイン後も認証状態が古いまま | syncHost が設定されていない | syncHost prop と host_permissions を追加します |
| ウェブでのサインイン後、サイドパネルにサインアウト状態が表示される | 既知の制限事項 | ユーザーがサイドパネルを閉じて再度開く必要があります |
| 60 秒経過後、バックグラウンドでトークンを取得できない | セッションの有効期限が切れ、バックグラウンドでの更新がない | createClerkClient({ background: true }) を使用します |
| コンテンツスクリプトから Clerk にアクセスできない | 分離されたワールドとオリジンの制限 | メッセージパッシングを使用してバックグラウンドサービスワーカーに渡します |
| 再ビルド後に認証が機能しなくなる | CRX ID が変わった | .env.chrome で安定したキーを設定します |
PLASMO_PUBLIC_ 変数が未定義 | 環境変数ファイルが間違っている | .env ではなく .env.development を使用します |
| ボット保護のエラーが発生する | Cloudflare は拡張機能でサポートされていない | Clerk Dashboard でボット保護を無効にします |
| トークンキャッシュが保持されない | ポップアップで localStorage を使用している | chrome.storage.local を使用するか、tokenCache prop を渡します |
プラン要件
| 機能 | プラン |
|---|---|
| 基本的なポップアップ認証(メールアドレス/パスワード、OTP) | Free |
| パスキー | Free |
| syncHost | Pro が必要(カスタムドメイン) |
| syncHost 経由の OAuth | Pro + ウェブアプリでの OAuth 設定 |
| syncHost 経由の SAML | Enterprise |
| ボット保護 | 該当なし。拡張機能では無効にする必要があります |
関連項目
clerk-setup- Clerk の初期インストールclerk-custom-ui- カスタムフローと外観