React permission checking library for the auth system
npm install @ldauth/reactA 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.
``bash`
pnpm add @ldauth/react react-oidc-context
`tsx
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 (
clientId="admin-panel"
autoFetch={true}
cacheDuration={5 60 1000} // 5 minutes
>
);
}
`
`tsx
import { CanAccess, RequireRole } from '@ldauth/react';
function MyComponent() {
return (
{/ Show admin panel only for admin role /}
{/ With fallback /}
action="update"
fallback={
Read-only access
}$3
`tsx
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 (
{/ User profile section /}
{avatarUrl &&
}
{user?.name || user?.email}
Groups: {groups.join(', ')}
{/ Actions based on permissions /}
{canCreateItems && (
)}
{items.map(item => (
{item.name}
{canDeleteItems && (
)}
))}
);
}
`Components
$3
Conditionally renders children based on a permission check.`tsx
resource="users"
action="delete"
profile="__default" // optional
fallback={No permission} // optional
loadingFallback={Checking permissions...} // optional
>
`$3
Check multiple permissions with AND/OR logic.`tsx
permissions={[
{ resource: 'users', action: 'read' },
{ resource: 'groups', action: 'read' }
]}
requireAll={true} // true = AND, false = OR
>
`$3
Conditionally renders based on role membership.`tsx
}>
`$3
Custom permission logic.`tsx
check={perms => perms.hasRole('admin') || perms.hasPermission('users', 'manage')}
>
`Hooks
$3
####
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.`tsx
import { useAuth } from '@ldauth/react';function IdentityBadge() {
const auth = useAuth();
if (!auth.isAuthenticated) {
return ;
}
return (
{auth.user?.profile?.email}
);
}
`####
usePermissions()
Access the permission context directly.`tsx
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.`tsx
const { allowed, isLoading } = usePermission('users', 'delete');
// Or destructure with custom names:
const { allowed: canDelete, isLoading: deleteLoading } = usePermission('users', 'delete');
`####
useRole(role)
Check role membership.`tsx
const isAdmin = useRole('admin');
`$3
####
useUser()
Access all authenticated user information from the ID token.`tsx
const user = useUser();if (user) {
console.log(user.email); // user@example.com
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.`tsx
const avatarUrl = useUserPicture();return (
{avatarUrl ? (

) : (
)}
);
`####
useUserGroups()
Get the current user's group memberships. Returns an empty array if not authenticated.`tsx
const groups = useUserGroups();
const isAdmin = groups.includes('admin');
const isModerator = groups.includes('moderator');return (
Your groups: {groups.join(', ')}
{isAdmin && }
);
`Note: All user information hooks read from the cached ID token (zero backend calls). The data is automatically refreshed when the user re-authenticates.
$3
####
useFeatureFlags()
Create feature flags based on permissions.`tsx
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.`tsx
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).`tsx
function ProtectedPage() {
useRequirePermission('admin', 'access');
// Will throw if user doesn't have permission
return ;
}
`HOC (Higher Order Component)
$3
Wrap a component with permission check.`tsx
const ProtectedUserList = withPermission(UserList, {
resource: 'users',
action: 'read',
fallback: Access denied
});
`Integration Example
$3
Each application defines its own permission structure based on its needs:
`tsx
// 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);
}
`$3
`tsx
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 (
);
}
`$3
`tsx
import { Routes, Route } from 'react-router-dom';function AdminRoutes() {
return (
}>
} />
}>
} />
}>
} />
);
}
`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
`tsx
apiUrl="..."
clientId="..."
onError={(error) => {
console.error('Permission check failed:', error);
// Show notification to user
}}
>
{children}
`TypeScript Support
The library is fully typed and provides type-safe component factories for schema-based type safety.
$3
Create typed components that provide full autocomplete for your permission schema:
`tsx
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();
const TypedCanAccess = createCanAccess();
const TypedCanAccessMultiple = createCanAccessMultiple();
const TypedPermissionGate = createPermissionGate();
// Usage with full autocomplete support
function MyComponent() {
// Typed hook with autocomplete
const { allowed: canCreateUser, isLoading } = useTypedPermission('user', 'create');
return (
{/ Single permission with autocomplete /}
resource="user"
action="delete"
fallback={Cannot delete users}
loadingFallback={Checking permissions...}
>
{/ Multiple permissions with type safety /}
permissions={[
{ resource: "user", action: "read" },
{ resource: "post", action: "read" }
]}
requireAll={true}
fallback={
Need both user and post read access}
>
{/ Custom logic with type-safe context /}
check={(client, loading) => {
if (loading) return false;
return client?.hasRole("admin") || client?.hasPermission("admin", "access");
}}
fallback={
Admin access required}
>
);
}
`$3
Import types as needed:
`tsx
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 permissions4. Group related checks: Use
useFeatureFlags()` to group related permission checks5. Cache appropriately: Balance between fresh data and performance
6. Test with different roles: Always test your UI with different permission levels
MIT