Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ServiceComposer/ServiceComposer.AspNetCore/llms.txt

Use this file to discover all available pages before exploring further.

ServiceComposer registers its composition endpoints directly in ASP.NET Core’s endpoint routing system. This means the full suite of standard ASP.NET Core authentication and authorization metadata — [Authorize], [AllowAnonymous], policy-based attributes, and custom requirement attributes — works on composition handlers exactly as it does on MVC controllers or Razor Pages. No special ServiceComposer configuration is required.

Applying authorization to a handler

Decorate a handler method with [Authorize] just as you would a controller action:
public class SampleHandlerWithAuthorization : ICompositionRequestsHandler
{
    [Authorize]
    [HttpGet("/sample/{id}")]
    public Task Handle(HttpRequest request)
    {
        return Task.CompletedTask;
    }
}
ServiceComposer collects the endpoint metadata from the handler and merges it onto the composed endpoint. Authorization is evaluated by the standard ASP.NET Core authorization middleware before any composition handler executes. If the request is not authorized, it is rejected at the middleware layer and no handlers run.

Multiple handlers with different requirements

When multiple handlers are registered for the same route, their authorization metadata is merged. The most restrictive combination applies. If one handler requires an authenticated user and another requires a specific policy, both requirements must be satisfied:
public class SalesHandler : ICompositionRequestsHandler
{
    [Authorize]
    [HttpGet("/product/{id}")]
    public Task Handle(HttpRequest request) { /* ... */ return Task.CompletedTask; }
}

public class InventoryHandler : ICompositionRequestsHandler
{
    [Authorize(Policy = "WarehouseStaff")]
    [HttpGet("/product/{id}")]
    public Task Handle(HttpRequest request) { /* ... */ return Task.CompletedTask; }
}
Both [Authorize] and [Authorize(Policy = "WarehouseStaff")] are collected from the respective handlers and applied to the /product/{id} route. A caller must be authenticated and belong to the WarehouseStaff policy to reach either handler.

Middleware setup

Add the standard ASP.NET Core authentication and authorization middleware before MapCompositionHandlers():
var builder = WebApplication.CreateBuilder();
builder.Services.AddViewModelComposition();
builder.Services.AddAuthentication(); // configure your scheme here
builder.Services.AddAuthorization();

var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapCompositionHandlers();
app.Run();
UseAuthentication() and UseAuthorization() must be called before MapCompositionHandlers() in the middleware pipeline. Reversing the order means authorization metadata is registered but the middleware has already passed, so requests are never challenged.

How metadata merging works

When MapCompositionHandlers() is called, ServiceComposer inspects every handler registered for each route template and collects all endpoint metadata attributes — [Authorize], [AllowAnonymous], [RequireAuthorization], custom policy attributes, and any other IAuthorizeData or IAllowAnonymous implementation. The collected metadata is merged onto the single composed endpoint that ASP.NET Core registers for that route.
Because authorization is enforced by ASP.NET Core’s standard middleware before composition begins, the request’s ClaimsPrincipal is fully populated and available on HttpContext.User inside every composition handler that does execute.

Common patterns

Apply a fallback authorization policy globally so that every endpoint — including composed ones — requires authentication unless explicitly opted out:
builder.Services.AddAuthorization(options =>
{
    options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
});
If a global or fallback policy is in place but a particular route should be publicly accessible, add [AllowAnonymous] to the relevant handler method. ServiceComposer forwards this attribute to the merged endpoint metadata and ASP.NET Core honours it.
For fine-grained, resource-based authorization decisions (e.g. “does this user own this resource?”), resolve IAuthorizationService from DI inside the handler and call AuthorizeAsync against the loaded resource after it has been fetched:
public class OrderHandler : ICompositionRequestsHandler
{
    readonly IAuthorizationService _authz;

    public OrderHandler(IAuthorizationService authz) => _authz = authz;

    [Authorize]
    [HttpGet("/order/{id}")]
    public async Task Handle(HttpRequest request)
    {
        // fetch the resource, then authorize against it
        var result = await _authz.AuthorizeAsync(
            request.HttpContext.User,
            resource: /* loaded resource */null,
            policy: "OrderOwner");

        if (!result.Succeeded)
        {
            request.HttpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
            return;
        }
        // continue composing the view model
    }
}

Build docs developers (and LLMs) love