Documentation
Documentation
Introduction

Getting Started

Getting StartedInstallationQuick StartProject Structure

Architecture

Architecture OverviewTech StacktRPC MiddlewareDesign Principles

Patterns

Code Patterns & ConventionsFeature ModulesError HandlingType Safety

Database

DatabaseSchema DefinitionDatabase OperationsMigrationsCaching

API

tRPCProceduresRouterstRPC Proxy Setup
APIsOpenAPIREST Endpoints

Auth & Access

AuthenticationConfigurationOAuth ProvidersRolesSession Management
AuthorizationUser RolesPermissions

Routing & i18n

RoutingDeclarative RoutingNavigation
InternationalizationTranslationsLocale Routing

Components & UI

ComponentsButtonsFormsNavigationDialogs
StylesTailwind CSSThemingTypography

Storage

StorageConfigurationUsageBuckets

Configuration

ConfigurationEnvironment VariablesFeature Flags

Templates

Template GuidesCreate New FeatureCreate New PageCreate Database TableCreate tRPC RouterAdd Translations

Development

DevelopmentCommandsAI AgentsBest Practices

Authorization

Role-based access control and permission system

Overview

Authorization determines what authenticated users can do in your application. While authentication verifies identity (who you are), authorization controls access (what you can do).

This template provides a comprehensive authorization system with:

  • Role-Based Access Control (RBAC) - User roles with different privilege levels
  • Permission System - Fine-grained access control for organizations
  • Resource Ownership - Users can only access their own resources
  • Row-Level Security - Database-level access policies

Authentication vs Authorization

AspectAuthenticationAuthorization
PurposeVerify identityControl access
QuestionWho are you?What can you do?
ImplementationLogin, sessionsRoles, permissions
ExampleEmail/password loginAdmin can delete users

Quick Start

Server-Side Authorization

import { requireRole } from "@/lib/auth/layout-guards";
import { UserRole } from "@/db/enums";

// In a layout or page
export default async function AdminLayout() {
  // Requires ADMIN role, redirects if unauthorized
  const auth = await requireRole(UserRole.ADMIN);
  
  return <div>Admin content for {auth.user.email}</div>;
}

tRPC Authorization

import { roleProcedure } from "@/trpc/procedures/trpc";
import { UserRole } from "@/db/enums";

// Only admins can call this procedure
deleteUser: roleProcedure(UserRole.ADMIN)
  .input(z.object({ userId: z.string() }))
  .mutation(async ({ ctx, input }) => {
    // ctx.user is guaranteed to be an admin
    await ctx.db.users.deleteUser({ id: input.userId });
    
    return { success: true };
  })

Client-Side Role Checks

"use client";

import { useAuth } from "@/lib/auth/client";
import { UserRole } from "@/db/enums";

export function AdminButton() {
  const { user, hasRole } = useAuth();
  
  if (!hasRole(UserRole.ADMIN)) return null;
  
  return <button>Admin Action</button>;
}

Authorization Levels

This template implements authorization at multiple layers:

1. Route Level

Entire pages/layouts protected by role requirements:

// app/[locale]/admin/layout.tsx
await requireRole(UserRole.ADMIN);

2. API Level

tRPC procedures protected by role or authentication:

// authProcedure - requires any authenticated user
// roleProcedure - requires specific role(s)

3. Component Level

UI elements conditionally rendered based on roles:

{hasRole(UserRole.ADMIN) && <AdminPanel />}

4. Database Level

Row-Level Security (RLS) policies enforce access at the database:

// Users can only read their own data
.enableRLS()
.withPolicy("select", { to: authenticatedRole, using: isOwner(users.id) })

Core Concepts

User Roles

System-wide roles defined in UserRole enum:

  • ADMIN - Full system access
  • USER - Standard user access

See Roles documentation for details.

Organization Permissions

Fine-grained permissions within organizations:

  • owner - Full organization access
  • admin - Can manage members and settings
  • viewer - Read-only access

See Permissions documentation for details.

Resource Ownership

Users have implicit access to resources they own:

import { isOwner } from "@/db/rls";

// Check if current user owns this resource
.withPolicy("update", {
  to: authenticatedRole,
  using: isOwner(tasks.ownerId)
})

Architecture

src/lib/auth/
├── layout-guards.ts    # requireRole(), requireUnauthenticated()
└── permissions.ts      # Access control definitions

src/trpc/procedures/
└── trpc.ts            # authProcedure, roleProcedure

src/db/
└── rls.ts             # Row-Level Security helpers

src/db/
└── enums.ts           # UserRole enum

Best Practices

Defense in Depth - Implement authorization checks at multiple layers (route, API, database) for maximum security.

Never Trust Client-Side - Always validate permissions server-side. Client-side checks are for UX only.

Fail Securely - Default to denying access. Explicitly grant permissions rather than explicitly denying them.

Next Steps

Roles

User roles and role-based procedures

Permissions

Organization permissions and RLS

On this page

Overview
Authentication vs Authorization
Quick Start
Server-Side Authorization
tRPC Authorization
Client-Side Role Checks
Authorization Levels
1. Route Level
2. API Level
3. Component Level
4. Database Level
Core Concepts
User Roles
Organization Permissions
Resource Ownership
Architecture
Best Practices
Next Steps