Skip to content
Tracon

IRunAuthorizationHandler

Namespace Tracon · Assembly Tracon.Abstractions.dll

Decides whether a caller may start a run or read/write a session.

public interface IRunAuthorizationHandler

Tracon draws ownership at the TENANT level. Without this handler, every Operator in a tenant can start a run as, read, and delete every other user’s runs and sessions in the same tenant. This interface does not teach Tracon that boundary — it asks the consumer to enforce their own.

One narrower boundary IS built in, for sessions only: TraconSessionOwnershipOptions records which user opened a session and narrows the session listing to that user. It is off by default, and it does not replace this handler — it answers “which user owns this session” so that a handler no longer has to reject a whole listing just to keep users apart, and leaves every other question (runs, attachments, approvals, scores, per-project rules) to the consumer’s own policy. The two compose: both are consulted, and either can refuse.

The default implementation (AllowAllRunAuthorizationHandler, registered with TryAdd) allows every call, so an installation that registers nothing keeps today’s behavior exactly. A consumer replaces the registration to enforce their own rule.

If this handler throws, the call is denied (fail-closed). A gate that fails open on an exception is not a gate. The same rule IToolAuthorizationHandler follows.

The implementation must be a singleton, for the same reason as IRunAttributionContext: singleton services take a dependency on it, and a scoped registration would be a captive dependency. Resolve per-request state through IHttpContextAccessor.

Tenant mode: expected tenant. Every request carries its own TenantId, resolved by the caller before the handler is invoked; the handler never reads ITenantContext itself. This is deliberate: a queued run’s authorization is checked before the tenant’s ambient scope (AmbientTenantScope) is necessarily active.

Delivery guarantee: no delivery guarantee applies. This is a synchronous decision, not a retried or queued operation — each call is made once, on the request thread, and its result is not persisted or replayed.

AuthorizeRunAsync(RunAuthorizationRequest, CancellationToken)

Section titled “ AuthorizeRunAsync(RunAuthorizationRequest, CancellationToken)”

Decides whether the run described by request may start.

ValueTask<RunAuthorizationResult> AuthorizeRunAsync(RunAuthorizationRequest request, CancellationToken cancellationToken = default)

request RunAuthorizationRequest

The run being authorized.

cancellationToken CancellationToken

The cancellation token.

ValueTask<RunAuthorizationResult>

The decision.

AuthorizeSessionAsync(SessionAuthorizationRequest, CancellationToken)

Section titled “ AuthorizeSessionAsync(SessionAuthorizationRequest, CancellationToken)”

Decides whether the session access described by request may proceed.

ValueTask<RunAuthorizationResult> AuthorizeSessionAsync(SessionAuthorizationRequest request, CancellationToken cancellationToken = default)

request SessionAuthorizationRequest

The session access being authorized.

cancellationToken CancellationToken

The cancellation token.

ValueTask<RunAuthorizationResult>

The decision.