Skip to content

Update authentication and authorization-related documentation with clarifications, stronger recommendations, and expected configurations for successful remote deployment.#2463

Open
vukelich wants to merge 1 commit intomainfrom
users/vukelich/authdoc

Conversation

@vukelich
Copy link
Copy Markdown
Member

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

  • Required for All PRs
    • Read contribution guidelines
    • PR title clearly describes the change
    • Commit history is clean with descriptive messages (cleanup guide)
    • Added comprehensive tests for new/modified functionality
    • Created a changelog entry if the change falls among the following: new feature, bug fix, UI/UX update, breaking change, or updated dependencies. Follow the changelog entry guide
  • For MCP tool changes:
    • One tool per PR: This PR adds or modifies only one MCP tool for faster review cycles
    • Updated servers/Azure.Mcp.Server/README.md and/or servers/Fabric.Mcp.Server/README.md documentation
    • Validate README.md changes running the script ./eng/scripts/Process-PackageReadMe.ps1. See Package README
    • For new or modified tool descriptions, ran ToolDescriptionEvaluator and obtained a score of 0.4 or more and a top 3 ranking for all related test prompts
    • For tools with new names, including new tools or renamed tools, update consolidated-tools.json
    • For renamed tools, follow the Tool Rename Checklist and tag the PR with the breaking-change label
    • For new tools associated with Azure services or publicly available tools/APIs/products, add URL to documentation in the PR description
  • Extra steps for Azure MCP Server tool changes:
    • Updated command list in servers/Azure.Mcp.Server/docs/azmcp-commands.md
    • Ran ./eng/scripts/Update-AzCommandsMetadata.ps1 to update tool metadata in azmcp-commands.md (required for CI)
    • Updated test prompts in servers/Azure.Mcp.Server/docs/e2eTestPrompts.md
    • 👉 For Community (non-Microsoft team member) PRs:
      • Security review: Reviewed code for security vulnerabilities, malicious code, or suspicious activities before running tests (crypto mining, spam, data exfiltration, etc.)
      • Manual tests run: added comment /azp run mcp - pullrequest - live to run Live Test Pipeline
…arifications, stronger recommendations, and expected configurations for successful remote deployment.
Comment thread docs/sovereign-clouds.md

## Authentication Considerations

> [!IMPORTANT]
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

FYI to @jairmyree

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We might want to fix this.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 --cloud and AzureAd.Instance must 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.
Comment thread docs/Authentication.md
# 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.
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

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

Suggested change
> 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.
Copilot uses AI. Check for mistakes.
Comment thread docs/Authentication.md
| **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.|
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
|**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. |
Copilot uses AI. Check for mistakes.
Comment thread docs/Authentication.md
@@ -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 |
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

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).

Suggested change
| [**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 |
Copilot uses AI. Check for mistakes.
Comment thread docs/Authentication.md

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.
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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.
Copilot uses AI. Check for mistakes.
Comment thread docs/sovereign-clouds.md

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.
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

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).

Suggested change
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.
Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@jairmyree a quick search online says login.partner.microsoftonline.cn is the "newer" URL. Which should we use in this doc?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Though, as I run az cloud show --name AzureChinaCloud --output json on my laptop, I see the https://login.chinacloudapi.cn URL primarily used.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@vukelich I would prefer to be consistent with azure cli. I would use https://login.chinacloudapi.cn

Copy link
Copy Markdown
Contributor

@jongio jongio left a comment

Choose a reason for hiding this comment

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

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.

Comment thread docs/Authentication.md
| **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 |
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.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

4 participants