clerk-chrome-extension-patterns

'Authentification d'extension Chrome avec @clerk/chrome-extension -- popup/sidepanel

npx skills add https://github.com/clerk/skills --skill clerk-chrome-extension-patterns

Modèles d'extension Chrome

RÈGLES CRITIQUES

  1. OAuth (Google, GitHub, etc.) et SAML ne sont PAS pris en charge dans les popups ou panneaux latéraux -- utilisez syncHost pour déléguer l'authentification à votre application web
  2. Les liens par email (magic links) ne fonctionnent pas dans les popups -- le popup se ferme quand l'utilisateur clique en dehors, réinitialisant l'état de connexion
  3. Les panneaux latéraux ne rafraîchissent pas automatiquement l'état d'authentification -- les utilisateurs doivent fermer et rouvrir le panneau latéral après s'être connectés via l'application web
  4. Les service workers et content scripts n'ont PAS accès aux hooks React de Clerk -- utilisez createClerkClient() ou le passage de messages
  5. Les URL d'extension utilisent chrome-extension:// et non http:// -- toutes les URL de redirection doivent utiliser chrome.runtime.getURL('.')
  6. Sans un CRX ID stable, chaque rebuild casse l'authentification -- configurez key dans le manifest AVANT le déploiement
  7. Les content scripts ne peuvent pas utiliser Clerk directement en raison des restrictions d'origine -- Clerk applique des origines autorisées strictes
  8. La protection contre les bots DOIT ÊTRE DÉSACTIVÉE dans le tableau de bord Clerk -- la détection de bots Cloudflare n'est pas prise en charge dans les environnements d'extension

Options d'authentification

Méthode Popup Panneau latéral syncHost (avec application web)
Email + OTP Oui Oui Oui
Email + Lien Non Non Oui
Email + Mot de passe Oui Oui Oui
Nom d'utilisateur + Mot de passe Oui Oui Oui
SMS + OTP Oui Oui Oui
OAuth (Google, GitHub, etc.) NON NON OUI
SAML NON NON OUI
Passkeys Oui Oui Oui
Google One Tap Non Non Oui
Web3 Non Non Oui

Démarrage rapide (Plasmo)

npx create-plasmo --with-tailwindcss --with-src my-extension
cd my-extension
npm install @clerk/chrome-extension

Activez Native API dans le tableau de bord Clerk sous Applications natives. Obligatoire pour toutes les intégrations d'extension.

.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

Utilisez mode="modal" pour SignInButton -- naviguer vers une page séparée casse le flux du popup.

syncHost -- Synchroniser l'authentification avec l'application web

Utilisez ceci quand vous avez besoin d'OAuth, SAML, ou si vous voulez que l'extension reflète la connexion depuis votre application web.

Comment ça marche : L'extension lit le cookie de session Clerk du domaine de votre application web via host_permissions.

Étape 1 -- Variables d'environnement:

.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

Étape 2 -- Ajouter la prop 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 })}
>

Étape 3 -- Configurer host_permissions dans package.json:

{
  "manifest": {
    "key": "$CRX_PUBLIC_KEY",
    "permissions": ["cookies", "storage"],
    "host_permissions": [
      "$PLASMO_PUBLIC_CLERK_SYNC_HOST/*",
      "$CLERK_FRONTEND_API/*"
    ]
  }
}

Étape 4 -- Ajouter l'ID de l'extension aux origines autorisées de l'application web via l'API Clerk:

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"]}'

Masquer les méthodes d'authentification non supportées dans le popup lors de l'utilisation de syncHost:

<SignIn
  appearance={{
    elements: {
      socialButtonsRoot: 'plasmo-hidden',
      dividerRow: 'plasmo-hidden',
    },
  }}
/>

Guide complet : references/sync-host.md

createClerkClient() pour Vanilla JS / Service Workers

Importez depuis @clerk/chrome-extension/client (pas @clerk/chrome-extension).

Service worker en arrière-plan (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
})

Le flag background: true garde les sessions fraîches même quand le popup/sidepanel est fermé. Sans lui, les tokens expirent après 60 secondes.

Popup avec 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()
})

Guide complet : references/create-clerk-client.md

Extension sans interface (pas de popup, pas de panneau latéral)

Pour les extensions qui s'exécutent entièrement en arrière-plan et se synchronisent avec une application web.

Utilise syncHost + createClerkClient avec background: true pour lire l'état d'authentification à partir des cookies de l'application web.

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
}

Nécessite host_permissions pour le domaine du sync host dans package.json.

Guide complet : references/headless-extension.md

Content Scripts

Les content scripts s'exécutent dans un monde JavaScript isolé injecté dans les pages web. Clerk ne peut pas être utilisé directement -- les restrictions d'origine l'empêchent.

Utilisez le passage de messages pour demander l'état d'authentification au service worker en arrière-plan :

// 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()

Guide complet : references/content-scripts.md

CRX ID stable

Sans une clé épinglée, Chrome dérive le CRX ID d'une clé aléatoire au moment de la build. Cela tourne à chaque rebuild, cassant les origines autorisées.

Option A -- Plasmo Itero (recommandé):

  1. Visitez Plasmo Itero Generate Keypairs
  2. Cliquez sur « Generate KeyPairs » -- sauvegardez la clé privée de manière sécurisée, copiez la clé publique et le CRX ID

Option B -- OpenSSL:

openssl genrsa -out key.pem 2048
# Utilisez Plasmo Itero pour convertir ou extraire la clé publique au format correct

.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/*"
    ]
  }
}

Ajoutez chrome-extension://YOUR_STABLE_CRX_ID au tableau de bord Clerk > Origines autorisées.

Cache de tokens (persister entre les fermetures du popup)

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}>
Type de stockage Portée Effacé lors de
chrome.storage.local Appareil Désinstallation ou effacement manuel
chrome.storage.session Session Fermeture du navigateur
chrome.storage.sync Tous les appareils Désinstallation (limité en taille, 8 KB)
localStorage Popup uniquement Fermeture du popup -- ne pas utiliser pour l'authentification

Pièges courants

Symptôme Cause Solution
Boucle de redirection à la connexion CRX URL manquante dans les props ClerkProvider Définissez afterSignOutUrl, signInFallbackRedirectUrl
Bouton OAuth non fonctionnel OAuth non pris en charge dans le popup Utilisez syncHost pour déléguer à l'application web
État d'authentification obsolète après connexion application web syncHost non configuré Ajoutez la prop syncHost + host_permissions
Panneau latéral affiche déconnecté après connexion web Limitation connue L'utilisateur doit fermer et rouvrir le panneau latéral
L'arrière-plan ne peut pas obtenir de token après 60s Session expirée, pas de rafraîchissement en arrière-plan Utilisez createClerkClient({ background: true })
Content script ne peut pas accéder à Clerk Monde isolé + restrictions d'origine Utilisez le passage de messages vers le service worker en arrière-plan
L'authentification se casse après rebuild CRX ID en rotation Configurez une clé stable via .env.chrome
Variable PLASMO_PUBLIC_ non définie Mauvais fichier env Utilisez .env.development, pas .env
Erreurs de protection contre les bots Cloudflare non pris en charge dans les extensions Désactivez la protection contre les bots dans le tableau de bord Clerk
Cache de tokens ne persiste pas Utilisation de localStorage dans le popup Utilisez chrome.storage.local ou passez la prop tokenCache

Exigences du plan

Fonctionnalité Plan
Authentification popup basique (email/mot de passe, OTP) Gratuit
Passkeys Gratuit
syncHost Nécessite Pro (domaine personnalisé)
OAuth via syncHost Pro + OAuth configuré sur l'application web
SAML via syncHost Enterprise
Protection contre les bots N/A -- doit être désactivée pour les extensions

Voir aussi

  • clerk-setup - Installation initiale de Clerk
  • clerk-custom-ui - Flux personnalisés et apparence

Skills similaires