@clerk/chrome-extension blocks email/password login due to Origin vs Authorization header conflict (Edge Network Security Rule)
Context & Environment
- Package:
@clerk/chrome-extension (v2.7.3+) / @clerk/clerk-js (v5.125.0)
- Browser: Google Chrome / Chromium (v120+)
- Environment: Chrome Extension MV3 Popup / Standalone Page
- Reproduction: 100% reproducible on the official clerk-chrome-extension-demo repository when testing out-of-the-box in production config (without
syncHost or when syncHost is blocked by third-party cookie restrictions).
Summary
When using the <SignIn /> component inside a Chrome Extension popup (or testing the official Plasmo demo), submitting the user's email address results in an immediate 400 rejection from the Clerk API.
The Clerk Edge Network returns the following error message inside the SignIn component UI:
"For security purposes, only one of the 'Origin' and 'Authorization' headers should be provided, but not both. In browser contexts, the 'Origin' header is set automatically by the browser. In native application contexts (e.g. mobile apps), set the 'Authorization' header."
Root Cause Analysis
- When
@clerk/chrome-extension initializes, it passes _is_native=1 to the Clerk API, thereby instructing the frontend SDK to behave as a native application and attach the Authorization: Bearer <publishable_key> header to network requests.
- However, because the component is rendered inside a Chrome Extension Popup, the rendering environment is inherently a web browser (
chrome-extension://...).
- Google Chrome natively forces the
Origin header (Origin: chrome-extension://<extension-id>) onto outgoing fetch requests. This cannot be prevented by JavaScript using standard fetch APIs.
- The Clerk Backend API receives BOTH the
Origin header (from Chrome) and the Authorization header (from the Clerk SDK).
- Clerk's edge router immediately blocks the request because it sees conflicting contexts (Browser Origin vs Native Auth), even if the extension ID is properly whitelisted in
allowed_origins in the dashboard!
Steps to Reproduce
- Clone the official demo:
git clone https://github.com/clerk/clerk-chrome-extension-demo.git
- Configure .env.production with your live publishable key (
PLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY) and live frontend API (CLERK_FRONTEND_API). Do not set/rely on syncHost since Third-Party Sandbox blocking neutralizes it for many users anyway.
- Build the extension:
pnpm run build
- Load the unpacked extension into Chrome.
- In the Clerk Dashboard, whitelist your generated
chrome-extension://... URL in "Allowed Redirect URIs" and "CORS Allowed Origins".
- Open the newly built Demo popup and enter an email address to sign in.
- Click "Continue".
- Result: The component flashes red with the
Origin vs Authorization conflict error.
The Temporary Workaround (For Users)
To bypass this architectural conflict in production apps without modifying Clerk's code, developers are currently forced to use Chrome's declarativeNetRequest API to manually strip the Origin header before it reaches Clerk:
manifest.json:
"permissions": ["declarativeNetRequest"],
"declarative_net_request": {
"rule_resources": [{
"id": "clerk_rules",
"enabled": true,
"path": "clerk_rules.json"
}]
}
clerk_rules.json:
[
{
"id": 1,
"priority": 1,
"action": {
"type": "modifyHeaders",
"requestHeaders": [{ "header": "Origin", "operation": "remove" }]
},
"condition": {
"urlFilter": "||clerk.xxx.com/*",
"resourceTypes": ["xmlhttprequest"]
}
}
]
By stripping Chrome's native Origin header, the Clerk backend only sees the Authorization header and correctly resolves the request as "Native", bypassing the conflict.
Expected Behavior
@clerk/chrome-extension should intelligently recognize that it is operating within a Chrome extension where the Origin header is immutable, and coordinate with the Edge Network to permit Authorization: Bearer requests originating from whitelisted chrome-extension:// origins, rather than rejecting them outright. Or, the SDK should defer entirely to cookies/CORS when the origin is known.
@clerk/chrome-extension blocks email/password login due to Origin vs Authorization header conflict (Edge Network Security Rule)
Context & Environment
@clerk/chrome-extension(v2.7.3+) /@clerk/clerk-js(v5.125.0)syncHostor whensyncHostis blocked by third-party cookie restrictions).Summary
When using the
<SignIn />component inside a Chrome Extension popup (or testing the official Plasmo demo), submitting the user's email address results in an immediate 400 rejection from the Clerk API.The Clerk Edge Network returns the following error message inside the
SignIncomponent UI:Root Cause Analysis
@clerk/chrome-extensioninitializes, it passes_is_native=1to the Clerk API, thereby instructing the frontend SDK to behave as a native application and attach theAuthorization: Bearer <publishable_key>header to network requests.chrome-extension://...).Originheader (Origin: chrome-extension://<extension-id>) onto outgoing fetch requests. This cannot be prevented by JavaScript using standard fetch APIs.Originheader (from Chrome) and theAuthorizationheader (from the Clerk SDK).allowed_originsin the dashboard!Steps to Reproduce
git clone https://github.com/clerk/clerk-chrome-extension-demo.gitPLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY) and live frontend API (CLERK_FRONTEND_API). Do not set/rely onsyncHostsince Third-Party Sandbox blocking neutralizes it for many users anyway.pnpm run buildchrome-extension://...URL in "Allowed Redirect URIs" and "CORS Allowed Origins".OriginvsAuthorizationconflict error.The Temporary Workaround (For Users)
To bypass this architectural conflict in production apps without modifying Clerk's code, developers are currently forced to use Chrome's
declarativeNetRequestAPI to manually strip theOriginheader before it reaches Clerk:manifest.json:
clerk_rules.json:
[ { "id": 1, "priority": 1, "action": { "type": "modifyHeaders", "requestHeaders": [{ "header": "Origin", "operation": "remove" }] }, "condition": { "urlFilter": "||clerk.xxx.com/*", "resourceTypes": ["xmlhttprequest"] } } ]By stripping Chrome's native
Originheader, the Clerk backend only sees theAuthorizationheader and correctly resolves the request as "Native", bypassing the conflict.Expected Behavior
@clerk/chrome-extensionshould intelligently recognize that it is operating within a Chrome extension where theOriginheader is immutable, and coordinate with the Edge Network to permitAuthorization: Bearerrequests originating from whitelistedchrome-extension://origins, rather than rejecting them outright. Or, the SDK should defer entirely to cookies/CORS when the origin is known.