Authorization

Learn how to protect routes based on user roles and permissions.

Authorization verifies that a user has the necessary permissions to access a resource.

Role-Based Access Control (RBAC)

Users have one of two roles:

RoleDescription
Role.USERDefault role for all new users.
Role.ADMINFull access to admin features.

Protecting Server-Side Routes

Use the pre-configured procedure types in tRPC:

import { protectedProcedure, adminProcedure, router } from "../../trpc";

export const exampleRouter = router({
  // Any authenticated user
  getData: protectedProcedure.query(({ ctx }) => {
    return service.getData(ctx.session.user.id);
  }),

  // Admin only
  getAllUsers: adminProcedure.query(() => {
    return service.getAllUsers();
  }),
});

Always derive the user ID from ctx.session.user.id — never trust client-supplied user IDs (IDOR prevention).

Protecting Client-Side Routes

You can check the user's role in client components:

"use client";

import { authClient } from "@package/auth/client";
import { Role } from "@package/utils";

export function AdminPanel() {
  const { data: session } = authClient.useSession();

  if (session?.user.role !== Role.ADMIN) {
    return <p>Access denied.</p>;
  }

  return <div>Admin content here.</div>;
}

Client-side protection is for UX only. Always enforce authorization on the server side through tRPC procedure types.