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:
- Middleware Protection: Fast, preliminary check
- 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:
- Start your Nextbase application
- Try accessing "/my-protected-page" without logging in
- You should be quickly redirected to the login page by the middleware
- Log in with a valid user account
- 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
- Invalidate your session (e.g., by logging out in another tab)
- 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.