Back to Blogs

Beyond Basic Auth: Building Multi-Role Systems with Clerk & Next.js 👥Beyond Basic Auth: Building Multi-Role Systems with Clerk & Next.js 👥

9 months ago
Beyond Basic Auth: Building Multi-Role Systems with Clerk & Next.js 👥

Building Multi-Role Systems with Clerk 👥

Most apps need more than just "logged in" vs "logged out". You need "Admins" who can delete things and "Users" who can only view them. This is RBAC (Role-Based Access Control).

The Problem I Faced

I needed an admin panel for my SaaS. I considered creating a separate database table for roles, but that required a database lookup on every request, slowing down the app.

The Solution: Clerk Custom Claims

We can attach the user's role directly to their session token (JWT). This means we know if they are an Admin instantly, without hitting the database.

Code Example 1: Assigning Roles (Metadata)

You can assign roles in the Clerk Dashboard or via API.

ts
// Determining access in a component import { auth } from "@clerk/nextjs"; export default function AdminDashboard() { const { sessionClaims } = auth(); // Custom metadata set in Clerk const role = sessionClaims?.metadata?.role; if (role !== "admin") { return <p>Access Denied</p>; } return <h1>Admin Section</h1>; }

Code Example 2: Middleware Protection

Protect entire routes using Next.js Middleware.

ts
import { authMiddleware } from "@clerk/nextjs"; import { NextResponse } from "next/server"; export default authMiddleware({ afterAuth(auth, req) { if (req.nextUrl.pathname.startsWith("/admin")) { if (auth.sessionClaims?.metadata?.role !== "admin") { return NextResponse.redirect(new URL("/", req.url)); } } } }); export const config = { matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"], };

Common Pitfalls

  • Mistake 1: Checking roles only on the frontend.
    • Don't: Rely on useUser() hook for security.
    • Do: Always verify metadata/claims on the server (API/Server Components).

Pro Tips

  1. Type Safety: Create a global.d.ts file to extend Clerk's CustomJwtSessionClaims interface so you get autocomplete for metadata.role.

Wrapping Up

Using session claims for RBAC is performant and secure. It keeps your authorization logic close to the authentication source.

Tech Stack: Clerk, Next.js, TypeScript