npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ldauth/react

v0.10.1

Published

React permission checking library for the auth system

Readme

@ldauth/react

A React library for fine-grained permission checking in applications using our authentication system. This library provides components, hooks, and utilities to conditionally render UI based on user permissions.

Installation

pnpm add @ldauth/react react-oidc-context

Quick Start

1. Wrap your app with PermissionProvider

import { AuthProvider } from 'react-oidc-context';
import { PermissionProvider } from '@ldauth/react';

const oidcConfig = {
  authority: 'http://localhost:7844',
  client_id: 'admin-panel',
  redirect_uri: 'http://localhost:7802/callback',
  // ... other OIDC config
};

function App() {
  return (
    <AuthProvider {...oidcConfig}>
      <PermissionProvider
        apiUrl="http://localhost:7801"
        clientId="admin-panel"
        autoFetch={true}
        cacheDuration={5 * 60 * 1000} // 5 minutes
      >
        <YourApp />
      </PermissionProvider>
    </AuthProvider>
  );
}

2. Use permission components

import { CanAccess, RequireRole } from '@ldauth/react';

function MyComponent() {
  return (
    <div>
      {/* Show delete button only if user has permission */}
      <CanAccess resource="users" action="delete">
        <button>Delete User</button>
      </CanAccess>

      {/* Show admin panel only for admin role */}
      <RequireRole role="admin">
        <AdminPanel />
      </RequireRole>

      {/* With fallback */}
      <CanAccess 
        resource="settings" 
        action="update"
        fallback={<p>Read-only access</p>}
      >
        <SettingsForm />
      </CanAccess>
    </div>
  );
}

3. Use hooks for permissions and user information

import {
  usePermission,
  useRole,
  useUser,
  useUserPicture,
  useUserGroups
} from '@ldauth/react';

function ItemList() {
  // Get user information from ID token (zero backend calls)
  const user = useUser();
  const avatarUrl = useUserPicture();
  const groups = useUserGroups();

  // Check permissions
  const { allowed: canDeleteItems, isLoading: deleteLoading } = usePermission('items', 'delete');
  const { allowed: canCreateItems } = usePermission('items', 'create');
  const isManager = useRole('manager');

  const handleDelete = (itemId) => {
    if (!canDeleteItems) {
      alert('You do not have permission to delete items');
      return;
    }
    // ... delete logic
  };

  return (
    <div>
      {/* User profile section */}
      <div className="user-profile">
        {avatarUrl && <img src={avatarUrl} alt="Avatar" />}
        <span>{user?.name || user?.email}</span>
        <span>Groups: {groups.join(', ')}</span>
      </div>

      {/* Actions based on permissions */}
      {canCreateItems && (
        <button>Create Item</button>
      )}

      {items.map(item => (
        <div key={item.id}>
          {item.name}
          {canDeleteItems && (
            <button onClick={() => handleDelete(item.id)}>Delete</button>
          )}
        </div>
      ))}
    </div>
  );
}

Components

<CanAccess>

Conditionally renders children based on a permission check.

<CanAccess 
  resource="users" 
  action="delete"
  profile="__default" // optional
  fallback={<span>No permission</span>} // optional
  loadingFallback={<span>Checking permissions...</span>} // optional
>
  <DeleteButton />
</CanAccess>

<CanAccessMultiple>

Check multiple permissions with AND/OR logic.

<CanAccessMultiple
  permissions={[
    { resource: 'users', action: 'read' },
    { resource: 'groups', action: 'read' }
  ]}
  requireAll={true} // true = AND, false = OR
>
  <UserGroupManager />
</CanAccessMultiple>

<RequireRole>

Conditionally renders based on role membership.

<RequireRole role="admin" fallback={<Unauthorized />}>
  <AdminDashboard />
</RequireRole>

<PermissionGate>

Custom permission logic.

<PermissionGate 
  check={perms => perms.hasRole('admin') || perms.hasPermission('users', 'manage')}
>
  <AdvancedFeature />
</PermissionGate>

Hooks

Core Hooks

useAuth()

Access the underlying react-oidc-context authentication state without importing another package. This is a direct re-export of useAuth from react-oidc-context.

import { useAuth } from '@ldauth/react';

function IdentityBadge() {
  const auth = useAuth();

  if (!auth.isAuthenticated) {
    return <button onClick={() => auth.signinRedirect()}>Sign in</button>;
  }

  return (
    <div>
      <span>{auth.user?.profile?.email}</span>
      <button onClick={() => auth.signoutRedirect()}>Logout</button>
    </div>
  );
}

usePermissions()

Access the permission context directly.

const { 
  permissions,      // Current permission evaluation
  loading,         // Loading state
  error,           // Error state
  hasPermission,   // Check specific permission
  hasRole,         // Check role membership
  refresh          // Refresh permissions
} = usePermissions();

usePermission(resource, action, profile?)

Check a single permission. Returns an object with permission status and loading state.

const { allowed, isLoading } = usePermission('users', 'delete');
// Or destructure with custom names:
const { allowed: canDelete, isLoading: deleteLoading } = usePermission('users', 'delete');

useRole(role)

Check role membership.

const isAdmin = useRole('admin');

User Information Hooks

useUser()

Access all authenticated user information from the ID token.

const user = useUser();

if (user) {
  console.log(user.email);    // [email protected]
  console.log(user.name);     // John Doe
  console.log(user.picture);  // https://api.example.com/avatars/123.jpg
  console.log(user.groups);   // ["admin", "users"]
  console.log(user.sub);      // User ID
}

useUserPicture()

Get the current user's profile picture URL. Returns null if not authenticated or no picture available.

const avatarUrl = useUserPicture();

return (
  <div>
    {avatarUrl ? (
      <img src={avatarUrl} alt="User avatar" className="avatar" />
    ) : (
      <DefaultAvatarIcon />
    )}
  </div>
);

useUserGroups()

Get the current user's group memberships. Returns an empty array if not authenticated.

const groups = useUserGroups();
const isAdmin = groups.includes('admin');
const isModerator = groups.includes('moderator');

return (
  <div>
    <p>Your groups: {groups.join(', ')}</p>
    {isAdmin && <AdminBadge />}
  </div>
);

Note: All user information hooks read from the cached ID token (zero backend calls). The data is automatically refreshed when the user re-authenticates.

Utility Hooks

useFeatureFlags()

Create feature flags based on permissions.

const features = useFeatureFlags({
  canEditUsers: { resource: 'users', action: 'update' },
  canDeleteUsers: { resource: 'users', action: 'delete' },
  canViewAudit: { resource: 'audit', action: 'read' }
});

if (features.canEditUsers) {
  // Show edit button
}

useFilterByPermission()

Filter items based on permissions.

const menuItems = [
  { label: 'Users', resource: 'users', action: 'read', path: '/users' },
  { label: 'Groups', resource: 'groups', action: 'read', path: '/groups' },
  { label: 'Audit', resource: 'audit', action: 'read', path: '/audit' }
];

const visibleMenuItems = useFilterByPermission(menuItems);
// Returns only items the user has permission to access

useRequirePermission()

Throw an error if permission check fails (useful for route protection).

function ProtectedPage() {
  useRequirePermission('admin', 'access');
  // Will throw if user doesn't have permission
  return <AdminContent />;
}

HOC (Higher Order Component)

withPermission()

Wrap a component with permission check.

const ProtectedUserList = withPermission(UserList, {
  resource: 'users',
  action: 'read',
  fallback: <div>Access denied</div>
});

Integration Example

Define Your Application's Permissions

Each application defines its own permission structure based on its needs:

// myAppPermissions.ts
const MY_APP_FEATURES = {
  // Define feature flags based on YOUR application's resources
  canViewDashboard: { resource: 'dashboard', action: 'view' },
  canEditSettings: { resource: 'settings', action: 'edit' },
  canManageContent: { resource: 'content', action: 'manage' },
  // ... whatever permissions make sense for your app
};

function useMyAppFeatures() {
  return useFeatureFlags(MY_APP_FEATURES);
}

Use in Navigation

function AppNavigation() {
  // Define navigation based on YOUR app's permissions
  const { allowed: canViewDashboard } = usePermission('dashboard', 'view');
  const { allowed: canViewReports } = usePermission('reports', 'view');
  const { allowed: canManageSettings } = usePermission('settings', 'manage');
  
  return (
    <nav>
      {canViewDashboard && (
        <Link to="/">Dashboard</Link>
      )}
      {canViewReports && (
        <Link to="/reports">Reports</Link>
      )}
      {canManageSettings && (
        <Link to="/settings">Settings</Link>
      )}
    </nav>
  );
}

Protect Routes

import { Routes, Route } from 'react-router-dom';

function AdminRoutes() {
  return (
    <Routes>
      <Route path="/" element={
        <CanAccess resource="dashboard" action="read" fallback={<Unauthorized />}>
          <Dashboard />
        </CanAccess>
      } />
      
      <Route path="/users/*" element={
        <CanAccess resource="users" action="read" fallback={<Unauthorized />}>
          <UserRoutes />
        </CanAccess>
      } />
      
      <Route path="/settings" element={
        <RequireRole role="admin" fallback={<Unauthorized />}>
          <Settings />
        </RequireRole>
      } />
    </Routes>
  );
}

Performance Considerations

  1. Caching: Permissions are cached by default for 5 minutes. Adjust cacheDuration in PermissionProvider.

  2. Batch Checks: Use CanAccessMultiple or useAllPermissions for checking multiple permissions at once.

  3. Memoization: Permission check functions are memoized to prevent unnecessary re-renders.

  4. Lazy Loading: Set autoFetch={false} and manually call refresh() when needed.

Error Handling

<PermissionProvider
  apiUrl="..."
  clientId="..."
  onError={(error) => {
    console.error('Permission check failed:', error);
    // Show notification to user
  }}
>
  {children}
</PermissionProvider>

TypeScript Support

The library is fully typed and provides type-safe component factories for schema-based type safety.

Type-Safe Components with Schema

Create typed components that provide full autocomplete for your permission schema:

import {
  createUsePermission,
  createCanAccess,
  createCanAccessMultiple,
  createPermissionGate
} from '@ldauth/react';

// Define your permission schema
interface MyAppSchema {
  resources: {
    user: { actions: ['create', 'read', 'update', 'delete'] };
    post: { actions: ['create', 'read', 'publish', 'archive'] };
    admin: { actions: ['access', 'manage'] };
  };
}

// Create typed versions
const useTypedPermission = createUsePermission<MyAppSchema>();
const TypedCanAccess = createCanAccess<MyAppSchema>();
const TypedCanAccessMultiple = createCanAccessMultiple<MyAppSchema>();
const TypedPermissionGate = createPermissionGate<MyAppSchema>();

// Usage with full autocomplete support
function MyComponent() {
  // Typed hook with autocomplete
  const { allowed: canCreateUser, isLoading } = useTypedPermission('user', 'create');

  return (
    <div>
      {/* Single permission with autocomplete */}
      <TypedCanAccess
        resource="user"
        action="delete"
        fallback={<div>Cannot delete users</div>}
        loadingFallback={<div>Checking permissions...</div>}
      >
        <DeleteUserButton />
      </TypedCanAccess>

      {/* Multiple permissions with type safety */}
      <TypedCanAccessMultiple
        permissions={[
          { resource: "user", action: "read" },
          { resource: "post", action: "read" }
        ]}
        requireAll={true}
        fallback={<div>Need both user and post read access</div>}
      >
        <AdminDashboard />
      </TypedCanAccessMultiple>

      {/* Custom logic with type-safe context */}
      <TypedPermissionGate
        check={(client, loading) => {
          if (loading) return false;
          return client?.hasRole("admin") || client?.hasPermission("admin", "access");
        }}
        fallback={<div>Admin access required</div>}
      >
        <AdminPanel />
      </TypedPermissionGate>
    </div>
  );
}

Basic Types

Import types as needed:

import type {
  Permission,
  PermissionCheck,
  PermissionEvaluation,
  PermissionProviderConfig,
  PermissionSchemaBase
} from '@ldauth/react';

Best Practices

  1. Use specific checks: Prefer usePermission('users', 'delete') over useRole('admin')

  2. Provide fallbacks: Always provide fallback UI for users without permissions

  3. Handle loading states: Use loadingFallback to show different content while checking permissions

  4. Group related checks: Use useFeatureFlags() to group related permission checks

  5. Cache appropriately: Balance between fresh data and performance

  6. Test with different roles: Always test your UI with different permission levels

License

MIT