Permissions
A simple RBAC (Role-Based Access Control) system with a twist: roles live in the database, while permissions are defined in config. This hybrid approach keeps role assignments flexible (database) while keeping permission definitions simple and version-controlled (config).
How it works
- Users are assigned roles (stored in
user_rolesdatabase table) - Roles map to permissions (defined in
packages/config/index.ts)
Configuration
Section titled “Configuration”Define roles and their permissions in packages/config/index.ts:
const config = { permissions: { roleToPermissions: { premium: { viewPremiumContent: true, }, elite: { viewPremiumContent: true, viewEliteContent: true, }, }, },}All functions are exported from @/permissions.
| Function | Purpose |
|---|---|
requireUserPermissions({ data: { userId, permissions } }) | Server function - throws if user lacks any of the specified permissions |
requireUserRole({ data: { userId, roleName } }) | Server function - throws if user doesn’t have the role |
hasUserRole(userId, roleName) | Returns boolean - use when you need to check without throwing |
grantUserRole(userId, roleName) | Assigns a role to user |
revokeUserRole(userId, roleName) | Removes a role from user |
Protecting Pages
Section titled “Protecting Pages”Use requireUserPermissions in beforeLoad to restrict access by permission:
import { createFileRoute } from "@tanstack/react-router"import { requireUserId } from "@/auth"import { requireUserPermissions } from "@/permissions"
export const Route = createFileRoute("/premium")({ beforeLoad: async () => { const userId = await requireUserId() await requireUserPermissions({ data: { userId, permissions: ["viewPremiumContent"] } }) // User has permission, continue... },})Or requireUserRole to restrict access by role:
import { createFileRoute } from "@tanstack/react-router"import { requireUserId } from "@/auth"import { requireUserRole } from "@/permissions"
export const Route = createFileRoute("/elite")({ beforeLoad: async () => { const userId = await requireUserId() await requireUserRole({ data: { userId, roleName: "elite" } }) // User has role, continue... },})For conditional checks without throwing:
import { hasUserRole } from "@/permissions"
const isElite = await hasUserRole(userId, "elite")Granting & Revoking Access
Section titled “Granting & Revoking Access”Assign or remove roles from users:
import { grantUserRole, revokeUserRole } from "@/permissions"
// After a purchaseawait grantUserRole(userId, "premium")
// When subscription endsawait revokeUserRole(userId, "premium")