Skip to content

Permissions

Role-based access control built on Better Auth’s admin plugin. Roles are stored on the user record; permissions are defined in code via Better Auth access control.

How it works

User

Roles (DB)

Permissions (access.ts)

  • Users are assigned roles (stored as a comma-separated role field on the Better Auth user table)
  • Roles map to permissions (defined in apps/auth/access.ts)

Built-in roles:

RolePermissions
user(none beyond default access)
adminBetter Auth admin statements
premiumviewPremium
eliteviewPremium, viewElite

Define roles and content permissions in apps/auth/access.ts:

import { createAccessControl } from "better-auth/plugins/access"
import { adminAc, defaultStatements } from "better-auth/plugins/admin/access"
const statement = {
...defaultStatements,
content: ["viewPremium", "viewElite"],
} as const
export const ac = createAccessControl(statement)
export const user = ac.newRole({})
export const admin = ac.newRole({ ...adminAc.statements })
export const premium = ac.newRole({ content: ["viewPremium"] })
export const elite = ac.newRole({ content: ["viewPremium", "viewElite"] })

These roles are registered on both the Better Auth server (apps/auth/auth.server.ts) and client (apps/auth/auth.ts).

All functions are exported from @/auth.

FunctionPurpose
requireUserPermissions({ data: { userId, permissions } })Server function - throws if user lacks 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

Use requireUserPermissions in beforeLoad to restrict access by permission:

import { createFileRoute } from "@tanstack/react-router"
import { requireUserId, requireUserPermissions } from "@/auth"
export const Route = createFileRoute("/premium")({
beforeLoad: async () => {
const userId = await requireUserId()
await requireUserPermissions({ data: { userId, permissions: ["viewPremium"] } })
// User has permission, continue...
},
})

Or requireUserRole to restrict access by role:

import { createFileRoute } from "@tanstack/react-router"
import { requireUserId, requireUserRole } from "@/auth"
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 "@/auth"
const isElite = await hasUserRole(userId, "elite")

Assign or remove roles from users:

import { grantUserRole, revokeUserRole } from "@/auth"
// After a purchase
await grantUserRole(userId, "premium")
// When subscription ends
await revokeUserRole(userId, "premium")