Authlink.Policy.AspNetCore
1.5.0
dotnet add package Authlink.Policy.AspNetCore --version 1.5.0
NuGet\Install-Package Authlink.Policy.AspNetCore -Version 1.5.0
<PackageReference Include="Authlink.Policy.AspNetCore" Version="1.5.0" />
<PackageVersion Include="Authlink.Policy.AspNetCore" Version="1.5.0" />
<PackageReference Include="Authlink.Policy.AspNetCore" />
paket add Authlink.Policy.AspNetCore --version 1.5.0
#r "nuget: Authlink.Policy.AspNetCore, 1.5.0"
#:package Authlink.Policy.AspNetCore@1.5.0
#addin nuget:?package=Authlink.Policy.AspNetCore&version=1.5.0
#tool nuget:?package=Authlink.Policy.AspNetCore&version=1.5.0
Authlink.Policy.AspNetCore
ASP.NET Core integration for Authlink policy-based authorization. Provides attributes, authorization handlers, and middleware for seamless integration with the ASP.NET Core authorization framework.
Installation
dotnet add package Authlink.Policy.AspNetCore
Quick Start
1. Register Services
// Program.cs
builder.Services.AddHttpContextAccessor();
// Register the policy client (from Authlink.Policy.Client.Http)
builder.Services.AddAuthlinkPolicyClient(options =>
{
options.BaseUrl = builder.Configuration["PolicyApi:BaseUrl"];
});
// Register policy authorization
builder.Services.AddAuthlinkPolicyAuthorization(options =>
{
options.SubjectClaimTypes = new[] { "sub", "user_id" };
options.DefaultResourceKey = "tenant";
});
// Register your resource attribute resolvers
builder.Services.AddScoped<IResourceAttributeResolver, UserResourceAttributeResolver>();
2. Use the PolicyAuthorize Attribute
// Simple permission check
[PolicyAuthorize("reports:view")]
[HttpGet("reports")]
public async Task<IActionResult> GetReports()
{
// Authorization already handled
return Ok(await _reportService.GetReportsAsync());
}
// With resource attributes
[PolicyAuthorize("user:update", ResourceKey = "user", ResourceIdProperty = "id")]
[HttpPut("users/{id}")]
public async Task<IActionResult> UpdateUser(Guid id, UpdateUserRequest request)
{
// Policy evaluated with user's resource attributes
return Ok(await _userService.UpdateAsync(id, request));
}
Features
PolicyAuthorize Attribute
The [PolicyAuthorize] attribute provides declarative policy-based authorization:
| Property | Description |
|---|---|
Permission |
Required. The permission key (e.g., "user:update") |
ResourceKey |
Optional. The resource type for attribute resolution |
ResourceIdProperty |
Optional. Property name containing the resource ID |
ResourceIdSource |
Where to find the resource ID (Auto, Route, Query, Form, Body) |
ResourceIdSource Values
| Value | Description |
|---|---|
Auto |
Tries route → query → form → body (in order) |
Route |
Extract from route parameters (e.g., /users/{id}) |
Query |
Extract from query string (e.g., ?id=123) |
Form |
Extract from form data |
Body |
Extract from JSON request body (requires request buffering) |
Resource Attribute Resolution
Implement IResourceAttributeResolver to provide resource attributes for policy evaluation:
public class UserResourceAttributeResolver : IResourceAttributeResolver
{
private readonly IUserRepository _userRepository;
public string ResourceKey => "user";
public async Task<Dictionary<string, string>> ResolveAsync(
object resourceId,
CancellationToken cancellationToken = default)
{
var userId = (Guid)resourceId;
var user = await _userRepository.GetByIdAsync(userId, cancellationToken);
return new Dictionary<string, string>
{
["tenant-id"] = user.TenantId.ToString(),
["department"] = user.Department ?? "",
["status"] = user.Status.ToString()
};
}
}
Configuration Options
services.AddAuthlinkPolicyAuthorization(options =>
{
// Claim types to search for subject ID (in order)
options.SubjectClaimTypes = new[] { "sub", "user_id" };
// Default resource key when not specified on attribute
options.DefaultResourceKey = "tenant";
// Throw exceptions instead of returning 401/403
options.ThrowOnMissingSubject = false;
options.ThrowOnPolicyError = false;
// Allow missing resource resolvers (logs warning, proceeds without attributes)
options.AllowMissingResourceResolver = false;
// Static audience (API resource) ID sent on every decision request, enabling
// audience-scoped policy statements to match (default: null, not sent)
options.AudienceId = Guid.Parse("550e8400-e29b-41d4-a716-446655440000");
// Claim types to search for tenant ID, purpose, and client ID (in order).
// Absent claims yield null; non-GUID tenant/client values are treated as
// absent and logged at warning level.
options.TenantClaimTypes = new[] { "tenant_id" };
options.PurposeClaimTypes = new[] { "purpose" };
options.ClientIdClaimTypes = new[] { "azp", "client_id" };
});
Per-Request Decision Caching
Decisions are cached in HttpContext.Items for the duration of a request, keyed on the
permission key and the resolved resource attributes. Stacked [PolicyAuthorize] attributes,
repeated checks for the same permission, and combined attribute + middleware checks share a
single PDP call per unique permission/resource pair. Both Allow and Deny decisions are cached;
transport errors are never cached, so a failed call is retried by the next check. The cache
never outlives the request — policy changes are always visible on the next request.
Custom Subject ID Provider
Override the default subject ID extraction by implementing ISubjectIdProvider:
public class CustomSubjectIdProvider : ISubjectIdProvider
{
private readonly IHttpContextAccessor _httpContextAccessor;
public Task<Guid?> GetSubjectIdAsync(CancellationToken cancellationToken = default)
{
var context = _httpContextAccessor.HttpContext;
if (context?.Items["CurrentUserId"] is Guid userId)
{
return Task.FromResult<Guid?>(userId);
}
return Task.FromResult<Guid?>(null);
}
}
// Register before AddAuthlinkPolicyAuthorization
services.AddScoped<ISubjectIdProvider, CustomSubjectIdProvider>();
Request Body Extraction
To extract resource IDs from the JSON request body (when using ResourceIdSource.Body or ResourceIdSource.Auto), you must enable request buffering in your middleware pipeline:
// Program.cs - Add before app.UseAuthorization()
app.Use(async (context, next) =>
{
context.Request.EnableBuffering();
await next();
});
This allows the authorization handler to read the request body while still allowing downstream handlers (like model binding) to read it as well.
Middleware-Based Authorization
As an alternative to attribute-based authorization, you can use route-based policy authorization middleware. This approach is useful when you prefer convention-based authorization or need to apply policies globally.
Register Middleware Services
// Program.cs
builder.Services.AddHttpContextAccessor();
// Register the policy client
builder.Services.AddAuthlinkPolicyClient(options =>
{
options.BaseUrl = builder.Configuration["PolicyApi:BaseUrl"];
});
// Register middleware services
builder.Services.AddAuthlinkPolicyMiddleware(options =>
{
// Routes that bypass authorization entirely
options.BypassPatterns.Add("/health");
options.BypassPatterns.Add("/swagger/**");
// Route-to-permission mappings
options.Mappings.Add(new RoutePolicyMapping
{
Method = "GET",
Pattern = "/api/users",
PermissionKey = "user:list"
});
options.Mappings.Add(new RoutePolicyMapping
{
Method = "GET",
Pattern = "/api/users/{id}",
PermissionKey = "user:read",
ResourceKey = "user",
ResourceIdRouteParameter = "id"
});
options.Mappings.Add(new RoutePolicyMapping
{
Method = "PUT",
Pattern = "/api/users/{id}",
PermissionKey = "user:update",
ResourceKey = "user",
ResourceIdRouteParameter = "id"
});
// Deny access to unmapped routes (optional, default: false)
options.DenyByDefault = false;
});
Add Middleware to Pipeline
var app = builder.Build();
app.UseAuthentication();
app.UsePolicyAuthorization(); // Add after authentication
app.UseRouting();
app.MapControllers();
app.Run();
Route Pattern Syntax
| Pattern | Description | Example |
|---|---|---|
/api/users |
Exact match | Matches /api/users only |
/api/users/{id} |
Route parameter | Matches /api/users/123, /api/users/abc |
/api/* |
Single segment wildcard | Matches /api/users but not /api/users/123 |
/api/** |
Multi-segment wildcard | Matches /api/users, /api/users/123/orders |
Middleware Configuration Options
builder.Services.AddAuthlinkPolicyMiddleware(
mappings =>
{
mappings.BypassPatterns.Add("/health");
mappings.DenyByDefault = true;
mappings.Mappings.Add(new RoutePolicyMapping { ... });
},
auth =>
{
// Throw exceptions instead of returning 401/403
auth.ThrowOnMissingSubject = false;
auth.ThrowOnPolicyError = false;
});
Custom Policy Mapping Provider
For advanced scenarios, implement IPolicyMappingProvider:
public class DatabasePolicyMappingProvider : IPolicyMappingProvider
{
private readonly IRoutePermissionRepository _repository;
public PolicyMapping? GetMapping(string method, string path)
{
// Load from database, cache, etc.
var mapping = _repository.FindMapping(method, path);
if (mapping == null) return null;
return new PolicyMapping
{
PermissionKey = mapping.Permission,
ResourceKey = mapping.ResourceType,
ResourceIdRouteParameter = mapping.ResourceIdParam
};
}
}
// Register before AddAuthlinkPolicyMiddleware
services.AddSingleton<IPolicyMappingProvider, DatabasePolicyMappingProvider>();
Requirements
- .NET 9.0 or later
- ASP.NET Core 9.0 or later
Authlink.Policy.Client.Core(automatically included)IPolicyClientimplementation (e.g.,Authlink.Policy.Client.Http)
Related Packages
Authlink.Policy.Client.Core- Core abstractions and DTOsAuthlink.Policy.Client.Http- HTTP client implementationAuthlink.Policy.ServiceStack- ServiceStack integration
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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
- Authlink.Policy.Client.Core (>= 1.5.0)
- Microsoft.AspNetCore.Authorization (>= 10.0.11)
- Microsoft.AspNetCore.Http.Abstractions (>= 2.3.0)
- Microsoft.AspNetCore.Routing.Abstractions (>= 2.3.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.11)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.11)
- Microsoft.Extensions.Options (>= 10.0.11)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.5.0 | 244 | 9/17/2026 |