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"
- 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
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