.NET: Python: Add optional OAuth consent origin allowlist to Foundry hosting - #8713
Conversation
Add a host-owned exact-origin allowlist for OAuth consent links in .NET and Python Foundry hosting. When configured, consent links from toolbox enumeration, per-call consent errors, and final output emission must match an allowed HTTPS origin. When omitted, the existing safe-HTTPS validation is unchanged.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Critical validation bypasses and unresolved policy-enforcement issues must be fixed before approval.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 2
Open (3)
What changed in this PR
Adds optional OAuth consent-origin allowlists to .NET and Python Foundry hosting.
Changes:
- Adds exact HTTPS-origin normalization and enforcement.
- Covers connection-time, tool-call, and final consent emission paths.
- Adds configuration documentation and tests.
| File | Review |
|---|---|
python/packages/foundry_hosting/tests/test_responses.py |
Adds Python allowlist and consent-path tests. |
python/packages/foundry_hosting/README.md |
Documents Python allowlist configuration. |
python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py |
Implements Python policy; IPv6 canonicalization and connect-time error clarity need correction. |
dotnet/tests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests/ToolboxConsentParserTests.cs |
Tests parsing and origin matching. |
dotnet/tests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests/OAuthConsentEmissionTests.cs |
Tests final emission enforcement. |
dotnet/tests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests/FoundryToolboxServiceTests.cs |
Tests configuration validation. |
dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/ToolboxConsentParser.cs |
Implements policy and enumeration checks; omitted allowlists bypass safe-URL validation, and policy rejection may be silently deferred. |
dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/ServiceCollectionExtensions.cs |
Updates configuration documentation. |
dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/FoundryToolboxService.cs |
Constructs and propagates the policy. |
dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/FoundryToolboxOptions.cs |
Adds the public allowlist option. |
dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/ConsentAwareMcpClientAIFunction.cs |
Adds mid-run enforcement, but matching and rejected outcomes lack unit coverage. |
dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/AgentFrameworkResponseHandler.cs |
Adds final emission enforcement; a null policy currently bypasses validation. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
MAF Automated Review — Iteration 1
Result: Findings reported
Scope: full PR (1 commit(s)): eb3def11f90e
Model: gpt-5.6-sol-fast
Overview
The PR adds host-owned exact-origin policies in both implementations, validates configuration eagerly, and rechecks links at each Python and .NET emission path. The immutable normalized sets, explicit null-versus-empty semantics, and Python end-to-end rejection tests provide strong guardrails. In .NET, however, rejection is routed through existing recovery paths that either keep a toolbox silently deferred or allow an ordinary tool exception to be handled by the model loop, so two configured-policy violations do not reliably fail the request.
Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
2 verified findings remained after source verification (2 medium) across 2 files. Details are attached to the affected lines below.
Affected areas: dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/ConsentAwareMcpClientAIFunction.cs, dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/ToolboxConsentParser.cs
Validate every surfaced OAuth consent link as a safe absolute HTTPS URL in .NET, matching Python, and apply the optional origin allowlist in AgentFrameworkResponseHandler before emission on all three consent paths. A rejected link now fails the response with a neutral message instead of being treated as a toolbox enumeration failure or returned to the model as a tool error. Clarify the Python connect-time error for links outside the configured allowlist.
There was a problem hiding this comment.
MAF Automated Review — Iteration 2
Result: No findings
Scope: 1 net-new commit(s): 645e4cf521af
Model: gpt-5.6-sol-fast
Overview
This revision centralizes .NET consent-link policy enforcement at the response boundary, applies safe-HTTPS validation even without an allowlist, and preserves retry behavior while turning rejected links into neutral response failures. The three .NET consent paths and the Python connect-time wording change are covered by focused tests, and no Critical, High, or Medium defect introduced by this incremental range remains supported.
Reviewed the supplied incremental change set across correctness, security/reliability, architecture, and failure behavior.
No publishable findings remained after source verification for this scope.
.NET: move Foundry toolbox resolution out of CreateAsync into ResolveToolboxToolsAsync, and route every consent-required response through a single EmitConsentRequiredResponse helper. Behavior is unchanged. New tests check that a rejected per-call consent link does not save the session and an allowed one does. Python: replace the _OAuthConsentLinkPolicy class with the _normalize_allowed_oauth_consent_origins and _is_allowed_oauth_consent_link functions.


Motivation & Context
Foundry hosting surfaces OAuth consent links that it receives from toolbox and MCP consent errors as
oauth_consent_requestoutput items. The links are not validated consistently across languages: Python requires an absolute HTTPS URL, while .NET only checks that the link is non-empty. Neither implementation lets a host restrict consent links to the authorization origins it expects.This PR aligns .NET with the existing Python URL-safety check and adds an optional, host-owned consent origin allowlist to both Foundry hosting packages. It does not hardcode any Foundry broker domain, because consent links can legitimately target Microsoft brokers as well as partner or custom OAuth providers.
Description & Review Guide
OAuthConsentLinkPolicyand publicFoundryToolboxOptions.AllowedOAuthConsentOrigins(IList<string>?).AgentFrameworkResponseHandlerapplies the policy before it surfaces a consent link on any of the three paths:-32006EmitOAuthConsentRequestalso applies the default policy when none is passed. Toolbox resolution is moved out ofCreateAsyncintoResolveToolboxToolsAsync, and all three paths emit through oneEmitConsentRequiredResponsehelper.ResponsesHostServer(allowed_oauth_consent_origins=...)keyword argument. It is applied to connect-time consent errors and to mid-runoauth_consent_requestcontent, on top of the existing safe-HTTPS check.http:orjavascript:are now rejected by default, matching Python.null/ omitted allowlist (the default): no origin restriction beyond that URL check.AgentFrameworkResponseHandlerand its coverage of all three consent paths.OAuthConsentLinkPolicy(.NET) and_normalize_allowed_oauth_consent_origins/_is_allowed_oauth_consent_link(Python).Related Issue
No tracking issue. This is a small hardening change for Foundry hosting consent links. I checked open PRs that touch the same files; none change consent-link handling.
Contribution Checklist
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.