diff --git a/Libraries/Microsoft.Teams.Api/Auth/ClientCredentials.cs b/Libraries/Microsoft.Teams.Api/Auth/ClientCredentials.cs
index e198eb73..07d90593 100644
--- a/Libraries/Microsoft.Teams.Api/Auth/ClientCredentials.cs
+++ b/Libraries/Microsoft.Teams.Api/Auth/ClientCredentials.cs
@@ -10,7 +10,12 @@ public class ClientCredentials : IHttpCredentials
public string ClientId { get; set; }
public string ClientSecret { get; set; }
public string? TenantId { get; set; }
- public CloudEnvironment Cloud { get; set; } = CloudEnvironment.Public;
+
+ ///
+ /// The Entra ID login endpoint, following the Microsoft Identity Web configuration schema.
+ /// Override this for sovereign clouds (e.g. "https://login.microsoftonline.us" for US Gov).
+ ///
+ public string Instance { get; set; } = "https://login.microsoftonline.com";
public ClientCredentials(string clientId, string clientSecret)
{
@@ -27,9 +32,10 @@ public ClientCredentials(string clientId, string clientSecret, string? tenantId)
public async Task Resolve(IHttpClient client, string[] scopes, CancellationToken cancellationToken = default)
{
- var tenantId = TenantId ?? Cloud.LoginTenant;
+ var tenantId = TenantId ?? "botframework.com";
+ var instance = Instance.TrimEnd('/');
var request = HttpRequest.Post(
- $"{Cloud.LoginEndpoint}/{tenantId}/oauth2/v2.0/token"
+ $"{instance}/{tenantId}/oauth2/v2.0/token"
);
request.Headers.Add("Content-Type", ["application/x-www-form-urlencoded"]);
diff --git a/Libraries/Microsoft.Teams.Api/Auth/CloudEnvironment.cs b/Libraries/Microsoft.Teams.Api/Auth/CloudEnvironment.cs
deleted file mode 100644
index 7910727a..00000000
--- a/Libraries/Microsoft.Teams.Api/Auth/CloudEnvironment.cs
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-namespace Microsoft.Teams.Api.Auth;
-
-///
-/// Bundles all cloud-specific service endpoints for a given Azure environment.
-/// Use predefined instances (, , , )
-/// or construct a custom one.
-///
-public class CloudEnvironment
-{
- ///
- /// The Azure AD login endpoint (e.g. "https://login.microsoftonline.com").
- ///
- public string LoginEndpoint { get; }
-
- ///
- /// The default multi-tenant login tenant (e.g. "botframework.com").
- ///
- public string LoginTenant { get; }
-
- ///
- /// The Bot Framework OAuth scope (e.g. "https://api.botframework.com/.default").
- ///
- public string BotScope { get; }
-
- ///
- /// The Bot Framework token service base URL (e.g. "https://token.botframework.com").
- ///
- public string TokenServiceUrl { get; }
-
- ///
- /// The OpenID metadata URL for token validation (e.g. "https://login.botframework.com/v1/.well-known/openidconfiguration").
- ///
- public string OpenIdMetadataUrl { get; }
-
- ///
- /// The token issuer for Bot Framework tokens (e.g. "https://api.botframework.com").
- ///
- public string TokenIssuer { get; }
-
- ///
- /// The channel service URL. Empty for public cloud; set for sovereign clouds
- /// (e.g. "https://botframework.azure.us").
- ///
- public string ChannelService { get; }
-
- ///
- /// The OAuth redirect URL (e.g. "https://token.botframework.com/.auth/web/redirect").
- ///
- public string OAuthRedirectUrl { get; }
-
- public CloudEnvironment(
- string loginEndpoint,
- string loginTenant,
- string botScope,
- string tokenServiceUrl,
- string openIdMetadataUrl,
- string tokenIssuer,
- string channelService,
- string oauthRedirectUrl)
- {
- LoginEndpoint = loginEndpoint;
- LoginTenant = loginTenant;
- BotScope = botScope;
- TokenServiceUrl = tokenServiceUrl;
- OpenIdMetadataUrl = openIdMetadataUrl;
- TokenIssuer = tokenIssuer;
- ChannelService = channelService;
- OAuthRedirectUrl = oauthRedirectUrl;
- }
-
- ///
- /// Microsoft public (commercial) cloud.
- ///
- public static readonly CloudEnvironment Public = new(
- loginEndpoint: "https://login.microsoftonline.com",
- loginTenant: "botframework.com",
- botScope: "https://api.botframework.com/.default",
- tokenServiceUrl: "https://token.botframework.com",
- openIdMetadataUrl: "https://login.botframework.com/v1/.well-known/openidconfiguration",
- tokenIssuer: "https://api.botframework.com",
- channelService: "",
- oauthRedirectUrl: "https://token.botframework.com/.auth/web/redirect"
- );
-
- ///
- /// US Government Community Cloud High (GCCH).
- ///
- public static readonly CloudEnvironment USGov = new(
- loginEndpoint: "https://login.microsoftonline.us",
- loginTenant: "MicrosoftServices.onmicrosoft.us",
- botScope: "https://api.botframework.us/.default",
- tokenServiceUrl: "https://tokengcch.botframework.azure.us",
- openIdMetadataUrl: "https://login.botframework.azure.us/v1/.well-known/openidconfiguration",
- tokenIssuer: "https://api.botframework.us",
- channelService: "https://botframework.azure.us",
- oauthRedirectUrl: "https://tokengcch.botframework.azure.us/.auth/web/redirect"
- );
-
- ///
- /// US Government Department of Defense (DoD).
- ///
- public static readonly CloudEnvironment USGovDoD = new(
- loginEndpoint: "https://login.microsoftonline.us",
- loginTenant: "MicrosoftServices.onmicrosoft.us",
- botScope: "https://api.botframework.us/.default",
- tokenServiceUrl: "https://apiDoD.botframework.azure.us",
- openIdMetadataUrl: "https://login.botframework.azure.us/v1/.well-known/openidconfiguration",
- tokenIssuer: "https://api.botframework.us",
- channelService: "https://botframework.azure.us",
- oauthRedirectUrl: "https://apiDoD.botframework.azure.us/.auth/web/redirect"
- );
-
- ///
- /// China cloud (21Vianet).
- ///
- public static readonly CloudEnvironment China = new(
- loginEndpoint: "https://login.partner.microsoftonline.cn",
- loginTenant: "microsoftservices.partner.onmschina.cn",
- botScope: "https://api.botframework.azure.cn/.default",
- tokenServiceUrl: "https://token.botframework.azure.cn",
- openIdMetadataUrl: "https://login.botframework.azure.cn/v1/.well-known/openidconfiguration",
- tokenIssuer: "https://api.botframework.azure.cn",
- channelService: "https://botframework.azure.cn",
- oauthRedirectUrl: "https://token.botframework.azure.cn/.auth/web/redirect"
- );
-
- ///
- /// Creates a new by applying non-null overrides on top of this instance.
- /// Returns the same instance if all overrides are null (no allocation).
- ///
- public CloudEnvironment WithOverrides(
- string? loginEndpoint = null,
- string? loginTenant = null,
- string? botScope = null,
- string? tokenServiceUrl = null,
- string? openIdMetadataUrl = null,
- string? tokenIssuer = null,
- string? channelService = null,
- string? oauthRedirectUrl = null)
- {
- if (loginEndpoint is null && loginTenant is null && botScope is null &&
- tokenServiceUrl is null && openIdMetadataUrl is null && tokenIssuer is null &&
- channelService is null && oauthRedirectUrl is null)
- {
- return this;
- }
-
- return new CloudEnvironment(
- loginEndpoint ?? LoginEndpoint,
- loginTenant ?? LoginTenant,
- botScope ?? BotScope,
- tokenServiceUrl ?? TokenServiceUrl,
- openIdMetadataUrl ?? OpenIdMetadataUrl,
- tokenIssuer ?? TokenIssuer,
- channelService ?? ChannelService,
- oauthRedirectUrl ?? OAuthRedirectUrl
- );
- }
-
- ///
- /// Resolves a cloud environment name (case-insensitive) to its corresponding instance.
- /// Valid names: "Public", "USGov", "USGovDoD", "China".
- ///
- public static CloudEnvironment FromName(string name) => name.ToLowerInvariant() switch
- {
- "public" => Public,
- "usgov" => USGov,
- "usgovdod" => USGovDoD,
- "china" => China,
- _ => throw new ArgumentException($"Unknown cloud environment: '{name}'. Valid values are: Public, USGov, USGovDoD, China.", nameof(name))
- };
-}
diff --git a/Libraries/Microsoft.Teams.Api/Clients/BotSignInClient.cs b/Libraries/Microsoft.Teams.Api/Clients/BotSignInClient.cs
index cf1e1f8f..52217361 100644
--- a/Libraries/Microsoft.Teams.Api/Clients/BotSignInClient.cs
+++ b/Libraries/Microsoft.Teams.Api/Clients/BotSignInClient.cs
@@ -7,8 +7,6 @@ namespace Microsoft.Teams.Api.Clients;
public class BotSignInClient : Client
{
- public string TokenServiceUrl { get; set; } = "https://token.botframework.com";
-
public BotSignInClient() : base()
{
@@ -33,7 +31,7 @@ public async Task GetUrlAsync(GetUrlRequest request)
{
var query = QueryString.Serialize(request);
var req = HttpRequest.Get(
- $"{TokenServiceUrl}/api/botsignin/GetSignInUrl?{query}"
+ $"https://token.botframework.com/api/botsignin/GetSignInUrl?{query}"
);
var res = await _http.SendAsync(req, _cancellationToken);
@@ -44,7 +42,7 @@ public async Task GetUrlAsync(GetUrlRequest request)
{
var query = QueryString.Serialize(request);
var req = HttpRequest.Get(
- $"{TokenServiceUrl}/api/botsignin/GetSignInResource?{query}"
+ $"https://token.botframework.com/api/botsignin/GetSignInResource?{query}"
);
var res = await _http.SendAsync(req, _cancellationToken);
diff --git a/Libraries/Microsoft.Teams.Api/Clients/BotTokenClient.cs b/Libraries/Microsoft.Teams.Api/Clients/BotTokenClient.cs
index 505144ba..8255d89c 100644
--- a/Libraries/Microsoft.Teams.Api/Clients/BotTokenClient.cs
+++ b/Libraries/Microsoft.Teams.Api/Clients/BotTokenClient.cs
@@ -7,9 +7,8 @@ namespace Microsoft.Teams.Api.Clients;
public class BotTokenClient : Client
{
- public static readonly string DefaultBotScope = "https://api.botframework.com/.default";
+ public static readonly string BotScope = "https://api.botframework.com/.default";
public static readonly string GraphScope = "https://graph.microsoft.com/.default";
- public string BotScope { get; set; } = DefaultBotScope;
public BotTokenClient() : this(default)
{
diff --git a/Libraries/Microsoft.Teams.Api/Clients/UserTokenClient.cs b/Libraries/Microsoft.Teams.Api/Clients/UserTokenClient.cs
index e2642629..cf264d6a 100644
--- a/Libraries/Microsoft.Teams.Api/Clients/UserTokenClient.cs
+++ b/Libraries/Microsoft.Teams.Api/Clients/UserTokenClient.cs
@@ -10,8 +10,6 @@ namespace Microsoft.Teams.Api.Clients;
public class UserTokenClient : Client
{
- public string TokenServiceUrl { get; set; } = "https://token.botframework.com";
-
private readonly JsonSerializerOptions _jsonSerializerOptions = new()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
@@ -40,7 +38,7 @@ public UserTokenClient(IHttpClientFactory factory, CancellationToken cancellatio
public async Task GetAsync(GetTokenRequest request)
{
var query = QueryString.Serialize(request);
- var req = HttpRequest.Get($"{TokenServiceUrl}/api/usertoken/GetToken?{query}");
+ var req = HttpRequest.Get($"https://token.botframework.com/api/usertoken/GetToken?{query}");
var res = await _http.SendAsync(req, _cancellationToken);
return res.Body;
}
@@ -48,7 +46,7 @@ public UserTokenClient(IHttpClientFactory factory, CancellationToken cancellatio
public async Task> GetAadAsync(GetAadTokenRequest request)
{
var query = QueryString.Serialize(request);
- var req = HttpRequest.Post($"{TokenServiceUrl}/api/usertoken/GetAadTokens?{query}", body: request);
+ var req = HttpRequest.Post($"https://token.botframework.com/api/usertoken/GetAadTokens?{query}", body: request);
var res = await _http.SendAsync>(req, _cancellationToken);
return res.Body;
}
@@ -56,7 +54,7 @@ public UserTokenClient(IHttpClientFactory factory, CancellationToken cancellatio
public async Task> GetStatusAsync(GetTokenStatusRequest request)
{
var query = QueryString.Serialize(request);
- var req = HttpRequest.Get($"{TokenServiceUrl}/api/usertoken/GetTokenStatus?{query}");
+ var req = HttpRequest.Get($"https://token.botframework.com/api/usertoken/GetTokenStatus?{query}");
var res = await _http.SendAsync>(req, _cancellationToken);
return res.Body;
}
@@ -64,7 +62,7 @@ public UserTokenClient(IHttpClientFactory factory, CancellationToken cancellatio
public async Task SignOutAsync(SignOutRequest request)
{
var query = QueryString.Serialize(request);
- var req = HttpRequest.Delete($"{TokenServiceUrl}/api/usertoken/SignOut?{query}");
+ var req = HttpRequest.Delete($"https://token.botframework.com/api/usertoken/SignOut?{query}");
await _http.SendAsync(req, _cancellationToken);
}
@@ -81,7 +79,7 @@ public async Task SignOutAsync(SignOutRequest request)
// This is required for the Bot Framework Token Service to process the request correctly.
var body = JsonSerializer.Serialize(request.GetBody(), _jsonSerializerOptions);
- var req = HttpRequest.Post($"{TokenServiceUrl}/api/usertoken/exchange?{query}", body);
+ var req = HttpRequest.Post($"https://token.botframework.com/api/usertoken/exchange?{query}", body);
req.Headers.Add("Content-Type", new List() { "application/json" });
var res = await _http.SendAsync(req, _cancellationToken);
diff --git a/Libraries/Microsoft.Teams.Apps/App.cs b/Libraries/Microsoft.Teams.Apps/App.cs
index ed2dd1f6..5f7768bc 100644
--- a/Libraries/Microsoft.Teams.Apps/App.cs
+++ b/Libraries/Microsoft.Teams.Apps/App.cs
@@ -51,8 +51,6 @@ internal string UserAgent
public App(AppOptions? options = null)
{
- var cloud = options?.Cloud ?? CloudEnvironment.Public;
-
Logger = options?.Logger ?? new ConsoleLogger();
Storage = options?.Storage ?? new LocalStorage