Loading...
Loading...
Compare original and translation side by side
syncHostcreateClerkClient()chrome-extension://http://chrome.runtime.getURL('.')keysyncHostcreateClerkClient()chrome-extension://http://chrome.runtime.getURL('.')key| Method | Popup | Side Panel | syncHost (with web app) |
|---|---|---|---|
| Email + OTP | Yes | Yes | Yes |
| Email + Link | No | No | Yes |
| Email + Password | Yes | Yes | Yes |
| Username + Password | Yes | Yes | Yes |
| SMS + OTP | Yes | Yes | Yes |
| OAuth (Google, GitHub, etc.) | NO | NO | YES |
| SAML | NO | NO | YES |
| Passkeys | Yes | Yes | Yes |
| Google One Tap | No | No | Yes |
| Web3 | No | No | Yes |
| 方法 | 弹窗 | 侧边面板 | syncHost(配合web应用) |
|---|---|---|---|
| 邮箱+OTP | 支持 | 支持 | 支持 |
| 邮箱+链接 | 不支持 | 不支持 | 支持 |
| 邮箱+密码 | 支持 | 支持 | 支持 |
| 用户名+密码 | 支持 | 支持 | 支持 |
| 短信+OTP | 支持 | 支持 | 支持 |
| OAuth(Google、GitHub等) | 不支持 | 不支持 | 支持 |
| SAML | 不支持 | 不支持 | 支持 |
| 通行密钥(Passkeys) | 支持 | 支持 | 支持 |
| Google One Tap | 不支持 | 不支持 | 支持 |
| Web3 | 不支持 | 不支持 | 支持 |
npx create-plasmo --with-tailwindcss --with-src my-extension
cd my-extension
npm install @clerk/chrome-extension.env.developmentPLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_FRONTEND_API=https://your-app.clerk.accounts.devsrc/popup.tsximport { 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 IndexPopupmode="modal"SignInButtonnpx create-plasmo --with-tailwindcss --with-src my-extension
cd my-extension
npm install @clerk/chrome-extension.env.developmentPLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_FRONTEND_API=https://your-app.clerk.accounts.devsrc/popup.tsximport { 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 IndexPopupSignInButtonmode="modal"host_permissions.env.developmentPLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_FRONTEND_API=https://your-app.clerk.accounts.dev
PLASMO_PUBLIC_CLERK_SYNC_HOST=http://localhost.env.productionPLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_...
CLERK_FRONTEND_API=https://clerk.your-domain.com
PLASMO_PUBLIC_CLERK_SYNC_HOST=https://clerk.your-domain.comsyncHostconst 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 })}
>host_permissionspackage.json{
"manifest": {
"key": "$CRX_PUBLIC_KEY",
"permissions": ["cookies", "storage"],
"host_permissions": [
"$PLASMO_PUBLIC_CLERK_SYNC_HOST/*",
"$CLERK_FRONTEND_API/*"
]
}
}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"]}'<SignIn
appearance={{
elements: {
socialButtonsRoot: 'plasmo-hidden',
dividerRow: 'plasmo-hidden',
},
}}
/>references/sync-host.mdhost_permissions.env.developmentPLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_FRONTEND_API=https://your-app.clerk.accounts.dev
PLASMO_PUBLIC_CLERK_SYNC_HOST=http://localhost.env.productionPLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_...
CLERK_FRONTEND_API=https://clerk.your-domain.com
PLASMO_PUBLIC_CLERK_SYNC_HOST=https://clerk.your-domain.comsyncHostconst 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 })}
>package.jsonhost_permissions{
"manifest": {
"key": "$CRX_PUBLIC_KEY",
"permissions": ["cookies", "storage"],
"host_permissions": [
"$PLASMO_PUBLIC_CLERK_SYNC_HOST/*",
"$CLERK_FRONTEND_API/*"
]
}
}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"]}'<SignIn
appearance={{
elements: {
socialButtonsRoot: 'plasmo-hidden',
dividerRow: 'plasmo-hidden',
},
}}
/>references/sync-host.md@clerk/chrome-extension/client@clerk/chrome-extensionsrc/background/index.tsimport { 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: truesrc/popup.tsimport { 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@clerk/chrome-extension/client@clerk/chrome-extensionsrc/background/index.tsimport { 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: truesrc/popup.tsimport { 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.mdsyncHostcreateClerkClientbackground: trueimport { 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
}host_permissionspackage.jsonreferences/headless-extension.mdsyncHostbackground: truecreateClerkClientimport { 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.jsonhost_permissionsreferences/headless-extension.md// 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// 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
// 使用token发起需要身份验证的API请求
}
main()references/content-scripts.mdopenssl genrsa -out key.pem 2048openssl genrsa -out key.pem 2048
**`.env.chrome`:**
**`package.json`:**
```json
{
"manifest": {
"key": "$CRX_PUBLIC_KEY",
"permissions": ["cookies", "storage"],
"host_permissions": [
"http://localhost/*",
"$CLERK_FRONTEND_API/*"
]
}
}chrome-extension://YOUR_STABLE_CRX_ID
**`.env.chrome`:**
**`package.json`:**
```json
{
"manifest": {
"key": "$CRX_PUBLIC_KEY",
"permissions": ["cookies", "storage"],
"host_permissions": [
"http://localhost/*",
"$CLERK_FRONTEND_API/*"
]
}
}chrome-extension://YOUR_STABLE_CRX_IDconst 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}>| Storage type | Scope | Clears on |
|---|---|---|
| Device | Uninstall or manual clear |
| Session | Browser close |
| All devices | Uninstall (size-limited, 8KB) |
| Popup only | Popup close -- do not use for auth |
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}>| 存储类型 | 作用范围 | 清除条件 |
|---|---|---|
| 设备 | 卸载扩展或手动清除 |
| 会话 | 浏览器关闭 |
| 所有同步设备 | 卸载扩展(大小限制8KB) |
| 仅弹窗 | 弹窗关闭——不要用于身份验证存储 |
| Symptom | Cause | Fix |
|---|---|---|
| Redirect loop on sign-in | Missing CRX URL in ClerkProvider props | Set |
| OAuth button not working | OAuth not supported in popup | Use |
| Auth state stale after web app sign-in | | Add |
| Side panel shows signed-out after web sign-in | Known limitation | User must close and reopen the side panel |
| Background can't get token after 60s | Session expired, no background refresh | Use |
| Content script can't access Clerk | Isolated world + origin restrictions | Use message passing to background service worker |
| Auth breaks after rebuild | CRX ID rotated | Configure stable key via |
| Wrong env file | Use |
| Bot protection errors | Cloudflare not supported in extensions | Disable bot protection in Clerk Dashboard |
| Token cache not persisting | Using | Use |
| 现象 | 原因 | 解决方法 |
|---|---|---|
| 登录时重定向循环 | ClerkProvider属性中缺少CRX URL配置 | 设置 |
| OAuth按钮无法工作 | 弹窗不支持OAuth | 使用 |
| web应用登录后扩展身份验证状态未更新 | 未配置 | 添加 |
| web端登录后边侧面板仍显示未登录 | 已知限制 | 用户需关闭并重新打开侧边面板 |
| 60秒后后台无法获取令牌 | 会话过期,无后台刷新机制 | 使用 |
| 内容脚本无法访问Clerk | 独立环境+源限制 | 使用消息传递机制调用后台Service Worker |
| 重新构建后身份验证失效 | CRX ID变动 | 通过 |
| 环境文件错误 | 使用 |
| 机器人保护报错 | 扩展不支持Cloudflare | 在Clerk管理后台关闭机器人保护 |
| 令牌缓存未持久化 | 弹窗中使用了 | 使用 |
| Feature | Plan |
|---|---|
| Basic popup auth (email/password, OTP) | Free |
| Passkeys | Free |
| syncHost | Requires Pro (custom domain) |
| OAuth through syncHost | Pro + OAuth configured on web app |
| SAML through syncHost | Enterprise |
| Bot protection | N/A -- must be disabled for extensions |
| 功能 | 所需套餐 |
|---|---|
| 基础弹窗验证(邮箱/密码、OTP) | 免费版 |
| 通行密钥(Passkeys) | 免费版 |
| syncHost | 专业版(自定义域名) |
| 通过syncHost使用OAuth | 专业版 + web端已配置OAuth |
| 通过syncHost使用SAML | 企业版 |
| 机器人保护 | 不适用——扩展必须关闭该功能 |
clerk-setupclerk-custom-uiclerk-setupclerk-custom-ui