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>
28 lines
1.1 KiB
C#
28 lines
1.1 KiB
C#
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Svrnty.CQRS.Abstractions.Security;
|
|
|
|
/// <summary>
|
|
/// Cross-cutting authorization check that runs alongside (not in place of) the
|
|
/// consumer's <see cref="ICommandAuthorizationService"/>. Multiple
|
|
/// implementations may be registered; the framework resolves them as
|
|
/// <c>IEnumerable<ICommandAuthorizationCheck></c> and runs each in
|
|
/// registration order. AND semantics — any non-<see cref="AuthorizationResult.Allowed"/>
|
|
/// short-circuits the pipeline.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Use this seam for self-applying, attribute-driven checks shipped by
|
|
/// framework modules (proof-of-work, mobile attestation, rate-limit gates,
|
|
/// IP allow-lists). The check is responsible for inspecting
|
|
/// <see cref="CommandAuthorizationCheckContext.CommandType"/> attributes and
|
|
/// no-op'ing (return <see cref="AuthorizationResult.Allowed"/>) when it
|
|
/// doesn't apply.
|
|
/// </remarks>
|
|
public interface ICommandAuthorizationCheck
|
|
{
|
|
Task<AuthorizationResult> CheckAsync(
|
|
CommandAuthorizationCheckContext context,
|
|
CancellationToken cancellationToken = default);
|
|
}
|