microsoft / microsoft/agent-framework

.NET: [Bug]: Hosted agent toolbox consent link is bound to the container's managed identity, so sign-in fails with "Code <id> not found"

Open
#8,379 0 comments 0 reactions 0 assignees View on GitHub
.NET needs-maintainer-triage triage
Dominant language
Python
Stars
13.6k
Forks
2.3k
Avg merge
2d 45m
Merged PRs (30d)
358

Description

### Description

When a hosted agent uses a pre-registered toolbox with a per-user OAuth MCP connection, the consent link in its `oauth_consent_request` can't be completed. Signing in ends on `/nextgen/closeMe` with `Something went wrong while authenticating. Error: Code not found.`

The callback's `userCtx` has `userId` set to the project's system-assigned managed identity, not the user who called the agent. The container authenticates to the toolbox proxy with that identity (the MSAL log shows the `system_assigned_managed_identity` token cache), so the proxy creates the consent session for it. The inbound request did carry the user: the container log shows `HasUserId=True HasCallId=True`.

The call id should reach the proxy too. `AgentFrameworkResponseHandler` re-applies `HostedCallContext.CallId` before toolbox egress, and `FoundryToolboxBearerTokenHandler` forwards it as `x-agent-foundry-call-id` "so the toolbox proxy can resolve the server-side caller context".

Credential lookup does follow the calling user. When the same user calls the toolbox MCP endpoint directly with their own token, `tools/list` returns `-32006` with a link bound to them, and sign-in succeeds. The agent's next request then lists and calls the toolbox tools with that user's credential. A principal that never signed in still gets `CONSENT_REQUIRED`. Only the link produced on the hosted path is bound to the wrong identity.

Steps to reproduce:

1. Create a project connection with custom OAuth2 (an Entra app with a delegated scope) to a remote MCP server, and a toolbox that references it.
2. Host an agent that calls `AddFoundryToolboxes(new DefaultAzureCredential(), "")` on the Responses protocol 2.0.0, and deploy it.
3. Call the agent endpoint with a user's Entra token. The response is `incomplete` with an `oauth_consent_request`.
4. Open the consent link and sign in as that user.

Expected: the sign-in is stored for the calling user, and resending the request runs the tool.

Actual: sign-in fails with the error above, and every later request returns a new link bound to the managed identity.

### Code Sample

```markdown
var credential = new DefaultAzureCredential();
var agent = new AIProjectClient(projectEndpoint, credential)
.AsAIAgent(model: deploymentName, instructions: instructions, name: "orchestrator");

var builder = AgentHost.CreateBuilder(args);
builder.Services.AddFoundryResponses(agent);
builder.Services.AddFoundryToolboxes(credential, toolboxName);
builder.RegisterProtocol("responses", endpoints => endpoints.MapFoundryResponses());
builder.Build().Run();
```

### Error Messages / Stack Traces

```markdown
Something went wrong while authenticating. Please close this window and try again.
Error: Code not found.
closeMe userCtx (decoded): {"userId":"","tenantId":"","connectionName":"..","projectUrl":""}
```

### Package Versions

Microsoft.Agents.AI.Foundry.Hosting: 1.21.0-preview.260911.1, Azure.AI.Projects: 3.0.0-beta.2, Azure.Identity: 1.21.0

### .NET Version

.NET 10.0 (Linux x64 container, runtime 10.0.12)

### Additional Context

Follows #6562, which made the consent link visible (fixed in #6718). `ToolboxOAuthConsentHostedAgentTests` is skipped on main, and it only asserts that a consent link is returned, so completing consent isn't covered.

Our workaround is taking the consent link from a direct toolbox `tools/list` call made with the user's token.

Contributor guide

Open the contributing guide

Research direction

Start with AgentFrameworkResponseHandler and FoundryToolboxBearerTokenHandler, tracing HostedCallContext.CallId and the caller identity through hosted-agent toolbox egress and the proxy context. Review the skipped ToolboxOAuthConsentHostedAgentTests and add coverage for completing consent, not only returning a link. Done means the consent session is bound to the calling user and the resubmitted request can list and call the toolbox tools.

Written by the indexing model from the issue text.

Assessment

Tech stack
azure, csharp
Domain
api, authentication, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.