JSPM

  • Created
  • Published
  • Downloads 1106
  • Score
    100M100P100Q98875F
  • License MIT

ExGuard RBAC client with realtime WebSocket support for EmpowerX applications

Package Exports

  • exguard-client
  • exguard-client/setup

Readme

exguard-client

ExGuard RBAC (Role-Based Access Control) client library with realtime WebSocket support for EmpowerX applications.

📦 Public Package: Available on npm for easy integration into React applications.

Features

  • 🔐 RBAC Authentication: Token verification and user access management
  • 🔄 Realtime Updates: WebSocket-based real-time RBAC changes
  • 🎯 Permission Guards: React components for route and feature protection
  • 🪝 React Hooks: Easy-to-use hooks for permission checking
  • 📦 TypeScript: Full type safety with TypeScript definitions
  • Auto-Configuration: Automatically detects API URL based on environment
  • 🚀 Zero Setup: Works out of the box with sensible defaults
  • 💾 Smart Caching: localStorage persistence for user access data, survives page refreshes, auto-invalidates on permission changes

Documentation

Installation

pnpm add exguard-client

Run the setup command to automatically configure your project:

pnpm exec exguard-setup

This will:

  • ✅ Create src/features/exguard/ module structure
  • ✅ Update protected-route.tsx to wrap <Outlet /> with ExGuardRealtimeProvider
  • ✅ Update auth-utils.ts with token event dispatch

Result in your protected-route.tsx:

return (
  <ExGuardRealtimeProvider>
    <Outlet />
  </ExGuardRealtimeProvider>
);

Then skip to Step 3 in FRONTEND_INTEGRATION_GUIDE.md!

Peer Dependencies

Make sure you have the required peer dependencies installed:

pnpm add react react-dom react-router axios socket.io-client

Quick Start

1. Add Provider to Protected Route

In your protected-route.tsx:

import { ExGuardRealtimeProvider } from '@/features/exguard';

return (
  <ExGuardRealtimeProvider>
    <Outlet />
  </ExGuardRealtimeProvider>
);

That's it! The package automatically:

  • Connects to ExGuard WebSocket when user is authenticated
  • Detects API URL based on your hostname (localhost → http://localhost:3000, production → https://yourdomain.com/api)
  • Reads auth tokens from localStorage/sessionStorage
  • Provides RBAC context to all protected routes

2. Use Permission Guards

import { PermissionGuard } from '@/features/exguard';

<PermissionGuard module="EXID">
  <ProfilesPage />
</PermissionGuard>

3. Check Permissions in Components

import { useUserAccess } from '@/features/exguard';

function MyComponent() {
  const { hasModuleAccess, hasPermission } = useUserAccess();
  
  return (
    <div>
      {hasModuleAccess('EXID') && <div>EXID Module</div>}
      {hasPermission('EXID', 'profile:create') && <button>Create</button>}
    </div>
  );
}

4. Caching (localStorage)

The package automatically caches /guard/me responses for 5 minutes with localStorage persistence:

  • localStorage Persistence: Data survives page refreshes
  • Single API Call: First call fetches data, subsequent calls use cache
  • Auto-Invalidation: Cache clears automatically when permissions change via WebSocket
  • No Manual Management: Works out of the box

Manual Cache Control (if needed):

import { useUserAccessSingleton } from '@/features/exguard';

function MyComponent() {
  const { refetch, invalidateCache, clearCache, isLoading } = useUserAccessSingleton();
  
  // Force refetch (bypasses cache)
  const handleRefresh = async () => {
    await refetch(true);
  };
  
  // Manual cache bust + refetch
  const handleForceRefresh = () => {
    invalidateCache();
    refetch();
  };
}

Clear Cache on Logout:

import { clearUserAccessCache } from 'exguard-client';

function LogoutButton() {
  const handleLogout = async () => {
    // Clear ExGuard cache first
    clearUserAccessCache();
    
    // Then perform your app logout
    await logout();
    navigate('/login');
  };
  
  return <button onClick={handleLogout}>Logout</button>;
}

📖 For complete examples and advanced usage, see FRONTEND_INTEGRATION_GUIDE.md

3. Optional: Custom Configuration

Only needed if you want to override defaults:

import { setExGuardConfig, ExGuardRealtimeProvider } from 'exguard-client';

// Optional: Override auto-detection
setExGuardConfig({
  apiUrl: 'https://custom-api.example.com',
  getAuthToken: () => localStorage.getItem('custom_token_key')
});

<ExGuardRealtimeProvider>
  <App />
</ExGuardRealtimeProvider>

Auto-Configuration

The package automatically configures itself based on your environment:

API URL Detection (Priority Order)

  1. Manual Configuration: setExGuardConfig({ apiUrl: '...' })
  2. Environment Variables:
    • Vite: VITE_GUARD_APP_URL
    • Next.js: NEXT_PUBLIC_GUARD_APP_URL
    • Window: window.__EXGUARD_API_URL__
  3. Auto-Detection:
    • localhost/127.0.0.1 → http://localhost:3000
    • Other domains → ${protocol}//${hostname}/api

Authentication Token

Automatically reads from:

  1. Custom getter if provided via setExGuardConfig
  2. localStorage.getItem('access_token')
  3. sessionStorage.getItem('access_token')

4. Use ExGuard Components

import { PermissionGuard, useUserAccess } from '@/features/exguard';

// Route protection
<PermissionGuard module="EXID">
  <ProfilesPage />
</PermissionGuard>

// Permission checking
function MyComponent() {
  const { hasModuleAccess, hasPermission } = useUserAccess();
  
  return (
    <div>
      {hasModuleAccess('EXID') && <div>EXID Module</div>}
      {hasPermission('EXID', 'profile:create') && <button>Create</button>}
    </div>
  );
}

API Reference

Configuration

setExGuardConfig(config)

Configure the ExGuard API URL and other settings.

import { setExGuardConfig } from 'exguard-client';

setExGuardConfig({
  apiUrl: 'https://your-exguard-api.com',
  withCredentials: true, // default: true
});

Components

ExGuardRealtimeProvider

Provider component that manages WebSocket connections and RBAC state.

<ExGuardRealtimeProvider>
  {/* Your app */}
</ExGuardRealtimeProvider>

PermissionGuard

Route guard component for RBAC permission checking.

<PermissionGuard
  module="EXID"                    // Required: Module key
  permission="profile:create"      // Optional: Specific permission
  requireModule={true}             // Optional: Require module access (default: true)
  requirePermission={true}         // Optional: Require permission (default: true if permission provided)
  fallbackPath="/unauthorized"     // Optional: Redirect path on access denied
>
  <YourProtectedComponent />
</PermissionGuard>

Hooks

useUserAccess(options?)

Hook to fetch and manage user access data with RBAC. Uses singleton pattern with caching.

const {
  userAccess,        // Full user access data
  isLoading,         // Loading state
  error,             // Error state
  hasModuleAccess,   // Function to check module access
  hasPermission,     // Function to check specific permission
} = useUserAccess({
  enabled: true,           // Optional: Enable/disable fetching (default: true)
  refetchInterval: 30000,  // Optional: Refetch interval in ms (default: 0, uses cache)
});

Caching Behavior:

  • Caches response for 5 minutes
  • Multiple components share the same cached data
  • Cache auto-invalidates when WebSocket sends user:access-changed event
  • Set refetchInterval > 0 to force periodic refetches

Methods:

  • hasModuleAccess(moduleKey: string): boolean - Check if user has access to a module
  • hasPermission(moduleKey: string, permission: string): boolean - Check if user has a specific permission

useUserAccessSingleton()

Singleton hook for accessing user access data with localStorage caching. Best for components that need user info (e.g., layout, sidebar).

const {
  userAccess,        // Full user access data
  isLoading,        // Loading state
  isFetching,      // Currently fetching (including cache checks)
  error,           // Error state
  refetch,         // Function to refetch (accepts force param)
  refetchSilent,   // Refetch without loading state
  invalidateCache, // Manually invalidate memory cache
  clearCache,      // Clear localStorage cache
} = useUserAccessSingleton();

// Refetch with cache (uses cached data if < 5 min old)
await refetch();

// Force refetch (ignores cache)
await refetch(true);

// Manual cache control
invalidateCache();
await refetch();

When to use:

  • useUserAccessSingleton - Layouts, headers, sidebars (single user context)
  • useUserAccess - Feature components, pages (isolated usage)

Note: The hook uses localStorage for persistence. On page load, it first checks localStorage for cached data before making an API call. This provides instant access to user data even on first render after page refresh.

useExGuardRealtime()

Hook to access the ExGuard realtime context.

const {
  isConnected,                   // WebSocket connection state
  registerRbacRefreshCallback,   // Register callback for RBAC updates
} = useExGuardRealtime();

useExGuardRealtimeSubscription(eventType, handler)

Hook to subscribe to specific realtime events.

import { useExGuardRealtimeSubscription, EXGUARD_RBAC_EVENTS } from 'exguard-client';

useExGuardRealtimeSubscription(
  EXGUARD_RBAC_EVENTS.RBAC_PERMISSIONS_UPDATED,
  (payload) => {
    console.log('Permissions updated:', payload);
    // Handle the event
  }
);

API Functions

clearUserAccessCache()

Clear the cached user access data from localStorage. Call this on logout.

import { clearUserAccessCache } from 'exguard-client';

async function handleLogout() {
  // Clear ExGuard cache
  clearUserAccessCache();
  
  // Perform app logout
  await logout();
  navigate('/login');
}

verifyToken()

Verify the current ID token with the ExGuard backend.

import { verifyToken } from 'exguard-client';

const result = await verifyToken();
if (result.isValid && result.user) {
  console.log('User:', result.user);
}

getUserAccess()

Fetch the current user's access data (roles, permissions, modules).

import { getUserAccess } from 'exguard-client';

const userAccess = await getUserAccess();
console.log('Modules:', userAccess.modules);
console.log('Roles:', userAccess.roles);

Constants

EXGUARD_RBAC_EVENTS

Constants for ExGuard RBAC event types.

import { EXGUARD_RBAC_EVENTS } from 'exguard-client';

// Available events:
EXGUARD_RBAC_EVENTS.ROLE_CREATED
EXGUARD_RBAC_EVENTS.ROLE_UPDATED
EXGUARD_RBAC_EVENTS.ROLE_DELETED
EXGUARD_RBAC_EVENTS.PERMISSION_CREATED
EXGUARD_RBAC_EVENTS.PERMISSION_UPDATED
EXGUARD_RBAC_EVENTS.PERMISSION_DELETED
EXGUARD_RBAC_EVENTS.USER_ROLES_UPDATED
EXGUARD_RBAC_EVENTS.USER_PERMISSIONS_CHANGED
EXGUARD_RBAC_EVENTS.USER_ACCESS_CHANGED
EXGUARD_RBAC_EVENTS.RBAC_PERMISSIONS_UPDATED
// ... and more

EXGUARD_STORAGE_KEYS

Constants for localStorage keys used by ExGuard.

import { EXGUARD_STORAGE_KEYS } from 'exguard-client';

// Available keys:
EXGUARD_STORAGE_KEYS.ACCESS_TOKEN
EXGUARD_STORAGE_KEYS.ID_TOKEN
EXGUARD_STORAGE_KEYS.REFRESH_TOKEN

Types

The package exports all necessary TypeScript types:

import type {
  UserAccessData,
  ModulePermissions,
  UserDetails,
  VerifyTokenResponse,
  RealtimeEventType,
  RealtimeEventPayload,
  ExGuardConfig,
} from 'exguard-client';

Environment Variables

The package automatically detects environment variables in consuming applications:

Vite

VITE_GUARD_APP_URL=https://your-exguard-api.com

Next.js

NEXT_PUBLIC_GUARD_APP_URL=https://your-exguard-api.com

Or set it programmatically using setExGuardConfig().

Storage

ExGuard uses localStorage for token management and user access caching:

Authentication Tokens

  • access_token - Access token for API authentication
  • id_token - ID token for user verification
  • refresh_token - Refresh token for token renewal

Cached Data

  • exguard_user_access - Cached user access data (roles, permissions, modules)
  • exguard_user_access_timestamp - Cache timestamp for TTL validation

The cache persists for 5 minutes and is automatically invalidated when:

  • User permissions change via WebSocket
  • You call invalidateCache() or clearUserAccessCache()

Security: Data is obfuscated using XOR cipher + base64 encoding to prevent casual reading of localStorage data.

License

MIT

Support

For issues or questions, please contact the EmpowerX development team.