Skip to content

Resolve the AI tool authorization caller through IUserAccessor - #117

Merged
MikeAlhayek merged 1 commit into
mainfrom
fix/hub-user-tool-authorization
Jul 28, 2026
Merged

Resolve the AI tool authorization caller through IUserAccessor#117
MikeAlhayek merged 1 commit into
mainfrom
fix/hub-user-tool-authorization

Conversation

@fsfh60

@fsfh60 fsfh60 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

What changed

AI tool authorization resolved the current caller from IHttpContextAccessor. HttpContext is documented as unreliable inside SignalR hub invocations — long-lived transports such as WebSockets, backplane-delivered invocations, and hosted SignalR services all run hub methods outside the request that opened the connection, so the accessor is frequently null there.

Because FunctionInvocationAICompletionServiceHandler passed that null principal straight to IAIToolAccessEvaluator, hosts that enforce permissions denied every tool for hub-based chat. The user simply got an answer with no tools and no explanation.

The fix

New IAIUserAccessor abstraction, backed by an AsyncLocal implementation that mirrors the HttpContextAccessor pattern:

  1. If a principal was pushed onto the current asynchronous flow, it wins.
  2. Otherwise fall back to HttpContext.User for ordinary HTTP requests.

AIChatHubCore and ChatInteractionHubBase now publish Context.User for the duration of every invocation via a private RunInScopeAsync wrapper. ExecuteInScopeAsync stays protected virtual, so existing host overrides keep working untouched.

Null policy

This is the one behavior change worth reviewing:

  • null means there is no caller at all — a background task, a workflow, a recipe. Authorization is skipped and every tool stays available, because trusted server-side code is not a security boundary.
  • An unauthenticated caller is not null. Hubs and HTTP requests always provide a ClaimsPrincipal with an unauthenticated identity, so the evaluator still runs and hosts can grant or deny tools for anonymous users as they see fit.

This makes the framework agree with the Orchard Core integration, which already used the "no principal means skip the check" rule.

Testing

  • 2559/2559 tests pass, zero warnings under -warnaserror
  • New AIUserAccessorTests covering the HTTP fallback, push/restore, null push, async continuation flow, and isolation between concurrent flows
  • New handler tests covering "no caller skips authorization" and "anonymous caller is still evaluated"

Docs

  • docs/core/tools.md — new IAIUserAccessor section documenting the resolution order and the null contract
  • docs/changelog/v1.0.0.md — changelog entry

@fsfh60
fsfh60 force-pushed the fix/hub-user-tool-authorization branch from 0f969e9 to d42431c Compare July 28, 2026 20:09
@fsfh60 fsfh60 changed the title Resolve the AI tool authorization caller through IAIUserAccessor Resolve the AI tool authorization caller through IUserAccessor Jul 28, 2026
Tool authorization resolved the current caller from IHttpContextAccessor,
which is unreliable inside SignalR hub invocations. Long-lived transports,
backplane-delivered invocations, and hosted SignalR services all run hub
methods outside the request that opened the connection, so HttpContext is
frequently null there and every tool was denied.

Introduce IAIUserAccessor, an ambient accessor that prefers a principal
pushed onto the current asynchronous flow and falls back to HttpContext.User
for ordinary requests. The AI chat and chat interaction hubs now publish
Context.User for the duration of every invocation.

A null principal now means there is no caller at all, such as a background
task, a workflow, or a recipe, so authorization is skipped and every tool
stays available. Unauthenticated callers remain non-null and are still
evaluated, so host-defined anonymous permissions keep applying.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@fsfh60
fsfh60 force-pushed the fix/hub-user-tool-authorization branch from d42431c to a99bc5d Compare July 28, 2026 20:24
@MikeAlhayek
MikeAlhayek merged commit e5b07bb into main Jul 28, 2026
10 checks passed
@MikeAlhayek
MikeAlhayek deleted the fix/hub-user-tool-authorization branch July 28, 2026 20:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants