diff --git a/.changeset/itchy-ways-tell.md b/.changeset/itchy-ways-tell.md index d0a785d381c..a1c7ee93502 100644 --- a/.changeset/itchy-ways-tell.md +++ b/.changeset/itchy-ways-tell.md @@ -2,4 +2,4 @@ "@clerk/shared": patch --- -Use shared safe environment variable access in Netlify cache handler +Make sure `process` is defined in environment when checking if application is running on Netlify. diff --git a/packages/shared/src/__tests__/netlifyCacheHandler.test.ts b/packages/shared/src/__tests__/netlifyCacheHandler.test.ts index 1bc2560ed16..b5bc9111cdf 100644 --- a/packages/shared/src/__tests__/netlifyCacheHandler.test.ts +++ b/packages/shared/src/__tests__/netlifyCacheHandler.test.ts @@ -60,4 +60,24 @@ describe('handleNetlifyCacheInDevInstance', () => { expect(requestStateHeaders.get('Location')).toBe('https://example.netlify.app'); }); + + it('should ignore the URL environment variable if it is not a string', () => { + // @ts-expect-error - Random object + process.env.URL = {}; + process.env.NETLIFY = 'true'; + + const requestStateHeaders = new Headers({ + Location: 'https://example.netlify.app', + }); + const locationHeader = requestStateHeaders.get('Location') || ''; + + handleNetlifyCacheInDevInstance({ + locationHeader, + requestStateHeaders, + publishableKey: mockPublishableKey, + }); + + const locationUrl = new URL(requestStateHeaders.get('Location') || ''); + expect(locationUrl.searchParams.has(CLERK_NETLIFY_CACHE_BUST_PARAM)).toBe(true); + }); }); diff --git a/packages/shared/src/getEnvVariable.ts b/packages/shared/src/getEnvVariable.ts index 795a016f902..5d3904a2d81 100644 --- a/packages/shared/src/getEnvVariable.ts +++ b/packages/shared/src/getEnvVariable.ts @@ -46,6 +46,5 @@ export const getEnvVariable = (name: string, context?: Record): str } catch { // This will raise an error in Cloudflare Pages } - return ''; }; diff --git a/packages/shared/src/netlifyCacheHandler.ts b/packages/shared/src/netlifyCacheHandler.ts index caf63216a08..3d43073a826 100644 --- a/packages/shared/src/netlifyCacheHandler.ts +++ b/packages/shared/src/netlifyCacheHandler.ts @@ -1,4 +1,4 @@ -import { getEnvVariable } from './getEnvVariable'; +/* eslint-disable turbo/no-undeclared-env-vars */ import { isDevelopmentFromPublishableKey } from './keys'; /** @@ -11,6 +11,23 @@ import { isDevelopmentFromPublishableKey } from './keys'; */ export const CLERK_NETLIFY_CACHE_BUST_PARAM = '__clerk_netlify_cache_bust'; +/** + * Returns true if running in a Netlify environment. + * Checks for Netlify-specific environment variables in process.env. + * Safe for browser and non-Node environments. + */ +function isNetlifyRuntime(): boolean { + if (typeof process === 'undefined' || !process.env) { + return false; + } + + return ( + Boolean(process.env.NETLIFY) || + Boolean(process.env.NETLIFY_FUNCTIONS_TOKEN) || + (typeof process.env.URL === 'string' && process.env.URL.endsWith('netlify.app')) + ); +} + /** * Prevents infinite redirects in Netlify's functions by adding a cache bust parameter * to the original redirect URL. This ensures that Netlify doesn't serve a cached response @@ -32,12 +49,9 @@ export function handleNetlifyCacheInDevInstance({ requestStateHeaders: Headers; publishableKey: string; }) { - const isOnNetlify = - getEnvVariable('NETLIFY') || - (typeof getEnvVariable('URL') === 'string' && getEnvVariable('URL').endsWith('netlify.app')) || - Boolean(getEnvVariable('NETLIFY_FUNCTIONS_TOKEN')); - + const isOnNetlify = isNetlifyRuntime(); const isDevelopmentInstance = isDevelopmentFromPublishableKey(publishableKey); + if (isOnNetlify && isDevelopmentInstance) { const hasHandshakeQueryParam = locationHeader.includes('__clerk_handshake'); // If location header is the original URL before the handshake flow, add cache bust param