Permissions and Access Control
Learn how to protect pages and display UI based on user roles or permissions.
We have already guided you through the process of how to implement access control in the API routes of your application. In this guide we will show you how you can protect pages and display UI based on the user's role or permissions.
Protect a route (server side)
For authenticated users
To protect a route to be only accessible for authenticated users, you can simply get the session in the RSC component and check if the user is authenticated.
Note: When you are inside the /app/(saas)/dashboard directory, you don't need to check if the user is authenticated, because the session is verified in the middleware.
import { redirect } from 'next/navigation';
import { getSession } from '@/lib/auth/server';
export default async function MyProtectedPage() {
const session = await getSession();
if (!session) {
return redirect('/auth/sign-in');
}
return <div>My protected page</div>;
}For specific roles
More interesting is to check if the user has the necessary permissions to access the page. For example, you can make a page only accessible for users with the admin role.
import { redirect } from 'next/navigation';
import { getSession } from '@/lib/auth/server';
export default async function MyAdminPage() {
const session = await getSession();
if (session?.user.role !== 'admin') {
return redirect('/app');
}
return <div>This page is only accessible for admins</div>;
}For active or specific subscription
Or if you want to check for an active subscription, you can do the following:
import { redirect } from 'next/navigation';
import { getSession } from '@/lib/auth/server';
import {
getActivePlanForOrganization,
requirePaidPlan,
requireSpecificPlan
} from '@/lib/billing/guards';
export default async function MyPremiumPage() {
const session = await getSession();
if (!session?.activeOrganizationId) {
return redirect('/app');
}
// Check for any paid plan
try {
const { planId } = await requirePaidPlan(session.activeOrganizationId);
// User has a paid plan
} catch {
return redirect('/app');
// or show a message to the user that they need to subscribe to the premium plan
}
// Or check for a specific plan
try {
const { planId } = await requireSpecificPlan(session.activeOrganizationId, [
'pro',
'enterprise'
]);
// User has pro or enterprise plan
} catch {
return (
<div>This page is only accessible for users with a pro subscription</div>
);
}
return (
<div>
This page is only accessible for users with an active subscription
</div>
);
}For organization role
You can also check if a user has a specific role inside the current organization. For example, you might want to add features that are only available for organization owners or admins.
import { redirect } from 'next/navigation';
import { getOrganizationById, getSession } from '@/lib/auth/server';
import { prisma } from '@/lib/db';
export default async function MyOrganizationPage({
params
}: {
params: Promise<{ organizationSlug: string }>;
}) {
const { organizationSlug } = await params;
const session = await getSession();
// First, find organization by slug
const org = await prisma.organization.findUnique({
where: { slug: organizationSlug },
select: { id: true }
});
if (!org) {
redirect('/app');
}
// Then get full organization with members
const organization = await getOrganizationById(org.id);
if (!organization) {
redirect('/app');
}
const membership = organization.members.find(
(member) => member.userId === session?.user.id
);
if (
!membership ||
(membership.role !== 'admin' && membership.role !== 'owner')
) {
return <div>This page is only accessible for organization admins</div>;
}
return <div>This page is only accessible for organization admins</div>;
}Display UI based on permissions (client side)
On client side, you can use the authClient.useSession() hook to get the session and then check if the user has the necessary permissions.
For authenticated users
'use client';
import { authClient } from '@/lib/auth/client';
export function MyComponent() {
const { data: session } = authClient.useSession();
if (!session) {
return <div>You need to be logged in to access this page</div>;
}
return <div>You are logged in</div>;
}Security Note You always want to check the permission on the server side first to avoid any security issues.
For specific roles
'use client';
import { authClient } from '@/lib/auth/client';
export function MyComponent() {
const { data: session } = authClient.useSession();
if (session?.user.role !== 'admin') {
return <div>This page is only accessible for admins</div>;
}
return <div>This page is only accessible for admins</div>;
}For active or specific subscription
'use client';
import { trpc } from '@/trpc/client';
export function MyComponent() {
const { data: subscriptionStatus } =
trpc.organization.subscription.getStatus.useQuery();
if (!subscriptionStatus?.enabled) {
return <div>Billing is not enabled</div>;
}
if (!subscriptionStatus.activePlan) {
return <div>You don't have an active subscription</div>;
}
if (
subscriptionStatus.activePlan.planId !== 'pro' &&
subscriptionStatus.activePlan.planId !== 'enterprise'
) {
return <div>You need to subscribe to the pro plan to access this page</div>;
}
return <div>You have an active subscription</div>;
}For organization role
'use client';
import { authClient } from '@/lib/auth/client';
export function MyComponent() {
const { data: activeOrganization } = authClient.useActiveOrganization();
const { data: session } = authClient.useSession();
if (!activeOrganization || !session) {
return <div>No active organization</div>;
}
// Find the user's membership in the active organization
const membership = activeOrganization.members.find(
(member) => member.userId === session.user.id
);
// Check if user is admin or owner
const isOrganizationAdmin =
membership && (membership.role === 'admin' || membership.role === 'owner');
if (!isOrganizationAdmin) {
return <div>This page is only accessible for organization admins</div>;
}
if (membership.role !== 'owner') {
return <div>This page is only accessible for organization owners</div>;
}
return <div>This page is only accessible for organization admins</div>;
}