-
Notifications
You must be signed in to change notification settings - Fork 0
05 ASP.NET Core Integration
The ASP.NET Core package connects framework authentication and authorization to RuleGate's provider-independent engine.
flowchart LR
A[Authentication handler] --> B[ClaimsPrincipal]
B --> C[Subject factory]
D[Endpoint metadata or domain object] --> E[Resource factory]
C --> F[Enrichment pipeline]
E --> F
F --> G[RuleGate engine]
H[Policy snapshot] --> G
G --> I{Decision}
I -->|Allowed| J[Endpoint or operation]
I -->|Denied| K[Challenge or forbid]
For an immutable compiled manifest:
var compilation = await new RuleGateManifestCompiler()
.CompileFromFileAsync("rulegate.yaml");
if (!compilation.IsSuccess)
{
throw new InvalidOperationException("Invalid RuleGate manifest.");
}
builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddAuthorization();
builder.Services
.AddRuleGate()
.AddHttpAuthorizationResultMapping()
.AddLoggingDiagnostics()
.AddPolicies(compilation.Policies);For a reloadable file source, replace AddPolicies with
AddYamlPolicyFile; see chapter 11.
The default subject factory maps:
- subject ID from
ClaimTypes.NameIdentifier; - roles from
ClaimTypes.Role; - permissions from the
permissionclaim type.
Configure claim types when the validated token uses different names:
builder.Services
.AddRuleGate()
.ConfigureSubjectMapping(options =>
{
options.SubjectIdClaimType = "sub";
options.RoleClaimTypes.Clear();
options.RoleClaimTypes.Add("role");
options.PermissionClaimTypes.Clear();
options.PermissionClaimTypes.Add("permissions");
})
.AddPolicies(compilation.Policies);Claims do not automatically become subject attributes. Use explicit mapping or trusted subject providers so the security boundary remains visible.
Instance operation:
app.MapGet("/documents/{id}", GetDocumentAsync)
.RequireRuleGate(
resourceType: "document",
action: "read",
resourceIdRouteValue: "id");Collection operation:
app.MapPost("/documents", CreateDocumentAsync)
.RequireRuleGate(
resourceType: "document",
action: "create");The optional route-value name supplies only the resource ID. It does not load a domain entity or attributes.
using Fotbiler.RuleGate.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/documents")]
public sealed class DocumentsController : ControllerBase
{
[HttpGet("{id}")]
[RuleGateAuthorize(
resourceType: "document",
action: "read",
resourceIdRouteValue: "id")]
public IActionResult Get(string id)
{
return Ok(new { id });
}
}The attribute can be placed on a controller or action. Conflicting RuleGate metadata fails closed.
Endpoint metadata is useful before the handler runs. Imperative authorization is useful when the domain object must be loaded first:
public static async Task<IResult> UpdateAsync(
string id,
UpdateDocumentRequest request,
ClaimsPrincipal user,
IDocumentRepository documents,
IAuthorizationService authorization,
CancellationToken cancellationToken)
{
var document = await documents.FindAsync(id, cancellationToken);
if (document is null)
{
return Results.NotFound();
}
var resource = new AuthorizationResource(
type: "document",
id: document.Id,
attributes: new AuthorizationAttributes(
[
new("ownerId", document.OwnerId),
new("organizationId", document.OrganizationId),
new("status", document.Status),
new("classificationLevel", document.ClassificationLevel),
]));
var result = await authorization.AuthorizeRuleGateAsync(
user,
resource,
action: "update");
if (!result.Succeeded)
{
return Results.Forbid();
}
await documents.UpdateAsync(document, request, cancellationToken);
return Results.NoContent();
}Do not perform the state change before authorization. Re-check authorization within the same consistency boundary when resource facts can change between read and write.
Workers, message consumers, and services can evaluate a fully constructed request:
var request = new AuthorizationRequest(
subject: new AuthorizationSubject(
id: "service-invoice-worker",
roles: ["INVOICE.WORKER"],
permissions: ["INVOICE.POST"],
attributes: new AuthorizationAttributes(
[
new("organizationId", "finance"),
])),
resource: new AuthorizationResource(
type: "invoice",
id: invoice.Id,
attributes: new AuthorizationAttributes(
[
new("organizationId", invoice.OrganizationId),
new("status", invoice.Status),
])),
action: "post",
context: new AuthorizationContext(
evaluationTime: DateTimeOffset.UtcNow,
attributes: new AuthorizationAttributes(
[
new("requestChannel", "worker"),
new("identityType", "service"),
])));
var decision = await engine.EvaluateAsync(request, cancellationToken);
if (!decision.IsAllowed)
{
// Stop the operation. Log only approved low-cardinality details.
}In production ASP.NET Core code, prefer the injected IRuleGateClock or the
integration pipeline over calling DateTimeOffset.UtcNow directly when tests
or consistent host time semantics matter.
The default resource factory accepts AuthorizationResource and HTTP endpoint
metadata. To authorize arbitrary domain objects, implement
IRuleGateAuthorizationResourceFactory and register it before AddRuleGate():
builder.Services.AddSingleton<
IRuleGateAuthorizationResourceFactory,
ApplicationRuleGateResourceFactory>();
builder.Services.AddRuleGate();The custom factory must preserve support for every framework resource the
application uses, including HttpContext if endpoint helpers remain enabled.
Return failure rather than inventing missing identifiers or attributes.
AddHttpAuthorizationResultMapping() supplies generic problem responses on
supported targets:
| Situation | HTTP behavior |
|---|---|
| No authenticated principal | Challenge, normally 401
|
| Authenticated principal denied | Forbid, normally 403
|
| Allowed | Endpoint executes |
The public response does not expose policy IDs, requirement IDs, roles, permissions, attributes, exception messages, or internal failure codes. Keep internal diagnostics separate from client errors.
Applications with a custom IAuthorizationMiddlewareResultHandler keep their
custom handler. Review its output for information disclosure.
Test at four levels:
- manifest tests for pure policy behavior;
- subject/resource/context provider unit tests;
- endpoint tests for
401,403, and success; - domain tests that prove the protected state change does not occur on deny.
Include wrong casing, missing route IDs, missing attributes, provider failure, cross-organization access, and stale resource state.
Previous: Policy language · Next: Trusted attributes and context
Canonical source: docs/guide · Documentation index · RuleGate 1.0.0
- Home
- 1. Authorization foundations
- 2. Packages and installation
- 3. First protected API
- 4. Policy language
- 5. ASP.NET Core integration
- 6. Trusted attributes and context
- 7. Identity and Keycloak
- 8. Frontend integration
- 9. CLI and policy lifecycle
- 10. Testing and diagnostics
- 11. Policy sources and reload
- 12. Extensibility
- 13. Real-world recipes
- 14. Production checklist
- Glossary