ZeroAlloc.Authorization
2.0.0
dotnet add package ZeroAlloc.Authorization --version 2.0.0
NuGet\Install-Package ZeroAlloc.Authorization -Version 2.0.0
<PackageReference Include="ZeroAlloc.Authorization" Version="2.0.0" />
<PackageVersion Include="ZeroAlloc.Authorization" Version="2.0.0" />
<PackageReference Include="ZeroAlloc.Authorization" />
paket add ZeroAlloc.Authorization --version 2.0.0
#r "nuget: ZeroAlloc.Authorization, 2.0.0"
#:package ZeroAlloc.Authorization@2.0.0
#addin nuget:?package=ZeroAlloc.Authorization&version=2.0.0
#tool nuget:?package=ZeroAlloc.Authorization&version=2.0.0
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.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- ZeroAlloc.Results (>= 0.1.4)
-
net8.0
- ZeroAlloc.Results (>= 0.1.4)
-
net9.0
- ZeroAlloc.Results (>= 0.1.4)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on ZeroAlloc.Authorization:
| Package | Downloads |
|---|---|
|
AI.Sentinel
Security monitoring middleware for IChatClient — prompt injection, hallucination, and operational anomaly detection with an intervention engine. |
|
|
ZeroAlloc.Mediator.Authorization
Pipeline behavior that authorizes IRequest<T> (or IAuthorizedRequest<T>) via ZeroAlloc.Authorization before dispatch. Source-generated per-request policy lookup; deny throws AuthorizationDeniedException or returns Result<T, AuthorizationFailure> depending on the request marker. |
GitHub repositories
This package is not used by any popular GitHub repositories.