Skip to content

Commit

Permalink
fix(astro): Prevent Netlify from doing a handshake redirect loop (#4745)
Browse files Browse the repository at this point in the history
  • Loading branch information
wobsoriano authored Dec 11, 2024
1 parent e174858 commit 12f92e9
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/hungry-fishes-invent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@clerk/astro": patch
---

Fix handshake redirect loop in Netlify deployments
5 changes: 4 additions & 1 deletion packages/astro/src/integration/create-integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,10 @@ function createIntegration<Params extends HotloadAstroClerkIntegrationParams>()
'page',
`
${command === 'dev' ? `console.log("${packageName}","Initialize Clerk: page")` : ''}
import { runInjectionScript, swapDocument } from "${buildImportPath}";
import { removeNetlifyCacheBustParam, runInjectionScript, swapDocument } from "${buildImportPath}";
// Fix an issue with infinite redirect in Netlify and Clerk dev instance
removeNetlifyCacheBustParam();
// Taken from https://github.com/withastro/astro/blob/e10b03e88c22592fbb42d7245b65c4f486ab736d/packages/astro/src/transitions/router.ts#L39.
// Importing it directly from astro:transitions/client breaks custom client-side routing
Expand Down
1 change: 1 addition & 0 deletions packages/astro/src/internal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export { runInjectionScript };

export { generateSafeId } from './utils/generateSafeId';
export { swapDocument } from './swap-document';
export { NETLIFY_CACHE_BUST_PARAM, removeNetlifyCacheBustParam } from './remove-query-param';
13 changes: 13 additions & 0 deletions packages/astro/src/internal/remove-query-param.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const NETLIFY_CACHE_BUST_PARAM = '__netlify_clerk_cache_bust';

/**
* Removes the temporary cache bust parameter that prevents infinite redirects
* in Netlify and Clerk's dev instance.
*/
export function removeNetlifyCacheBustParam() {
const url = new URL(window.location.href);
if (url.searchParams.has(NETLIFY_CACHE_BUST_PARAM)) {
url.searchParams.delete(NETLIFY_CACHE_BUST_PARAM);
window.history.replaceState(window.history.state, '', url);
}
}
24 changes: 23 additions & 1 deletion packages/astro/src/server/clerk-middleware.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { AuthObject, ClerkClient } from '@clerk/backend';
import type { AuthenticateRequestOptions, ClerkRequest, RedirectFun, RequestState } from '@clerk/backend/internal';
import { AuthStatus, constants, createClerkRequest, createRedirect } from '@clerk/backend/internal';
import { isDevelopmentFromSecretKey } from '@clerk/shared/keys';
import { isDevelopmentFromPublishableKey, isDevelopmentFromSecretKey } from '@clerk/shared/keys';
import { isHttpOrHttps } from '@clerk/shared/proxy';
import { eventMethodCalled } from '@clerk/shared/telemetry';
import { handleValueOrFn } from '@clerk/shared/utils';
Expand All @@ -10,6 +10,7 @@ import type { APIContext } from 'astro';
// @ts-ignore
import { authAsyncStorage } from '#async-local-storage';

import { NETLIFY_CACHE_BUST_PARAM } from '../internal';
import { buildClerkHotloadScript } from './build-clerk-hotload-script';
import { clerkClient } from './clerk-client';
import { createCurrentUser } from './current-user';
Expand Down Expand Up @@ -83,6 +84,8 @@ export const clerkMiddleware: ClerkMiddleware = (...args: unknown[]): any => {

const locationHeader = requestState.headers.get(constants.Headers.Location);
if (locationHeader) {
handleNetlifyCacheInDevInstance(locationHeader, requestState);

const res = new Response(null, { status: 307, headers: requestState.headers });
return decorateResponseWithObservabilityHeaders(res, requestState);
} else if (requestState.status === AuthStatus.Handshake) {
Expand Down Expand Up @@ -234,6 +237,25 @@ Check if signInUrl is missing from your configuration or if it is not an absolut
PUBLIC_CLERK_SIGN_IN_URL='SOME_URL'
PUBLIC_CLERK_IS_SATELLITE='true'`;

/**
* Prevents infinite redirects in Netlify's functions
* by adding a cache bust parameter to the original redirect URL. This ensures Netlify
* doesn't serve a cached response during the authentication flow.
*/
function handleNetlifyCacheInDevInstance(locationHeader: string, requestState: RequestState) {
// Only run on Netlify environment and Clerk development instance
// eslint-disable-next-line turbo/no-undeclared-env-vars
if (import.meta.env.NETLIFY && isDevelopmentFromPublishableKey(requestState.publishableKey)) {
const hasHandshakeQueryParam = locationHeader.includes('__clerk_handshake');
// If location header is the original URL before the handshake redirects, add cache bust param
if (!hasHandshakeQueryParam) {
const url = new URL(locationHeader);
url.searchParams.append(NETLIFY_CACHE_BUST_PARAM, Date.now().toString());
requestState.headers.set('Location', url.toString());
}
}
}

function decorateAstroLocal(clerkRequest: ClerkRequest, context: APIContext, requestState: RequestState) {
const { reason, message, status, token } = requestState;
context.locals.authToken = token;
Expand Down

0 comments on commit 12f92e9

Please sign in to comment.