Authorization in ASP.NET Core
When your application uses Cratis.Arc, two layers can reject a request: ASP.NET Core middleware at the HTTP boundary and Arc filters in the command/query pipelines. Keep their configuration separate so a policy tested through HTTP is not mistaken for an in-process guarantee.
The two boundaries
Section titled “The two boundaries”| Boundary | Checks | Applies to |
|---|---|---|
| ASP.NET Core authorization | Microsoft endpoint/controller metadata, registered policies, schemes, default/fallback policy | Requests that traverse the configured middleware and matching endpoint metadata |
| Arc authorization filters | Authenticated principal and OR-role membership from Arc attributes | Commands and queries executed through Arc pipelines |
Cratis.Arc.Authorization.RolesAttribute derives from Arc’s AuthorizeAttribute, not Microsoft’s. Arc’s evaluator does not evaluate Policy or AuthenticationSchemes, and it takes the first applicable authorization attribute instead of combining multiple attributes. Do not stack Arc attributes to express AND requirements.
Current limitation: the ASP.NET adapter’s authorization evaluator resolves Arc’s attribute type too. Do not rely on a Microsoft [Authorize] placed only on a model-bound command record to protect direct pipeline calls. This does not mean Microsoft authorization cannot work: middleware can enforce Microsoft metadata on controllers or explicitly configured endpoints. Verify the actual HTTP route and the pipeline separately.
Configure a real authentication scheme for your deployment before adding the middleware. The following configuration fragment belongs in an existing WebApplication program after authentication service registration:
using Cratis.Arc;using Microsoft.AspNetCore.Authorization;
builder.AddCratisArc();builder.Services.AddAuthorizationBuilder() .SetFallbackPolicy(new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .Build());
var app = builder.Build();app.UseAuthentication();app.UseAuthorization();app.UseCratisArc();app.Run();See Microsoft Identity integration for one authentication option and its trusted-ingress prerequisites. A fallback policy is not a substitute for authentication service configuration.
Protecting all endpoints by default
Section titled “Protecting all endpoints by default”ASP.NET’s fallback policy applies to endpoints without applicable authorization metadata. Its default policy applies when Microsoft [Authorize] supplies no named policy. Explicit anonymous metadata bypasses these policies.
Role-based authorization
Section titled “Role-based authorization”For model-bound commands and queries, use explicit Arc imports. These are complete type fragments for an existing Arc host; neither needs a database or Chronicle:
using Cratis.Arc.Authorization;using Cratis.Arc.Commands.ModelBound;
[Roles("Admin", "Manager")][Command]public record ReviewRequest(Guid RequestId){ public Guid Handle() => RequestId;}Arc requires authentication and at least one listed role. The returned Guid remains ordinary response data.
using Cratis.Arc.Authorization;using Cratis.Arc.Queries.ModelBound;
[ReadModel]public record ServiceStatus(string State){ [Roles("Admin", "Auditor")] public static ServiceStatus Internal() => new("Running");}Arc query method attributes take precedence over read-model type attributes. For the full Arc rules, see Core authorization.
Authorization in controllers
Section titled “Authorization in controllers”For ASP.NET controller policy enforcement, use Microsoft attributes explicitly. This complete controller fragment assumes MVC is registered and controllers are mapped in the host:
using Microsoft.AspNetCore.Authorization;using Microsoft.AspNetCore.Mvc;
[ApiController][Route("api/reports")][Authorize(Roles = "Manager")]public class ReportsController : ControllerBase{ [HttpGet] public IActionResult Get() => Ok(new { State = "Available" });}Microsoft controller/action requirements compose according to ASP.NET Core rules; do not transfer Arc’s method-overrides-type rule to them. See ASP.NET Core authorization.
Policy-based authorization
Section titled “Policy-based authorization”Register policies with ASP.NET Core and apply them to an endpoint that participates in its middleware. These are configuration fragments in an existing ASP.NET host:
builder.Services.AddAuthorizationBuilder() .AddPolicy("ReportsReader", policy => policy .RequireAuthenticatedUser() .RequireClaim("permission", "reports:read"));After building the app and enabling its authentication/authorization middleware:
app.MapGet("/reports/availability", () => new { Available = true }) .RequireAuthorization("ReportsReader");This protects that HTTP endpoint. It does not attach the policy to unrelated generated Arc routes or to direct pipeline calls. For permissions that must hold through every transport, implement a server-side permission check in the Arc pipeline using trusted claims and authoritative application data.
Authorization results for commands and queries
Section titled “Authorization results for commands and queries”Arc authorization failures return results with IsAuthorized = false and skip the handler/query logic. Mapped Arc results use HTTP 403; ASP.NET middleware can challenge or forbid before Arc executes and may return a different response shape.
An ordinary call to ServiceStatus.Internal() bypasses the query pipeline and does not evaluate attributes. Calling a command’s Handle() directly likewise bypasses authorization, validation, filters, and result handling. Use the query pipeline, command pipeline, or mapped HTTP endpoint when those guarantees matter.
Custom authorization
Section titled “Custom authorization”A command filter denying permission returns CommandResult.Unauthorized(context.CorrelationId); a query filter uses QueryResult.Unauthorized(context.CorrelationId). CommandResult.Error(...) describes an exception result, not an authorization rejection. See command filters for filter contracts.
Read the trusted principal through ICurrentPrincipalAccessor (Cratis.Arc.Authorization), or IHttpContextAccessor.HttpContext.User when intentionally writing ASP.NET-specific code. IProvideIdentityDetails composes frontend details; returning roles or IsUserAuthorized in its payload does not add claims or authorize every command/query. The identity cookie is client-controlled and must not be used as authorization evidence.
Verification checklist
Section titled “Verification checklist”Test anonymous, authenticated-without-role, and permitted-role callers through both HTTP and direct pipelines. Where HTTP policies or schemes matter, test wrong-policy/wrong-scheme principals against the actual endpoint. Include discovery endpoints with Production settings and a fallback policy enabled. Test custom ownership and tenant membership checks independently of UI visibility.
See also
Section titled “See also”- Core authorization — host-independent attribute and principal contracts.
- Identity — identity enrichment and its trust boundary.
- Microsoft Identity — authentication setup.
- Tenancy — selection is not membership authorization.
- Proxy generation — clients transport results; they do not enforce server permissions.