# Command Authorization Secure your commands with authorization services. ## Overview Command authorization controls who can execute commands using the `ICommandAuthorizationService` interface. ## Interface ```csharp public interface ICommandAuthorizationService { Task CanExecuteAsync( TCommand command, ClaimsPrincipal user, CancellationToken cancellationToken = default); } ``` ## Basic Authorization ### Authenticated Users Only ```csharp public class CreateUserAuthorizationService : ICommandAuthorizationService { public Task CanExecuteAsync( CreateUserCommand command, ClaimsPrincipal user, CancellationToken cancellationToken) { return Task.FromResult(user.Identity?.IsAuthenticated == true); } } // Registration builder.Services.AddScoped, CreateUserAuthorizationService>(); ``` ### Role-Based Authorization ```csharp public class DeleteUserAuthorizationService : ICommandAuthorizationService { public Task CanExecuteAsync( DeleteUserCommand command, ClaimsPrincipal user, CancellationToken cancellationToken) { return Task.FromResult(user.IsInRole("Admin")); } } ``` ## Advanced Authorization ### Resource-Based Authorization ```csharp public class UpdateUserAuthorizationService : ICommandAuthorizationService { private readonly IUserRepository _userRepository; public UpdateUserAuthorizationService(IUserRepository userRepository) { _userRepository = userRepository; } public async Task CanExecuteAsync( UpdateUserCommand command, ClaimsPrincipal user, CancellationToken cancellationToken) { // Admins can update any user if (user.IsInRole("Admin")) return true; // Users can only update their own profile var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value; return command.UserId.ToString() == userId; } } ``` ### Claims-Based Authorization ```csharp public class DeleteOrderAuthorizationService : ICommandAuthorizationService { private readonly IOrderRepository _orderRepository; public async Task CanExecuteAsync( DeleteOrderCommand command, ClaimsPrincipal user, CancellationToken cancellationToken) { var order = await _orderRepository.GetByIdAsync(command.OrderId, cancellationToken); if (order == null) return false; // Check if user has required claim var canDelete = user.HasClaim("Permission", "DeleteOrder"); if (!canDelete) return false; // Check if user owns the order var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value; return order.UserId.ToString() == userId; } } ``` ### Multi-Tenant Authorization ```csharp public class CreateProductAuthorizationService : ICommandAuthorizationService { public Task CanExecuteAsync( CreateProductCommand command, ClaimsPrincipal user, CancellationToken cancellationToken) { var userTenantId = user.FindFirst("TenantId")?.Value; if (string.IsNullOrEmpty(userTenantId)) return Task.FromResult(false); // Ensure command is for user's tenant return Task.FromResult(command.TenantId == userTenantId); } } ``` ## HTTP Responses ### Unauthorized (401) When user is not authenticated: ``` HTTP/1.1 401 Unauthorized ``` ### Forbidden (403) When user is authenticated but not authorized: ``` HTTP/1.1 403 Forbidden ``` ## Combining with ASP.NET Core Authorization ```csharp var app = builder.Build(); // Require authentication for all endpoints app.UseAuthentication(); app.UseAuthorization(); // CQRS endpoints inherit auth requirements app.UseSvrntyCqrs(); ``` ## Best Practices ### ✅ DO - Use for access control - Check resource ownership - Validate tenant isolation - Log authorization failures - Return boolean (true/false) ### ❌ DON'T - Don't throw exceptions - Don't perform business logic - Don't modify data - Don't bypass framework checks ## See Also - [Query Authorization](../queries/query-authorization.md) - [Best Practices: Security](../../best-practices/security.md) - [Extensibility Points](../../architecture/extensibility-points.md)