From c8d1c4dcc172c88fee3315406ddff0bdf6c9e75f Mon Sep 17 00:00:00 2001 From: Vaggelis Yfantis Date: Wed, 11 Oct 2023 10:47:21 +0300 Subject: [PATCH 1/4] feat(remix): Refactor getEnvVariable to support Cloudflare Pages --- packages/remix/src/ssr/authenticateRequest.ts | 43 ++++++------------- packages/remix/src/utils.ts | 14 ++++-- 2 files changed, 24 insertions(+), 33 deletions(-) diff --git a/packages/remix/src/ssr/authenticateRequest.ts b/packages/remix/src/ssr/authenticateRequest.ts index 2167c01aa8..2a4be94d77 100644 --- a/packages/remix/src/ssr/authenticateRequest.ts +++ b/packages/remix/src/ssr/authenticateRequest.ts @@ -27,8 +27,8 @@ export function authenticateRequest(args: LoaderFunctionArgs, opts: RootAuthLoad // 2. Then try from process.env if exists (Node). // 3. Then try from globalThis (Cloudflare Workers). // 4. Then from loader context (Cloudflare Pages). - const secretKey = opts.secretKey || getEnvVariable('CLERK_SECRET_KEY') || (context?.CLERK_SECRET_KEY as string) || ''; - const apiKey = opts.apiKey || getEnvVariable('CLERK_API_KEY') || (context?.CLERK_API_KEY as string) || ''; + const secretKey = opts.secretKey || getEnvVariable('CLERK_SECRET_KEY', context) || ''; + const apiKey = opts.apiKey || getEnvVariable('CLERK_API_KEY', context) || ''; if (apiKey) { if (getEnvVariable('CLERK_API_KEY')) { deprecated('CLERK_API_KEY', 'Use `CLERK_SECRET_KEY` instead.'); @@ -41,8 +41,7 @@ export function authenticateRequest(args: LoaderFunctionArgs, opts: RootAuthLoad throw new Error(noSecretKeyOrApiKeyError); } - const frontendApi = - opts.frontendApi || getEnvVariable('CLERK_FRONTEND_API') || (context?.CLERK_FRONTEND_API as string) || ''; + const frontendApi = opts.frontendApi || getEnvVariable('CLERK_FRONTEND_API', context) || ''; if (frontendApi) { if (getEnvVariable('CLERK_FRONTEND_API')) { deprecated('CLERK_FRONTEND_API', 'Use `CLERK_PUBLISHABLE_KEY` instead.'); @@ -51,23 +50,17 @@ export function authenticateRequest(args: LoaderFunctionArgs, opts: RootAuthLoad } } - const publishableKey = - opts.publishableKey || getEnvVariable('CLERK_PUBLISHABLE_KEY') || (context?.CLERK_PUBLISHABLE_KEY as string) || ''; + const publishableKey = opts.publishableKey || getEnvVariable('CLERK_PUBLISHABLE_KEY', context) || ''; - const jwtKey = opts.jwtKey || getEnvVariable('CLERK_JWT_KEY') || (context?.CLERK_JWT_KEY as string); + const jwtKey = opts.jwtKey || getEnvVariable('CLERK_JWT_KEY', context); - const apiUrl = getEnvVariable('CLERK_API_URL') || (context?.CLERK_API_URL as string); + const apiUrl = getEnvVariable('CLERK_API_URL', context); - const domain = - handleValueOrFn(opts.domain, new URL(request.url)) || - getEnvVariable('CLERK_DOMAIN') || - (context?.CLERK_DOMAIN as string) || - ''; + const domain = handleValueOrFn(opts.domain, new URL(request.url)) || getEnvVariable('CLERK_DOMAIN', context) || ''; const isSatellite = handleValueOrFn(opts.isSatellite, new URL(request.url)) || - getEnvVariable('CLERK_IS_SATELLITE') === 'true' || - (context?.CLERK_IS_SATELLITE as string) === 'true' || + getEnvVariable('CLERK_IS_SATELLITE', context) === 'true' || false; const requestURL = buildRequestUrl(request); @@ -75,7 +68,7 @@ export function authenticateRequest(args: LoaderFunctionArgs, opts: RootAuthLoad const relativeOrAbsoluteProxyUrl = handleValueOrFn( opts?.proxyUrl, requestURL, - getEnvVariable('CLERK_PROXY_URL') || (context?.CLERK_PROXY_URL as string), + getEnvVariable('CLERK_PROXY_URL', context), ); let proxyUrl; @@ -85,23 +78,13 @@ export function authenticateRequest(args: LoaderFunctionArgs, opts: RootAuthLoad proxyUrl = relativeOrAbsoluteProxyUrl; } - const signInUrl = - opts.signInUrl || getEnvVariable('CLERK_SIGN_IN_URL') || (context?.CLERK_SIGN_IN_URL as string) || ''; + const signInUrl = opts.signInUrl || getEnvVariable('CLERK_SIGN_IN_URL', context) || ''; - const signUpUrl = - opts.signUpUrl || getEnvVariable('CLERK_SIGN_UP_URL') || (context?.CLERK_SIGN_UP_URL as string) || ''; + const signUpUrl = opts.signUpUrl || getEnvVariable('CLERK_SIGN_UP_URL', context) || ''; - const afterSignInUrl = - opts.afterSignInUrl || - getEnvVariable('CLERK_AFTER_SIGN_IN_URL') || - (context?.CLERK_AFTER_SIGN_IN_URL as string) || - ''; + const afterSignInUrl = opts.afterSignInUrl || getEnvVariable('CLERK_AFTER_SIGN_IN_URL', context) || ''; - const afterSignUpUrl = - opts.afterSignUpUrl || - getEnvVariable('CLERK_AFTER_SIGN_UP_URL') || - (context?.CLERK_AFTER_SIGN_UP_URL as string) || - ''; + const afterSignUpUrl = opts.afterSignUpUrl || getEnvVariable('CLERK_AFTER_SIGN_UP_URL', context) || ''; if (isSatellite && !proxyUrl && !domain) { throw new Error(satelliteAndMissingProxyUrlAndDomain); diff --git a/packages/remix/src/utils.ts b/packages/remix/src/utils.ts index c21e0fa30b..e1d3040943 100644 --- a/packages/remix/src/utils.ts +++ b/packages/remix/src/utils.ts @@ -1,3 +1,5 @@ +import type { AppLoadContext } from '@remix-run/server-runtime'; + import type { ClerkState } from './client/types'; import { invalidClerkStatePropError, noClerkStateError } from './errors'; @@ -29,16 +31,22 @@ export function assertValidClerkState(val: any): asserts val is ClerkState | und * @param name * @returns */ -export const getEnvVariable = (name: string): string => { +export const getEnvVariable = (name: string, context?: AppLoadContext): string => { // Node envs if (typeof process !== 'undefined') { return (process.env && process.env[name]) || ''; } + // Cloudflare pages + if (context) { + const contextEnv = context?.env as Record; + + return contextEnv[name] || (context[name] as string) || ''; + } + // Cloudflare workers try { - // @ts-expect-error - return globalThis[name]; + return globalThis[name as keyof typeof globalThis]; } catch (_) { // This will raise an error in Cloudflare Pages } From b3aa9409681def9cbcc908db9af223ab07a4a822 Mon Sep 17 00:00:00 2001 From: Vaggelis Yfantis Date: Thu, 12 Oct 2023 22:48:33 +0300 Subject: [PATCH 2/4] chore(repo): Adds changeset --- .changeset/thin-geese-peel.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changeset/thin-geese-peel.md diff --git a/.changeset/thin-geese-peel.md b/.changeset/thin-geese-peel.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/thin-geese-peel.md @@ -0,0 +1,2 @@ +--- +--- From f5f61142029b2db9ac0da28eb5e6d801d563edc6 Mon Sep 17 00:00:00 2001 From: Vaggelis Yfantis Date: Thu, 12 Oct 2023 22:56:46 +0300 Subject: [PATCH 3/4] chore(repo): Update Changeset --- .changeset/thin-geese-peel.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.changeset/thin-geese-peel.md b/.changeset/thin-geese-peel.md index a845151cc8..c478065785 100644 --- a/.changeset/thin-geese-peel.md +++ b/.changeset/thin-geese-peel.md @@ -1,2 +1,5 @@ --- +'@clerk/remix': patch --- + +Internal improvements for retrieving environment variables. From 4717fefb7c6ac7bbf58b26c8763c07ac815674a9 Mon Sep 17 00:00:00 2001 From: Vaggelis Yfantis Date: Fri, 13 Oct 2023 12:14:36 +0300 Subject: [PATCH 4/4] fix(remix): Updated all uses of getEnvVariable to use the Remix context --- packages/remix/src/ssr/authenticateRequest.ts | 4 ++-- packages/remix/src/ssr/getAuth.ts | 2 +- packages/remix/src/ssr/rootAuthLoader.ts | 8 +++---- packages/remix/src/ssr/utils.ts | 21 +++++++++++++------ packages/remix/src/utils.ts | 4 ++-- 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/packages/remix/src/ssr/authenticateRequest.ts b/packages/remix/src/ssr/authenticateRequest.ts index 2a4be94d77..713e7c2343 100644 --- a/packages/remix/src/ssr/authenticateRequest.ts +++ b/packages/remix/src/ssr/authenticateRequest.ts @@ -30,7 +30,7 @@ export function authenticateRequest(args: LoaderFunctionArgs, opts: RootAuthLoad const secretKey = opts.secretKey || getEnvVariable('CLERK_SECRET_KEY', context) || ''; const apiKey = opts.apiKey || getEnvVariable('CLERK_API_KEY', context) || ''; if (apiKey) { - if (getEnvVariable('CLERK_API_KEY')) { + if (getEnvVariable('CLERK_API_KEY', context)) { deprecated('CLERK_API_KEY', 'Use `CLERK_SECRET_KEY` instead.'); } else { deprecated('apiKey', 'Use `secretKey` instead.'); @@ -43,7 +43,7 @@ export function authenticateRequest(args: LoaderFunctionArgs, opts: RootAuthLoad const frontendApi = opts.frontendApi || getEnvVariable('CLERK_FRONTEND_API', context) || ''; if (frontendApi) { - if (getEnvVariable('CLERK_FRONTEND_API')) { + if (getEnvVariable('CLERK_FRONTEND_API', context)) { deprecated('CLERK_FRONTEND_API', 'Use `CLERK_PUBLISHABLE_KEY` instead.'); } else { deprecated('frontendApi', 'Use `publishableKey` instead.'); diff --git a/packages/remix/src/ssr/getAuth.ts b/packages/remix/src/ssr/getAuth.ts index 9f40da486b..fbfa4fd879 100644 --- a/packages/remix/src/ssr/getAuth.ts +++ b/packages/remix/src/ssr/getAuth.ts @@ -18,7 +18,7 @@ export async function getAuth(args: LoaderFunctionArgs, opts?: GetAuthOptions): } if (requestState.isInterstitial) { - throw interstitialJsonResponse(requestState, { loader: 'nested' }); + throw interstitialJsonResponse(requestState, { loader: 'nested' }, args.context); } return sanitizeAuthObject(requestState.toAuth()); diff --git a/packages/remix/src/ssr/rootAuthLoader.ts b/packages/remix/src/ssr/rootAuthLoader.ts index f1c561d1c8..5158528a01 100644 --- a/packages/remix/src/ssr/rootAuthLoader.ts +++ b/packages/remix/src/ssr/rootAuthLoader.ts @@ -53,12 +53,12 @@ export const rootAuthLoader: RootAuthLoader = async ( } if (requestState.isInterstitial) { - throw interstitialJsonResponse(requestState, { loader: 'root' }); + throw interstitialJsonResponse(requestState, { loader: 'root' }, args.context); } if (!handler) { // if the user did not provide a handler, simply inject requestState into an empty response - return injectRequestStateIntoResponse(new Response(JSON.stringify({})), requestState); + return injectRequestStateIntoResponse(new Response(JSON.stringify({})), requestState, args.context); } const handlerResult = await handler(injectAuthIntoRequest(args, sanitizeAuthObject(requestState.toAuth()))); @@ -72,7 +72,7 @@ export const rootAuthLoader: RootAuthLoader = async ( } // clone and try to inject requestState into all json-like responses // if this fails, the user probably didn't return a json object or a valid json string - return injectRequestStateIntoResponse(handlerResult, requestState); + return injectRequestStateIntoResponse(handlerResult, requestState, args.context); } catch (e) { throw new Error(invalidRootLoaderCallbackReturn); } @@ -81,5 +81,5 @@ export const rootAuthLoader: RootAuthLoader = async ( // if the return value of the user's handler is null or a plain object, create an empty response to inject Clerk's state into const responseBody = JSON.stringify(handlerResult ?? {}); - return injectRequestStateIntoResponse(new Response(responseBody), requestState); + return injectRequestStateIntoResponse(new Response(responseBody), requestState, args.context); }; diff --git a/packages/remix/src/ssr/utils.ts b/packages/remix/src/ssr/utils.ts index a75519e090..d8607a3282 100644 --- a/packages/remix/src/ssr/utils.ts +++ b/packages/remix/src/ssr/utils.ts @@ -1,5 +1,6 @@ import type { AuthObject, RequestState } from '@clerk/backend'; import { constants, debugRequestState, loadInterstitialFromLocal } from '@clerk/backend'; +import type { AppLoadContext } from '@remix-run/server-runtime'; import { json } from '@remix-run/server-runtime'; import cookie from 'cookie'; @@ -75,7 +76,11 @@ export const unknownResponse = (requestState: RequestState) => { return json(null, { status: 401, headers: observabilityHeadersFromRequestState(requestState) }); }; -export const interstitialJsonResponse = (requestState: RequestState, opts: { loader: 'root' | 'nested' }) => { +export const interstitialJsonResponse = ( + requestState: RequestState, + opts: { loader: 'root' | 'nested' }, + context: AppLoadContext, +) => { return json( wrapWithClerkState({ __loader: opts.loader, @@ -85,8 +90,8 @@ export const interstitialJsonResponse = (requestState: RequestState, opts: { loa publishableKey: requestState.publishableKey, // TODO: This needs to be the version of clerk/remix not clerk/react // pkgVersion: LIB_VERSION, - clerkJSUrl: getEnvVariable('CLERK_JS'), - clerkJSVersion: getEnvVariable('CLERK_JS_VERSION'), + clerkJSUrl: getEnvVariable('CLERK_JS', context), + clerkJSVersion: getEnvVariable('CLERK_JS_VERSION', context), proxyUrl: requestState.proxyUrl, isSatellite: requestState.isSatellite, domain: requestState.domain, @@ -97,7 +102,11 @@ export const interstitialJsonResponse = (requestState: RequestState, opts: { loa ); }; -export const injectRequestStateIntoResponse = async (response: Response, requestState: RequestState) => { +export const injectRequestStateIntoResponse = async ( + response: Response, + requestState: RequestState, + context: AppLoadContext, +) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { reason, message, isSignedIn, isInterstitial, ...rest } = requestState; const clone = response.clone(); @@ -114,8 +123,8 @@ export const injectRequestStateIntoResponse = async (response: Response, request __afterSignInUrl: requestState.afterSignInUrl, __afterSignUpUrl: requestState.afterSignUpUrl, __clerk_debug: debugRequestState(requestState), - __clerkJSUrl: getEnvVariable('CLERK_JS'), - __clerkJSVersion: getEnvVariable('CLERK_JS_VERSION'), + __clerkJSUrl: getEnvVariable('CLERK_JS', context), + __clerkJSVersion: getEnvVariable('CLERK_JS_VERSION', context), }); // set the correct content-type header in case the user returned a `Response` directly // without setting the header, instead of using the `json()` helper diff --git a/packages/remix/src/utils.ts b/packages/remix/src/utils.ts index e1d3040943..d5f19dc27d 100644 --- a/packages/remix/src/utils.ts +++ b/packages/remix/src/utils.ts @@ -31,14 +31,14 @@ export function assertValidClerkState(val: any): asserts val is ClerkState | und * @param name * @returns */ -export const getEnvVariable = (name: string, context?: AppLoadContext): string => { +export const getEnvVariable = (name: string, context: AppLoadContext | undefined): string => { // Node envs if (typeof process !== 'undefined') { return (process.env && process.env[name]) || ''; } // Cloudflare pages - if (context) { + if (typeof context !== 'undefined') { const contextEnv = context?.env as Record; return contextEnv[name] || (context[name] as string) || '';