From 7ab5392813ef19a972148e81f7156b3cf2011137 Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Mon, 27 Jan 2025 16:48:43 -0600 Subject: [PATCH 01/12] Attempt to inline crypto-js through pre-compiling --- .changeset/rich-trees-smoke.md | 5 ++++ packages/nextjs/package.json | 2 +- packages/nextjs/src/server/utils.ts | 6 ++--- packages/nextjs/src/vendor/README.md | 7 ++++++ packages/nextjs/src/vendor/crypto-js.js | 5 ++++ packages/nextjs/tsup.config.ts | 32 ++++++++++++++++++++++++- pnpm-lock.yaml | 8 +++---- 7 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 .changeset/rich-trees-smoke.md create mode 100644 packages/nextjs/src/vendor/README.md create mode 100644 packages/nextjs/src/vendor/crypto-js.js diff --git a/.changeset/rich-trees-smoke.md b/.changeset/rich-trees-smoke.md new file mode 100644 index 00000000000..e1076213b8a --- /dev/null +++ b/.changeset/rich-trees-smoke.md @@ -0,0 +1,5 @@ +--- +'@clerk/nextjs': patch +--- + +Vendor `crypto-js` dependency to avoid bundling issues. diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 41a3846ae0f..9689f4d2bb3 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -70,12 +70,12 @@ "@clerk/clerk-react": "workspace:^", "@clerk/shared": "workspace:^", "@clerk/types": "workspace:^", - "crypto-js": "4.2.0", "server-only": "0.0.1", "tslib": "catalog:repo" }, "devDependencies": { "@types/crypto-js": "4.2.2", + "crypto-js": "4.2.0", "next": "^14.2.23" }, "peerDependencies": { diff --git a/packages/nextjs/src/server/utils.ts b/packages/nextjs/src/server/utils.ts index df09682bd8a..9b71909abe5 100644 --- a/packages/nextjs/src/server/utils.ts +++ b/packages/nextjs/src/server/utils.ts @@ -4,13 +4,11 @@ import { isDevelopmentFromSecretKey } from '@clerk/shared/keys'; import { logger } from '@clerk/shared/logger'; import { isHttpOrHttps } from '@clerk/shared/proxy'; import { handleValueOrFn, isProductionEnvironment } from '@clerk/shared/utils'; -import AES from 'crypto-js/aes'; -import encUtf8 from 'crypto-js/enc-utf8'; -import hmacSHA1 from 'crypto-js/hmac-sha1'; import { NextResponse } from 'next/server'; import { constants as nextConstants } from '../constants'; import { canUseKeyless } from '../utils/feature-flags'; +import { AES, encUtf8, hmacSHA1 } from '../vendor/crypto-js'; import { DOMAIN, ENCRYPTION_KEY, IS_SATELLITE, PROXY_URL, SECRET_KEY, SIGN_IN_URL } from './constants'; import { authSignatureInvalid, @@ -34,7 +32,7 @@ export const setRequestHeadersOnNextResponse = ( if (!res.headers.get(OVERRIDE_HEADERS)) { // Emulate a user setting overrides by explicitly adding the required nextjs headers // https://github.com/vercel/next.js/pull/41380 - // @ts-expect-error + // @ts-expect-error -- property keys does not exist on type Headers res.headers.set(OVERRIDE_HEADERS, [...req.headers.keys()]); req.headers.forEach((val, key) => { res.headers.set(`${MIDDLEWARE_HEADER_PREFIX}-${key}`, val); diff --git a/packages/nextjs/src/vendor/README.md b/packages/nextjs/src/vendor/README.md new file mode 100644 index 00000000000..bc5624c69f5 --- /dev/null +++ b/packages/nextjs/src/vendor/README.md @@ -0,0 +1,7 @@ +# Vendored dependencies + +The modules in this folder contain vendored dependencies that are built and inlined into the published package. + +## crypto-js + +Used as a synchronous replacement for the [Web Crypto APIs](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API). Currently, we use crypto-js to encrypt and decrypt data transferred between Next.js middleware and the application server. diff --git a/packages/nextjs/src/vendor/crypto-js.js b/packages/nextjs/src/vendor/crypto-js.js new file mode 100644 index 00000000000..713a9400d02 --- /dev/null +++ b/packages/nextjs/src/vendor/crypto-js.js @@ -0,0 +1,5 @@ +import AES from 'crypto-js/aes'; +import encUtf8 from 'crypto-js/enc-utf8'; +import hmacSHA1 from 'crypto-js/hmac-sha1'; + +export { AES, encUtf8, hmacSHA1 }; diff --git a/packages/nextjs/tsup.config.ts b/packages/nextjs/tsup.config.ts index 0873f41802c..fdce080af10 100644 --- a/packages/nextjs/tsup.config.ts +++ b/packages/nextjs/tsup.config.ts @@ -15,6 +15,7 @@ export default defineConfig(overrideOptions => { '!./src/**/*.test.{ts,tsx}', '!./src/**/server-actions.ts', '!./src/**/keyless-actions.ts', + '!./src/vendor/**', ], // We want to preserve original file structure // so that the "use client" directives are not lost @@ -22,6 +23,7 @@ export default defineConfig(overrideOptions => { bundle: false, clean: true, minify: false, + noExternal: ['crypto-js'], external: ['#safe-node-apis'], sourcemap: true, legacyOutput: true, @@ -43,6 +45,9 @@ export default defineConfig(overrideOptions => { outDir: './dist/cjs', }; + /** + * When server actions are built with sourcemaps, Next.js does not resolve the sourcemap URLs during build and so browser dev tools attempt to load source maps for these files from incorrect locations + */ const serverActionsEsm: Options = { ...esm, entry: ['./src/**/server-actions.ts', './src/**/keyless-actions.ts'], @@ -55,6 +60,31 @@ export default defineConfig(overrideOptions => { sourcemap: false, }; + /** + * We vendor certain dependencies to control the output and minimize transitive dependency surface area. + */ + const vendorsEsm: Options = { + ...esm, + bundle: true, + minify: true, + entry: ['./src/vendor/**'], + outDir: './dist/esm/vendor', + legacyOutput: false, + outExtension: () => ({ + js: '.js', + }), + sourcemap: false, + }; + + const vendorsCjs: Options = { + ...cjs, + bundle: true, + minify: true, + entry: ['./src/vendor/**'], + outDir: './dist/cjs/vendor', + sourcemap: false, + }; + const copyPackageJson = (format: 'esm' | 'cjs') => `cp ./package.${format}.json ./dist/${format}/package.json`; // Tsup will not output the generated file in the same location as the source file // So we need to move the server-actions.js file to the app-router folder manually @@ -73,5 +103,5 @@ export default defineConfig(overrideOptions => { moveKeylessActions('esm'), moveKeylessActions('cjs'), shouldPublish && 'pnpm publish:local', - ])(esm, cjs, serverActionsEsm, serverActionsCjs); + ])(esm, cjs, serverActionsEsm, serverActionsCjs, vendorsEsm, vendorsCjs); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d01fa0e40ef..e1a7396f4b5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -704,9 +704,6 @@ importers: '@clerk/types': specifier: workspace:^ version: link:../types - crypto-js: - specifier: 4.2.0 - version: 4.2.0 react: specifier: catalog:peer-react version: 18.3.1 @@ -723,6 +720,9 @@ importers: '@types/crypto-js': specifier: 4.2.2 version: 4.2.2 + crypto-js: + specifier: 4.2.0 + version: 4.2.0 next: specifier: ^14.2.23 version: 14.2.23(@babel/core@7.26.0)(@playwright/test@1.44.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -2825,7 +2825,7 @@ packages: '@expo/bunyan@4.0.1': resolution: {integrity: sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==} - engines: {node: '>=0.10.0'} + engines: {'0': node >=0.10.0} '@expo/cli@0.18.30': resolution: {integrity: sha512-V90TUJh9Ly8stYo8nwqIqNWCsYjE28GlVFWEhAFCUOp99foiQr8HSTpiiX5GIrprcPoWmlGoY+J5fQA29R4lFg==} From b617332a668a7137476a45859ef40863f3d0956d Mon Sep 17 00:00:00 2001 From: Jacek Date: Mon, 27 Jan 2025 21:22:29 -0600 Subject: [PATCH 02/12] tweaks --- packages/nextjs/tsup.config.ts | 4 ++-- pnpm-lock.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/nextjs/tsup.config.ts b/packages/nextjs/tsup.config.ts index fdce080af10..b7877682cca 100644 --- a/packages/nextjs/tsup.config.ts +++ b/packages/nextjs/tsup.config.ts @@ -67,7 +67,7 @@ export default defineConfig(overrideOptions => { ...esm, bundle: true, minify: true, - entry: ['./src/vendor/**'], + entry: ['./src/vendor/*.js'], outDir: './dist/esm/vendor', legacyOutput: false, outExtension: () => ({ @@ -80,7 +80,7 @@ export default defineConfig(overrideOptions => { ...cjs, bundle: true, minify: true, - entry: ['./src/vendor/**'], + entry: ['./src/vendor/*.js'], outDir: './dist/cjs/vendor', sourcemap: false, }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e1a7396f4b5..2278a415698 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2825,7 +2825,7 @@ packages: '@expo/bunyan@4.0.1': resolution: {integrity: sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==} - engines: {'0': node >=0.10.0} + engines: {node: '>=0.10.0'} '@expo/cli@0.18.30': resolution: {integrity: sha512-V90TUJh9Ly8stYo8nwqIqNWCsYjE28GlVFWEhAFCUOp99foiQr8HSTpiiX5GIrprcPoWmlGoY+J5fQA29R4lFg==} From c04502a6e5b6160718a4940b858ab819bc03e7a2 Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Tue, 28 Jan 2025 11:05:52 -0600 Subject: [PATCH 03/12] Make next build tests more resilient --- integration/tests/next-build.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/next-build.test.ts b/integration/tests/next-build.test.ts index d22e13096c3..4570ad45ac3 100644 --- a/integration/tests/next-build.test.ts +++ b/integration/tests/next-build.test.ts @@ -193,7 +193,7 @@ export default function RootLayout({ children }: { children: React.ReactNode }) // Get the indicator from the build output const indicator = getIndicator(app.buildOutput, type); - const pageLine = app.buildOutput.split('\n').find(msg => msg.includes(page)); + const pageLine = app.buildOutput.split('\n').find(msg => msg.includes(` ${page}`)); expect(pageLine).toContain(indicator); }); From 42df740a0a127c2685cc9025c924f2e92d469641 Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Tue, 28 Jan 2025 15:23:40 -0600 Subject: [PATCH 04/12] Migrate to crypto-es --- packages/nextjs/package.json | 3 +-- packages/nextjs/src/server/utils.ts | 6 +++--- packages/nextjs/src/vendor/crypto-js.js | 8 ++++---- pnpm-lock.yaml | 21 +++++++++------------ 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 9689f4d2bb3..32a10b4a937 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -74,8 +74,7 @@ "tslib": "catalog:repo" }, "devDependencies": { - "@types/crypto-js": "4.2.2", - "crypto-js": "4.2.0", + "crypto-es": "^2.1.0", "next": "^14.2.23" }, "peerDependencies": { diff --git a/packages/nextjs/src/server/utils.ts b/packages/nextjs/src/server/utils.ts index 9b71909abe5..7c4bb6edc45 100644 --- a/packages/nextjs/src/server/utils.ts +++ b/packages/nextjs/src/server/utils.ts @@ -8,7 +8,7 @@ import { NextResponse } from 'next/server'; import { constants as nextConstants } from '../constants'; import { canUseKeyless } from '../utils/feature-flags'; -import { AES, encUtf8, hmacSHA1 } from '../vendor/crypto-js'; +import { AES, HmacSHA1, Utf8 } from '../vendor/crypto-js'; import { DOMAIN, ENCRYPTION_KEY, IS_SATELLITE, PROXY_URL, SECRET_KEY, SIGN_IN_URL } from './constants'; import { authSignatureInvalid, @@ -158,7 +158,7 @@ export function assertKey(key: string | undefined, onError: () => never): string * Compute a cryptographic signature from a session token and provided secret key. Used to validate that the token has not been modified when transferring between middleware and the Next.js origin. */ function createTokenSignature(token: string, key: string): string { - return hmacSHA1(token, key).toString(); + return HmacSHA1(token, key).toString(); } /** @@ -256,6 +256,6 @@ function throwInvalidEncryptionKey(): never { function decryptData(data: string, key: string) { const decryptedBytes = AES.decrypt(data, key); - const encoded = decryptedBytes.toString(encUtf8); + const encoded = decryptedBytes.toString(Utf8); return JSON.parse(encoded); } diff --git a/packages/nextjs/src/vendor/crypto-js.js b/packages/nextjs/src/vendor/crypto-js.js index 713a9400d02..1db2952dd1b 100644 --- a/packages/nextjs/src/vendor/crypto-js.js +++ b/packages/nextjs/src/vendor/crypto-js.js @@ -1,5 +1,5 @@ -import AES from 'crypto-js/aes'; -import encUtf8 from 'crypto-js/enc-utf8'; -import hmacSHA1 from 'crypto-js/hmac-sha1'; +import { AES } from 'crypto-es/lib/aes'; +import { Utf8 } from 'crypto-es/lib/core'; +import { HmacSHA1 } from 'crypto-es/lib/sha1'; -export { AES, encUtf8, hmacSHA1 }; +export { AES, HmacSHA1, Utf8 }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2278a415698..367de44d6ca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -717,12 +717,9 @@ importers: specifier: catalog:repo version: 2.4.1 devDependencies: - '@types/crypto-js': - specifier: 4.2.2 - version: 4.2.2 - crypto-js: - specifier: 4.2.0 - version: 4.2.0 + crypto-es: + specifier: ^2.1.0 + version: 2.1.0 next: specifier: ^14.2.23 version: 14.2.23(@babel/core@7.26.0)(@playwright/test@1.44.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -2825,7 +2822,7 @@ packages: '@expo/bunyan@4.0.1': resolution: {integrity: sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==} - engines: {node: '>=0.10.0'} + engines: {'0': node >=0.10.0} '@expo/cli@0.18.30': resolution: {integrity: sha512-V90TUJh9Ly8stYo8nwqIqNWCsYjE28GlVFWEhAFCUOp99foiQr8HSTpiiX5GIrprcPoWmlGoY+J5fQA29R4lFg==} @@ -5246,9 +5243,6 @@ packages: '@types/cross-spawn@6.0.3': resolution: {integrity: sha512-BDAkU7WHHRHnvBf5z89lcvACsvkz/n7Tv+HyD/uW76O29HoH1Tk/W6iQrepaZVbisvlEek4ygwT8IW7ow9XLAA==} - '@types/crypto-js@4.2.2': - resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==} - '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -7245,6 +7239,9 @@ packages: crypt@0.0.2: resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} + crypto-es@2.1.0: + resolution: {integrity: sha512-C5Dbuv4QTPGuloy5c5Vv/FZHtmK+lobLAypFfuRaBbwCsk3qbCWWESCH3MUcBsrgXloRNMrzwUAiPg4U6+IaKA==} + crypto-js@4.2.0: resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} @@ -20620,8 +20617,6 @@ snapshots: dependencies: '@types/node': 22.10.10 - '@types/crypto-js@4.2.2': {} - '@types/debug@4.1.12': dependencies: '@types/ms': 0.7.34 @@ -23155,6 +23150,8 @@ snapshots: crypt@0.0.2: {} + crypto-es@2.1.0: {} + crypto-js@4.2.0: {} crypto-random-string@1.0.0: {} From bd22175b3d54663634e558887e9e884537460286 Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Tue, 28 Jan 2025 15:25:27 -0600 Subject: [PATCH 05/12] Apply suggestions from code review --- packages/nextjs/src/vendor/README.md | 2 +- packages/nextjs/tsup.config.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/nextjs/src/vendor/README.md b/packages/nextjs/src/vendor/README.md index bc5624c69f5..53dc85b4c86 100644 --- a/packages/nextjs/src/vendor/README.md +++ b/packages/nextjs/src/vendor/README.md @@ -2,6 +2,6 @@ The modules in this folder contain vendored dependencies that are built and inlined into the published package. -## crypto-js +## crypto-es Used as a synchronous replacement for the [Web Crypto APIs](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API). Currently, we use crypto-js to encrypt and decrypt data transferred between Next.js middleware and the application server. diff --git a/packages/nextjs/tsup.config.ts b/packages/nextjs/tsup.config.ts index b7877682cca..294ddab325f 100644 --- a/packages/nextjs/tsup.config.ts +++ b/packages/nextjs/tsup.config.ts @@ -23,7 +23,6 @@ export default defineConfig(overrideOptions => { bundle: false, clean: true, minify: false, - noExternal: ['crypto-js'], external: ['#safe-node-apis'], sourcemap: true, legacyOutput: true, From 5b0a6bdac570b1d28259daf3026fe2c596e69a49 Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Tue, 28 Jan 2025 15:35:15 -0600 Subject: [PATCH 06/12] Migrate keyless to use web crypto APIs --- .../nextjs/src/app-router/keyless-actions.ts | 4 +-- packages/nextjs/src/server/clerkMiddleware.ts | 4 +-- packages/nextjs/src/server/keyless.ts | 25 +++++++++++-------- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/packages/nextjs/src/app-router/keyless-actions.ts b/packages/nextjs/src/app-router/keyless-actions.ts index cd4ff10e23d..0db2a98d1fd 100644 --- a/packages/nextjs/src/app-router/keyless-actions.ts +++ b/packages/nextjs/src/app-router/keyless-actions.ts @@ -11,7 +11,7 @@ import { canUseKeyless } from '../utils/feature-flags'; export async function syncKeylessConfigAction(args: AccountlessApplication & { returnUrl: string }): Promise { const { claimUrl, publishableKey, secretKey, returnUrl } = args; const cookieStore = await cookies(); - cookieStore.set(getKeylessCookieName(), JSON.stringify({ claimUrl, publishableKey, secretKey }), { + cookieStore.set(await getKeylessCookieName(), JSON.stringify({ claimUrl, publishableKey, secretKey }), { secure: true, httpOnly: true, }); @@ -55,7 +55,7 @@ export async function createOrReadKeylessAction(): Promise { // Handles the case where `options` is a callback function to dynamically access `NextRequest` const resolvedParams = typeof params === 'function' ? params(request) : params; - const keyless = getKeylessCookieValue(name => request.cookies.get(name)?.value); + const keyless = await getKeylessCookieValue(name => request.cookies.get(name)?.value); const publishableKey = assertKey( resolvedParams.publishableKey || PUBLISHABLE_KEY || keyless?.publishableKey, @@ -218,7 +218,7 @@ export const clerkMiddleware: ClerkMiddleware = (...args: unknown[]) => { } const resolvedParams = typeof params === 'function' ? params(request) : params; - const keyless = getKeylessCookieValue(name => request.cookies.get(name)?.value); + const keyless = await getKeylessCookieValue(name => request.cookies.get(name)?.value); const isMissingPublishableKey = !(resolvedParams.publishableKey || PUBLISHABLE_KEY || keyless?.publishableKey); /** * In keyless mode, if the publishable key is missing, let the request through, to render `` that will resume the flow gracefully. diff --git a/packages/nextjs/src/server/keyless.ts b/packages/nextjs/src/server/keyless.ts index 8cd71998dfd..ca40e33ca7b 100644 --- a/packages/nextjs/src/server/keyless.ts +++ b/packages/nextjs/src/server/keyless.ts @@ -1,12 +1,19 @@ import type { AccountlessApplication } from '@clerk/backend'; -import hex from 'crypto-js/enc-hex'; -import sha256 from 'crypto-js/sha256'; import { canUseKeyless } from '../utils/feature-flags'; const keylessCookiePrefix = `__clerk_keys_`; -const getKeylessCookieName = (): string => { +async function hashString(str: string) { + const encoder = new TextEncoder(); + const data = encoder.encode(str); + const hashBuffer = await crypto.subtle.digest('SHA-256', data); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); + return hashHex.slice(0, 16); // Take only the first 16 characters +} + +async function getKeylessCookieName(): Promise { // eslint-disable-next-line turbo/no-undeclared-env-vars const PATH = process.env.PWD; @@ -18,21 +25,19 @@ const getKeylessCookieName = (): string => { const lastThreeDirs = PATH.split('/').filter(Boolean).slice(-3).reverse().join('/'); // Hash the resulting string - const hash = hashString(lastThreeDirs); + const hash = await hashString(lastThreeDirs); return `${keylessCookiePrefix}${hash}`; -}; - -function hashString(str: string) { - return sha256(str).toString(hex).slice(0, 16); // Take only the first 16 characters } -function getKeylessCookieValue(getter: (cookieName: string) => string | undefined): AccountlessApplication | undefined { +async function getKeylessCookieValue( + getter: (cookieName: string) => string | undefined, +): Promise { if (!canUseKeyless) { return undefined; } - const keylessCookieName = getKeylessCookieName(); + const keylessCookieName = await getKeylessCookieName(); let keyless; try { From abf039ac6f750598bfa45ab97a2d5d4e31037f1e Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Tue, 28 Jan 2025 16:37:27 -0600 Subject: [PATCH 07/12] remove usage in tests --- packages/nextjs/src/server/__tests__/createGetAuth.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nextjs/src/server/__tests__/createGetAuth.test.ts b/packages/nextjs/src/server/__tests__/createGetAuth.test.ts index 56a9e99bd12..dfe4b547029 100644 --- a/packages/nextjs/src/server/__tests__/createGetAuth.test.ts +++ b/packages/nextjs/src/server/__tests__/createGetAuth.test.ts @@ -1,8 +1,8 @@ import { AuthStatus, constants } from '@clerk/backend/internal'; -import hmacSHA1 from 'crypto-js/hmac-sha1'; import { NextRequest } from 'next/server'; import { describe, expect, it } from 'vitest'; +import { HmacSHA1 } from '../../vendor/crypto-js'; import { createSyncGetAuth, getAuth } from '../createGetAuth'; const mockSecretKey = 'sk_test_mock'; @@ -12,7 +12,7 @@ const mockToken = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyLWlkIn0.0u5CllULtDVD9DUU // { alg: 'HS256' }.{ sub: 'user-id-2' }.sig const mockToken2 = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyLWlkLTIifQ.K-mhz0Ber1Hfh2xCwmvsLwhZO_IKLtKt78KTHsecEas'; -const mockTokenSignature = hmacSHA1(mockToken, 'sk_test_mock').toString(); +const mockTokenSignature = HmacSHA1(mockToken, 'sk_test_mock').toString(); describe('createGetAuth(opts)', () => { it('returns a getAuth function', () => { From 4bc6ea64ab3b5e35b88e5ffa108ab77a591628ee Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Tue, 28 Jan 2025 19:25:26 -0600 Subject: [PATCH 08/12] Update rich-trees-smoke.md Co-authored-by: Jacek Radko --- .changeset/rich-trees-smoke.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/rich-trees-smoke.md b/.changeset/rich-trees-smoke.md index e1076213b8a..2efaf1f1503 100644 --- a/.changeset/rich-trees-smoke.md +++ b/.changeset/rich-trees-smoke.md @@ -2,4 +2,4 @@ '@clerk/nextjs': patch --- -Vendor `crypto-js` dependency to avoid bundling issues. +Switch to `crypto-es` and bundle the dependency to avoid downstream build issues From a146a0b28864fddcb8f2d5fe3057a91d783df84c Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Tue, 28 Jan 2025 16:50:49 -0600 Subject: [PATCH 09/12] lock update --- pnpm-lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f2da75efbc..12f432b7938 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2822,7 +2822,7 @@ packages: '@expo/bunyan@4.0.1': resolution: {integrity: sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==} - engines: {'0': node >=0.10.0} + engines: {node: '>=0.10.0'} '@expo/cli@0.18.30': resolution: {integrity: sha512-V90TUJh9Ly8stYo8nwqIqNWCsYjE28GlVFWEhAFCUOp99foiQr8HSTpiiX5GIrprcPoWmlGoY+J5fQA29R4lFg==} From 5da61e14bea0556fad46e1e564a81b17ed94d668 Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Tue, 28 Jan 2025 21:32:10 -0600 Subject: [PATCH 10/12] Rename crypto-js -> crypto-es --- packages/nextjs/src/server/__tests__/createGetAuth.test.ts | 2 +- packages/nextjs/src/server/utils.ts | 2 +- packages/nextjs/src/vendor/{crypto-js.js => crypto-es.js} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename packages/nextjs/src/vendor/{crypto-js.js => crypto-es.js} (100%) diff --git a/packages/nextjs/src/server/__tests__/createGetAuth.test.ts b/packages/nextjs/src/server/__tests__/createGetAuth.test.ts index 8ffbb5e0a14..3298ce3cd96 100644 --- a/packages/nextjs/src/server/__tests__/createGetAuth.test.ts +++ b/packages/nextjs/src/server/__tests__/createGetAuth.test.ts @@ -2,7 +2,7 @@ import { AuthStatus, constants } from '@clerk/backend/internal'; import { NextRequest } from 'next/server'; import { describe, expect, it } from 'vitest'; -import { HmacSHA1 } from '../../vendor/crypto-js'; +import { HmacSHA1 } from '../../vendor/crypto-es'; import { createGetAuth, getAuth } from '../createGetAuth'; const mockSecretKey = 'sk_test_mock'; diff --git a/packages/nextjs/src/server/utils.ts b/packages/nextjs/src/server/utils.ts index 7c4bb6edc45..45e15f68a9d 100644 --- a/packages/nextjs/src/server/utils.ts +++ b/packages/nextjs/src/server/utils.ts @@ -8,7 +8,7 @@ import { NextResponse } from 'next/server'; import { constants as nextConstants } from '../constants'; import { canUseKeyless } from '../utils/feature-flags'; -import { AES, HmacSHA1, Utf8 } from '../vendor/crypto-js'; +import { AES, HmacSHA1, Utf8 } from '../vendor/crypto-es'; import { DOMAIN, ENCRYPTION_KEY, IS_SATELLITE, PROXY_URL, SECRET_KEY, SIGN_IN_URL } from './constants'; import { authSignatureInvalid, diff --git a/packages/nextjs/src/vendor/crypto-js.js b/packages/nextjs/src/vendor/crypto-es.js similarity index 100% rename from packages/nextjs/src/vendor/crypto-js.js rename to packages/nextjs/src/vendor/crypto-es.js From c5f0a06fe46b30a9c323e64c1bf662f541f2dbdd Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Wed, 29 Jan 2025 10:40:07 -0600 Subject: [PATCH 11/12] Update packages/nextjs/src/vendor/README.md --- packages/nextjs/src/vendor/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nextjs/src/vendor/README.md b/packages/nextjs/src/vendor/README.md index 53dc85b4c86..463fc75ab19 100644 --- a/packages/nextjs/src/vendor/README.md +++ b/packages/nextjs/src/vendor/README.md @@ -4,4 +4,4 @@ The modules in this folder contain vendored dependencies that are built and inli ## crypto-es -Used as a synchronous replacement for the [Web Crypto APIs](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API). Currently, we use crypto-js to encrypt and decrypt data transferred between Next.js middleware and the application server. +Used as a synchronous replacement for the [Web Crypto APIs](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API). Currently, we use crypto-es to encrypt and decrypt data transferred between Next.js middleware and the application server. From 0616f1c8f6e86f3360fd6a4d8c9b53e02a11e99f Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Tue, 25 Feb 2025 16:35:30 -0600 Subject: [PATCH 12/12] add missing await --- packages/nextjs/src/app-router/keyless-actions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nextjs/src/app-router/keyless-actions.ts b/packages/nextjs/src/app-router/keyless-actions.ts index 59b3ad24f17..3219ff8155d 100644 --- a/packages/nextjs/src/app-router/keyless-actions.ts +++ b/packages/nextjs/src/app-router/keyless-actions.ts @@ -13,7 +13,7 @@ export async function syncKeylessConfigAction(args: AccountlessApplication & { r const cookieStore = await cookies(); const request = new Request('https://placeholder.com', { headers: await headers() }); - const keyless = getKeylessCookieValue(name => cookieStore.get(name)?.value); + const keyless = await getKeylessCookieValue(name => cookieStore.get(name)?.value); const pksMatch = keyless?.publishableKey === publishableKey; const sksMatch = keyless?.secretKey === secretKey; if (pksMatch && sksMatch) {