Skip to content

About

Authorization primitives for .NET — IAuthorizationPolicy, ISecurityContext, [Authorize], [AuthorizationPolicy]. Source-of-truth contract package shared across hosts (AI.Sentinel, ZeroAlloc.Mediator.Authorization).

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

215 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ZeroAlloc.Authorization

NuGet Build codecov License: MIT AOT GitHub Sponsors

Source-generated authorization policies for any .NET host — policy names checked at compile time, no reflection, 0 B allocated per check, NativeAOT-ready.

Why ZeroAlloc.Authorization

  • 0 B per authorization check. AllocationGate tests assert a 0-byte budget on every run for the allow and deny paths, a parameterized policy and an any-of group.
  • A typo is a build error, not a 403 in production. Requiring a policy name that no [Policy] declares fails the build (ZAUTH001); eight generator diagnostics in total catch wiring mistakes at compile time.
  • No reflection, works under NativeAOT and trimming. The bundled generator emits one dispatcher per request type plus the DI registrations. The library builds with IsAotCompatible, and CI publishes and runs a PublishAot=true sample on every run.
  • Not tied to ASP.NET Core. Policies see a small ISecurityContext (id, roles, claims). Mediators, agent tool calls and your own dispatchers can all evaluate the same policies.

Install

dotnet add package ZeroAlloc.Authorization
dotnet add package Microsoft.Extensions.DependencyInjection # the generated registrations target it

Targets net8.0, net9.0 and net10.0. The source generator ships inside the package. ASP.NET Core and generic-host apps already reference the DI package.

Quick start

using Microsoft.Extensions.DependencyInjection;
using ZeroAlloc.Authorization;
using ZeroAlloc.Authorization.Generated;
using ZeroAlloc.Results;

var services = new ServiceCollection().AddZeroAllocAuthorization(); // generated
using var scope = services.BuildServiceProvider().CreateScope();
var authorizer = scope.ServiceProvider.GetRequiredService<AuthorizerFor<DeleteUser>>();

var result = await authorizer.EvaluateAsync(AnonymousSecurityContext.Instance);
Console.WriteLine(result.IsFailure ? $"denied: {result.Error.Code}" : "allowed");
// denied: auth.admin_required

[Policy("AdminOnly")]
public sealed class AdminOnly : IAuthorizationPolicy
{
    public ValueTask<UnitResult<AuthorizationFailure>> EvaluateAsync(ISecurityContext ctx, CancellationToken ct = default)
        => new(ctx.Roles.Contains("Admin")
            ? UnitResult<AuthorizationFailure>.Success()
            : new AuthorizationFailure("auth.admin_required", "Admin role required"));
}

[RequirePolicy("AdminOnly")]
public sealed record DeleteUser(string UserId);

AddZeroAllocAuthorization() and AuthorizerFor<DeleteUser> come from the generator: it found the [Policy] class and the [RequirePolicy] request and wrote the dispatch for you. Rename the policy without updating the request and the build fails.

What you get

  • Named policies with [Policy("Name")], bound to request types with [RequirePolicy("Name")]. Stack several and all must allow.
  • Any-of groups with [RequireAnyPolicy("A", "B")]: the first policy that allows short-circuits the group.
  • Parameterized policies such as [RequirePolicy("MinAge", 18)]. Arguments are type-checked against the policy at compile time and passed typed, without boxing.
  • Resource-based authorization through IResourceSecurityContext<TResource>: when the host supplies it, a policy can inspect the resource it guards.
  • Structured denials. A failure carries a machine-readable code and a reason, returned as a UnitResult<AuthorizationFailure> your host maps to HTTP 403, an exception or a refused tool call.
  • Async policies for checks that need I/O, such as a tenant lookup.
  • Your own context. Extend ISecurityContext with host-specific data; AnonymousSecurityContext.Instance covers callers with no identity.
  • Mediator integration: ZeroAlloc.Mediator.Authorization runs these policies as a pipeline behavior before the handler of any request that carries [RequirePolicy].
  • Used by AI.Sentinel for tool-call authorization of IChatClient-based agents.

Diagnostics ZAUTH001–ZAUTH008 are listed in Getting started.

Performance

Allocation per call, asserted by the AllocationGate tests in AllocationBudgetTests.cs:

Path Allocated
EvaluateAsync, allow and deny 0 B
Generated dispatch, parameterized [RequirePolicy("MinAge", 18)] 0 B
Generated dispatch, [RequireAnyPolicy] with the first policy allowing 0 B

No head-to-head benchmark against another authorization library is published yet. Same-run ratios, the benchmark sources and how zero allocation is enforced are in docs/performance.md.

Learn more

About

Authorization primitives for .NET — IAuthorizationPolicy, ISecurityContext, [Authorize], [AuthorizationPolicy]. Source-of-truth contract package shared across hosts (AI.Sentinel, ZeroAlloc.Mediator.Authorization).

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages