add-clerkeydnapp-wo.md
- wiki/engineering/clerk-auth/eydn-app-8b02e3a-add-cloudflare-turnstile-to-csp-for-clerk-captcha.md
- wiki/engineering/clerk-auth/eydn-app-a68fb64-proxy-clerk-through-own-domain-to-bypass-content-b.md
- wiki/engineering/clerk-auth/eydn-app-d2bfcb3-fix-clerk-proxy-use-built-in-frontendapiproxy-in-c.md
- wiki/engineering/clerk-auth/labelcheck-b17b657-fix-broken-clerk-component-rendering---proper-css.md
- wiki/engineering/clerk-auth/labelcheck-581190c-fix-clerk-sign-in---remove-problematic-global-border.md
- wiki/engineering/clerk-auth/labelcheck-34dc030-standardize-admin-authorization-across-all-routes.md
- wiki/engineering/clerk-auth/labelcheck-53973b6-fix-user-id-mapping-and-improve-admin-authenticati.md
- wiki/engineering/clerk-auth/labelcheck-09dac26-refactor-admin-system-to-use-database-based-roles.md
- wiki/engineering/clerk-auth/eydn-app-593a77a-add-playwright-auth-setup-and-dashboard-visual-tes.md
contradicting_sources:
- wiki/engineering/clerk-auth/eydn-app-db84008-revert-clerk-proxy-causing-auth-failure.md
- wiki/engineering/clerk-auth/labelcheck-11686de-revert-clerk-styling-changes---restore-to-working.md
contradicting_count: 2
first_seen: unknown
last_updated: "2025-01-31"
hypothesis: false
rate_of_change: high
web_monitoring_frequency: weekly
fragment_count: 29
evolution_timeline: []
evolution_start: null
superseded_by: null
superseded_date: null
deprecation_notice: null
tags:
- auth
- clerk
- nextjs
- csp
- supabase
Summary
CSP misconfiguration is the single most common reason Clerk components fail silently in production — the component renders nothing, no console error surfaces in the app, and the fix requires adding a non-obvious set of domains across five CSP directives. Global CSS universal selectors (* { @apply ... }) are the second most common breakage vector, corrupting Clerk's internal component styles in ways that look like a Clerk bug but aren't. Admin authorization built on Clerk publicMetadata alone creates bypass vulnerabilities; database-backed role checks are the correct pattern. When integrating Clerk with Supabase, Clerk user IDs and Supabase internal user IDs are not interchangeable — using the wrong one silently breaks data queries.
TL;DR
What we've learned
- CSP needs entries across five directives to support Clerk: script-src, style-src, connect-src, frame-src, and worker-src blob:. Missing any one of them causes silent component failure in production.
- Global CSS selectors targeting * corrupt Clerk's internal elements. Scope them with :not([class*='cl-']) or remove them entirely.
- Admin authorization must be database-backed. Clerk publicMetadata is acceptable for feature gating but not for access control decisions.
- Clerk user IDs and Supabase internal user IDs are different things. Queries against Supabase using a Clerk ID will fail silently.
- Clerk v7's frontendApiProxy option in clerkMiddleware is the correct way to proxy Clerk through your own domain — manual proxy setup breaks with "Invalid host" unless the domain is registered in the Clerk dashboard.
External insights
No external sources ingested yet for this topic.
Common Failure Modes
1. Clerk component renders blank in production — CSP is blocking it
Established failure mode across Eydn and LabelCheck. The symptom is a completely empty sign-in or sign-up page in production with no visible error. The browser console shows CSP violations, but these are easy to miss if you're not looking.
Clerk requires entries across five CSP directives. When using a custom Clerk domain (e.g., clerk.eydn.app), all five need that domain explicitly:
script-src 'self' https://clerk.eydn.app https://*.clerk.com;
style-src 'self' 'unsafe-inline' https://*.clerk.accounts.dev https://*.clerk.com;
connect-src 'self' https://clerk.eydn.app https://*.clerk.com;
frame-src 'self' https://clerk.eydn.app;
worker-src 'self' blob:;
The worker-src 'self' blob: entry is non-obvious — Clerk uses blob: workers internally and this directive is frequently omitted. Clerk also uses Cloudflare Turnstile for bot protection, which requires challenges.cloudflare.com and *.turnstile.com in frame-src and script-src.
The failed_to_load_clerk_js error in the browser console confirms CSP is the cause. In Eydn, broadening CSP also required adding *.google.com, *.googleadservices.com, and *.doubleclick.net because Clerk's sign-up flow triggers Google tracking requests that CSP was blocking as a side effect.
2. Global CSS * { @apply ... } breaks Clerk component styling
Consistent across LabelCheck (hit this three times in separate commits). A global universal selector like:
* {
@apply border-border;
}
applies to every element in the DOM, including Clerk's internal component elements. The result is broken layouts, missing borders, and corrupted form rendering that looks like a Clerk rendering bug.
Two valid fixes. The surgical fix scopes the selector away from Clerk:
*:not([class*='cl-']) {
@apply border-border;
}
The blunt fix removes the universal selector entirely and applies the style only to the elements that actually need it. Both work. The :not([class*='cl-']) approach is preferable when the global style is genuinely needed across many app elements.
3. Content blockers (uBlock Origin, Brave) cause blank sign-in pages
Observed in Eydn. When Clerk JS loads from a third-party domain (clerk.accounts.dev or similar), content blockers intercept the request and the sign-in page renders empty. The user sees nothing — no error, no fallback.
Two mitigations:
Short-term: Add a ClerkFallback component that shows a loading spinner immediately and a troubleshooting message after 5 seconds. This at least tells the user something is wrong.
Long-term: Proxy Clerk through your own domain using Clerk v7's built-in frontendApiProxy option in clerkMiddleware (see Gotchas section for why manual proxy setup fails).
4. Admin authorization bypass from mixed Clerk metadata / database checks
Observed in LabelCheck. When some routes check publicMetadata.role === 'admin' and others check a database is_admin field, the authorization surface is inconsistent. An attacker (or a bug) that manipulates one source doesn't affect the other, creating bypass paths.
The fix is a centralized requireAdmin() helper that checks a single source of truth — the database is_system_admin field — and is called identically across all admin routes. In LabelCheck this refactor touched 6 files and 5 admin routes.
Clerk publicMetadata is acceptable as a convenience layer for feature gating (e.g., granting unlimited analysis access to admin users), but it must not be the authoritative check for access control decisions.
5. Clerk user ID ≠ Supabase internal user ID
Observed in LabelCheck. When querying Supabase with a Clerk user ID (format: user_xxxxxxxx), the query returns no results because Supabase's internal user table uses its own UUID. The failure is silent — no error, just empty data in the dashboard and API responses.
The fix is to maintain an explicit mapping table or always resolve the Supabase internal ID from the Clerk ID at the boundary (e.g., in middleware or a shared auth helper) before passing it to any Supabase query.
6. <SignedIn> / <SignedOut> Show components leave UI empty on initial load
Observed in Eydn. Clerk's <SignedIn> and <SignedOut> components don't render until auth state resolves from the server. On a public landing page, this means navigation buttons (Sign In, Start Free) are invisible for a flash on every page load.
The fix for public landing pages: don't use Show components for navigation buttons at all. Render them as plain links unconditionally. Clerk will redirect authenticated users to the dashboard when they click Sign In anyway — that redirect is the correct behavior, not a problem to prevent.
7. UserButton renders oversized without explicit size constraints
Observed in LabelCheck. The Clerk <UserButton /> component doesn't constrain its own size and will expand to fill its container. In a navigation header this produces an oversized avatar that breaks the layout.
Fix: wrap in an explicit size container:
<div className="h-8 w-8">
<UserButton />
</div>
What Works
Clerk v7 frontendApiProxy for content blocker bypass
In Eydn, the correct way to proxy Clerk through your own domain is via the built-in frontendApiProxy option in clerkMiddleware (Clerk v7+). This handles header forwarding, body streaming, and redirect rewriting automatically — things a manual Next.js rewrite rule gets wrong. See the Gotchas section for why manual proxy setup fails.
useAuth() hook to prevent sign-in flash and redirect loops
In Eydn, using useAuth() to detect active session state before rendering sign-in UI prevents the flash where an already-authenticated user briefly sees the sign-in page before being redirected. Check isSignedIn from useAuth() and conditionally render a "Go to Dashboard" button instead of the sign-in form.
@clerk/testing + Playwright for E2E auth setup
In Eydn, the @clerk/testing library handles auth setup for Playwright by signing in once and saving storageState. Test execution uses E2E_CLERK_USER_EMAIL and E2E_CLERK_USER_PASSWORD environment variables. This produced a 67-test suite (1 setup + 21 public + 45 dashboard tests) without needing to re-authenticate per test.
Appearance API for branded sign-in pages
In Eydn, Clerk's appearance prop accepts Tailwind class names for card shadow, borders, buttons, and inputs — enough to match a custom brand without fighting the component's internal styles. Pair with NEXT_PUBLIC_CLERK_SIGN_IN_URL and NEXT_PUBLIC_CLERK_SIGN_UP_URL env vars pointing to your custom routes.
Session-based auth over hardcoded bearer tokens
Consistent across AsymXray and LabelCheck. API calls from frontend components should use credentials: 'include' and rely on Clerk's session cookie, not hardcoded bearer tokens embedded in component code. Hardcoded tokens are a security vulnerability and break when tokens rotate.
Gotchas and Edge Cases
Manual Clerk proxy setup fails with "Invalid host" unless the proxy domain is registered in the Clerk dashboard. In Eydn, setting NEXT_PUBLIC_CLERK_PROXY_URL to the app's own domain caused auth failures immediately. The domain must be explicitly registered in the Clerk dashboard before the proxy works. The v7 frontendApiProxy option in clerkMiddleware is the correct path — it doesn't require manual domain registration in the same way. [13]
Clerk uses blob: workers internally. This is not documented prominently. If your CSP has a worker-src directive at all, it must include blob: or Clerk's internal workers will be blocked silently. [14]
Clerk uses Cloudflare Turnstile for bot protection. This is a third-party dependency you don't control. challenges.cloudflare.com and *.turnstile.com must be in your CSP frame-src and script-src. If you have a strict CSP, Turnstile challenges will silently fail and users may be unable to complete sign-up. [15]
Clerk test mode cookies need JS access. The general rule of HttpOnly on all cookies has an exception for Clerk test mode, which requires JS-accessible cookies. Applying HttpOnly to Clerk's test cookies breaks the test flow. [16]
min-h-screen on sign-in/sign-up page containers conflicts with root flex layout. In LabelCheck, adding min-h-screen to the sign-in page wrapper caused a blank screen because the root layout was already a flex container. The page div expanded to fill the flex parent and then min-h-screen pushed content off-screen. [17]
Clerk dev mode works on production domains. The assumption that dev mode requires a dev/localhost domain is wrong. In LabelCheck, redirect loops in dev mode were caused by environment variable mismatches (NEXT_PUBLIC_CLERK_SIGN_IN_URL pointing to the wrong path), not by using dev mode on a production domain. [18]
Where Docs Disagree With Practice
Docs imply Clerk components are style-isolated; in practice they inherit global CSS. Clerk's documentation presents its components as encapsulated, but global universal selectors (*) reach inside Clerk's internal elements and corrupt their styles. The :not([class*='cl-']) workaround is not in the official docs — it was discovered through repeated breakage in LabelCheck. [19]
Docs don't enumerate the full CSP surface. The Clerk documentation mentions adding Clerk domains to CSP but doesn't enumerate all five directives or call out worker-src blob: and Cloudflare Turnstile as separate requirements. In practice, getting CSP right for Clerk required 4+ separate commits across Eydn alone. [20]
Styling approach reversed between attempts in LabelCheck. An initial fix added Clerk appearance API configuration to resolve styling issues; a subsequent commit reverted all of it because the simpler approach (allowing unsafe-inline and letting Clerk use its own inline styles) worked without the custom configuration. The docs don't make clear that Clerk's inline styles are self-sufficient when CSP allows unsafe-inline — the appearance API is additive, not required. [21]
Tool and Version Notes
Clerk v7 introduced a native frontendApiProxy option in clerkMiddleware that handles proxy setup automatically. Manual proxy configuration via NEXT_PUBLIC_CLERK_PROXY_URL and Next.js rewrites predates this and is error-prone — prefer the v7 built-in. [8]
@clerk/testing is a separate package from @clerk/nextjs and must be installed independently for Playwright/E2E auth setup. It is not included in the main Clerk SDK. [10]
Clerk keyless mode allows running Clerk auth without API keys during development. Observed in Eydn for the wedding planner app. Suitable for prototyping; requires switching to keyed mode before production. [22]
Dev-to-production migration timing (single-project finding): LabelCheck's internal guide recommends migrating from Clerk dev mode to production after 5–10 paying customers and 2+ weeks of stability — roughly 2–4 weeks post-launch. This is an internal heuristic, not a Clerk recommendation. [23]
Related Topics
- Supabase Auth Integration
- Nextjs Middleware
- Nextjs Csp Configuration
- Playwright E2E Auth
- Coolify Environment Variables
Sources
Synthesized from 29 fragments: 29 git commits across AsymXray, Eydn, and LabelCheck. No external sources ingested yet. Date range: unknown.
Sources
- Eydn App A1Bf95C Fix Csp Blocking Clerk Sign In Add Clerkeydnapp Wo, Eydn App 8B02E3A Add Cloudflare Turnstile To Csp For Clerk Captcha, Labelcheck 73D6F34 Fix Clerk Styling Csp Was Blocking External Styl, Eydn App 3A5F82E Broaden Csp To Fix Clerk Sign Up And Google Tracki ↩
- Labelcheck B17B657 Fix Broken Clerk Component Rendering Proper Css, Labelcheck 581190C Fix Clerk Sign In Remove Problematic Global Border, Labelcheck 774572E Fix Clerk Component Styling Conflicts And Renderin ↩
- Eydn App 7092F16 Add Loading Fallback To Sign In And Sign Up Pages, Eydn App A68Fb64 Proxy Clerk Through Own Domain To Bypass Content B ↩
- Labelcheck 34Dc030 Standardize Admin Authorization Across All Routes, Labelcheck 09Dac26 Refactor Admin System To Use Database Based Roles, Labelcheck E0Bc36D Add Unlimited Analysis Access For Admin Users ↩
- Labelcheck 53973B6 Fix User Id Mapping And Improve Admin Authenticati ↩
- Eydn App 557Ffbf Fix Nav Buttons Always Visible Remove Clerk Show D ↩
- Labelcheck 0F3D56B Fix Oversized Userbutton In Navigation Header ↩
- Eydn App D2Bfcb3 Fix Clerk Proxy Use Built In Frontendapiproxy In C ↩
- Eydn App 6A19Fa2 Fix Sign In Flash Show Dashboard Button When Alrea ↩
- Eydn App 593A77A Add Playwright Auth Setup And Dashboard Visual Tes ↩
- Eydn App 9953Ee5 Add Branded Sign In And Sign Up Pages With Forestc ↩
- Asymxray 857265B Replace Hardcoded Bearer Tokens With Proper Sessio, Labelcheck 3068709 Fix Cookie Security With Proper Set Cookie Header ↩
- Eydn App Db84008 Revert Clerk Proxy Causing Auth Failure ↩
- Eydn App A1Bf95C Fix Csp Blocking Clerk Sign In Add Clerkeydnapp Wo ↩
- Eydn App 8B02E3A Add Cloudflare Turnstile To Csp For Clerk Captcha ↩
- Labelcheck 3068709 Fix Cookie Security With Proper Set Cookie Header ↩
- Labelcheck Ad7E1E4 Fix Sign Up Page Blank Screen And Dashboard Displa ↩
- Labelcheck 40F2090 Document Clerk Development Mode Configuration ↩
- Labelcheck B17B657 Fix Broken Clerk Component Rendering Proper Css, Labelcheck 581190C Fix Clerk Sign In Remove Problematic Global Border ↩
- Eydn App A1Bf95C Fix Csp Blocking Clerk Sign In Add Clerkeydnapp Wo, Eydn App 8B02E3A Add Cloudflare Turnstile To Csp For Clerk Captcha, Eydn App 8398E2E Broaden Csp To Allow All Clerk Domains Clerkcom ↩
- Labelcheck Ad7E1E4 Fix Sign Up Page Blank Screen And Dashboard Displa, Labelcheck 11686De Revert Clerk Styling Changes Restore To Working ↩
- Eydn App Faa18Aa Add Wedding Planner App With Clerk Auth Supabase A ↩
- Labelcheck F4D565F Add Comprehensive Clerk Production Migration Guide ↩