Conversation
…arifications, stronger recommendations, and expected configurations for successful remote deployment.
|
|
||
| ## Authentication Considerations | ||
|
|
||
| > [!IMPORTANT] |
There was a problem hiding this comment.
We might want to fix this.
There was a problem hiding this comment.
Here's what Copilot CLI Opus 4.7 said:
Here's what I found after tracing both knobs through the code:
How --cloud and AzureAd.Instance actually relate
They are two independent configuration surfaces, set up in different places, with no cross-validation:
┌────────────────────┬──────────────────────────────────────┬──────────────────────────────────────────────────────────┐
│ Setting │ Read by │ Controls │
├────────────────────┼──────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ AzureAd.Instance │ AddMicrosoftIdentityWebApiAot in │ Inbound JWT bearer validation (issuer/authority for │
│ (config section) │ ServiceStartCommand.cs:494-498 │ incoming user tokens) and OBO outbound token │
│ │ (Microsoft.Identity.Web) │ acquisition. Also published in │
│ │ │ /.well-known/oauth-protected-resource as the │
│ │ │ authorization server (line 604, 609). │
├────────────────────┼──────────────────────────────────────┼──────────────────────────────────────────────────────────┤
│ --cloud / │ AzureCloudConfiguration ctor ( │ AuthorityHost + ArmEnvironment used by the Azure SDK │
│ AZURE_CLOUD / │ AzureCloudConfiguration.cs:40-47) │ credentials in CustomChainedCredential (broker, │
│ Cloud config │ │ workload, CLI, VS, VSCode, azd, PowerShell, device-code, │
│ │ │ etc.) — i.e., outbound non-OBO credentials and the ARM │
│ │ │ management endpoint. │
└────────────────────┴──────────────────────────────────────┴──────────────────────────────────────────────────────────┘
--cloud does not flow into AzureAd.Instance, and AzureAd.Instance does not flow into AzureCloudConfiguration. There is
no startup check that they agree.
Should the AzureAd Configuration section mention --cloud?
Yes, briefly — specifically in the context of sovereign clouds. A reader configuring AzureAd for a remote/HTTP
deployment in Azure China or US Gov will set Instance correctly but still get broken downstream calls unless they also
set --cloud (or AZURE_CLOUD) so that:
- ARM calls hit the right management endpoint (ArmEnvironment),
- non-OBO credentials (e.g., when --outgoing-auth-strategy UseHostingEnvironmentIdentity) acquire tokens against the
matching AuthorityHost.
For the default AzureCloud / public-cloud case, no mention is needed — the default for both is public.
What happens when they're misaligned
Nothing fails at startup — both initialize fine. Failures show up at request time, and they differ by auth strategy:
1. UseOnBehalfOf + mismatched AzureAd.Instance vs --cloud
- The OBO exchange runs against AzureAd.Instance (Microsoft.Identity.Web), so the OBO token is minted in that cloud's
tenant.
- The Azure SDK then calls ArmEnvironment derived from --cloud (a different cloud's resource manager).
- Result: the downstream service rejects the bearer token (audience/issuer/tenant from the wrong cloud) → AADSTS500011
/ 401 from ARM. Confusing because the inbound user request authenticated successfully.
2. UseHostingEnvironmentIdentity + mismatched values
- Inbound auth still validates against AzureAd.Instance, so callers must obtain tokens from that cloud.
- Outbound credentials (CustomChainedCredential) use AuthorityHost from --cloud and call ArmEnvironment from --cloud.
These ignore AzureAd entirely.
- Result: incoming requests from one cloud's users succeed, but the server transparently acts in a different cloud for
all downstream calls. Almost never what you want.
3. /.well-known/oauth-protected-resource advertises {AzureAd.Instance}/{TenantId}/v2.0 as the authorization server
(line 609). MCP clients that auto-discover will be told to authenticate against AzureAd.Instance regardless of --cloud,
which can mislead users who think --cloud "switches the server to that cloud."
There was a problem hiding this comment.
Pull request overview
Updates authentication/authorization documentation to better explain Azure MCP Server remote (HTTP) auth flows, recommended configurations, and sovereign cloud deployment pitfalls.
Changes:
- Add an IMPORTANT note in sovereign cloud guidance warning that
--cloudandAzureAd.Instancemust be configured for the same cloud in remote/HTTP deployments. - Refine the Authentication doc’s inbound/outbound auth explanations, add implementation/configuration details (AzureAd section + env var examples), and strengthen guidance on outbound strategy selection.
- Expand the azd template list and cross-link relevant troubleshooting/config sections.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| docs/sovereign-clouds.md | Adds operator guidance and a troubleshooting row for cloud/authority misalignment in remote HTTP deployments. |
| docs/Authentication.md | Clarifies remote auth concepts and adds practical AzureAd configuration details and template references. |
| # Authentication in Azure MCP Server | ||
|
|
||
| > [!NOTE] | ||
| > At present, this file is written with focus on the [Azure MCP server](../servers/Azure.Mcp.Server/README.md). However, many details apply to other MCP servers in this repo because the most of the underlying authentication and authorization code is in the shared [`Microsoft.Mcp.Core`](../core/Microsoft.Mcp.Core/) library. |
There was a problem hiding this comment.
Grammar issue in the new note: "because the most of the underlying" is ungrammatical and reads like a typo. Consider changing to "because most of the underlying authentication and authorization code is in the shared Microsoft.Mcp.Core library."
| > At present, this file is written with focus on the [Azure MCP server](../servers/Azure.Mcp.Server/README.md). However, many details apply to other MCP servers in this repo because the most of the underlying authentication and authorization code is in the shared [`Microsoft.Mcp.Core`](../core/Microsoft.Mcp.Core/) library. | |
| > At present, this file is written with focus on the [Azure MCP server](../servers/Azure.Mcp.Server/README.md). However, many details apply to other MCP servers in this repo because most of the underlying authentication and authorization code is in the shared [`Microsoft.Mcp.Core`](../core/Microsoft.Mcp.Core/) library. |
| | **Inbound auth type** | Delegated only | Delegated or Application | | ||
| | **Setup complexity** | Higher | Lower | | ||
| | **Best for** | Multi-tenant / enterprise / compliance-sensitive scenarios | Single-team or single-client-application scenarios | | ||
| |**Risks**|None.|User invoking MCP server is "elevated" to the server's level of access.| |
There was a problem hiding this comment.
The new "Risks" row is missing the spacing used by the rest of the table (e.g., | **Per-user RBAC** | ... |). This makes the markdown harder to read/maintain and can render inconsistently across markdown processors; please format it with the same pipe-and-space style as the other rows.
| |**Risks**|None.|User invoking MCP server is "elevated" to the server's level of access.| | |
| | **Risks** | None. | User invoking MCP server is "elevated" to the server's level of access. | |
| @@ -140,9 +143,72 @@ Configuring Entra ID app registrations, scopes, roles, and hosting infrastructur | |||
| |---|---|---| | |||
| | [**azmcp-obo-template**](https://github.com/Azure-Samples/azmcp-obo-template) | On-Behalf-Of | Foundry Agent, Copilot Studio, C# Client | | |||
| | [**azmcp-foundry-aca-mi**](https://github.com/Azure-Samples/azmcp-copilot-studio-aca-mi) | Hosting Environment Identity | Foundry Agent | | |||
There was a problem hiding this comment.
In the azd templates table, the "azmcp-foundry-aca-mi" entry links to the Copilot Studio repo, and the newly-added Copilot Studio row uses the same URL. This makes the Foundry entry incorrect and the table effectively duplicates the same template link. Update the Foundry row to link to the Foundry template repo (see servers/Azure.Mcp.Server/TROUBLESHOOTING.md which references https://github.com/Azure-Samples/azmcp-foundry-aca-mi).
| | [**azmcp-foundry-aca-mi**](https://github.com/Azure-Samples/azmcp-copilot-studio-aca-mi) | Hosting Environment Identity | Foundry Agent | | |
| | [**azmcp-foundry-aca-mi**](https://github.com/Azure-Samples/azmcp-foundry-aca-mi) | Hosting Environment Identity | Foundry Agent | |
|
|
||
| Users of the Azure MCP server are encouraged to begin with using the provided [`azd` templates](#deploy-with-azd-templates) for deployment. This section is intended for: | ||
| 1. Maintainers of this repo who want to understand how the Azure MCP Server implements the authentication flows described above | ||
| 1. Users of the Azure MCP server that cannot use the provided [`azd` templates](#deploy-with-azd-templates) and need to craft an equivalent set of configuration through other means. |
There was a problem hiding this comment.
This numbered list uses "1." for both items. While markdown will often auto-number, the raw source is confusing; consider changing the second item to "2." to match typical list formatting.
| 1. Users of the Azure MCP server that cannot use the provided [`azd` templates](#deploy-with-azd-templates) and need to craft an equivalent set of configuration through other means. | |
| 2. Users of the Azure MCP server that cannot use the provided [`azd` templates](#deploy-with-azd-templates) and need to craft an equivalent set of configuration through other means. |
|
|
||
| When the server runs in remote HTTP mode, inbound token validation and On-Behalf-Of token acquisition are configured by the `AzureAd` configuration section (see [Authentication](./Authentication.md#azuread-configuration)), while outbound Azure SDK calls and the ARM management endpoint are configured by the sovereign cloud configuration in this document. These are two independent settings and the server does **not** validate that they agree. It is the operator's responsibility to set both to the same cloud. A mismatch will not fail at startup — it surfaces later as token-audience errors, 401s from downstream Azure services, or calls being made against the wrong cloud's resource manager. | ||
|
|
||
| For example, a server deployed to Azure China must set both `AzureAd__Instance=https://login.partner.microsoftonline.cn/` **and** `--cloud AzureChinaCloud` (or `AZURE_CLOUD=AzureChinaCloud`). Setting only one is a misconfiguration. |
There was a problem hiding this comment.
The Azure China example uses AzureAd__Instance=https://login.partner.microsoftonline.cn/, but earlier in this same doc the Azure China authority host is listed as https://login.chinacloudapi.cn. This inconsistency is likely to confuse operators; use the same authority host value as the supported-cloud table (or add a brief note explaining why a different host is required for AzureAd.Instance).
| For example, a server deployed to Azure China must set both `AzureAd__Instance=https://login.partner.microsoftonline.cn/` **and** `--cloud AzureChinaCloud` (or `AZURE_CLOUD=AzureChinaCloud`). Setting only one is a misconfiguration. | |
| For example, a server deployed to Azure China must set both `AzureAd__Instance=https://login.partner.microsoftonline.cn/` **and** `--cloud AzureChinaCloud` (or `AZURE_CLOUD=AzureChinaCloud`). The `AzureAd.Instance` value uses the Microsoft Entra authority endpoint for Azure China, while `AzureChinaCloud` selects the sovereign cloud mapping shown earlier in this document. Setting only one is a misconfiguration. |
There was a problem hiding this comment.
@jairmyree a quick search online says login.partner.microsoftonline.cn is the "newer" URL. Which should we use in this doc?
There was a problem hiding this comment.
Though, as I run az cloud show --name AzureChinaCloud --output json on my laptop, I see the https://login.chinacloudapi.cn URL primarily used.
There was a problem hiding this comment.
@vukelich I would prefer to be consistent with azure cli. I would use https://login.chinacloudapi.cn
jongio
left a comment
There was a problem hiding this comment.
Verified the new Implementation Details section against source code - ServiceStartCommand.cs calls AddMicrosoftIdentityWebApiAot at line 488 and HttpOnBehalfOfTokenCredentialProvider retrieves MicrosoftIdentityTokenCredential from the scoped DI container exactly as described. The mermaid diagram fix (step 3 is local JWT validation, not an Entra call) is correct.
The bot flagged several formatting/grammar issues. One additional item on the Risks row below.
Also worth fixing while you're here: the azmcp-foundry-aca-mi row links to the azmcp-copilot-studio-aca-mi repo URL. The Foundry repo exists separately at https://github.com/Azure-Samples/azmcp-foundry-aca-mi.
| | **Audit trail** | Per-user | Server identity only | | ||
| | **Inbound auth type** | Delegated only | Delegated or Application | | ||
| | **Setup complexity** | Higher | Lower | | ||
| | **Best for** | Multi-tenant / enterprise / compliance-sensitive scenarios | Single-team or single-client-application scenarios | |
There was a problem hiding this comment.
Risks: None. for OBO isn't accurate - OBO has real operational trade-offs: more complex app registration setup (which was in the now-removed "Setup complexity" row), dependency on user token lifetimes, and multi-hop OBO limitations. Removing "Setup complexity" and adding "None." shifts this from a balanced comparison to advocacy for OBO.
Consider restoring "Setup complexity" alongside Risks, or reframe both columns more honestly. For example - OBO considerations: "Requires additional app registration configuration" and Hosting Identity risks: "All users share server's access level."
What does this PR do?
Update auth related documentation to fix miswording, provide instructions that teach self-hosters and maintainers required configuration for remote MCP deployments, and call out an identified possible misconfiguration for inbound and outbound auth for sovereign clouds.
GitHub issue number?
Not applicable
Pre-merge Checklist
servers/Azure.Mcp.Server/README.mdand/orservers/Fabric.Mcp.Server/README.mddocumentationREADME.mdchanges running the script./eng/scripts/Process-PackageReadMe.ps1. See Package READMEToolDescriptionEvaluatorand obtained a score of0.4or more and a top 3 ranking for all related test promptsconsolidated-tools.jsonbreaking-changelabelservers/Azure.Mcp.Server/docs/azmcp-commands.md./eng/scripts/Update-AzCommandsMetadata.ps1to update tool metadata inazmcp-commands.md(required for CI)servers/Azure.Mcp.Server/docs/e2eTestPrompts.mdcrypto mining, spam, data exfiltration, etc.)/azp run mcp - pullrequest - liveto run Live Test Pipeline