From fd9cfd5b04a08183d2473baf56e4edca9b5f195b Mon Sep 17 00:00:00 2001 From: panteliselef Date: Mon, 27 Jan 2025 17:10:56 +0200 Subject: [PATCH] chore(nextjs): Gracefully handle failure to create keyless --- .changeset/kind-crabs-unite.md | 5 +++ .../src/app-router/client/ClerkProvider.tsx | 6 ++-- .../client/keyless-creator-reader.tsx | 4 +-- .../nextjs/src/app-router/keyless-actions.ts | 4 ++- .../src/app-router/server/ClerkProvider.tsx | 16 ++++++++- packages/nextjs/src/server/keyless-node.ts | 35 ++++++++++--------- 6 files changed, 47 insertions(+), 23 deletions(-) create mode 100644 .changeset/kind-crabs-unite.md diff --git a/.changeset/kind-crabs-unite.md b/.changeset/kind-crabs-unite.md new file mode 100644 index 00000000000..da62a4f65be --- /dev/null +++ b/.changeset/kind-crabs-unite.md @@ -0,0 +1,5 @@ +--- +'@clerk/nextjs': patch +--- + +Gracefully handle failure to create keyless. diff --git a/packages/nextjs/src/app-router/client/ClerkProvider.tsx b/packages/nextjs/src/app-router/client/ClerkProvider.tsx index 57b3f7db58f..9b12d1c84a6 100644 --- a/packages/nextjs/src/app-router/client/ClerkProvider.tsx +++ b/packages/nextjs/src/app-router/client/ClerkProvider.tsx @@ -123,11 +123,11 @@ const NextClientClerkProvider = (props: NextClerkProviderProps) => { ); }; -export const ClientClerkProvider = (props: NextClerkProviderProps) => { - const { children, ...rest } = props; +export const ClientClerkProvider = (props: NextClerkProviderProps & { disableKeyless?: boolean }) => { + const { children, disableKeyless = false, ...rest } = props; const safePublishableKey = mergeNextClerkPropsWithEnv(rest).publishableKey; - if (safePublishableKey || !canUseKeyless) { + if (safePublishableKey || !canUseKeyless || disableKeyless) { return {children}; } diff --git a/packages/nextjs/src/app-router/client/keyless-creator-reader.tsx b/packages/nextjs/src/app-router/client/keyless-creator-reader.tsx index 6b575fe0781..fb986f74bbc 100644 --- a/packages/nextjs/src/app-router/client/keyless-creator-reader.tsx +++ b/packages/nextjs/src/app-router/client/keyless-creator-reader.tsx @@ -19,8 +19,8 @@ export const KeylessCreatorOrReader = (props: NextClerkProviderProps) => { return React.cloneElement(children, { key: state?.publishableKey, publishableKey: state?.publishableKey, - __internal_claimKeylessApplicationUrl: state?.claimUrl, - __internal_copyInstanceKeysUrl: state?.apiKeysUrl, + __internal_keyless_claimKeylessApplicationUrl: state?.claimUrl, + __internal_keyless_copyInstanceKeysUrl: state?.apiKeysUrl, __internal_bypassMissingPublishableKey: true, } as any); }; diff --git a/packages/nextjs/src/app-router/keyless-actions.ts b/packages/nextjs/src/app-router/keyless-actions.ts index 1512a85aa24..cd4ff10e23d 100644 --- a/packages/nextjs/src/app-router/keyless-actions.ts +++ b/packages/nextjs/src/app-router/keyless-actions.ts @@ -3,6 +3,7 @@ import type { AccountlessApplication } from '@clerk/backend'; import { cookies, headers } from 'next/headers'; import { redirect, RedirectType } from 'next/navigation'; +import { errorThrower } from '../server/errorThrower'; import { detectClerkMiddleware } from '../server/headers-utils'; import { getKeylessCookieName } from '../server/keyless'; import { canUseKeyless } from '../utils/feature-flags'; @@ -35,9 +36,10 @@ export async function createOrReadKeylessAction(): Promise m.createOrReadKeyless()); + const result = await import('../server/keyless-node.js').then(m => m.createOrReadKeyless()).catch(() => null); if (!result) { + errorThrower.throwMissingPublishableKeyError(); return null; } diff --git a/packages/nextjs/src/app-router/server/ClerkProvider.tsx b/packages/nextjs/src/app-router/server/ClerkProvider.tsx index 02e039fdc76..96db748ec7d 100644 --- a/packages/nextjs/src/app-router/server/ClerkProvider.tsx +++ b/packages/nextjs/src/app-router/server/ClerkProvider.tsx @@ -85,7 +85,9 @@ export async function ClerkProvider( if (shouldRunAsKeyless) { // NOTE: Create or read keys on every render. Usually this means only on hard refresh or hard navigations. - const newOrReadKeys = await import('../../server/keyless-node.js').then(mod => mod.createOrReadKeyless()); + const newOrReadKeys = await import('../../server/keyless-node.js') + .then(mod => mod.createOrReadKeyless()) + .catch(() => null); const { keylessLogger, createConfirmationMessage, createKeylessModeMessage } = await import( '../../server/keyless-log-cache.js' ); @@ -142,6 +144,18 @@ export async function ClerkProvider( output = {clientProvider}; } + } else { + // When case keyless should run, but keys are not available, then fallback to throwing for missing keys + output = ( + + {children} + + ); } } diff --git a/packages/nextjs/src/server/keyless-node.ts b/packages/nextjs/src/server/keyless-node.ts index f83f88028f2..8cedd257b76 100644 --- a/packages/nextjs/src/server/keyless-node.ts +++ b/packages/nextjs/src/server/keyless-node.ts @@ -123,7 +123,7 @@ const isFileWritingLocked = () => { return isCreatingFile || existsSync(CLERK_LOCK); }; -async function createOrReadKeyless(): Promise { +async function createOrReadKeyless(): Promise { const { writeFileSync, mkdirSync } = safeNodeRuntimeFs(); /** @@ -131,7 +131,7 @@ async function createOrReadKeyless(): Promise null); - writeFileSync(CONFIG_PATH, JSON.stringify(accountlessApplication), { - encoding: 'utf8', - mode: '0777', - flag: 'w', - }); + if (accountlessApplication) { + writeFileSync(CONFIG_PATH, JSON.stringify(accountlessApplication), { + encoding: 'utf8', + mode: '0777', + flag: 'w', + }); - // TODO-KEYLESS: Add link to official documentation. - const README_NOTIFICATION = ` + // TODO-KEYLESS: Add link to official documentation. + const README_NOTIFICATION = ` ## DO NOT COMMIT This directory is auto-generated from \`@clerk/nextjs\` because you are running in Keyless mode. Avoid committing the \`.clerk/\` directory as it includes the secret key of the unclaimed instance. `; - writeFileSync(README_PATH, README_NOTIFICATION, { - encoding: 'utf8', - mode: '0777', - flag: 'w', - }); - + writeFileSync(README_PATH, README_NOTIFICATION, { + encoding: 'utf8', + mode: '0777', + flag: 'w', + }); + } /** * Clean up locks. */