Skip to content

Commit d58fda9

Browse files
authored
chore(nextjs): Gracefully handle failure to create keyless (#5015)
1 parent 1345cb4 commit d58fda9

File tree

6 files changed

+47
-23
lines changed

6 files changed

+47
-23
lines changed

.changeset/kind-crabs-unite.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/nextjs': patch
3+
---
4+
5+
Gracefully handle failure to create keyless.

packages/nextjs/src/app-router/client/ClerkProvider.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,11 @@ const NextClientClerkProvider = (props: NextClerkProviderProps) => {
112112
);
113113
};
114114

115-
export const ClientClerkProvider = (props: NextClerkProviderProps) => {
116-
const { children, ...rest } = props;
115+
export const ClientClerkProvider = (props: NextClerkProviderProps & { disableKeyless?: boolean }) => {
116+
const { children, disableKeyless = false, ...rest } = props;
117117
const safePublishableKey = mergeNextClerkPropsWithEnv(rest).publishableKey;
118118

119-
if (safePublishableKey || !canUseKeyless) {
119+
if (safePublishableKey || !canUseKeyless || disableKeyless) {
120120
return <NextClientClerkProvider {...rest}>{children}</NextClientClerkProvider>;
121121
}
122122

packages/nextjs/src/app-router/client/keyless-creator-reader.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ export const KeylessCreatorOrReader = (props: NextClerkProviderProps) => {
1919
return React.cloneElement(children, {
2020
key: state?.publishableKey,
2121
publishableKey: state?.publishableKey,
22-
__internal_claimKeylessApplicationUrl: state?.claimUrl,
23-
__internal_copyInstanceKeysUrl: state?.apiKeysUrl,
22+
__internal_keyless_claimKeylessApplicationUrl: state?.claimUrl,
23+
__internal_keyless_copyInstanceKeysUrl: state?.apiKeysUrl,
2424
__internal_bypassMissingPublishableKey: true,
2525
} as any);
2626
};

packages/nextjs/src/app-router/keyless-actions.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { AccountlessApplication } from '@clerk/backend';
33
import { cookies, headers } from 'next/headers';
44
import { redirect, RedirectType } from 'next/navigation';
55

6+
import { errorThrower } from '../server/errorThrower';
67
import { detectClerkMiddleware } from '../server/headers-utils';
78
import { getKeylessCookieName } from '../server/keyless';
89
import { canUseKeyless } from '../utils/feature-flags';
@@ -35,9 +36,10 @@ export async function createOrReadKeylessAction(): Promise<null | Omit<Accountle
3536
return null;
3637
}
3738

38-
const result = await import('../server/keyless-node.js').then(m => m.createOrReadKeyless());
39+
const result = await import('../server/keyless-node.js').then(m => m.createOrReadKeyless()).catch(() => null);
3940

4041
if (!result) {
42+
errorThrower.throwMissingPublishableKeyError();
4143
return null;
4244
}
4345

packages/nextjs/src/app-router/server/ClerkProvider.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ export async function ClerkProvider(
8585
if (shouldRunAsKeyless) {
8686
// NOTE: Create or read keys on every render. Usually this means only on hard refresh or hard navigations.
8787

88-
const newOrReadKeys = await import('../../server/keyless-node.js').then(mod => mod.createOrReadKeyless());
88+
const newOrReadKeys = await import('../../server/keyless-node.js')
89+
.then(mod => mod.createOrReadKeyless())
90+
.catch(() => null);
8991
const { keylessLogger, createConfirmationMessage, createKeylessModeMessage } = await import(
9092
'../../server/keyless-log-cache.js'
9193
);
@@ -142,6 +144,18 @@ export async function ClerkProvider(
142144

143145
output = <KeylessCookieSync {...newOrReadKeys}>{clientProvider}</KeylessCookieSync>;
144146
}
147+
} else {
148+
// When case keyless should run, but keys are not available, then fallback to throwing for missing keys
149+
output = (
150+
<ClientClerkProvider
151+
{...mergeNextClerkPropsWithEnv(rest)}
152+
nonce={await generateNonce()}
153+
initialState={await generateStatePromise()}
154+
disableKeyless
155+
>
156+
{children}
157+
</ClientClerkProvider>
158+
);
145159
}
146160
}
147161

packages/nextjs/src/server/keyless-node.ts

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -123,15 +123,15 @@ const isFileWritingLocked = () => {
123123
return isCreatingFile || existsSync(CLERK_LOCK);
124124
};
125125

126-
async function createOrReadKeyless(): Promise<AccountlessApplication | undefined> {
126+
async function createOrReadKeyless(): Promise<AccountlessApplication | null> {
127127
const { writeFileSync, mkdirSync } = safeNodeRuntimeFs();
128128

129129
/**
130130
* If another request is already in the process of acquiring keys return early.
131131
* Using both an in-memory and file system lock seems to be the most effective solution.
132132
*/
133133
if (isFileWritingLocked()) {
134-
return undefined;
134+
return null;
135135
}
136136

137137
lockFileWriting();
@@ -156,26 +156,29 @@ async function createOrReadKeyless(): Promise<AccountlessApplication | undefined
156156
* At this step, it is safe to create new keys and store them.
157157
*/
158158
const client = createClerkClientWithOptions({});
159-
const accountlessApplication = await client.__experimental_accountlessApplications.createAccountlessApplication();
159+
const accountlessApplication = await client.__experimental_accountlessApplications
160+
.createAccountlessApplication()
161+
.catch(() => null);
160162

161-
writeFileSync(CONFIG_PATH, JSON.stringify(accountlessApplication), {
162-
encoding: 'utf8',
163-
mode: '0777',
164-
flag: 'w',
165-
});
163+
if (accountlessApplication) {
164+
writeFileSync(CONFIG_PATH, JSON.stringify(accountlessApplication), {
165+
encoding: 'utf8',
166+
mode: '0777',
167+
flag: 'w',
168+
});
166169

167-
// TODO-KEYLESS: Add link to official documentation.
168-
const README_NOTIFICATION = `
170+
// TODO-KEYLESS: Add link to official documentation.
171+
const README_NOTIFICATION = `
169172
## DO NOT COMMIT
170173
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.
171174
`;
172175

173-
writeFileSync(README_PATH, README_NOTIFICATION, {
174-
encoding: 'utf8',
175-
mode: '0777',
176-
flag: 'w',
177-
});
178-
176+
writeFileSync(README_PATH, README_NOTIFICATION, {
177+
encoding: 'utf8',
178+
mode: '0777',
179+
flag: 'w',
180+
});
181+
}
179182
/**
180183
* Clean up locks.
181184
*/

0 commit comments

Comments
 (0)