Skip to content

core: add sovereign cloud support via AzureAd configuration#507

Merged
corinagum merged 8 commits into
mainfrom
cg/core-sovereign
May 20, 2026
Merged

core: add sovereign cloud support via AzureAd configuration#507
corinagum merged 8 commits into
mainfrom
cg/core-sovereign

Conversation

@corinagum
Copy link
Copy Markdown
Collaborator

@corinagum corinagum commented May 13, 2026

Summary

Enables sovereign cloud (GCC-High, DoD, China) Bot Framework token validation by making the three cloud-dependent values in JwtExtensions configurable, mirroring the IConfiguration pattern already used by UserTokenClient.

Three new properties on BotConfig, populated by BotConfig.Resolve:

Property Config key Default Purpose
EntraInstance AzureAd:Instance https://login.microsoftonline.com/ Entra login instance for validating Entra-issued tokens (standard Microsoft.Identity.Web key, stays in the AzureAd section)
OpenIdMetadataUrl BotFramework:OpenIdMetadataUrl https://login.botframework.com/v1/.well-known/openid-configuration Bot Framework OIDC metadata endpoint used to fetch signing keys for inbound BF tokens
BotTokenIssuer BotFramework:BotTokenIssuer https://api.botframework.com Expected iss claim on inbound Bot Framework tokens

Bot-Framework-specific keys live in a separate BotFramework configuration section so they are not conflated with MSAL's AzureAd section. Instance remains under AzureAd because it is a standard Microsoft.Identity.Web key.

Each override is validated as an absolute URI in BotConfig.Resolve; malformed values throw InvalidOperationException with the section:key path so misconfiguration fails fast instead of deferring to a runtime JWT failure.

JwtExtensions.AddTeamsJwtBearer resolves BotConfig once and uses these values in both the issuer-signing-key resolver (ResolveSigningAuthority) and the issuer validator (ValidateTeamsIssuer). When IConfiguration is not registered (manual-credentials callers on a plain ServiceCollection), all three fall back to the public-cloud defaults.

Example sovereign config (USGov)

{
  "AzureAd": {
    "TenantId": "...",
    "ClientId": "...",
    "Instance": "https://login.microsoftonline.us/"
  },
  "BotFramework": {
    "OpenIdMetadataUrl": "https://login.botframework.azure.us/v1/.well-known/openid-configuration",
    "BotTokenIssuer": "https://api.botframework.us"
  }
}

Out of scope

  • Authority fallback for EntraInstance is intentionally not implemented. Instance is the canonical Microsoft.Identity.Web field and matches the keys Microsoft Learn uses for sovereign cloud configuration.
  • BotConfig source formats other than the AzureAd schema are not extended in this PR.
  • v1.0 sts.windows.net issuer validation remains accepted (covers commercial v1.0 tokens).

Test plan

  • dotnet build core/core.slnx clean (0 warnings, 0 errors)
  • dotnet test core/test/Microsoft.Teams.Core.UnitTests (130 passed)
  • Default values returned when overrides absent
  • Configured values used for sovereign US Gov and China clouds
  • Sovereign BF tokens route to the configured OpenIdMetadataUrl for key resolution
  • Malformed URI in any of the three keys throws InvalidOperationException with the section:key path
  • BotFramework section is read independently of the AzureAd section name
  • Manual AddBotAuthentication(clientId, tenantId) overload does not require IConfiguration

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings May 13, 2026 23:47
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

This PR adds configurable cloud-specific authentication endpoints to core bot authentication so sovereign cloud deployments can override Bot Framework OIDC metadata and Entra login instance values via configuration.

Changes:

  • Adds OpenIdMetadataUrl and EntraInstance to BotConfig, sourced from the configured auth section with public-cloud defaults.
  • Updates JWT signing-key discovery to use these resolved values instead of hardcoded constants.
  • Adds unit tests covering default and overridden BotConfig values.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
core/src/Microsoft.Teams.Core/Hosting/BotConfig.cs Adds configurable sovereign-cloud endpoint properties and resolves them from configuration.
core/src/Microsoft.Teams.Core/Hosting/JwtExtensions.cs Uses resolved Bot Framework and Entra endpoint values during JWT signing-key discovery.
core/test/Microsoft.Teams.Core.UnitTests/Hosting/BotConfigTests.cs Adds tests for default and configured endpoint resolution.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread core/src/Microsoft.Teams.Core/Hosting/JwtExtensions.cs Outdated
Comment thread core/src/Microsoft.Teams.Core/Hosting/JwtExtensions.cs Outdated
Comment thread core/src/Microsoft.Teams.Core/Hosting/JwtExtensions.cs Outdated
Copilot AI review requested due to automatic review settings May 14, 2026 00:26
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

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comment thread core/src/Microsoft.Teams.Core/Hosting/JwtExtensions.cs Outdated
Comment thread core/src/Microsoft.Teams.Core/Hosting/BotConfig.cs
corinagum and others added 4 commits May 14, 2026 15:11
Adds sovereign cloud support for inbound JWT validation by reading two
URLs from the AzureAd configuration section (or whatever section name is
passed to AddBotAuthentication):

- AzureAd:OpenIdMetadataUrl: Bot Framework OIDC metadata endpoint used to
  fetch signing keys for inbound Bot Framework tokens. Defaults to the
  public-cloud endpoint when not set.
- AzureAd:Instance: Entra login instance used when validating
  Entra-issued tokens. Standard Microsoft.Identity.Web key. Defaults to
  https://login.microsoftonline.com/ when not set.

Example sovereign appsettings.json:

  {
    "AzureAd": {
      "TenantId": "...",
      "ClientId": "...",
      "Instance": "https://login.microsoftonline.us/",
      "OpenIdMetadataUrl": "https://login.botframework.azure.us/v1/.well-known/openid-configuration"
    }
  }

The api.botframework.com issuer string stays hardcoded; it is constant
across clouds for Bot Framework tokens.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two fixes from PR review:

1. ValidateTeamsIssuer now derives the expected Entra v2.0 issuer from
   the configured EntraInstance instead of hardcoding the public-cloud
   URL. Without this, sovereign Entra tokens would fetch signing keys
   successfully but get rejected at issuer validation. The v1.0
   sts.windows.net issuer check stays in place (covers commercial v1.0
   tokens).

2. AddTeamsJwtBearer no longer requires IConfiguration to be registered.
   The manual-credentials overload AddBotAuthentication(clientId,
   tenantId, ...) is documented to work on a plain ServiceCollection
   without configuration, but the previous version called
   BotConfig.Resolve unconditionally, which throws when IConfiguration
   is absent. Now falls back to public-cloud defaults when
   IConfiguration is not registered.

Test coverage:
- ValidateTeamsIssuer: BF issuer, public Entra v2.0, sovereign Entra v2.0,
  rejection of public issuer when sovereign Instance configured, v1.0
  sts.windows.net path.
- AddBotAuthentication manual overload does not throw when no
  IConfiguration is registered.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a third sovereign-aware property on BotConfig populated from the
AzureAd section, matching the existing OpenIdMetadataUrl and Instance
pattern:

- AzureAd:BotTokenIssuer: expected issuer claim on inbound Bot Framework
  tokens. Defaults to https://api.botframework.com. For sovereign clouds
  set to e.g. "https://api.botframework.us" for USGov.

Replaces the two hardcoded "https://api.botframework.com" literals in
JwtExtensions (the OIDC routing branch and the early-accept inside
ValidateTeamsIssuer) with the configured value.

Without this, sovereign Bot Framework tokens (which carry a per-cloud
issuer claim) would fall through to the Entra validation branch, fail
issuer validation, and be rejected even with OpenIdMetadataUrl pointing
at the correct sovereign OIDC URL.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous commit threaded BotTokenIssuer through the issuer validator
but missed the matching branch in IssuerSigningKeyResolver, which still
compared against a hardcoded "https://api.botframework.com" literal.
Effect: sovereign BF tokens (iss="https://api.botframework.us" etc.)
were routed to the Entra metadata URL for signing-key resolution and
failed signature validation before the issuer validator could accept
them.

Extracts the authority-picking logic into ResolveSigningAuthority for
unit testability, and routes any token whose iss matches the configured
BotTokenIssuer to BotOIDC.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 14, 2026 22:14
@corinagum corinagum force-pushed the cg/core-sovereign branch from ff2e752 to 61861cb Compare May 14, 2026 22:14
@rido-min rido-min added the CORE label May 18, 2026
Copy link
Copy Markdown
Member

@rido-min rido-min left a comment

Choose a reason for hiding this comment

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

added some comments.

Not sure the new fields should live inside the AzureAd section... maybe in a new section ?

@corinagum does this PR includes the changes in #483?

Comment thread core/src/Microsoft.Teams.Core/Hosting/BotConfig.cs Outdated
Comment thread core/src/Microsoft.Teams.Core/Hosting/BotConfig.cs Outdated
Comment thread core/src/Microsoft.Teams.Core/Hosting/BotConfig.cs Outdated
Comment thread core/src/Microsoft.Teams.Core/Hosting/JwtExtensions.cs
@corinagum
Copy link
Copy Markdown
Collaborator Author

corinagum commented May 19, 2026

@rido-min regarding your question:

@corinagum does this PR includes the changes in #483?

No, #483 is a separate v1 issuer fix; v1 issuer exists across every cloud. This PR doesn't port those changes because sts.windows.net/{effectiveTeanant}/ branch was already in core ValidateTeamsIssuer.

corinagum and others added 2 commits May 19, 2026 12:13
- OpenIdMetadataUrl and BotTokenIssuer now read from a new BotFramework
  section, separating Bot-Framework-specific keys from MSAL's AzureAd.
- AzureAd:Instance still reads from the AzureAd section (MSAL convention).
- Validate that each override is an absolute URI; throw
  InvalidOperationException with the section:key path on malformed input.
- Consolidate CA1056 suppressions in GlobalSuppressions.cs.
- Wrap the Bot Framework issuer-equality branch in braces.
Comment thread core/src/Microsoft.Teams.Core/Hosting/BotConfig.cs Outdated
Comment thread core/src/Microsoft.Teams.Core/Hosting/BotConfig.cs Outdated
@corinagum corinagum merged commit 5893bff into main May 20, 2026
10 checks passed
@corinagum corinagum deleted the cg/core-sovereign branch May 20, 2026 22:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants