Some pages should only be visible to logged-in users. Some only to admins. Here's how.
Don't worry — you won't write any auth or middleware code by hand. Your AI tool handles all the technical parts. This guide helps you understand the concepts so you can describe what you want.
Every page on your site falls into one of three buckets:
Public
Anyone can see it. Homepage, /about, /pricing, /blog. No login needed.
Authenticated
Only logged-in users can see it. /dashboard, /settings, /profile. If not logged in, redirect to /login.
Admin only
Only users with the admin role. /admin, /admin/users, /admin/billing. If not admin, show 404 or forbidden.
The right place to check "is this user allowed?" is in middleware. Middleware runs before any page loads. It's the bouncer at the door, not a sign on the wall.
How middleware works
ADD AUTH MIDDLEWARE
"Make the /dashboard and /settings pages only visible to signed-in visitors (using Supabase for login). If someone who is not signed in tries to open one of those pages, send them to the /login page, then bring them right back to where they were going as soon as they sign in. Leave the homepage, /about, and /pricing open to everyone."
Hiding a button in the UI is not protection. A determined user can open the browser console, edit the HTML, or just type the URL directly. Real protection happens on the server.
❌ Client-only check
"If user is not admin, hide the <AdminPanel /> component."
Problem: the data still loads. Anyone can see it in the network tab.
✅ Server-side check
"On the server, before fetching admin data, verify the user is admin. If not, return a 404."
Why it works: the data never reaches the browser at all.
A "role" is a label on a user that says what they can do. The simplest setup is one of two roles: user (everyone) and admin (you and your team).
Roles live in the database. Add a role column to your profiles table. Then in middleware, query that column to decide what to do.
Common role setups
2 roles: user, admin (most apps)
3 roles: user, moderator, admin (forums, community sites)
4 roles: free, pro, team, admin (SaaS with paid tiers)
Many roles: use a permissions system instead (RBAC)
The admin area is just a folder of pages that only admins can see. The convention: put them all under /admin and protect that whole prefix in middleware.
BUILD AN ADMIN AREA
"Build a basic admin area for my app. Add a 'role' field to each user (everyone starts as a regular user, I'll mark myself as admin). If someone who isn't an admin tries to open any /admin page, send them away. Create /admin (dashboard with stats), /admin/users (a table of everyone with the ability to change their role), and /admin/posts (a list of all posts with delete buttons). Make sure the protection happens on the server so nobody can sneak in by editing their browser — not just by hiding things in the interface."
Chicken-and-egg problem: you need to be an admin to access the admin area, but there's no UI to promote yourself. The fix: do it directly in the database, once.
Sign up for your own app like a normal user
Open Supabase → Table Editor → profiles
Find your row, click the role column, change it from "user" to "admin", save
Refresh your app — you can now access /admin
"Logged out" flash on page load
The page renders first, then checks auth. Move the check to middleware so it runs before render.
Can access /admin by typing the URL directly
Middleware isn't covering the route. Check the matcher in middleware.ts includes /admin/:path*.
Admin role works locally but not in production
You changed your local profile's role but not your production user. Promote the production user too.
Logged-in users get redirected to login over and over
Cookie domain mismatch or session storage issue. Ask AI to debug the auth callback.
You built a project management SaaS. Right now anyone can access /dashboard, /settings, and /admin if they know the URL. Lock everything down: dashboard and settings need login, admin needs the admin role, and unauthenticated users get redirected to a sign-in page that returns them to where they were trying to go.
Build this with AI
"Lock down my SaaS app. Make /dashboard, /projects, and /settings only visible to signed-in visitors, and make /admin only visible to admins. Use Supabase for login. Anyone who is not signed in should be sent to /login and then bounced back to the page they were trying to reach as soon as they sign in. Admins are the users whose 'role' is set to admin in my users data."
GROUP-BASED PERMISSIONS
"In my project management app, users belong to teams. Make sure each user only ever sees projects that belong to their team. Enforce this in the database itself, not just by hiding things in the interface — so there's no way to sneak around it."
HIDE SENSITIVE NAV LINKS
"In my navigation bar, hide links the visitor doesn't have permission to use. Hide the /admin link for anyone who isn't an admin. Hide the /billing link for visitors on the free plan. Keep this in addition to the real protection on the server — not as a replacement for it."
SHOW A PROPER 403 PAGE
"When a logged-in user tries to access a page they don't have permission for, show a friendly 403 Forbidden page (not a 404 or a redirect to login). The page should say "You don't have access to this page" and have a button to go back home."