ZeroAlloc.Authorization.Generator
2.0.1
See the version list below for details.
dotnet add package ZeroAlloc.Authorization.Generator --version 2.0.1
NuGet\Install-Package ZeroAlloc.Authorization.Generator -Version 2.0.1
<PackageReference Include="ZeroAlloc.Authorization.Generator" Version="2.0.1" />
<PackageVersion Include="ZeroAlloc.Authorization.Generator" Version="2.0.1" />
<PackageReference Include="ZeroAlloc.Authorization.Generator" />
paket add ZeroAlloc.Authorization.Generator --version 2.0.1
#r "nuget: ZeroAlloc.Authorization.Generator, 2.0.1"
#:package ZeroAlloc.Authorization.Generator@2.0.1
#addin nuget:?package=ZeroAlloc.Authorization.Generator&version=2.0.1
#tool nuget:?package=ZeroAlloc.Authorization.Generator&version=2.0.1
ZeroAlloc.Authorization
Authorization primitives for .NET — ISecurityContext, IAuthorizationPolicy, [Policy], [RequirePolicy], AnonymousSecurityContext, and an AuthorizerFor<TRequest> dispatcher emitted by the bundled source generator.
Used by:
- AI.Sentinel — tool-call authorization for
IChatClient-based agents ZeroAlloc.Mediator.Authorizationv5 — request-handler authorization
Install
dotnet add package ZeroAlloc.Authorization
Targets net8.0, net9.0, net10.0. The package bundles a Roslyn source generator — no separate *.Generator install.
The contract
public interface ISecurityContext
{
string Id { get; }
IReadOnlySet<string> Roles { get; }
IReadOnlyDictionary<string, string> Claims { get; }
}
public interface IAuthorizationPolicy
{
ValueTask<UnitResult<AuthorizationFailure>> EvaluateAsync(
ISecurityContext ctx, CancellationToken ct = default);
}
Writing a policy
[Policy("AdminOnly")]
public sealed class AdminOnlyPolicy : IAuthorizationPolicy
{
public ValueTask<UnitResult<AuthorizationFailure>> EvaluateAsync(
ISecurityContext ctx, CancellationToken ct = default)
=> new(ctx.Roles.Contains("Admin")
? UnitResult<AuthorizationFailure>.Success()
: new AuthorizationFailure(AuthorizationFailure.DefaultDenyCode, "Admin role required"));
}
Bind it on a request type — [RequirePolicy] is class/struct-level only, and stacks:
[RequirePolicy("AdminOnly")]
public sealed record DeleteUserCommand(string UserId);
[RequirePolicy("ActiveTenant")]
[RequirePolicy("AdminOnly")]
public sealed record PurgeTenantCommand(string TenantId);
Host wiring
The bundled generator emits one AuthorizerFor<TRequest> subclass per [RequirePolicy]-decorated type plus an AddZeroAllocAuthorization() extension on IServiceCollection. Hosts call the extension at startup and resolve AuthorizerFor<T> per request:
using ZeroAlloc.Authorization.Generated;
builder.Services.AddZeroAllocAuthorization();
var authorizer = sp.GetService<AuthorizerFor<DeleteUserCommand>>();
if (authorizer is not null)
{
var result = await authorizer.EvaluateAsync(securityContext, ct);
if (result.IsFailure)
return Forbid(result.Error); // host maps Code / Reason to its outcome shape
}
Hosts can extend ISecurityContext
Hosts define their own subinterface for richer payloads. AI.Sentinel adds IToolCallSecurityContext : ISecurityContext with ToolName + Args. ZeroAlloc.Mediator.Authorization v5 adds IRequestSecurityContext<TRequest>. Inside the policy body, downcast:
public ValueTask<UnitResult<AuthorizationFailure>> EvaluateAsync(
ISecurityContext ctx, CancellationToken ct = default)
=> new(ctx is IToolCallSecurityContext tc && tc.ToolName == "delete_database"
? new AuthorizationFailure("tool.destructive", "destructive tool blocked")
: UnitResult<AuthorizationFailure>.Success());
I/O-bound policies
For checks that need to await something (tenant lookup, external claims validation), mark EvaluateAsync as async:
[Policy("ActiveTenant")]
public sealed class ActiveTenantPolicy(ITenantService tenants) : IAuthorizationPolicy
{
public async ValueTask<UnitResult<AuthorizationFailure>> EvaluateAsync(
ISecurityContext ctx, CancellationToken ct = default)
{
var active = await tenants.IsActiveAsync(ctx.Id, ct).ConfigureAwait(false);
return active
? UnitResult<AuthorizationFailure>.Success()
: new AuthorizationFailure("tenant.inactive", "tenant is suspended");
}
}
Generator diagnostics
The bundled generator emits five compile-time diagnostics:
| ID | Fires when |
|---|---|
ZAUTH001 |
[RequirePolicy("X")] references a policy name with no matching [Policy("X")]. |
ZAUTH002 |
Two [Policy("X")] declarations share the same name. |
ZAUTH003 |
[Policy] class doesn't implement IAuthorizationPolicy. |
ZAUTH004 |
[Policy] class is abstract or static. |
ZAUTH005 |
[RequirePolicy] placed on a non-class/non-struct target. |
Performance
BenchmarkDotNet (BDN ShortRun, .NET 10 release build, x64) — happy path on a simple role-check policy:
| Method | Mean | Allocated |
|---|---|---|
EvaluateAsync |
~99 ns | 0 B |
Source: benchmarks/ZeroAlloc.Authorization.Benchmarks/PolicyEvaluationBenchmarks.cs.
The contract is enforced as zero-allocation by:
<IsAotCompatible>true</IsAotCompatible>on the main library (build-time IL2026/IL3050 analyzers fire on any reflection regression).- The
samples/ZeroAlloc.Authorization.AotSmoke/console app, exercised on each CI run withPublishAot=true. - The benchmark project above.
License
MIT.
Learn more about Target Frameworks and .NET Standard.
This package has no dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.