Documentation
Authentication

Permissions and Access Control

Learn how to manage permissions, access control, and user details on both client and server side.

AI Skill for permissions

Prompt: Type /permissions in your Copilot / Cursor or other chat to use skill with the provided context.
/permissions Implement or update permissions and access control in this feature.

Requirements:
- Cover both client and server checks where needed.
- Use existing auth and permission helpers in this codebase.
- Keep access checks explicit, minimal, and consistent with current patterns.

When building your application, you may need to restrict access to certain components, pages, or API endpoints based on the user's authentication status, role, or subscription plan.

Below are practical examples of how to verify user access on both the client (React Components) and the server (oRPC, Server Actions, or API Routes).

API Routes (oRPC)

In your oRPC routers, use contexts to enforce authentication globally for specific endpoints without writing repetitive checks.

import { authorized } from '@/lib/orpc/context/authorized';
import { base } from '@/lib/orpc/context/base';
import { z } from 'zod';

export const exampleRouter = {
  // Accessible to anyone
  publicData: base
    .route({ method: 'GET', path: '/public' })
    .handler(async () => {
      return { message: "Hello Guest" };
    }),

  // Protects the route automatically; execution only proceeds if session exists
  privateData: authorized
    .route({ method: 'GET', path: '/private' })
    .handler(async ({ context }) => {
      // The `authorized` context guarantees an active session
      return { message: `Hello ${context.user.name}` };
    }),
};

Authenticated User

Verify if there is a currently logged-in user to show/hide specific UI elements or protect server logic.

'use client';

import { useSession } from '@/lib/auth-client';

export function DashboardLink() {
  const { data: session, isPending } = useSession();
  
  if (isPending) return <p>Loading...</p>;
  if (!session) return <p>Please log in.</p>;
  
  return <a href="/dashboard">Go to Dashboard, {session.user.name}</a>;
}
import { headers } from 'next/headers';
import { auth } from '@/lib/auth';

export async function ProtectedServerComponent() {
  const session = await auth.api.getSession({
    headers: await headers()
  });
  
  if (!session) {
    return <p>Unauthorized</p>;
  }
  
  return <div>Welcome back, {session.user.name}</div>;
}

Specific Role (Admin)

Check if the current user has the admin role, usually required for administrative dashboards or settings.

'use client';

import { useIsAdmin } from '@/shared/hooks/use-is-admin';

export function AdminSettingsButton() {
  const { isAdmin, session } = useIsAdmin();
  
  if (!isAdmin) return null;
  
  return <button>Delete System</button>;
}
import { headers } from 'next/headers';
import { auth } from '@/lib/auth';
import { isAdminRole } from '@/lib/auth/roles';

export async function AdminOnlyComponent() {
  const session = await auth.api.getSession({
    headers: await headers()
  });
  
  if (!isAdminRole(session?.user?.role)) {
    return <p>Access Denied: Admins Only</p>;
  }
  
  return <div>Admin Panel Controls</div>;
}

Specific Subscription Plan

Check the user's active billing plan (e.g., pro, premium) for features hidden behind a paywall. By default, the current plan gets synced from your payment provider to the user's plan field on the database.

'use client';

import { usePlan } from '@/features/common/payments/hooks/use-plan';

export function ProFeature() {
  const { hasPlan, isPending } = usePlan();
  
  if (isPending) return <p>Loading...</p>;
  if (!hasPlan('pro')) return <p>Upgrade to Pro to use this feature.</p>;
  
  return <div>Advanced Pro Feature Unlocked!</div>;
}
import { getUserPlan } from '@/features/common/payments/server/get-user-plan';

export async function ProServerAction() {
  const { hasPlan } = await getUserPlan();
  
  if (!hasPlan('pro')) {
    throw new Error("Pro subscription required");
  }
  
  // Proceed with restricted business logic...
  return "Success";
}

On this page