);
}
```
**Or using Can component:**
```typescript
import { Can } from '@/contexts/AbilityContext';
function MyComponent() {
return (
Users
);
}
```
## Adding New Permissions
### 1. Define New Action (If Needed)
**Backend:** `backend/src/auth/abilities/ability.factory.ts`
**Frontend:** `frontend/src/lib/abilities.ts`
```typescript
export enum Action {
// ... existing actions
Export = 'export', // New action
}
```
### 2. Update Ability Definitions
**Backend:** `backend/src/auth/abilities/ability.factory.ts`
```typescript
defineAbilitiesFor(user: User): AppAbility {
const { can, cannot, build } = new AbilityBuilder(/* ... */);
if (user.role === Role.ADMINISTRATOR) {
can(Action.Manage, 'all');
can(Action.Export, 'all'); // Admins can export anything
} else if (user.role === Role.COORDINATOR) {
can(Action.Export, 'VIP'); // Coordinators can only export VIPs
}
return build();
}
```
**Frontend:** `frontend/src/lib/abilities.ts` (same pattern)
### 3. Use in Controllers
```typescript
import { CheckAbilities } from '../auth/decorators/check-ability.decorator';
import { Action } from '../auth/abilities/ability.factory';
@Get('export')
@CheckAbilities({ action: Action.Export, subject: 'VIP' })
export() {
return this.service.exportToCSV();
}
```
### 4. Use in Components
```typescript
import { Can } from '@/contexts/AbilityContext';
function VIPList() {
return (
);
}
```
## Best Practices
### ✅ DO
```typescript
// Use semantic ability checks
if (ability.can(Action.Create, 'VIP')) { }
// Use Can component for declarative rendering
// Group related permissions in decorators
@CheckAbilities(
{ action: Action.Read, subject: 'VIP' },
{ action: Action.Read, subject: 'Driver' }
)
// Define abilities based on resources, not roles
can(Action.Update, 'VIP') // Good
can(Action.Manage, 'all') // For admins only
```
### ❌ DON'T
```typescript
// Don't check roles directly (use abilities instead)
if (user.role === 'ADMINISTRATOR') { } // Bad
// Don't mix role checks and ability checks
if (isAdmin || ability.can(Action.Create, 'VIP')) { } // Confusing
// Don't create overly specific actions
Action.CreateVIPForJamboree // Too specific
Action.Create // Better
// Don't forget to check both frontend and backend
// Backend enforces security, frontend improves UX
```
## Debugging
### Check User Abilities
**Backend:**
```typescript
const ability = this.abilityFactory.defineAbilitiesFor(user);
console.log('Can create VIP?', ability.can(Action.Create, 'VIP'));
console.log('Can manage all?', ability.can(Action.Manage, 'all'));
```
**Frontend:**
```typescript
const ability = useAbility();
console.log('Can create VIP?', ability.can(Action.Create, 'VIP'));
console.log('User abilities:', ability.rules);
```
### Common Issues
**"User does not have required permissions" error:**
1. Check user role in database
2. Verify ability definitions match frontend/backend
3. Ensure AbilitiesGuard is applied to controller
4. Check if decorator is correctly specified
**Navigation items not showing:**
1. Verify AbilityProvider wraps the app
2. Check ability.can() returns true for expected permissions
3. Ensure user is authenticated and role is set
**Tests failing:**
1. Mock AbilityFactory in tests
2. Provide test abilities in test setup
3. Use `@casl/ability` test utilities
---
**Last Updated:** 2026-01-25
**See also:**
- [CLAUDE.md](./CLAUDE.md) - General project documentation
- [ERROR_HANDLING.md](./ERROR_HANDLING.md) - Error handling guide
- [CASL Documentation](https://casl.js.org/) - Official CASL docs