diff --git a/sdk/keyvault/Azure.Security.KeyVault.Administration/CHANGELOG.md b/sdk/keyvault/Azure.Security.KeyVault.Administration/CHANGELOG.md
index 14c21894fcee2..033c93428657b 100644
--- a/sdk/keyvault/Azure.Security.KeyVault.Administration/CHANGELOG.md
+++ b/sdk/keyvault/Azure.Security.KeyVault.Administration/CHANGELOG.md
@@ -4,6 +4,8 @@
### Features Added
+- Support multi-tenant authentication against Managed HSM when using Azure.Identity 1.5.0 or newer. ([#18359](https://github.com/Azure/azure-sdk-for-net/issues/18359))
+
### Breaking Changes
### Bugs Fixed
diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/CHANGELOG.md b/sdk/keyvault/Azure.Security.KeyVault.Certificates/CHANGELOG.md
index 9633f201915f7..9bb77ebc84b19 100644
--- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/CHANGELOG.md
+++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/CHANGELOG.md
@@ -5,6 +5,7 @@
### Features Added
- Added `KeyVaultCertificateIdentifier.TryCreate` to parse certificate URIs without throwing an exception when invalid. ([#23146](https://github.com/Azure/azure-sdk-for-net/issues/23146))
+- Support multi-tenant authentication against Key Vault and Managed HSM when using Azure.Identity 1.5.0 or newer. ([#18359](https://github.com/Azure/azure-sdk-for-net/issues/18359))
### Breaking Changes
diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/CHANGELOG.md b/sdk/keyvault/Azure.Security.KeyVault.Keys/CHANGELOG.md
index 790aa48aa514c..2903013934174 100644
--- a/sdk/keyvault/Azure.Security.KeyVault.Keys/CHANGELOG.md
+++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/CHANGELOG.md
@@ -8,6 +8,7 @@
- Added `KeyClient.GetCryptographyClient` to get a `CryptographyClient` that uses the same options, policies, and pipeline as the `KeyClient` that created it. ([#23786](https://github.com/Azure/azure-sdk-for-net/issues/23786))
- Added `KeyRotationPolicy` class and new methods including `KeyClient.GetKeyRotationPolicy`, `KeyClient.RotateKey`, and `KeyClient.UpdateKeyRotationPolicy`.
- Added `KeyVaultKeyIdentifier.TryCreate` to parse key URIs without throwing an exception when invalid. ([#23146](https://github.com/Azure/azure-sdk-for-net/issues/23146))
+- Support multi-tenant authentication against Key Vault and Managed HSM when using Azure.Identity 1.5.0 or newer. ([#18359](https://github.com/Azure/azure-sdk-for-net/issues/18359))
### Breaking Changes
diff --git a/sdk/keyvault/Azure.Security.KeyVault.Secrets/CHANGELOG.md b/sdk/keyvault/Azure.Security.KeyVault.Secrets/CHANGELOG.md
index 87d6f21a2bde2..db7510178ec5c 100644
--- a/sdk/keyvault/Azure.Security.KeyVault.Secrets/CHANGELOG.md
+++ b/sdk/keyvault/Azure.Security.KeyVault.Secrets/CHANGELOG.md
@@ -5,6 +5,7 @@
### Features Added
- Added `KeyVaultSecretIdentifier.TryCreate` to parse secret URIs without throwing an exception when invalid. ([#23146](https://github.com/Azure/azure-sdk-for-net/issues/23146))
+- Support multi-tenant authentication against Key Vault and Managed HSM when using Azure.Identity 1.5.0 or newer. ([#18359](https://github.com/Azure/azure-sdk-for-net/issues/18359))
### Breaking Changes
diff --git a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/Azure.Security.KeyVault.Secrets.csproj b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/Azure.Security.KeyVault.Secrets.csproj
index f02625d98045c..13679cd23cf54 100644
--- a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/Azure.Security.KeyVault.Secrets.csproj
+++ b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/Azure.Security.KeyVault.Secrets.csproj
@@ -25,6 +25,11 @@
+
+
+
+
+
diff --git a/sdk/keyvault/Azure.Security.KeyVault.Secrets/tests/SecretClientLiveTests.cs b/sdk/keyvault/Azure.Security.KeyVault.Secrets/tests/SecretClientLiveTests.cs
index 7796faf5a1f34..67a33c342af28 100644
--- a/sdk/keyvault/Azure.Security.KeyVault.Secrets/tests/SecretClientLiveTests.cs
+++ b/sdk/keyvault/Azure.Security.KeyVault.Secrets/tests/SecretClientLiveTests.cs
@@ -4,10 +4,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Text;
using System.Threading.Tasks;
using NUnit.Framework;
+using Azure.Core;
using Azure.Core.TestFramework;
-using System.Text;
using NUnit.Framework.Constraints;
namespace Azure.Security.KeyVault.Secrets.Tests
@@ -460,5 +461,19 @@ public async Task GetDeletedSecrets()
AssertSecretPropertiesEqual(deletedSecret.Properties, returnedSecret.Properties, compareId: false);
}
}
+
+ [Test]
+ public async Task AuthenticateCrossTenant()
+ {
+ TokenCredential credential = GetCredential(Recording.Random.NewGuid().ToString());
+ SecretClient client = GetClient(credential);
+
+ string secretName = Recording.GenerateId();
+
+ Response response = await client.SetSecretAsync(secretName, "secret");
+ RegisterForCleanup(secretName);
+
+ Assert.AreEqual(200, response.GetRawResponse().Status);
+ }
}
}
diff --git a/sdk/keyvault/Azure.Security.KeyVault.Secrets/tests/SecretsTestBase.cs b/sdk/keyvault/Azure.Security.KeyVault.Secrets/tests/SecretsTestBase.cs
index 5a4d94edc5b47..2f4f8722157dc 100644
--- a/sdk/keyvault/Azure.Security.KeyVault.Secrets/tests/SecretsTestBase.cs
+++ b/sdk/keyvault/Azure.Security.KeyVault.Secrets/tests/SecretsTestBase.cs
@@ -5,7 +5,9 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
+using Azure.Core;
using Azure.Core.TestFramework;
+using Azure.Identity;
using Azure.Security.KeyVault.Tests;
using NUnit.Framework;
@@ -41,12 +43,12 @@ protected SecretsTestBase(bool isAsync, SecretClientOptions.ServiceVersion servi
_serviceVersion = serviceVersion;
}
- internal SecretClient GetClient()
+ internal SecretClient GetClient(TokenCredential credential = default)
{
return InstrumentClient
(new SecretClient(
new Uri(TestEnvironment.KeyVaultUrl),
- TestEnvironment.Credential,
+ credential ?? TestEnvironment.Credential,
InstrumentClientOptions(
new SecretClientOptions(_serviceVersion)
{
@@ -256,5 +258,23 @@ protected Task WaitForSecret(string name)
return TestRetryHelper.RetryAsync(async () => await Client.GetSecretAsync(name).ConfigureAwait(false), delay: PollingInterval);
}
}
+
+ protected TokenCredential GetCredential(string tenantId)
+ {
+ if (Mode == RecordedTestMode.Playback)
+ {
+ return new MockCredential();
+ }
+
+ return new ClientSecretCredential(
+ tenantId ?? TestEnvironment.TenantId,
+ TestEnvironment.ClientId,
+ TestEnvironment.ClientSecret,
+ new ClientSecretCredentialOptions()
+ {
+ AuthorityHost = new Uri(TestEnvironment.AuthorityHostUrl),
+ }
+ );
+ }
}
}
diff --git a/sdk/keyvault/Azure.Security.KeyVault.Secrets/tests/SessionRecords/SecretClientLiveTests/AuthenticateCrossTenant.json b/sdk/keyvault/Azure.Security.KeyVault.Secrets/tests/SessionRecords/SecretClientLiveTests/AuthenticateCrossTenant.json
new file mode 100644
index 0000000000000..729025efd1731
--- /dev/null
+++ b/sdk/keyvault/Azure.Security.KeyVault.Secrets/tests/SessionRecords/SecretClientLiveTests/AuthenticateCrossTenant.json
@@ -0,0 +1,98 @@
+{
+ "Entries": [
+ {
+ "RequestUri": "https://heathskeyvault.vault.azure.net/secrets/1976825381?api-version=7.3-preview",
+ "RequestMethod": "PUT",
+ "RequestHeaders": {
+ "Accept": "application/json",
+ "Content-Type": "application/json",
+ "traceparent": "00-4ccf898de50d824686a88d142ae3949b-942c9ef9409b7f48-00",
+ "User-Agent": [
+ "azsdk-net-Security.KeyVault.Secrets/4.3.0-alpha.20211006.1",
+ "(.NET 5.0.10; Microsoft Windows 10.0.22000)"
+ ],
+ "x-ms-client-request-id": "3ffda9192896abb52c556d714ba4f0f5",
+ "x-ms-return-client-request-id": "true"
+ },
+ "RequestBody": null,
+ "StatusCode": 401,
+ "ResponseHeaders": {
+ "Cache-Control": "no-cache",
+ "Content-Length": "97",
+ "Content-Type": "application/json; charset=utf-8",
+ "Date": "Thu, 07 Oct 2021 00:58:44 GMT",
+ "Expires": "-1",
+ "Pragma": "no-cache",
+ "Strict-Transport-Security": "max-age=31536000;includeSubDomains",
+ "WWW-Authenticate": "Bearer authorization=\u0022https://login.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47\u0022, resource=\u0022https://vault.azure.net\u0022",
+ "X-Content-Type-Options": "nosniff",
+ "x-ms-client-request-id": "3ffda9192896abb52c556d714ba4f0f5",
+ "x-ms-keyvault-network-info": "conn_type=Ipv4;addr=67.171.12.239;act_addr_fam=InterNetwork;",
+ "x-ms-keyvault-region": "westus2",
+ "x-ms-keyvault-service-version": "1.9.132.3",
+ "x-ms-request-id": "22a7e4d6-d334-4870-b379-ef40c2c01df4",
+ "X-Powered-By": "ASP.NET"
+ },
+ "ResponseBody": {
+ "error": {
+ "code": "Unauthorized",
+ "message": "AKV10000: Request is missing a Bearer or PoP token."
+ }
+ }
+ },
+ {
+ "RequestUri": "https://heathskeyvault.vault.azure.net/secrets/1976825381?api-version=7.3-preview",
+ "RequestMethod": "PUT",
+ "RequestHeaders": {
+ "Accept": "application/json",
+ "Authorization": "Sanitized",
+ "Content-Length": "18",
+ "Content-Type": "application/json",
+ "traceparent": "00-4ccf898de50d824686a88d142ae3949b-942c9ef9409b7f48-00",
+ "User-Agent": [
+ "azsdk-net-Security.KeyVault.Secrets/4.3.0-alpha.20211006.1",
+ "(.NET 5.0.10; Microsoft Windows 10.0.22000)"
+ ],
+ "x-ms-client-request-id": "3ffda9192896abb52c556d714ba4f0f5",
+ "x-ms-return-client-request-id": "true"
+ },
+ "RequestBody": {
+ "value": "secret"
+ },
+ "StatusCode": 200,
+ "ResponseHeaders": {
+ "Cache-Control": "no-cache",
+ "Content-Length": "258",
+ "Content-Type": "application/json; charset=utf-8",
+ "Date": "Thu, 07 Oct 2021 00:58:47 GMT",
+ "Expires": "-1",
+ "Pragma": "no-cache",
+ "Strict-Transport-Security": "max-age=31536000;includeSubDomains",
+ "X-Content-Type-Options": "nosniff",
+ "x-ms-client-request-id": "3ffda9192896abb52c556d714ba4f0f5",
+ "x-ms-keyvault-network-info": "conn_type=Ipv4;addr=67.171.12.239;act_addr_fam=InterNetwork;",
+ "x-ms-keyvault-region": "westus2",
+ "x-ms-keyvault-service-version": "1.9.132.3",
+ "x-ms-request-id": "585c4eeb-c666-4760-9150-9a2d31a97bbf",
+ "X-Powered-By": "ASP.NET"
+ },
+ "ResponseBody": {
+ "value": "secret",
+ "id": "https://heathskeyvault.vault.azure.net/secrets/1976825381/7f9f6a57d81f4997aa71365da5923a7c",
+ "attributes": {
+ "enabled": true,
+ "created": 1633568327,
+ "updated": 1633568327,
+ "recoveryLevel": "CustomizedRecoverable\u002BPurgeable",
+ "recoverableDays": 7
+ }
+ }
+ }
+ ],
+ "Variables": {
+ "AZURE_AUTHORITY_HOST": "https://login.microsoftonline.com/",
+ "AZURE_KEYVAULT_URL": "https://heathskeyvault.vault.azure.net/",
+ "CLIENT_ID": "f9ab11db-b032-44b3-af0a-44713541cc40",
+ "RandomSeed": "914486277"
+ }
+}
\ No newline at end of file
diff --git a/sdk/keyvault/Azure.Security.KeyVault.Secrets/tests/SessionRecords/SecretClientLiveTests/AuthenticateCrossTenantAsync.json b/sdk/keyvault/Azure.Security.KeyVault.Secrets/tests/SessionRecords/SecretClientLiveTests/AuthenticateCrossTenantAsync.json
new file mode 100644
index 0000000000000..9bf761bbb3fb0
--- /dev/null
+++ b/sdk/keyvault/Azure.Security.KeyVault.Secrets/tests/SessionRecords/SecretClientLiveTests/AuthenticateCrossTenantAsync.json
@@ -0,0 +1,98 @@
+{
+ "Entries": [
+ {
+ "RequestUri": "https://heathskeyvault.vault.azure.net/secrets/1361693861?api-version=7.3-preview",
+ "RequestMethod": "PUT",
+ "RequestHeaders": {
+ "Accept": "application/json",
+ "Content-Type": "application/json",
+ "traceparent": "00-68a7ae7bababd243a4177c95c8a17a84-adf102b50b9e4549-00",
+ "User-Agent": [
+ "azsdk-net-Security.KeyVault.Secrets/4.3.0-alpha.20211006.1",
+ "(.NET 5.0.10; Microsoft Windows 10.0.22000)"
+ ],
+ "x-ms-client-request-id": "e871c4ae36cf4c9ba7851ef729df4ae2",
+ "x-ms-return-client-request-id": "true"
+ },
+ "RequestBody": null,
+ "StatusCode": 401,
+ "ResponseHeaders": {
+ "Cache-Control": "no-cache",
+ "Content-Length": "97",
+ "Content-Type": "application/json; charset=utf-8",
+ "Date": "Thu, 07 Oct 2021 00:58:49 GMT",
+ "Expires": "-1",
+ "Pragma": "no-cache",
+ "Strict-Transport-Security": "max-age=31536000;includeSubDomains",
+ "WWW-Authenticate": "Bearer authorization=\u0022https://login.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47\u0022, resource=\u0022https://vault.azure.net\u0022",
+ "X-Content-Type-Options": "nosniff",
+ "x-ms-client-request-id": "e871c4ae36cf4c9ba7851ef729df4ae2",
+ "x-ms-keyvault-network-info": "conn_type=Ipv4;addr=67.171.12.239;act_addr_fam=InterNetwork;",
+ "x-ms-keyvault-region": "westus2",
+ "x-ms-keyvault-service-version": "1.9.132.3",
+ "x-ms-request-id": "459be702-5cea-4e8f-bf20-40a0ba829746",
+ "X-Powered-By": "ASP.NET"
+ },
+ "ResponseBody": {
+ "error": {
+ "code": "Unauthorized",
+ "message": "AKV10000: Request is missing a Bearer or PoP token."
+ }
+ }
+ },
+ {
+ "RequestUri": "https://heathskeyvault.vault.azure.net/secrets/1361693861?api-version=7.3-preview",
+ "RequestMethod": "PUT",
+ "RequestHeaders": {
+ "Accept": "application/json",
+ "Authorization": "Sanitized",
+ "Content-Length": "18",
+ "Content-Type": "application/json",
+ "traceparent": "00-68a7ae7bababd243a4177c95c8a17a84-adf102b50b9e4549-00",
+ "User-Agent": [
+ "azsdk-net-Security.KeyVault.Secrets/4.3.0-alpha.20211006.1",
+ "(.NET 5.0.10; Microsoft Windows 10.0.22000)"
+ ],
+ "x-ms-client-request-id": "e871c4ae36cf4c9ba7851ef729df4ae2",
+ "x-ms-return-client-request-id": "true"
+ },
+ "RequestBody": {
+ "value": "secret"
+ },
+ "StatusCode": 200,
+ "ResponseHeaders": {
+ "Cache-Control": "no-cache",
+ "Content-Length": "258",
+ "Content-Type": "application/json; charset=utf-8",
+ "Date": "Thu, 07 Oct 2021 00:58:50 GMT",
+ "Expires": "-1",
+ "Pragma": "no-cache",
+ "Strict-Transport-Security": "max-age=31536000;includeSubDomains",
+ "X-Content-Type-Options": "nosniff",
+ "x-ms-client-request-id": "e871c4ae36cf4c9ba7851ef729df4ae2",
+ "x-ms-keyvault-network-info": "conn_type=Ipv4;addr=67.171.12.239;act_addr_fam=InterNetwork;",
+ "x-ms-keyvault-region": "westus2",
+ "x-ms-keyvault-service-version": "1.9.132.3",
+ "x-ms-request-id": "4413f9d6-d556-452e-b579-ca7a96b7c477",
+ "X-Powered-By": "ASP.NET"
+ },
+ "ResponseBody": {
+ "value": "secret",
+ "id": "https://heathskeyvault.vault.azure.net/secrets/1361693861/2bbf7785b5444a07850ed04d2bbd19b1",
+ "attributes": {
+ "enabled": true,
+ "created": 1633568330,
+ "updated": 1633568330,
+ "recoveryLevel": "CustomizedRecoverable\u002BPurgeable",
+ "recoverableDays": 7
+ }
+ }
+ }
+ ],
+ "Variables": {
+ "AZURE_AUTHORITY_HOST": "https://login.microsoftonline.com/",
+ "AZURE_KEYVAULT_URL": "https://heathskeyvault.vault.azure.net/",
+ "CLIENT_ID": "f9ab11db-b032-44b3-af0a-44713541cc40",
+ "RandomSeed": "1322165825"
+ }
+}
\ No newline at end of file
diff --git a/sdk/keyvault/Azure.Security.KeyVault.Shared/src/ChallengeBasedAuthenticationPolicy.cs b/sdk/keyvault/Azure.Security.KeyVault.Shared/src/ChallengeBasedAuthenticationPolicy.cs
index 59e584e22081d..3bfdb385e8a87 100644
--- a/sdk/keyvault/Azure.Security.KeyVault.Shared/src/ChallengeBasedAuthenticationPolicy.cs
+++ b/sdk/keyvault/Azure.Security.KeyVault.Shared/src/ChallengeBasedAuthenticationPolicy.cs
@@ -12,9 +12,13 @@ namespace Azure.Security.KeyVault
{
internal class ChallengeBasedAuthenticationPolicy : BearerTokenAuthenticationPolicy
{
- private static ConcurrentDictionary _scopeCache = new ConcurrentDictionary();
private const string KeyVaultStashedContentKey = "KeyVaultContent";
- private AuthorityScope _scope;
+
+ ///
+ /// Challenges are cached using the Key Vault or Managed HSM endpoint URI authority as the key.
+ ///
+ private static readonly ConcurrentDictionary s_challengeCache = new();
+ private ChallengeParameters _challenge;
public ChallengeBasedAuthenticationPolicy(TokenCredential credential) : base(credential, Array.Empty())
{ }
@@ -34,17 +38,17 @@ private async ValueTask AuthorizeRequestInternal(HttpMessage message, bool async
throw new InvalidOperationException("Bearer token authentication is not permitted for non TLS protected (https) endpoints.");
}
- // If this policy doesn't have _scope cached try to get it from the static challenge cache.
- if (_scope == null)
+ // If this policy doesn't have challenge parameters cached try to get it from the static challenge cache.
+ if (_challenge == null)
{
string authority = GetRequestAuthority(message.Request);
- _scopeCache.TryGetValue(authority, out _scope);
+ s_challengeCache.TryGetValue(authority, out _challenge);
}
- if (_scope != null)
+ if (_challenge != null)
{
- // We fetched the scope from the cache, but we have not initialized the Scopes in the base yet.
- var context = new TokenRequestContext(_scope.Scopes, message.Request.ClientRequestId);
+ // We fetched the challenge from the cache, but we have not initialized the Scopes in the base yet.
+ var context = new TokenRequestContext(_challenge.Scopes, parentRequestId: message.Request.ClientRequestId, tenantId: _challenge.TenantId);
if (async)
{
await AuthenticateAndAuthorizeRequestAsync(message, context).ConfigureAwait(false);
@@ -53,6 +57,7 @@ private async ValueTask AuthorizeRequestInternal(HttpMessage message, bool async
{
AuthenticateAndAuthorizeRequest(message, context);
}
+
return;
}
@@ -86,7 +91,7 @@ private async ValueTask AuthorizeRequestOnChallengeAsyncInternal(HttpMessa
string scope = AuthorizationChallengeParser.GetChallengeParameterFromResponse(message.Response, "Bearer", "resource");
if (scope != null)
{
- scope = scope + "/.default";
+ scope += "/.default";
}
else
{
@@ -95,18 +100,29 @@ private async ValueTask AuthorizeRequestOnChallengeAsyncInternal(HttpMessa
if (scope is null)
{
- if (_scopeCache.TryGetValue(authority, out _scope))
+ if (s_challengeCache.TryGetValue(authority, out _challenge))
{
return false;
}
}
else
{
- _scope = new AuthorityScope(authority, new string[] { scope });
- _scopeCache[authority] = _scope;
+ string authorization = AuthorizationChallengeParser.GetChallengeParameterFromResponse(message.Response, "Bearer", "authorization");
+ if (authorization is null)
+ {
+ authorization = AuthorizationChallengeParser.GetChallengeParameterFromResponse(message.Response, "Bearer", "authorization_uri");
+ }
+
+ if (!Uri.TryCreate(authorization, UriKind.Absolute, out Uri authorizationUri))
+ {
+ throw new UriFormatException($"The challenge authorization URI '{authorization}' is invalid.");
+ }
+
+ _challenge = new ChallengeParameters(authorizationUri, new string[] { scope });
+ s_challengeCache[authority] = _challenge;
}
- var context = new TokenRequestContext(_scope.Scopes, message.Request.ClientRequestId);
+ var context = new TokenRequestContext(_challenge.Scopes, parentRequestId: message.Request.ClientRequestId, tenantId: _challenge.TenantId);
if (async)
{
await AuthenticateAndAuthorizeRequestAsync(message, context).ConfigureAwait(false);
@@ -115,36 +131,53 @@ private async ValueTask AuthorizeRequestOnChallengeAsyncInternal(HttpMessa
{
AuthenticateAndAuthorizeRequest(message, context);
}
+
return true;
}
- internal class AuthorityScope
+ internal class ChallengeParameters
{
- internal AuthorityScope(string authrority, string[] scopes)
+ internal ChallengeParameters(Uri authorizationUri, string[] scopes)
{
- Authority = authrority;
+ AuthorizationUri = authorizationUri;
+ TenantId = authorizationUri.Segments[1].Trim('/');
Scopes = scopes;
}
- public string Authority { get; }
+ ///
+ /// Gets the "authorization" or "authorization_uri" parameter from the challenge response.
+ ///
+ public Uri AuthorizationUri { get; }
+ ///
+ /// Gets the "resource" or "scope" parameter from the challenge response. This should end with "/.default".
+ ///
public string[] Scopes { get; }
+
+ ///
+ /// Gets the tenant ID from .
+ ///
+ public string TenantId { get; }
}
internal static void ClearCache()
{
- _scopeCache.Clear();
+ s_challengeCache.Clear();
}
+ ///
+ /// Gets the host name and port of the Key Vault or Managed HSM endpoint.
+ ///
+ ///
+ ///
private static string GetRequestAuthority(Request request)
{
Uri uri = request.Uri.ToUri();
string authority = uri.Authority;
-
if (!authority.Contains(":") && uri.Port > 0)
{
- // Append port for complete authority
+ // Append port for complete authority.
authority = uri.Authority + ":" + uri.Port.ToString(CultureInfo.InvariantCulture);
}