General
Authentication

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.

app/dashboard/page.tsx
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.

app/admin/page.tsx
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:

app/premium/page.tsx
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.

app/[organizationSlug]/settings/page.tsx
import { redirect } from 'next/navigation';
import { eq } from 'drizzle-orm';

import { getOrganizationById, getSession } from '@/lib/auth/server';
import { db } from '@/lib/db';
import { organizationTable } from '@/lib/db/schema';

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 db
    .select({ id: organizationTable.id })
    .from(organizationTable)
    .where(eq(organizationTable.slug, organizationSlug))
    .limit(1);

  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

components/protected-component.tsx
'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>;
}

For specific roles

components/admin-component.tsx
'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

components/premium-component.tsx
'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

components/organization-component.tsx
'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>;
}