Nextbase Docs
Guides

Creating a Protected Page

A complete guide on creating and managing protected routes in Nextbase using a two-tier protection system, including correct middleware configuration and an example scenario.

Nextbase implements a two-tier protection system for routes that require authentication:

  1. Middleware Protection: Fast, preliminary check
  2. Layout-based Protection: Thorough, database-validated check

Middleware Protection

The middleware provides a quick, preliminary check based on the presence of an authentication cookie. This happens before the page rendering begins, offering a performance-optimized way to redirect unauthenticated users.

// Simplified middleware.ts
import { NextRequest, NextResponse } from 'next/server';
import { createSupabaseMiddlewareClient } from './supabase-clients/user/createSupabaseMiddlewareClient';
 
const protectedPaths = ['/dashboard', '/settings', '/profile' /* ... */];
 
const middlewares = [
  {
    matcher: protectedPaths.map((path) =>
      urlJoin('/', `(${LOCALE_GLOB_PATTERN})`, path),
    ),
    middleware: async (req) => {
      const supabase = createSupabaseMiddlewareClient(req);
      const {
        data: { session },
      } = await supabase.auth.getSession();
      if (!session) {
        return NextResponse.redirect(new URL('/login', req.url));
      }
      return NextResponse.next();
    },
  },
  // Other middleware configurations...
];
 
export async function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;
  for (const { matcher, middleware } of middlewares) {
    if (matchesPath(matcher, pathname)) {
      return await middleware(request);
    }
  }
  return NextResponse.next();
}

This middleware quickly checks for the presence of a session, providing a fast redirect to the login page if no session is found. This enhances website performance by avoiding unnecessary page renders for unauthenticated users.

Layout-based Protection

The layout protection provides a more thorough check, validating the session against the database to ensure it's still valid and not stale.

// src/app/(dynamic-pages)/(authenticated-pages)/layout.tsx
import { ReactNode } from 'react';
import { redirect } from 'next/navigation';
import { createSupabaseUserServerComponentClient } from '@/supabase-clients/user/createSupabaseUserServerComponentClient';
 
export default async function Layout({ children }: { children: ReactNode }) {
  const supabase = createSupabaseUserServerComponentClient();
  const { data, error } = await supabase.auth.getUser();
 
  if (error || !data.user) {
    return redirect('/login');
  }
 
  // Additional checks can be performed here, e.g., user roles, permissions, etc.
 
  return <>{children}</>;
}

This layout-based check provides a more comprehensive validation, ensuring that even if a user has a cookie, it corresponds to a valid, active session in the database. This prevents access with stale or invalid sessions.

Example: Creating a New Protected Route "/my-protected-page"

Let's walk through the correct process of creating a new protected route "/my-protected-page" in Nextbase, leveraging the two-tier protection system.

Step 1: Create the Page File

Create a new file at the following path:

src/app/(dynamic-pages)/(authenticated-pages)/my-protected-page/page.tsx

Add your page content to this file:

export default function MyProtectedPage() {
  return (
    <div>
      <h1>My Protected Page</h1>
      <p>This is a protected page only accessible to authenticated users.</p>
    </div>
  );
}

Step 2: Update Middleware Configuration

You need to manually add the new route to the protectedPaths array in the middleware configuration. Open the middleware.ts file and update it as follows:

// In middleware.ts
const protectedPaths = [
  // ... existing protected paths ...
  `/my-protected-page(/.*)?`,
];
 
const middlewares = [
  {
    matcher: protectedPaths.map((path) =>
      urlJoin('/', `(${LOCALE_GLOB_PATTERN})`, path),
    ),
    middleware: async (req) => {
      // Existing middleware logic
    },
  },
  // If you need custom logic for this specific route, you can add it here:
  {
    matcher: ['/my-protected-page'],
    middleware: async (req) => {
      // Custom logic for /my-protected-page if needed
      // For example, checking for specific user roles
      const supabase = createSupabaseMiddlewareClient(req);
      const {
        data: { session },
      } = await supabase.auth.getSession();
      if (!session) {
        return NextResponse.redirect(new URL('/login', req.url));
      }
      // Additional checks can be performed here
      return NextResponse.next();
    },
  },
];

By manually adding the new route to the protectedPaths array, you ensure that it's covered by the middleware protection, providing the fast, preliminary check.

Step 3: Layout-based Protection (Automatic)

Your new page is automatically protected by the authenticated layout, as it's placed under the (authenticated-pages) directory:

src/app/(dynamic-pages)/(authenticated-pages)/layout.tsx

This layout performs the thorough, database-validated check for all pages within this directory, ensuring that even if a user passes the middleware check, their session is still valid and active.

Step 4: Add Navigation (Optional)

If you want to add navigation to your new protected page, update your navigation component:

// In your navigation component
<Link href="/my-protected-page">My Protected Page</Link>

Testing Your New Protected Route

To test your new protected route and verify the two-tier protection:

  1. Start your Nextbase application
  2. Try accessing "/my-protected-page" without logging in
    • You should be quickly redirected to the login page by the middleware
  3. Log in with a valid user account
  4. Access "/my-protected-page"
    • The middleware will allow access (fast check)
    • The layout protection will perform a thorough check
    • You should see your protected page content
  5. Invalidate your session (e.g., by logging out in another tab)
  6. Try accessing "/my-protected-page" again
    • The middleware might initially allow access (if the cookie is still present)
    • The layout protection should detect the invalid session and redirect to login

By following these steps and understanding the two-tier protection system, you've successfully created a new protected route in your Nextbase application. This route is secured by both fast middleware checks and thorough layout-based validation, ensuring optimal performance and robust security for your application.

On this page