← All posts

2026-07-15

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

AuthRBACNext.jsUXBizee

Bizee multi-role auth and owner/client view toggle cover

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 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 (not just a cosmetic toggle).

The objective

You will learn how to:

  1. Model users who can hold more than one role.
  2. Separate authorization (what they’re allowed to do) from view mode (which chrome they see).
  3. 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?”

ConcernSource of truthExample
PermissionDB roles / claimsBUSINESS_OWNER, CLIENT
Chrome / IAviewMode in state + localStoragebusiness vs client
Route homeRouter 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 — I keep saying this because it’s easy to forget.
  • Toggle must redirect to the correct home.
  • Layout follows route family for multi-role users.
  • APIs stay permission-based, always.

Call to Action

See owner and client experiences in the Glow Studio demo: bizee.life/demo.
Building multi-role SaaS? I’m curious whether you store view mode in the JWT, the DB, or localStorage.

References