fix: allow custom Clerk frontend API domain in CSP (NEXT_PUBLIC_CLERK_FAPI_HOST)
Some checks are pending
CI / build (push) Waiting to run

This commit is contained in:
dimitar 2026-08-03 19:43:28 +02:00
parent 2af9c823fb
commit 89b1dc9a51
3 changed files with 25 additions and 4 deletions

View File

@ -13,6 +13,7 @@ RUN apk add --no-cache openssl
# them via build args (Coolify: mark them as build variables) or the browser # them via build args (Coolify: mark them as build variables) or the browser
# ClerkProvider / public URLs will be missing. # ClerkProvider / public URLs will be missing.
ARG NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY ARG NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
ARG NEXT_PUBLIC_CLERK_FAPI_HOST
ARG NEXT_PUBLIC_CLERK_SIGN_IN_URL ARG NEXT_PUBLIC_CLERK_SIGN_IN_URL
ARG NEXT_PUBLIC_CLERK_SIGN_UP_URL ARG NEXT_PUBLIC_CLERK_SIGN_UP_URL
ARG NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL ARG NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL

View File

@ -108,6 +108,9 @@ DATABASE_URL=postgresql://postgres:YOUR_PASSWORD@spomeniqr-db:5432/monuments
# Clerk # Clerk
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_... NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_...
CLERK_SECRET_KEY=sk_live_... CLERK_SECRET_KEY=sk_live_...
# Custom Clerk frontend API domain (if configured in Clerk dashboard) — the CSP
# allowlist needs it. Build variable. Omit if not using a custom domain.
NEXT_PUBLIC_CLERK_FAPI_HOST=clerk.testbed.mk
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/ NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/
@ -148,7 +151,10 @@ NODE_ENV=production
node -e "import('bcryptjs').then(b => b.default.hash('YOUR_PASSWORD', 12).then(console.log))" node -e "import('bcryptjs').then(b => b.default.hash('YOUR_PASSWORD', 12).then(console.log))"
``` ```
- Mark **every `NEXT_PUBLIC_*` variable as a build variable** (checkbox) so it is - Mark **every `NEXT_PUBLIC_*` variable as a build variable** (checkbox) so it is
inlined into the client bundle at `next build`. Server-only vars inlined into the client bundle at `next build` and visible to `next.config.ts`.
`NEXT_PUBLIC_CLERK_FAPI_HOST` in particular must be a build variable — the CSP
is generated at build time and will otherwise block Clerk JS if you use a
custom Clerk frontend API domain. Server-only vars
(`CLERK_SECRET_KEY`, `DATABASE_URL`, `ADMIN_SESSION_SECRET`, (`CLERK_SECRET_KEY`, `DATABASE_URL`, `ADMIN_SESSION_SECRET`,
`SUPER_ADMIN_PASSWORD_HASH`, `S3_*`) stay as normal runtime variables. `SUPER_ADMIN_PASSWORD_HASH`, `S3_*`) stay as normal runtime variables.
- The `super` admin row is provisioned automatically on container start by - The `super` admin row is provisioned automatically on container start by
@ -344,6 +350,7 @@ npx prisma db push
|----------|----------|-------------| |----------|----------|-------------|
| `DATABASE_URL` | Yes | PostgreSQL connection string (Coolify internal) | | `DATABASE_URL` | Yes | PostgreSQL connection string (Coolify internal) |
| `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` | Yes | Clerk publishable key (`pk_live_...`) | | `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` | Yes | Clerk publishable key (`pk_live_...`) |
| `NEXT_PUBLIC_CLERK_FAPI_HOST` | No* | Custom Clerk frontend API domain (`clerk.testbed.mk`) — required when using a custom domain; build variable |
| `CLERK_SECRET_KEY` | Yes | Clerk secret key (`sk_live_...`) | | `CLERK_SECRET_KEY` | Yes | Clerk secret key (`sk_live_...`) |
| `NEXT_PUBLIC_CLERK_SIGN_IN_URL` | Yes | `/sign-in` | | `NEXT_PUBLIC_CLERK_SIGN_IN_URL` | Yes | `/sign-in` |
| `NEXT_PUBLIC_CLERK_SIGN_UP_URL` | Yes | `/sign-up` | | `NEXT_PUBLIC_CLERK_SIGN_UP_URL` | Yes | `/sign-up` |

View File

@ -4,9 +4,12 @@ const s3Host = process.env.S3_ENDPOINT
? new URL(process.env.S3_ENDPOINT).hostname ? new URL(process.env.S3_ENDPOINT).hostname
: ""; : "";
// Derive the Clerk frontend API host from the publishable key so the // Derive the Clerk frontend API host so the CSP allowlist always matches the
// CSP allowlist always matches the active environment. Clerk supports // active environment. When a custom Clerk frontend API domain is configured in
// two publishable key formats: // the Clerk dashboard (e.g. clerk.testbed.mk), it is resolved at runtime by
// clerk-js and can NOT be derived from the publishable key — so it must be
// provided explicitly via NEXT_PUBLIC_CLERK_FAPI_HOST. Otherwise Clerk
// supports two publishable key formats:
// //
// 1) "Encoded" form (older): pk_test_<base64slug>$ // 1) "Encoded" form (older): pk_test_<base64slug>$
// The base64 portion decodes to "<slug>.clerk.accounts.dev" (test) // The base64 portion decodes to "<slug>.clerk.accounts.dev" (test)
@ -17,6 +20,16 @@ const s3Host = process.env.S3_ENDPOINT
// The slug may itself contain hyphens and digits, so only the // The slug may itself contain hyphens and digits, so only the
// final dash-group is captured as the suffix. // final dash-group is captured as the suffix.
function clerkFrontendApiHost(): string | null { function clerkFrontendApiHost(): string | null {
const custom = process.env.NEXT_PUBLIC_CLERK_FAPI_HOST?.trim();
if (custom) {
try {
const host = new URL(custom.includes("://") ? custom : `https://${custom}`).hostname;
if (host) return host;
} catch {
// fall through to derivation from the publishable key
}
}
const key = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY; const key = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY;
if (!key) return null; if (!key) return null;