diff --git a/.changeset/deprecate-require-auth.md b/.changeset/deprecate-require-auth.md new file mode 100644 index 00000000000..064c3d51de4 --- /dev/null +++ b/.changeset/deprecate-require-auth.md @@ -0,0 +1,33 @@ +--- +'@clerk/express': minor +--- + +Deprecated `requireAuth()` middleware. It will be removed in the next major version. + +The `requireAuth()` middleware redirects unauthenticated requests to a sign-in page, which is often unexpected for API routes where a 401 response is more appropriate. Use `clerkMiddleware()` with `getAuth()` instead for explicit control over authentication behavior. + +**Before (deprecated):** + +```js +import { requireAuth } from '@clerk/express'; + +app.get('/api/protected', requireAuth(), (req, res) => { + // handle authenticated request +}); +``` + +**After (recommended):** + +```js +import { clerkMiddleware, getAuth } from '@clerk/express'; + +app.use(clerkMiddleware()); + +app.get('/api/protected', (req, res) => { + const { userId } = getAuth(req); + if (!userId) { + return res.status(401).json({ error: 'Unauthorized' }); + } + // handle authenticated request +}); +``` diff --git a/packages/express/src/__tests__/requireAuth.test.ts b/packages/express/src/__tests__/requireAuth.test.ts index e35e6c4f651..9fab628bd7f 100644 --- a/packages/express/src/__tests__/requireAuth.test.ts +++ b/packages/express/src/__tests__/requireAuth.test.ts @@ -15,11 +15,19 @@ vi.mock('../authenticateRequest', () => ({ authenticateRequest: (options = {}) => mockAuthenticateRequest(options), })); +const { mockDeprecated } = vi.hoisted(() => ({ + mockDeprecated: vi.fn(), +})); +vi.mock('@clerk/shared/deprecated', () => ({ + deprecated: mockDeprecated, +})); + describe('requireAuth', () => { beforeEach(() => { vi.clearAllMocks(); mockAuthenticateAndDecorateRequest = vi.fn(); mockAuthenticateRequest = vi.fn(); + mockDeprecated.mockClear(); }); it('should redirect to sign-in page when user is not authenticated', async () => { @@ -97,4 +105,20 @@ describe('requireAuth', () => { expect(response.status).toBe(302); expect(response.headers.location).toBe('/sign-in'); }); + + it('should emit a deprecation warning when called', async () => { + mockAuthenticateAndDecorateRequest.mockImplementation((): RequestHandler => { + return (req, _res, next) => { + Object.assign(req, mockRequestWithAuth({ userId: 'user_123' })); + next(); + }; + }); + + await runMiddleware(requireAuth()); + + expect(mockDeprecated).toHaveBeenCalledWith( + 'requireAuth', + 'Use `clerkMiddleware()` with `getAuth()` instead. `requireAuth` will be removed in the next major version.', + ); + }); }); diff --git a/packages/express/src/requireAuth.ts b/packages/express/src/requireAuth.ts index 019634331dd..27ec4e0c0be 100644 --- a/packages/express/src/requireAuth.ts +++ b/packages/express/src/requireAuth.ts @@ -1,3 +1,4 @@ +import { deprecated } from '@clerk/shared/deprecated'; import type { RequestHandler } from 'express'; import { authenticateAndDecorateRequest } from './authenticateRequest'; @@ -7,30 +8,27 @@ import type { ClerkMiddlewareOptions, ExpressRequestWithAuth } from './types'; * Middleware to require authentication for user requests. * Redirects unauthenticated requests to the sign-in url. * + * @deprecated Use `clerkMiddleware()` with `getAuth()` instead. + * `requireAuth` will be removed in the next major version. + * * @example - * // Basic usage + * // Before (deprecated) * import { requireAuth } from '@clerk/express' - * - * router.use(requireAuth()) - * //or * router.get('/path', requireAuth(), getHandler) * * @example - * // Customizing the sign-in path - * router.use(requireAuth({ signInUrl: '/sign-in' })) + * // After (recommended) + * import { clerkMiddleware, getAuth } from '@clerk/express' * - * @example - * // Combining with permission check - * import { getAuth, requireAuth } from '@clerk/express' + * app.use(clerkMiddleware()) * - * const hasPermission = (req, res, next) => { - * const auth = getAuth(req) - * if (!auth.has({ permission: 'permission' })) { - * return res.status(403).send('Forbidden') - * } - * return next() - * } - * router.get('/path', requireAuth(), hasPermission, getHandler) + * app.get('/api/protected', (req, res) => { + * const { userId } = getAuth(req); + * if (!userId) { + * return res.status(401).json({ error: 'Unauthorized' }); + * } + * // handle authenticated request + * }) */ export const requireAuth = (options: ClerkMiddlewareOptions = {}): RequestHandler => { const authMiddleware = authenticateAndDecorateRequest({ @@ -39,6 +37,11 @@ export const requireAuth = (options: ClerkMiddlewareOptions = {}): RequestHandle }); return (request, response, next) => { + deprecated( + 'requireAuth', + 'Use `clerkMiddleware()` with `getAuth()` instead. `requireAuth` will be removed in the next major version.', + ); + authMiddleware(request, response, err => { if (err) { return next(err);