Skip to content

Add sovereign cloud support (GCCH, DoD, China)#352

Draft
rajan-chari wants to merge 3 commits intomainfrom
feature/sovereign-cloud-support
Draft

Add sovereign cloud support (GCCH, DoD, China)#352
rajan-chari wants to merge 3 commits intomainfrom
feature/sovereign-cloud-support

Conversation

@rajan-chari
Copy link
Contributor

@rajan-chari rajan-chari commented Feb 26, 2026

Summary

  • Introduces CloudEnvironment class (Microsoft.Teams.Api.Auth) bundling all cloud-specific service endpoints with predefined static instances: Public, USGov (GCCH), USGovDoD, and China (21Vianet)
  • Threads cloud environment through ClientCredentials, BotTokenClient, UserTokenClient, BotSignInClient, App, TeamsValidationSettings, and DI host builders so all previously hardcoded endpoints are configurable per cloud
  • Adds Cloud property to AppOptions (programmatic) and TeamsSettings (appsettings.json) for easy configuration
  • Adds individual endpoint override properties to TeamsSettings (e.g. LoginEndpoint, LoginTenant) for scenarios requiring per-endpoint customization — such as China single-tenant bots that need a tenant-specific login URL

Usage

Named cloud preset (appsettings.json):

{
  "Teams": {
    "ClientId": "...",
    "ClientSecret": "...",
    "Cloud": "USGov"
  }
}

Per-endpoint overrides (appsettings.json):

{
  "Teams": {
    "ClientId": "...",
    "ClientSecret": "...",
    "TenantId": "your-tenant",
    "Cloud": "China",
    "LoginTenant": "your-tenant-id"
  }
}

Programmatic:

var app = new App(new AppOptions
{
    Cloud = CloudEnvironment.USGov,
    Credentials = new ClientCredentials("id", "secret")
});

// Or with per-endpoint overrides:
var cloud = CloudEnvironment.China.WithOverrides(loginTenant: "my-tenant-id");

Valid cloud names: Public (default), USGov, USGovDoD, China

Override properties: LoginEndpoint, LoginTenant, BotScope, TokenServiceUrl, OpenIdMetadataUrl, TokenIssuer, ChannelService, OAuthRedirectUrl

Files changed

Action File
New Libraries/Microsoft.Teams.Api/Auth/CloudEnvironment.cs
New Tests/Microsoft.Teams.Api.Tests/Auth/CloudEnvironmentTests.cs
Modified Libraries/Microsoft.Teams.Api/Auth/ClientCredentials.cs
Modified Libraries/Microsoft.Teams.Api/Clients/BotTokenClient.cs
Modified Libraries/Microsoft.Teams.Api/Clients/UserTokenClient.cs
Modified Libraries/Microsoft.Teams.Api/Clients/BotSignInClient.cs
Modified Libraries/Microsoft.Teams.Apps/AppOptions.cs
Modified Libraries/Microsoft.Teams.Apps/App.cs
Modified TeamsSettings.cs (Extensions.Configuration) — endpoint override properties + ResolveCloud()
Modified HostApplicationBuilder.cs (Extensions.Hosting) — unified cloud resolution via ResolveCloud()
Modified HostApplicationBuilder.cs (Plugins.AspNetCore) — unified cloud resolution via ResolveCloud()
Modified TeamsValidationSettings.cs (Plugins.AspNetCore)

Test plan

  • dotnet build — 0 errors
  • dotnet test — all existing + new tests pass
  • CloudEnvironment.Public URLs match current hardcoded values (backward compat)
  • WithOverrides() with all nulls returns same instance (no allocation)
  • Individual and multiple endpoint overrides replace correct properties while preserving others
  • Verify CloudEnvironment.USGov URLs match BF GCCH docs
  • Verify CloudEnvironment.China URLs match BF China docs
  • Test sample app (Samples.Echo) starts correctly with no cloud config (defaults to Public)

🤖 Generated with Claude Code

rajan-chari and others added 2 commits February 26, 2026 10:59
- Add Startup section to CLAUDE.md with knowledge file loading, session
  context, private notes support, and quick-start commands
- Add Lessons Learned section to CLAUDE.md for persistent knowledge
- Create Claude-KB.md for cross-session learning
- Add session-context.md and *-private.md to .gitignore

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce CloudEnvironment class that bundles all cloud-specific service
endpoints, with predefined instances for Public, USGov (GCCH), USGovDoD,
and China (21Vianet). Thread the cloud environment through ClientCredentials,
token clients, validation settings, and DI host builders so that all
previously hardcoded endpoints are now configurable per cloud.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@rajan-chari
Copy link
Contributor Author

Manual Test Results

Tested locally by linking a scaffolded C# echo bot to this branch via ProjectReference (all 7 SDK library projects).

Environment

  • Windows 11, .NET 9, PowerShell 7
  • Bot registered in Azure with Teams channel + DevTunnel
  • SDK linked from local checkout of this branch

Tests

# Test Result
1 Negative test — invalid cloud name Pass. Set "Cloud": "Narnia" in appsettings.json under Teams. Bot crashed on startup with: ArgumentException: Unknown cloud environment: 'Narnia'. Valid values are: Public, USGov, USGovDoD, China. — clear, actionable error message.
2 Default behavior (no Cloud key) Pass. Without "Cloud" in config, bot starts normally and echoes messages in Teams. Defaults to CloudEnvironment.Public as expected.
3 Config-driven preset Pass. Setting "Cloud": "Public" in config starts the bot normally — CloudEnvironment.FromName resolves correctly.

Observation

The config path (appsettings.jsonTeamsSettings.Cloud) supports only the 4 named presets. Custom endpoints (arbitrary LoginEndpoint, TokenServiceUrl, etc.) are only available via the code path (new CloudEnvironment(...)). This seems fine for the target scenario but worth noting.

Tested with teams-sdk-dev tooling.

Allow users to override specific CloudEnvironment endpoints (e.g.
LoginEndpoint, LoginTenant) via appsettings.json, enabling scenarios
like China single-tenant bots that require a tenant-specific login URL.

- Add CloudEnvironment.WithOverrides() for layering nullable overrides
- Add 8 endpoint override properties + ResolveCloud() helper to TeamsSettings
- Unify cloud resolution across Apply(), AddTeamsCore(), and AddTeamsTokenAuthentication()
- Add WithOverrides unit tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@rajan-chari
Copy link
Contributor Author

Updated Test Results (after e0b56f6 — individual endpoint overrides)

Re-tested with the new WithOverrides / ResolveCloud / per-endpoint config support.

Tests

# Test Result
1 Invalid cloud name ("Cloud": "Narnia") Pass. Crashes with: Unknown cloud environment: 'Narnia'. Valid values are: Public, USGov, USGovDoD, China.
2 Default behavior (no Cloud key) Pass. Bot starts, echoes messages. Defaults to CloudEnvironment.Public.
3 Named preset ("Cloud": "Public") Pass. Starts normally.
4 Single endpoint override ("TokenServiceUrl": "https://bogus.example.com/token") Pass. Bot starts (override accepted at startup, used at runtime). Proves individual override is wired through ResolveCloudWithOverrides.
5 Preset + override ("Cloud": "USGov" + "LoginEndpoint": "https://custom-login.example.com") Pass. Bot starts with USGov as base and the custom LoginEndpoint applied on top.
6 Unit tests (CloudEnvironmentTests) Pass. All 21 tests pass, including 4 new WithOverrides tests (all-nulls, single, multiple, all overrides).

Design Notes

  • WithOverrides returns this when all overrides are null — nice zero-allocation fast path.
  • ResolveCloud chains cleanly: programmatic AppOptions.Cloud → config "Cloud" preset → Public fallback → per-endpoint overrides. Priority order is clear.
  • All 8 endpoint properties are individually overridable from appsettings.json under the Teams section.

@rajan-chari
Copy link
Contributor Author

URL Verification: CloudEnvironment presets vs Bot Framework docs

Cross-referenced all CloudEnvironment preset URLs against the official Bot Framework sovereign cloud docs.

USGov (GCCH)

Property PR value BF Docs value Match
LoginEndpoint https://login.microsoftonline.us https://login.microsoftonline.us Yes
LoginTenant MicrosoftServices.onmicrosoft.us MicrosoftServices.onmicrosoft.us Yes
BotScope https://api.botframework.us/.default https://api.botframework.us Yes*
TokenServiceUrl https://tokengcch.botframework.azure.us https://tokengcch.botframework.azure.us/ Yes*
OpenIdMetadataUrl https://login.botframework.azure.us/v1/.well-known/openidconfiguration https://login.botframework.azure.us/v1/.well-known/openidconfiguration Yes
TokenIssuer https://api.botframework.us https://api.botframework.us Yes
ChannelService https://botframework.azure.us https://botframework.azure.us Yes
OAuthRedirectUrl https://tokengcch.botframework.azure.us/.auth/web/redirect https://tokengcch.botframework.azure.us/.auth/web/redirect Yes

USGovDoD

Property PR value BF Docs value Match
LoginEndpoint https://login.microsoftonline.us https://login.microsoftonline.us Yes
LoginTenant MicrosoftServices.onmicrosoft.us MicrosoftServices.onmicrosoft.us Yes
BotScope https://api.botframework.us/.default https://api.botframework.us Yes*
TokenServiceUrl https://apiDoD.botframework.azure.us https://apiDoD.botframework.azure.us Yes
OpenIdMetadataUrl https://login.botframework.azure.us/v1/.well-known/openidconfiguration https://login.botframework.azure.us/v1/.well-known/openidconfiguration Yes
TokenIssuer https://api.botframework.us https://api.botframework.us Yes
ChannelService https://botframework.azure.us https://botframework.azure.us Yes
OAuthRedirectUrl https://apiDoD.botframework.azure.us/.auth/web/redirect (not in docs, consistent with OAuthUrl pattern) Yes

Notes on the two * items

  • BotScope /.default suffix: The PR uses https://api.botframework.us/.default — this is the standard MSAL/OAuth2 scope format. The docs show the resource identifier (https://api.botframework.us), but /.default is the correct programmatic form. This is intentional and correct.
  • TokenServiceUrl trailing slash: Docs show a trailing / on GCCH (https://tokengcch.botframework.azure.us/), PR omits it. Functionally equivalent.

All URLs match. Source: Deploy Bots to Azure Government and Office 365 GCC High

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant