Skip to content

Conversation

@ph1losof
Copy link

@ph1losof ph1losof commented Dec 2, 2025

Description

This PR fixes the Better Auth middleware implementation in the migration documentation to resolve session validation errors and improve middleware compatibility.

Key Changes:

  • Updated packages/auth/middleware.ts implementation to use getSessionCookie() instead of fetch-based session validation
  • Implemented proper middleware composition pattern that maintains compatibility with existing next-forge middlewares (internationalization and security)
  • Updated documentation to reflect the recommended middleware approach

Benefits:

  • Eliminates unnecessary HTTP requests during middleware execution
  • Improves performance by checking session cookies directly
  • Ensures compatibility with all existing next-forge middleware packages
  • Follows the same composition pattern used by Clerk's original middleware implementation

Related Issues

Closes #384

Checklist

  • My code follows the code style of this project.
  • I have performed a self-review of my code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have updated the documentation, if necessary.
  • I have added tests that prove my fix is effective or my feature works.
  • New and existing tests pass locally with my changes.

Screenshots (if applicable)

Additional Notes

Breaking Changes

None. This is a documentation update that provides a corrected implementation for users migrating from Clerk to Better Auth.

Migration Path

Users who have already followed the previous documentation should:

  1. Replace their packages/auth/middleware.ts with the new implementation
  2. No other files need to be changed
  3. Existing middleware compositions will continue to work

Why This Approach?

  • Performance: Direct cookie reading is faster than making fetch requests
  • Reliability: Avoids potential fetch failures in middleware context
  • Compatibility: Matches the middleware composition pattern established by Clerk
  • Simplicity: Reduces complexity by eliminating the auth endpoint dependency

Credits

Solution inspired by the Clerk middleware implementation

@vercel
Copy link

vercel bot commented Dec 2, 2025

@ph1losof is attempting to deploy a commit to the Vercel Team on Vercel.

A member of the Team first needs to authorize it.

Comment on lines +229 to +233
if (
response &&
(!response.headers.get("x-middleware-next") ||
response.headers.get("Location"))
) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (
response &&
(!response.headers.get("x-middleware-next") ||
response.headers.get("Location"))
) {
if (response && response.headers.get("Location")) {

The middleware composition logic has a critical flaw: it returns responses from custom middleware without the x-middleware-next header set, causing the authentication check to be skipped for all protected routes.

View Details

Analysis

Middleware composition logic bypasses authentication check for non-redirect responses

What fails: The authMiddleware function in the Better Auth migration guide checks for an internal x-middleware-next header that external middleware libraries don't set, causing normal middleware responses to return early and skip the authentication check for protected routes.

How to reproduce: Follow the Better Auth migration guide and use external middleware (like @nosecone/next for security headers or next-international for i18n) within the authMiddleware callback. These libraries return Response objects without setting the internal x-middleware-next header. The middleware will return immediately, bypassing authentication validation:

export default authMiddleware(async (_auth, request, event) => { const headersResponse = securityHeaders(); // Returns Response without x-middleware-next return headersResponse; }) as unknown as NextMiddleware;

Result: Unauthenticated users can bypass protected route authentication because the condition:

if (response && (!response.headers.get("x-middleware-next") || response.headers.get("Location"))) { return response; // Returns immediately, skipping auth check }

returns true when external middleware doesn't set the x-middleware-next header (which they don't).

Expected: Only redirect responses (with Location header) should bypass the authentication check. Normal middleware responses from security headers, i18n, or other utilities should continue to the authentication validation step.

Fix applied: Changed the condition to only check for Location header (which indicates a redirect). This allows external middleware to add headers without prematurely bypassing authentication. The corrected logic is:

if (response && response.headers.get("Location")) { return response; }

This pattern aligns with standard Next.js middleware composition where only redirects should interrupt the middleware chain, while header-adding middleware (security, i18n, etc.) should allow the chain to continue.

References:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant