diff --git a/frontend/src/auth/clerk.tsx b/frontend/src/auth/clerk.tsx
index 691b3a0e..fed6cf24 100644
--- a/frontend/src/auth/clerk.tsx
+++ b/frontend/src/auth/clerk.tsx
@@ -17,11 +17,26 @@ import {
import type { ComponentProps } from "react";
+export function isValidClerkPublishableKey(key: string | undefined): key is string {
+ if (!key) return false;
+ // Clerk publishable keys look like: pk_test_... or pk_live_...
+ // In CI we want builds to stay secretless; if the key isn't present/valid,
+ // we skip Clerk entirely so `next build` can prerender.
+ //
+ // Note: Clerk appears to validate key *contents*, not just shape. We therefore
+ // use a conservative heuristic to avoid treating obvious placeholders as valid.
+ const m = /^pk_(test|live)_([A-Za-z0-9]+)$/.exec(key);
+ if (!m) return false;
+ const body = m[2];
+ if (body.length < 16) return false;
+ if (/^0+$/.test(body)) return false;
+ return true;
+}
+
export function isClerkEnabled(): boolean {
- // Invariant: Clerk is disabled ONLY when the publishable key is absent.
- // If a key is present, we assume Clerk is intended to be enabled and we let
- // Clerk fail fast if the key is invalid/misconfigured.
- return Boolean(process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY);
+ // IMPORTANT: keep this in sync with AuthProvider; otherwise components like
+ // may render without a and crash during prerender.
+ return isValidClerkPublishableKey(process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY);
}
export function SignedIn(props: { children: ReactNode }) {
diff --git a/frontend/src/components/providers/AuthProvider.tsx b/frontend/src/components/providers/AuthProvider.tsx
index 4da743eb..30000cf1 100644
--- a/frontend/src/components/providers/AuthProvider.tsx
+++ b/frontend/src/components/providers/AuthProvider.tsx
@@ -3,26 +3,12 @@
import { ClerkProvider } from "@clerk/nextjs";
import type { ReactNode } from "react";
-function isLikelyValidClerkPublishableKey(key: string | undefined): key is string {
- if (!key) return false;
- // Clerk publishable keys look like: pk_test_... or pk_live_...
- // In CI we want builds to stay secretless; if the key isn't present/valid,
- // we skip Clerk entirely so `next build` can prerender.
- //
- // Note: Clerk appears to validate key *contents*, not just shape. We therefore
- // use a conservative heuristic to avoid treating obvious placeholders as valid.
- const m = /^pk_(test|live)_([A-Za-z0-9]+)$/.exec(key);
- if (!m) return false;
- const body = m[2];
- if (body.length < 16) return false;
- if (/^0+$/.test(body)) return false;
- return true;
-}
+import { isValidClerkPublishableKey } from "@/auth/clerk";
export function AuthProvider({ children }: { children: ReactNode }) {
const publishableKey = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY;
- if (!isLikelyValidClerkPublishableKey(publishableKey)) {
+ if (!isValidClerkPublishableKey(publishableKey)) {
return <>{children}>;
}