Middleware (src/middleware.ts)
Explains the logic within the Next.js middleware.
Root Path Handling (/)
A key function of the middleware is handling requests to the root path (/). Notably, there isn't a dedicated page.tsx file directly serving the / route.
Instead, the middleware intercepts requests for / and performs the following logic:
- It checks for the presence of the session cookie.
- If a session cookie exists, it internally rewrites the URL to
/app. The user sees the content from/appbut the browser URL remains/. - If a session cookie does not exist, it internally rewrites the URL to
/home.
Benefits & Implications:
- Performance: This URL rewriting is very fast, avoiding the need to load a root page component just to check authentication and then perform a client-side redirect. This prevents a flash of loading content or a spinner solely for the purpose of routing.
- Clear Separation: The main application lives under the
/approute group, and the public landing page lives under/home. - Accessing Landing Page When Logged In: Even when logged in, a user can explicitly navigate to
/hometo view the landing page content.
Route Protection Logic
Beyond the root path, the middleware primarily handles route protection based on authentication status and basic role checks.
- Session Check: It checks for the existence of the session cookie using
getSessionCookiefrombetter-auth/cookies. - Path Matching: It identifies the current pathname and compares it against predefined lists:
authRoutes: Pages like/signin,/signup.passwordRoutes: Pages like/reset-password.adminRoutes: Currently/admin.alwaysAllowedRoutes: Publicly accessible routes like/home,/blog.
- Redirection Logic:
- Root Path (
/): Redirects to/appif logged in, or/homeif not. - Always Allowed: If the path matches
alwaysAllowedRoutes, it allows the request (NextResponse.next()). - No Session: If no session cookie exists:
- Allows access to auth/password routes.
- Redirects all other requests to
/signin.
- Active Session: If a session cookie exists:
- Redirects away from auth/password routes back to the root (
/). - Checks if the route is an admin route (
/admin).
- Redirects away from auth/password routes back to the root (
- Root Path (
- Admin Route Check: If the route is
/adminand the user has a session:- It makes an API call (
betterFetch) to/api/auth/get-sessionto get the full session details, including the user's role. - It checks if
session.user.role === 'admin'. - If the user is not an admin, it redirects to the root (
/). - If the user is an admin, it allows the request (
NextResponse.next()).
- It makes an API call (
- Default: If none of the above conditions cause a redirect, the request is allowed (
NextResponse.next()).
Admin Route Protection Clarification
The check within the middleware for /admin routes (if (isAdminRoute) { ... }) serves as a preliminary UI protection layer. It prevents non-admin users from even seeing the /admin page layout.
However, the true security enforcement for admin-only actions happens within the tRPC backend using the adminProcedure (src/server/api/trpc.ts). Even if a non-admin user somehow bypassed the middleware (e.g., by directly calling an admin API endpoint), the adminProcedure would check ctx.session.user.role === 'admin' and throw an UNAUTHORIZED error, preventing the action.
The middleware check is primarily for user experience and preventing unnecessary rendering/API calls from the admin UI for non-admins.