Skip to content

Dhaneyl/ControlHub

Repository files navigation

ControlHub

Enterprise-grade authentication, authorization, and feature flag management system built with React.

Overview

ControlHub demonstrates senior-level React architecture patterns including:

  • Role-based access control (RBAC)
  • Feature flags with role-scoped visibility
  • Granular error boundaries
  • Lazy loading by module
  • Clean separation of concerns

Tech Stack

Technology Purpose
React 18 UI Framework
TypeScript Type safety
React Router v6 Routing & navigation
Zustand State management
MSW Mock API (Service Worker)
Vite Build tool

Quick Start

# Install dependencies
npm install

# Start development server
npm run dev

Open http://localhost:5173 and login with test credentials.

Test Accounts

Role Email Password
Admin admin@test.com admin123
Editor editor@test.com editor123
Viewer viewer@test.com viewer123

Architecture

Project Structure

src/
├── app/                    # App entry point & providers
├── auth/                   # Authentication module
│   ├── store/              # Zustand store
│   ├── hooks/              # useAuth hook
│   ├── types/              # TypeScript types
│   └── utils/              # Token utilities
├── permissions/            # Authorization module
│   ├── store/              # Permissions store
│   ├── hooks/              # usePermissions, useHasPermission
│   └── components/         # PermissionGate
├── features/               # Feature flags module
│   ├── store/              # Feature flags store
│   ├── hooks/              # useFeatureFlag
│   ├── components/         # FeatureGate
│   └── config/             # Feature definitions
├── routes/                 # Routing configuration
│   └── guards/             # AuthGuard, RoleGuard, FeatureGuard
├── layouts/                # Layout components by role
├── errors/                 # Error boundaries & fallbacks
├── components/             # Shared UI components
├── pages/                  # Page components
└── mocks/                  # MSW handlers & mock data

Permission Model

┌─────────────────────────────────────────────────────────────┐
│                         ADMIN                                │
│  users:* | content:* | reports:* | settings:* | features:*  │
├─────────────────────────────────────────────────────────────┤
│                        EDITOR                                │
│  content:read,write,delete | reports:read | settings:read   │
├─────────────────────────────────────────────────────────────┤
│                        VIEWER                                │
│  content:read | reports:read                                 │
├─────────────────────────────────────────────────────────────┤
│                         GUEST                                │
│  (no permissions - public routes only)                       │
└─────────────────────────────────────────────────────────────┘

Feature Flags

Feature Roles Default
new-dashboard admin, editor enabled
export-data admin enabled
beta-mode admin disabled
advanced-reports admin, editor, viewer enabled
bulk-actions admin, editor enabled

Key Decisions

1. Auth Lives in Store, Not Components

// auth.store.ts - Single source of truth
const useAuthStore = create<AuthState>((set) => ({
  user: null,
  token: null,
  isAuthenticated: false,
  login: async (credentials) => { /* ... */ },
  logout: () => { /* ... */ },
}));

Why: Components should only consume state, never manage auth logic directly. This ensures consistency across the app and makes testing easier.

2. Guards are Composable Wrappers

<AuthGuard>
  <RoleGuard allowedRoles={['admin', 'editor']}>
    <FeatureGuard feature="new-dashboard">
      <Dashboard />
    </FeatureGuard>
  </RoleGuard>
</AuthGuard>

Why: Single responsibility principle. Each guard does one thing. They can be composed as needed without tight coupling.

3. Feature Flags Have Role Scope

const feature = {
  id: 'export-data',
  enabled: true,
  roles: ['admin'], // Only visible to admins even when enabled
};

Why: Real-world feature flags often need role-based visibility. A feature might be "on" but only for certain user segments.

4. Lazy Loading by Module

const AdminDashboard = lazy(() => import('./pages/admin/Dashboard'));
const EditorContent = lazy(() => import('./pages/editor/Content'));

Why: Users only download code for their role. Admins don't load editor code, editors don't load admin code. Smaller bundles, faster loads.

5. Granular Error Boundaries

App
└── GlobalErrorBoundary (catches everything)
    └── Layout
        └── ModuleErrorBoundary (catches module errors)
            └── Page (if this crashes, only this section shows error)

Why: One component crashing shouldn't take down the entire app. Users see a scoped error message and can still navigate.

6. Permissions vs Feature Flags

  • Permissions: What a user CAN do (CRUD operations)
  • Feature Flags: What the SYSTEM allows (toggle functionality)
// Permission: Can user export?
<PermissionGate permission="reports:export">
  {/* Feature: Is export enabled? */}
  <FeatureGate feature="export-data">
    <ExportButton />
  </FeatureGate>
</PermissionGate>

Authentication Flow

1. App loads → checkSession()
   ├── Token exists & valid → Hydrate user state
   └── No/invalid token → Show login

2. User logs in → POST /api/auth/login
   ├── Success → Store token, set user, redirect to dashboard
   └── Failure → Show error, stay on login

3. Protected route access → AuthGuard
   ├── Authenticated → RoleGuard → FeatureGuard → Render
   └── Not authenticated → Redirect to /login (preserve intended URL)

4. Logout → Clear token & state → Redirect to home

Limitations

  1. Mock API only - No real backend. Data resets on page refresh.
  2. No token refresh - JWT expiry is simple (24h), no refresh token flow.
  3. Client-side security - Guards are for UX, not security. Real API should validate too.
  4. No persistent feature flags - Toggles reset on refresh.
  5. Basic error logging - Only console.error, no monitoring service.

What I Would Add With a Real Backend

  1. Token refresh mechanism - Automatic token renewal before expiry
  2. Server-side session validation - Don't trust client state alone
  3. Audit logging - Track who changed what and when
  4. Feature flag persistence - Store flag state in database
  5. A/B testing support - Random flag assignment for experiments
  6. Rate limiting - Prevent brute force attacks
  7. CSRF protection - For state-changing requests
  8. Real error tracking - Sentry, DataDog, etc.

Scripts

npm run dev      # Start dev server
npm run build    # Production build
npm run preview  # Preview production build
npm run lint     # Run ESLint

License

MIT

ControlHub

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors