diff --git a/.changeset/orange-clouds-relax.md b/.changeset/orange-clouds-relax.md new file mode 100644 index 00000000000..2f30b96fdb6 --- /dev/null +++ b/.changeset/orange-clouds-relax.md @@ -0,0 +1,5 @@ +--- +'@clerk/nextjs': patch +--- + +Revert: Improve error messages when clerkMiddleware is missing by suggesting the correct path to place the middleware.ts file (#4979). diff --git a/packages/nextjs/src/app-router/server/ClerkProvider.tsx b/packages/nextjs/src/app-router/server/ClerkProvider.tsx index 96db748ec7d..5fc4e040138 100644 --- a/packages/nextjs/src/app-router/server/ClerkProvider.tsx +++ b/packages/nextjs/src/app-router/server/ClerkProvider.tsx @@ -7,7 +7,6 @@ import { getDynamicAuthData } from '../../server/buildClerkProps'; import type { NextClerkProviderProps } from '../../types'; import { canUseKeyless } from '../../utils/feature-flags'; import { mergeNextClerkPropsWithEnv } from '../../utils/mergeNextClerkPropsWithEnv'; -import { onlyTry } from '../../utils/only-try'; import { isNext13 } from '../../utils/sdk-versions'; import { ClientClerkProvider } from '../client/ClerkProvider'; import { deleteKeylessAction } from '../keyless-actions'; @@ -24,6 +23,15 @@ const getNonceFromCSPHeader = React.cache(async function getNonceFromCSPHeader() return getScriptNonceFromHeader((await headers()).get('Content-Security-Policy') || '') || ''; }); +/** Discards errors thrown by attempted code */ +const onlyTry = (cb: () => unknown) => { + try { + cb(); + } catch { + // ignore + } +}; + export async function ClerkProvider( props: Without, ) { diff --git a/packages/nextjs/src/app-router/server/auth.ts b/packages/nextjs/src/app-router/server/auth.ts index e090b849adb..b46a003c500 100644 --- a/packages/nextjs/src/app-router/server/auth.ts +++ b/packages/nextjs/src/app-router/server/auth.ts @@ -3,13 +3,12 @@ import { constants, createClerkRequest, createRedirect, type RedirectFun } from import { notFound, redirect } from 'next/navigation'; import { PUBLISHABLE_KEY, SIGN_IN_URL, SIGN_UP_URL } from '../../server/constants'; -import { createAsyncGetAuth } from '../../server/createGetAuth'; +import { createGetAuth } from '../../server/createGetAuth'; import { authAuthHeaderMissing } from '../../server/errors'; import { getAuthKeyFromRequest, getHeader } from '../../server/headers-utils'; import type { AuthProtect } from '../../server/protect'; import { createProtect } from '../../server/protect'; import { decryptClerkRequestData } from '../../server/utils'; -import { isNextWithUnstableServerActions } from '../../utils/sdk-versions'; import { buildRequestLike } from './utils'; /** @@ -26,10 +25,8 @@ type Auth = AuthObject & { */ redirectToSignIn: RedirectFun>; }; - export interface AuthFn { (): Promise; - /** * `auth` includes a single property, the `protect()` method, which you can use in two ways: * - to check if a user is authenticated (signed in) @@ -63,22 +60,9 @@ export const auth: AuthFn = async () => { require('server-only'); const request = await buildRequestLike(); - - const stepsBasedOnSrcDirectory = async () => { - if (isNextWithUnstableServerActions) { - return []; - } - - try { - const isSrcAppDir = await import('../../server/keyless-node.js').then(m => m.hasSrcAppDir()); - return [`Your Middleware exists at ./${isSrcAppDir ? 'src/' : ''}middleware.ts`]; - } catch { - return []; - } - }; - const authObject = await createAsyncGetAuth({ + const authObject = createGetAuth({ debugLoggerName: 'auth()', - noAuthStatusMessage: authAuthHeaderMissing('auth', await stepsBasedOnSrcDirectory()), + noAuthStatusMessage: authAuthHeaderMissing(), })(request); const clerkUrl = getAuthKeyFromRequest(request, 'ClerkUrl'); diff --git a/packages/nextjs/src/app-router/server/utils.ts b/packages/nextjs/src/app-router/server/utils.ts index 1c6e24a38f4..490166d3027 100644 --- a/packages/nextjs/src/app-router/server/utils.ts +++ b/packages/nextjs/src/app-router/server/utils.ts @@ -34,7 +34,7 @@ export async function buildRequestLike(): Promise { } throw new Error( - `Clerk: auth(), currentUser() and clerkClient(), are only supported in App Router (/app directory).\nIf you're using /pages, try getAuth() instead.\nOriginal error: ${e}`, + `Clerk: auth() and currentUser() are only supported in App Router (/app directory).\nIf you're using /pages, try getAuth() instead.\nOriginal error: ${e}`, ); } } diff --git a/packages/nextjs/src/server/__tests__/createGetAuth.test.ts b/packages/nextjs/src/server/__tests__/createGetAuth.test.ts index 56a9e99bd12..e881ab7368b 100644 --- a/packages/nextjs/src/server/__tests__/createGetAuth.test.ts +++ b/packages/nextjs/src/server/__tests__/createGetAuth.test.ts @@ -3,7 +3,7 @@ import hmacSHA1 from 'crypto-js/hmac-sha1'; import { NextRequest } from 'next/server'; import { describe, expect, it } from 'vitest'; -import { createSyncGetAuth, getAuth } from '../createGetAuth'; +import { createGetAuth, getAuth } from '../createGetAuth'; const mockSecretKey = 'sk_test_mock'; @@ -16,7 +16,7 @@ const mockTokenSignature = hmacSHA1(mockToken, 'sk_test_mock').toString(); describe('createGetAuth(opts)', () => { it('returns a getAuth function', () => { - expect(createSyncGetAuth({ debugLoggerName: 'test', noAuthStatusMessage: 'test' })).toBeInstanceOf(Function); + expect(createGetAuth({ debugLoggerName: 'test', noAuthStatusMessage: 'test' })).toBeInstanceOf(Function); }); }); diff --git a/packages/nextjs/src/server/createGetAuth.ts b/packages/nextjs/src/server/createGetAuth.ts index 7e6caeb0f12..f95ea6871b7 100644 --- a/packages/nextjs/src/server/createGetAuth.ts +++ b/packages/nextjs/src/server/createGetAuth.ts @@ -1,63 +1,29 @@ +import type { AuthObject } from '@clerk/backend'; import { constants } from '@clerk/backend/internal'; import { isTruthy } from '@clerk/shared/underscore'; import { withLogger } from '../utils/debugLogger'; -import { isNextWithUnstableServerActions } from '../utils/sdk-versions'; import { getAuthDataFromRequest } from './data/getAuthDataFromRequest'; import { getAuthAuthHeaderMissing } from './errors'; -import { detectClerkMiddleware, getHeader } from './headers-utils'; +import { getHeader } from './headers-utils'; import type { RequestLike } from './types'; import { assertAuthStatus } from './utils'; -export const createAsyncGetAuth = ({ - debugLoggerName, +export const createGetAuth = ({ noAuthStatusMessage, -}: { - debugLoggerName: string; - noAuthStatusMessage: string; -}) => - withLogger(debugLoggerName, logger => { - return async (req: RequestLike, opts?: { secretKey?: string }) => { - if (isTruthy(getHeader(req, constants.Headers.EnableDebug))) { - logger.enable(); - } - - if (!detectClerkMiddleware(req)) { - // Keep the same behaviour for versions that may have issues with bundling `node:fs` - if (isNextWithUnstableServerActions) { - assertAuthStatus(req, noAuthStatusMessage); - } - - const missConfiguredMiddlewareLocation = await import('./keyless-node.js') - .then(m => m.suggestMiddlewareLocation()) - .catch(() => undefined); - - if (missConfiguredMiddlewareLocation) { - throw new Error(missConfiguredMiddlewareLocation); - } - - // still throw there is no suggested move location - assertAuthStatus(req, noAuthStatusMessage); - } - - return getAuthDataFromRequest(req, { ...opts, logger }); - }; - }); - -export const createSyncGetAuth = ({ debugLoggerName, - noAuthStatusMessage, }: { debugLoggerName: string; noAuthStatusMessage: string; }) => withLogger(debugLoggerName, logger => { - return (req: RequestLike, opts?: { secretKey?: string }) => { + return (req: RequestLike, opts?: { secretKey?: string }): AuthObject => { if (isTruthy(getHeader(req, constants.Headers.EnableDebug))) { logger.enable(); } assertAuthStatus(req, noAuthStatusMessage); + return getAuthDataFromRequest(req, { ...opts, logger }); }; }); @@ -141,7 +107,7 @@ export const createSyncGetAuth = ({ * } * ``` */ -export const getAuth = createSyncGetAuth({ +export const getAuth = createGetAuth({ debugLoggerName: 'getAuth()', noAuthStatusMessage: getAuthAuthHeaderMissing(), }); diff --git a/packages/nextjs/src/server/errors.ts b/packages/nextjs/src/server/errors.ts index 24235da292b..0bd1e7be064 100644 --- a/packages/nextjs/src/server/errors.ts +++ b/packages/nextjs/src/server/errors.ts @@ -20,9 +20,9 @@ Check if signInUrl is missing from your configuration or if it is not an absolut export const getAuthAuthHeaderMissing = () => authAuthHeaderMissing('getAuth'); -export const authAuthHeaderMissing = (helperName = 'auth', prefixSteps?: string[]) => +export const authAuthHeaderMissing = (helperName = 'auth') => `Clerk: ${helperName}() was called but Clerk can't detect usage of clerkMiddleware(). Please ensure the following: -- ${prefixSteps ? [...prefixSteps, ''].join('\n- ') : ' '}clerkMiddleware() is used in your Next.js Middleware. +- clerkMiddleware() is used in your Next.js Middleware. - Your Middleware matcher is configured to match this route or page. - If you are using the src directory, make sure the Middleware file is inside of it. diff --git a/packages/nextjs/src/server/keyless-node.ts b/packages/nextjs/src/server/keyless-node.ts index 8cedd257b76..5de03b2be55 100644 --- a/packages/nextjs/src/server/keyless-node.ts +++ b/packages/nextjs/src/server/keyless-node.ts @@ -212,47 +212,4 @@ function removeKeyless() { unlockFileWriting(); } -function hasSrcAppDir() { - const { existsSync } = safeNodeRuntimeFs(); - const path = safeNodeRuntimePath(); - - const projectWithAppSrc = path.join(process.cwd(), 'src', 'app'); - - return !!existsSync(projectWithAppSrc); -} - -function suggestMiddlewareLocation() { - const suggestionMessage = (to?: 'src/', from?: 'src/app/' | 'app/') => - `Clerk: Move your middleware file to ./${to || ''}middleware.ts. Currently located at ./${from || ''}middleware.ts`; - - const { existsSync } = safeNodeRuntimeFs(); - const path = safeNodeRuntimePath(); - - const projectWithAppSrcPath = path.join(process.cwd(), 'src', 'app'); - const projectWithAppPath = path.join(process.cwd(), 'app'); - - if (existsSync(projectWithAppSrcPath)) { - if (existsSync(path.join(projectWithAppSrcPath, 'middleware.ts'))) { - return suggestionMessage('src/', 'src/app/'); - } - - if (existsSync(path.join(process.cwd(), 'middleware.ts'))) { - return suggestionMessage('src/'); - } - - // default error - return undefined; - } - - if (existsSync(projectWithAppPath)) { - if (existsSync(path.join(projectWithAppPath, 'middleware.ts'))) { - return suggestionMessage(undefined, 'app/'); - } - // default error - return undefined; - } - - return undefined; -} - -export { createOrReadKeyless, removeKeyless, suggestMiddlewareLocation, hasSrcAppDir }; +export { createOrReadKeyless, removeKeyless }; diff --git a/packages/nextjs/src/utils/only-try.ts b/packages/nextjs/src/utils/only-try.ts deleted file mode 100644 index 5f8c62e87f8..00000000000 --- a/packages/nextjs/src/utils/only-try.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Discards errors thrown by attempted code - */ -const onlyTry = (cb: () => unknown) => { - try { - cb(); - } catch { - // ignore - } -}; - -export { onlyTry };