2026-07-15
Multi-Role Auth in Next.js: Owner, Client, and a Toggle That Doesn’t Lie

Part of Building Bizee — Technical Notes. See also the Bizee case study.
Introduction
The hook
Your user is a business owner and also a client of another shop on the same platform. They log in once. Then the UI shows the wrong nav: owner tools on a client route, or a consumer chrome on /dashboard. Support tickets write themselves.
That dual identity is normal on Bizee. Solving it meant roles in the session plus an explicit view mode that redirects for real.
The objective
You will learn how to:
- Model users who can hold more than one role.
- Separate authorization (what they’re allowed to do) from view mode (which chrome they see).
- Toggle owner ↔ client and land on the correct home route every time.
Prerequisites
- Next.js App Router + a session/cookie auth helper
- Familiarity with RBAC concepts (roles ≠ UI skin)
- React hooks basics
Body
Step 1: Roles answer “can they?” — view mode answers “what are they looking at?”
| Concern | Source of truth | Example |
|---|---|---|
| Permission | DB roles / claims | BUSINESS_OWNER, CLIENT |
| Chrome / IA | viewMode in state + localStorage | business vs client |
| Route home | Router after toggle | /dashboard vs /client/dashboard |
If you only check role === CLIENT for layout, multi-role owners never see client chrome when they need it.
Step 2: Persist view mode, but don’t trust it alone
We keep bizee_view_mode in localStorage so refresh doesn’t yank someone mid-task. On toggle, we also navigate:
// Pattern from useViewMode
const toggleViewMode = () => {
const newMode = viewMode === 'business' ? 'client' : 'business';
setViewMode(newMode);
localStorage.setItem('bizee_view_mode', newMode);
if (newMode === 'client') {
router.push('/client/dashboard');
} else {
router.push('/dashboard');
}
};
Without the router.push, the toggle is a costume change on the wrong page.
Step 3: Apply the right layout for the route family
Auth wrappers should attach owner or client shell based on path + auth, not only user.role === CLIENT:
- Business family:
/dashboard,/business/*,/clients, … - Client family:
/client/*
Multi-role users on /client/book get client top/bottom bars. On /business/walk-ins, they get owner chrome. Same session. Different IA.
Step 4: Gate APIs by permission, not by view mode
View mode is UX. Never authorize a write because viewMode === 'business'. Always check server-side roles / ownership of the businessId.
Mental model
Session (user + roles)
│
├─► API guards (can they mutate?)
│
└─► viewMode + pathname
└─► MainLayout chrome + default home
Conclusion
Summary
- Multi-role users need view mode separate from RBAC.
- Toggle must redirect to the correct home.
- Layout follows route family for multi-role users.
- APIs stay permission-based.
Call to Action
See owner and client experiences in the Glow Studio demo: bizee.life/demo.
Building multi-role SaaS? Comment whether you store view mode in the JWT, the DB, or localStorage.