Skip to content

Cratis Arc authorization: protect commands and queries

Cratis Arc checks role and policy attributes when a command or query runs through its pipeline. Use them when an action or a read needs an access rule, such as allowing only librarians to register authors. Direct C# calls to Handle() or a static query bypass these checks.

Authorize at the boundary, not in the logic

Section titled “Authorize at the boundary, not in the logic”

Put the access rule on the command record or query method, using attributes from Cratis.Arc.Authorization. [Roles] requires an authenticated caller with at least one listed role, so your handler does not need to repeat that role check.

For a named policy, use [Authorize(Policy = "...")] and register the policy. It requires authentication unless the registration explicitly opts into guest evaluation. Named AuthenticationSchemes work only on the ASP.NET Core host; the standalone Arc host fails at startup rather than ignoring them.

Put [Roles(...)] on the [Command] record to check the caller before the command runs. This fragment uses application-owned AuthorId, AuthorName, and UserRole types with the optional MongoDB integration. It does not require Chronicle:

[Command]
[Roles(nameof(UserRole.Librarian))]
public record RegisterAuthor(AuthorId Id, AuthorName Name)
{
public Task Handle(IMongoCollection<Author> authors) =>
authors.InsertOneAsync(new Author(Id, Name));
}

Apply the same attribute to a query method to restrict who can read its result:

[ReadModel]
public record Author([property: Key] AuthorId Id, AuthorName Name)
{
[Roles(nameof(UserRole.Librarian))]
public static ISubject<IEnumerable<Author>> AllAuthors(IMongoCollection<Author> collection) =>
collection.Observe();
}

The evaluator reads roles from the authenticated ClaimsPrincipal. Arc integrates with ASP.NET Core authentication, while its lightweight host has its own handlers. Read the principal through ICurrentPrincipalAccessor from Cratis.Arc.Authorization.

Identity details serve a different purpose: IProvideIdentityDetails supplies application-specific presentation data, not trusted claims or permission to run an operation. Continue with Identity to configure authentication or supply a principal during local development.

Selecting a tenant ID is not permission to access that tenant’s data. A multi-tenant application also needs membership authorization and integration-specific storage isolation; tenant selection covers only the selection step.

Generated TypeScript proxies call the same protected endpoints as other clients. Access checks happen on the server, not in the proxy.

  • Identity — authentication, identity details, and local-dev principals.
  • Tenancy — isolating data per tenant.
  • Commands and Queries — the full model.