Skip to content

.NET: Python: Add optional OAuth consent origin allowlist to Foundry hosting - #8713

Merged
Roger Barreto (rogerbarreto) merged 4 commits into
microsoft:mainfrom
rogerbarreto:i957r245-consent-link-validation
Sep 29, 2026
Merged

Roger Barreto (rogerbarreto) merged 4 commits into
microsoft:mainfrom
rogerbarreto:i957r245-consent-link-validation

Conversation

@rogerbarreto

@rogerbarreto Roger Barreto (rogerbarreto) commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Motivation & Context

Foundry hosting surfaces OAuth consent links that it receives from toolbox and MCP consent errors as oauth_consent_request output 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

  • What are the major changes?
    • .NET: new internal OAuthConsentLinkPolicy and public FoundryToolboxOptions.AllowedOAuthConsentOrigins (IList<string>?). AgentFrameworkResponseHandler applies the policy before it surfaces a consent link on any of the three paths:
      • a pre-registered toolbox that is waiting for consent
      • a per-request toolbox marker that needs consent
      • a tool call that returns JSON-RPC -32006
    • .NET: the toolbox consent parser and the MCP tool wrapper keep only capturing consent information. The policy is enforced in one place, so a rejected link fails the response cleanly. It is never treated as a toolbox enumeration failure or returned to the model as a tool error. EmitOAuthConsentRequest also applies the default policy when none is passed. Toolbox resolution is moved out of CreateAsync into ResolveToolboxToolsAsync, and all three paths emit through one EmitConsentRequiredResponse helper.
    • Python: new ResponsesHostServer(allowed_oauth_consent_origins=...) keyword argument. It is applied to connect-time consent errors and to mid-run oauth_consent_request content, on top of the existing safe-HTTPS check.
    • Both implementations normalize configured entries as exact HTTPS origins (scheme, host, and port) and reject entries that contain a path, query, or fragment. The runtime consent link keeps its path and query.
  • What is the impact of these changes?
    • Every surfaced consent link must be an absolute HTTPS URL with a valid host and no user info, whitespace, control characters, or backslashes. For .NET this is new: non-HTTPS links such as http: or javascript: are now rejected by default, matching Python.
    • null / omitted allowlist (the default): no origin restriction beyond that URL check.
    • A provided allowlist makes the origin check mandatory. An empty allowlist rejects every consent link.
    • A rejected link is not emitted. The response fails with a neutral message that does not include the link. A pre-registered toolbox stays pending, so a later request retries it.
    • Invalid allowlist entries fail fast when the host or toolbox service is constructed.
  • What do you want reviewers to focus on?
    • The new .NET default URL check and whether rejecting non-HTTPS consent links is acceptable as a non-breaking hardening.
    • The single enforcement point in AgentFrameworkResponseHandler and its coverage of all three consent paths.
    • Origin normalization parity between 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

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

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.
Copilot AI balanced review requested due to automatic review settings September 24, 2026 09:38
@agent-framework-automation agent-framework-automation Bot added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python .NET Usage: [Issues, PRs], Target: .Net labels Sep 24, 2026
@github-actions github-actions Bot changed the title Python: .NET: Add optional OAuth consent origin allowlist to Foundry hosting .NET: Python: .NET: Add optional OAuth consent origin allowlist to Foundry hosting Sep 24, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 High severity · 1 Low severity

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.

Comment thread dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/AgentFrameworkResponseHandler.cs Outdated
Comment thread dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/ToolboxConsentParser.cs Outdated
Comment thread python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/ToolboxConsentParser.cs Outdated
Comment thread dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/ConsentAwareMcpClientAIFunction.cs Outdated
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.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@rogerbarreto Roger Barreto (rogerbarreto) changed the title .NET: Python: .NET: Add optional OAuth consent origin allowlist to Foundry hosting .NET: Python: Add optional OAuth consent origin allowlist to Foundry hosting Sep 24, 2026
Comment thread dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/AgentFrameworkResponseHandler.cs Outdated
.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.
@rogerbarreto
Roger Barreto (rogerbarreto) added this pull request to the merge queue Sep 29, 2026
Merged via the queue into microsoft:main with commit 06e9b48 Sep 29, 2026
50 checks passed
@rogerbarreto
Roger Barreto (rogerbarreto) deleted the i957r245-consent-link-validation branch September 29, 2026 10:20

This branch was successfully deployed

2 active deployments
github-app-auth — 5f6771dc Deployed Sep 29, 2026 by rogerbarreto via add_label #23930
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs .NET Usage: [Issues, PRs], Target: .Net python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants