Skip to content

Commit e64e5f0

Browse files
committed
Improve satellite redirect loop diagnostics
1 parent 0f8aed2 commit e64e5f0

2 files changed

Lines changed: 42 additions & 1 deletion

File tree

packages/backend/src/tokens/__tests__/request.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,40 @@ describe('tokens.authenticateRequest(options)', () => {
954954
});
955955
});
956956

957+
test('cookieToken: logs satellite-domain guidance when satellite sync enters a redirect loop', async () => {
958+
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
959+
960+
const requestState = await authenticateRequest(
961+
mockRequestWithCookies(
962+
{ ...defaultHeaders, 'sec-fetch-dest': 'document' },
963+
{
964+
__client_uat: '0',
965+
__clerk_redirect_count: '3',
966+
},
967+
`http://satellite.example/path?__clerk_synced=false`,
968+
),
969+
mockOptions({
970+
secretKey: 'deadbeef',
971+
publishableKey: PK_LIVE,
972+
signInUrl: 'https://primary.example/sign-in',
973+
isSatellite: true,
974+
domain: 'satellite.example',
975+
}),
976+
);
977+
978+
expect(requestState).toBeSignedOut({
979+
reason: AuthErrorReason.SatelliteCookieNeedsSyncing,
980+
isSatellite: true,
981+
domain: 'satellite.example',
982+
signInUrl: 'https://primary.example/sign-in',
983+
});
984+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Satellite-domain authentication'));
985+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('configured primary or satellite domain'));
986+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('preview deployments'));
987+
988+
consoleSpy.mockRestore();
989+
});
990+
957991
test('cookieToken: triggers handshake when satelliteAutoSync is not set but __clerk_synced=false is present - dev', async () => {
958992
const requestState = await authenticateRequest(
959993
mockRequestWithCookies(

packages/backend/src/tokens/request.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ export const authenticateRequest: AuthenticateRequest = (async (
338338
// proceed with triggering handshake.
339339
const isRedirectLoop = handshakeService.checkAndTrackRedirectLoop(handshakeHeaders);
340340
if (isRedirectLoop) {
341-
const msg = `Clerk: Refreshing the session token resulted in an infinite redirect loop. This usually means that your Clerk instance keys do not match - make sure to copy the correct publishable and secret keys from the Clerk dashboard.`;
341+
const msg = getHandshakeRedirectLoopMessage(reason);
342342
console.log(msg);
343343
return signedOut({
344344
tokenType: TokenType.SessionToken,
@@ -351,6 +351,13 @@ export const authenticateRequest: AuthenticateRequest = (async (
351351
return handshake(authenticateContext, reason, message, handshakeHeaders);
352352
}
353353

354+
function getHandshakeRedirectLoopMessage(reason: string): string {
355+
if (reason === AuthErrorReason.SatelliteCookieNeedsSyncing) {
356+
return `Clerk: Satellite-domain authentication resulted in an infinite redirect loop. Check that this request is using a configured primary or satellite domain for the production instance. For preview deployments, use a development/staging Clerk instance or a supported configured preview-domain setup.`;
357+
}
358+
return `Clerk: Refreshing the session token resulted in an infinite redirect loop. This usually means that your Clerk instance keys do not match - make sure to copy the correct publishable and secret keys from the Clerk dashboard.`;
359+
}
360+
354361
/**
355362
* Determines if a handshake must occur to resolve a mismatch between the organization as specified
356363
* by the URL (according to the options) and the actual active organization on the session.

0 commit comments

Comments
 (0)