Policy-based authorization with custom requirements, handlers, and permission-based access control. Trigger: authorization, policy, permission, HasPermission, requirement, handler.
From dotnet-ai-kitnpx claudepluginhub faysilalshareef/dotnet-ai-kit --plugin dotnet-ai-kitThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Enables AI agents to execute x402 payments with per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents pay for APIs, services, or other agents.
IAuthorizationRequirement and IAuthorizationHandler pairsIAuthorizationPolicyProvider for permission-based authpublic static class Permissions
{
public const string OrdersRead = "orders:read";
public const string OrdersCreate = "orders:create";
public const string OrdersUpdate = "orders:update";
public const string OrdersDelete = "orders:delete";
public const string ReportsView = "reports:view";
public const string UsersManage = "users:manage";
public const string SettingsManage = "settings:manage";
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class HasPermissionAttribute(string permission)
: AuthorizeAttribute(permission)
{
public string Permission { get; } = permission;
}
public sealed class PermissionRequirement(string permission)
: IAuthorizationRequirement
{
public string Permission { get; } = permission;
}
public sealed class PermissionAuthorizationHandler
: AuthorizationHandler<PermissionRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
PermissionRequirement requirement)
{
var permissions = context.User
.FindFirstValue("permissions")?
.Split(',', StringSplitOptions.RemoveEmptyEntries)
?? [];
if (permissions.Contains(requirement.Permission))
context.Succeed(requirement);
return Task.CompletedTask;
}
}
public sealed class PermissionAuthorizationPolicyProvider(
IOptions<AuthorizationOptions> options)
: DefaultAuthorizationPolicyProvider(options)
{
public override async Task<AuthorizationPolicy?> GetPolicyAsync(
string policyName)
{
// Check for pre-defined policies first
var policy = await base.GetPolicyAsync(policyName);
if (policy is not null)
return policy;
// Create dynamic policy for permission strings
return new AuthorizationPolicyBuilder()
.AddRequirements(new PermissionRequirement(policyName))
.Build();
}
}
// Program.cs
builder.Services.AddSingleton<IAuthorizationPolicyProvider,
PermissionAuthorizationPolicyProvider>();
builder.Services.AddSingleton<IAuthorizationHandler,
PermissionAuthorizationHandler>();
// Pre-defined policies (optional, for complex requirements)
builder.Services.AddAuthorizationBuilder()
.AddPolicy("AdminOnly", policy =>
policy.RequireRole("Admin"))
.AddPolicy("OrderManager", policy =>
policy.RequireAssertion(context =>
context.User.HasClaim("permissions", Permissions.OrdersCreate) &&
context.User.HasClaim("permissions", Permissions.OrdersUpdate)));
// Minimal API
app.MapGet("/orders", GetOrders)
.RequireAuthorization(Permissions.OrdersRead);
app.MapPost("/orders", CreateOrder)
.RequireAuthorization(Permissions.OrdersCreate);
app.MapDelete("/orders/{id}", DeleteOrder)
.RequireAuthorization(Permissions.OrdersDelete);
// Controller
[ApiController]
[Route("api/orders")]
public sealed class OrdersController : ControllerBase
{
[HttpGet]
[HasPermission(Permissions.OrdersRead)]
public async Task<IActionResult> GetOrders() { /* ... */ }
[HttpPost]
[HasPermission(Permissions.OrdersCreate)]
public async Task<IActionResult> CreateOrder() { /* ... */ }
[HttpDelete("{id}")]
[HasPermission(Permissions.OrdersDelete)]
public async Task<IActionResult> DeleteOrder(Guid id) { /* ... */ }
}
// Requirement for resource ownership
public sealed class OrderOwnerRequirement : IAuthorizationRequirement { }
// Handler checks if user owns the resource
public sealed class OrderOwnerAuthorizationHandler
: AuthorizationHandler<OrderOwnerRequirement, Order>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
OrderOwnerRequirement requirement,
Order resource)
{
var userId = context.User
.FindFirstValue(JwtRegisteredClaimNames.Sub);
if (resource.CreatedBy == userId)
context.Succeed(requirement);
return Task.CompletedTask;
}
}
// Usage in handler
internal sealed class UpdateOrderHandler(
IOrderRepository repository,
IAuthorizationService authorizationService,
IHttpContextAccessor httpContextAccessor)
: IRequestHandler<UpdateOrderCommand, Result>
{
public async Task<Result> Handle(
UpdateOrderCommand request, CancellationToken ct)
{
var order = await repository.FindAsync(request.OrderId, ct);
if (order is null)
return Result.Failure(Error.NotFound("Order.NotFound",
"Order not found"));
var user = httpContextAccessor.HttpContext!.User;
var authResult = await authorizationService.AuthorizeAsync(
user, order, new OrderOwnerRequirement());
if (!authResult.Succeeded)
return Result.Failure(Error.Forbidden("Order.Forbidden",
"You can only update your own orders"));
// Update order...
return Result.Success();
}
}
// Alternative: load permissions from DB instead of JWT claims
public sealed class DbPermissionAuthorizationHandler(
IServiceScopeFactory scopeFactory)
: AuthorizationHandler<PermissionRequirement>
{
protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context,
PermissionRequirement requirement)
{
var userId = context.User
.FindFirstValue(JwtRegisteredClaimNames.Sub);
if (userId is null) return;
using var scope = scopeFactory.CreateScope();
var db = scope.ServiceProvider
.GetRequiredService<AppDbContext>();
var hasPermission = await db.UserPermissions
.AnyAsync(p =>
p.UserId == Guid.Parse(userId) &&
p.Permission == requirement.Permission);
if (hasPermission)
context.Succeed(requirement);
}
}
User.IsInRole("Admin"))IAuthorizationPolicyProvider for dynamic policiesAuthorizeAttribute subclassesIAuthorizationPolicyProvider implementationsIAuthorizationHandler implementations"permissions" claim in token generationRequireAuthorization calls on endpointsHasPermissionAttribute and requirement/handler pair[HasPermission] or RequireAuthorization to all endpoints