Introduces a non-breaking, multi-instance authorization-check pipeline
that runs alongside the existing single-instance auth services.
Motivation
- Cross-cutting checks (proof-of-work, mobile attestation, rate-limit
gates, IP allow-lists) don't belong in consumer auth services — they
ship from framework modules and self-apply via attributes.
- The existing ICommandAuthorizationService takes only a Type; checks
need the request *instance* to read payload fields (e.g. an Altcha
solution carried on the command).
Shape
- New abstractions: ICommandAuthorizationCheck, IQueryAuthorizationCheck,
CommandAuthorizationCheckContext, QueryAuthorizationCheckContext.
- Context carries (Type, Instance, IServiceProvider, Items dict). The
Items dict lets sibling checks signal one another — e.g. a future
mobile-attestation check stamps "mobile_attested" for the Altcha
check to read as a bypass.
- AND semantics: framework resolves IEnumerable<…Check>, runs each in
registration order, first non-Allowed short-circuits.
- Wired into MinimalApi (commands + queries, POST + GET) and the
Svrnty.CQRS.Grpc.Generators source generator (commands, queries,
dynamic queries). In all paths the checks run AFTER the instance
is materialized and validated, BEFORE handler invocation.
Backward compatibility
- No registered checks = today's behavior exactly.
- ICommandAuthorizationService / IQueryAuthorizationService signatures
unchanged; consumers' existing auth services keep working untouched.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Automated formatting: BOM removal, using sort order, final newlines,
whitespace normalization across all projects.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>