From 6f38280f350a6193239678aa4f99f23b86925ba8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 01:09:36 +0000 Subject: [PATCH 1/6] Initial plan From 0079b3b47ea6e7db58069036ab004c7a0584c2f3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 01:17:25 +0000 Subject: [PATCH 2/6] Add UseDefaultProxyCredentials setting for authenticated proxy support Co-authored-by: philnach <19275540+philnach@users.noreply.github.com> --- ExampleConfigs.md | 21 +++++++++++++++ .../CosmosSinkSettingsTests.cs | 27 +++++++++++++++++++ .../CosmosSourceSettingsTests.cs | 27 +++++++++++++++++++ .../CosmosExtensionServices.cs | 7 ++++- .../CosmosSettingsBase.cs | 1 + Extensions/Cosmos/README.md | 13 ++++++--- 6 files changed, 91 insertions(+), 5 deletions(-) diff --git a/ExampleConfigs.md b/ExampleConfigs.md index 6007bd01..f74a931b 100644 --- a/ExampleConfigs.md +++ b/ExampleConfigs.md @@ -202,3 +202,24 @@ ] } ``` + +## JSON to Cosmos-NoSQL (Using Authenticated Proxy) + +```json +{ + "Source": "json", + "Sink": "cosmos-nosql", + "SourceSettings": { + "FilePath": "c:\\data\\sales-data.json" + }, + "SinkSettings": { + "ConnectionString": "AccountEndpoint=https://...", + "Database": "myDb", + "Container": "myContainer", + "PartitionKeyPath": "/id", + "WriteMode": "Insert", + "WebProxy": "http://yourproxy.server.com/", + "UseDefaultProxyCredentials": true + } +} +``` diff --git a/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension.UnitTests/CosmosSinkSettingsTests.cs b/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension.UnitTests/CosmosSinkSettingsTests.cs index 68cbeabc..251bf953 100644 --- a/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension.UnitTests/CosmosSinkSettingsTests.cs +++ b/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension.UnitTests/CosmosSinkSettingsTests.cs @@ -209,4 +209,31 @@ public void GetValidationErrors_WhenRbacWithoutInitClientEncryption_Succeeds() Assert.AreEqual(0, validationErrors.Count()); } + + [TestMethod] + public void UseDefaultProxyCredentials_DefaultsToFalse() + { + var settings = new CosmosSinkSettings + { + ConnectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=", + Database = "db", + Container = "container", + }; + + Assert.IsFalse(settings.UseDefaultProxyCredentials); + } + + [TestMethod] + public void UseDefaultProxyCredentials_CanBeSetToTrue() + { + var settings = new CosmosSinkSettings + { + ConnectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=", + Database = "db", + Container = "container", + UseDefaultProxyCredentials = true, + }; + + Assert.IsTrue(settings.UseDefaultProxyCredentials); + } } \ No newline at end of file diff --git a/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension.UnitTests/CosmosSourceSettingsTests.cs b/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension.UnitTests/CosmosSourceSettingsTests.cs index 3b894e1f..ccf52605 100644 --- a/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension.UnitTests/CosmosSourceSettingsTests.cs +++ b/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension.UnitTests/CosmosSourceSettingsTests.cs @@ -60,4 +60,31 @@ public void Validate_WithAccountEndpoint_Succeeds() settings.Validate(); } + + [TestMethod] + public void UseDefaultProxyCredentials_DefaultsToFalse() + { + var settings = new CosmosSourceSettings + { + ConnectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=", + Database = "db", + Container = "container", + }; + + Assert.IsFalse(settings.UseDefaultProxyCredentials); + } + + [TestMethod] + public void UseDefaultProxyCredentials_CanBeSetToTrue() + { + var settings = new CosmosSourceSettings + { + ConnectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=", + Database = "db", + Container = "container", + UseDefaultProxyCredentials = true, + }; + + Assert.IsTrue(settings.UseDefaultProxyCredentials); + } } \ No newline at end of file diff --git a/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosExtensionServices.cs b/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosExtensionServices.cs index dd9729f4..23661aa2 100644 --- a/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosExtensionServices.cs +++ b/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosExtensionServices.cs @@ -37,7 +37,12 @@ public static CosmosClient CreateClient(CosmosSettingsBase settings, string disp }; if (!string.IsNullOrEmpty(settings.WebProxy)){ - clientOptions.WebProxy = new WebProxy(settings.WebProxy); + var webProxy = new WebProxy(settings.WebProxy); + if (settings.UseDefaultProxyCredentials) + { + webProxy.UseDefaultCredentials = true; + } + clientOptions.WebProxy = webProxy; } CosmosClient? cosmosClient; diff --git a/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosSettingsBase.cs b/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosSettingsBase.cs index a81fac44..118d682f 100644 --- a/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosSettingsBase.cs +++ b/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosSettingsBase.cs @@ -12,6 +12,7 @@ public abstract class CosmosSettingsBase : IValidatableObject public string? Container { get; set; } public ConnectionMode ConnectionMode { get; set; } = ConnectionMode.Gateway; public string? WebProxy { get; set; } + public bool UseDefaultProxyCredentials { get; set; } = false; public bool UseRbacAuth { get; set; } public string? AccountEndpoint { get; set; } public bool EnableInteractiveCredentials { get; set; } diff --git a/Extensions/Cosmos/README.md b/Extensions/Cosmos/README.md index 82ea46fb..ca62606d 100644 --- a/Extensions/Cosmos/README.md +++ b/Extensions/Cosmos/README.md @@ -16,7 +16,7 @@ Source and sink settings also both require parameters to specify the data locati - `Database` - `Container` -Source supports an optional `IncludeMetadataFields` parameter (`false` by default) to enable inclusion of built-in Cosmos fields prefixed with `"_"`, for example `"_etag"` and `"_ts"`. An optional PartitionKeyValue setting allows for filtering to a single partition. The optional Query setting allows further filtering using a Cosmos SQL statement. An optional `WebProxy` parameter (`null` by default) enables connections through a proxy. +Source supports an optional `IncludeMetadataFields` parameter (`false` by default) to enable inclusion of built-in Cosmos fields prefixed with `"_"`, for example `"_etag"` and `"_ts"`. An optional PartitionKeyValue setting allows for filtering to a single partition. The optional Query setting allows further filtering using a Cosmos SQL statement. An optional `WebProxy` parameter (`null` by default) enables connections through a proxy. When using an authenticated proxy, set `UseDefaultProxyCredentials` to `true` (defaults to `false`) to include default credentials in the proxy request. ### Always Encrypted @@ -36,7 +36,8 @@ The extension will also automatically handle the encryption keys and encryption "IncludeMetadataFields": false, "PartitionKeyValue":"123", "Query":"SELECT * FROM c WHERE c.category='event'", - "WebProxy":"http://yourproxy.server.com/" + "WebProxy":"http://yourproxy.server.com/", + "UseDefaultProxyCredentials": true } ``` @@ -52,8 +53,9 @@ Or with RBAC: "IncludeMetadataFields": false, "PartitionKeyValue":"123", "Query":"SELECT * FROM c WHERE c.category='event'", - "InitClientEncryption": false - "WebProxy":"http://yourproxy.server.com/" + "InitClientEncryption": false, + "WebProxy":"http://yourproxy.server.com/", + "UseDefaultProxyCredentials": true } ``` @@ -85,6 +87,9 @@ Or with RBAC: - `Gateway` (default) - `Direct` +- **`WebProxy`**: Optional. Specifies the proxy server URL to use for connections (e.g., `http://yourproxy.server.com/`). +- **`UseDefaultProxyCredentials`**: Optional, defaults to `false`. When `true`, includes default credentials in the proxy request. Use this when connecting through an authenticated proxy that returns `407 Proxy Authentication Required`. + - **`LimitToEndpoint`**: Optional, defaults to `false`. When the value of this property is false, the Cosmos DB SDK will automatically discover write and read regions, and use them when the configured application region is not available. When set to `true`, availability is limited to the endpoint specified. From 6c60ffff5b81adcc7847d0efb40cecf2a16a1f0c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 01:20:58 +0000 Subject: [PATCH 3/6] Improve README formatting for source parameters Co-authored-by: philnach <19275540+philnach@users.noreply.github.com> --- Extensions/Cosmos/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Extensions/Cosmos/README.md b/Extensions/Cosmos/README.md index ca62606d..cf60a273 100644 --- a/Extensions/Cosmos/README.md +++ b/Extensions/Cosmos/README.md @@ -16,7 +16,12 @@ Source and sink settings also both require parameters to specify the data locati - `Database` - `Container` -Source supports an optional `IncludeMetadataFields` parameter (`false` by default) to enable inclusion of built-in Cosmos fields prefixed with `"_"`, for example `"_etag"` and `"_ts"`. An optional PartitionKeyValue setting allows for filtering to a single partition. The optional Query setting allows further filtering using a Cosmos SQL statement. An optional `WebProxy` parameter (`null` by default) enables connections through a proxy. When using an authenticated proxy, set `UseDefaultProxyCredentials` to `true` (defaults to `false`) to include default credentials in the proxy request. +Source supports the following optional parameters: +- `IncludeMetadataFields` (`false` by default) - Enables inclusion of built-in Cosmos fields prefixed with `"_"`, for example `"_etag"` and `"_ts"`. +- `PartitionKeyValue` - Allows for filtering to a single partition. +- `Query` - Allows further filtering using a Cosmos SQL statement. +- `WebProxy` (`null` by default) - Enables connections through a proxy. +- `UseDefaultProxyCredentials` (`false` by default) - When `true`, includes default credentials in the proxy request. Use this when connecting through an authenticated proxy that returns `407 Proxy Authentication Required`. ### Always Encrypted From 64ecff695685000074cc2abc387fd6228a76d376 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 01:31:10 +0000 Subject: [PATCH 4/6] Add Microsoft Learn link for 407 error documentation Co-authored-by: philnach <19275540+philnach@users.noreply.github.com> --- Extensions/Cosmos/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Extensions/Cosmos/README.md b/Extensions/Cosmos/README.md index cf60a273..fe6a92b9 100644 --- a/Extensions/Cosmos/README.md +++ b/Extensions/Cosmos/README.md @@ -21,7 +21,7 @@ Source supports the following optional parameters: - `PartitionKeyValue` - Allows for filtering to a single partition. - `Query` - Allows further filtering using a Cosmos SQL statement. - `WebProxy` (`null` by default) - Enables connections through a proxy. -- `UseDefaultProxyCredentials` (`false` by default) - When `true`, includes default credentials in the proxy request. Use this when connecting through an authenticated proxy that returns `407 Proxy Authentication Required`. +- `UseDefaultProxyCredentials` (`false` by default) - When `true`, includes default credentials in the proxy request. Use this when connecting through an authenticated proxy that returns [`407 Proxy Authentication Required`](https://learn.microsoft.com/dotnet/api/system.net.webproxy.credentials?view=net-10.0#remarks). ### Always Encrypted @@ -93,7 +93,7 @@ Or with RBAC: - `Direct` - **`WebProxy`**: Optional. Specifies the proxy server URL to use for connections (e.g., `http://yourproxy.server.com/`). -- **`UseDefaultProxyCredentials`**: Optional, defaults to `false`. When `true`, includes default credentials in the proxy request. Use this when connecting through an authenticated proxy that returns `407 Proxy Authentication Required`. +- **`UseDefaultProxyCredentials`**: Optional, defaults to `false`. When `true`, includes default credentials in the proxy request. Use this when connecting through an authenticated proxy that returns [`407 Proxy Authentication Required`](https://learn.microsoft.com/dotnet/api/system.net.webproxy.credentials?view=net-10.0#remarks). - **`LimitToEndpoint`**: Optional, defaults to `false`. When the value of this property is false, the Cosmos DB SDK will automatically discover write and read regions, and use them when the configured application region is not available. From a52dec46b0c2733f32d260a3cbf5eb813030b5a2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 17:18:04 +0000 Subject: [PATCH 5/6] Add HttpClient with default credentials for authenticated proxy support Co-authored-by: philnach <19275540+philnach@users.noreply.github.com> --- .../CosmosExtensionServices.cs | 20 +++++++++++++++++++ Extensions/Cosmos/README.md | 4 ++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosExtensionServices.cs b/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosExtensionServices.cs index 23661aa2..5f742f0d 100644 --- a/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosExtensionServices.cs +++ b/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosExtensionServices.cs @@ -9,11 +9,24 @@ using Microsoft.Azure.Cosmos.Encryption; using Azure.Security.KeyVault.Keys.Cryptography; using System.Net; +using System.Net.Http; namespace Cosmos.DataTransfer.CosmosExtension { public static class CosmosExtensionServices { + // Static HttpClient with default credentials for reuse across connections + // This avoids connection exhaustion and properly handles credentials + private static readonly Lazy _httpClientWithDefaultCredentials = new Lazy(() => + { + var handler = new HttpClientHandler + { + Credentials = CredentialCache.DefaultNetworkCredentials, + PreAuthenticate = true + }; + return new HttpClient(handler); + }); + public static CosmosClient CreateClient(CosmosSettingsBase settings, string displayName, string? sourceDisplayName = null) { string userAgentString = CreateUserAgentString(displayName, sourceDisplayName); @@ -44,6 +57,13 @@ public static CosmosClient CreateClient(CosmosSettingsBase settings, string disp } clientOptions.WebProxy = webProxy; } + + // When using default credentials, also configure the HttpClient with credentials + // This ensures authenticated proxy support for the underlying HTTP connections + if (settings.UseDefaultProxyCredentials) + { + clientOptions.HttpClientFactory = () => _httpClientWithDefaultCredentials.Value; + } CosmosClient? cosmosClient; if (settings.UseRbacAuth) diff --git a/Extensions/Cosmos/README.md b/Extensions/Cosmos/README.md index fe6a92b9..b72577b2 100644 --- a/Extensions/Cosmos/README.md +++ b/Extensions/Cosmos/README.md @@ -21,7 +21,7 @@ Source supports the following optional parameters: - `PartitionKeyValue` - Allows for filtering to a single partition. - `Query` - Allows further filtering using a Cosmos SQL statement. - `WebProxy` (`null` by default) - Enables connections through a proxy. -- `UseDefaultProxyCredentials` (`false` by default) - When `true`, includes default credentials in the proxy request. Use this when connecting through an authenticated proxy that returns [`407 Proxy Authentication Required`](https://learn.microsoft.com/dotnet/api/system.net.webproxy.credentials?view=net-10.0#remarks). +- `UseDefaultProxyCredentials` (`false` by default) - When `true`, includes default credentials in both the WebProxy and the underlying HttpClient connection to CosmosDB. Use this when connecting through an authenticated proxy that returns [`407 Proxy Authentication Required`](https://learn.microsoft.com/dotnet/api/system.net.webproxy.credentials?view=net-10.0#remarks). ### Always Encrypted @@ -93,7 +93,7 @@ Or with RBAC: - `Direct` - **`WebProxy`**: Optional. Specifies the proxy server URL to use for connections (e.g., `http://yourproxy.server.com/`). -- **`UseDefaultProxyCredentials`**: Optional, defaults to `false`. When `true`, includes default credentials in the proxy request. Use this when connecting through an authenticated proxy that returns [`407 Proxy Authentication Required`](https://learn.microsoft.com/dotnet/api/system.net.webproxy.credentials?view=net-10.0#remarks). +- **`UseDefaultProxyCredentials`**: Optional, defaults to `false`. When `true`, includes default credentials in both the WebProxy and the underlying HttpClient connection to CosmosDB. Use this when connecting through an authenticated proxy that returns [`407 Proxy Authentication Required`](https://learn.microsoft.com/dotnet/api/system.net.webproxy.credentials?view=net-10.0#remarks). - **`LimitToEndpoint`**: Optional, defaults to `false`. When the value of this property is false, the Cosmos DB SDK will automatically discover write and read regions, and use them when the configured application region is not available. From 36c521fe140665d4406dad77c00997c360e378c2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 21:51:55 +0000 Subject: [PATCH 6/6] Split proxy and HttpClient credential settings, add PreAuthenticate option Co-authored-by: philnach <19275540+philnach@users.noreply.github.com> --- ExampleConfigs.md | 4 +- .../CosmosSinkSettingsTests.cs | 54 +++++++++++++++++++ .../CosmosSourceSettingsTests.cs | 54 +++++++++++++++++++ .../CosmosExtensionServices.cs | 22 ++++++-- .../CosmosSettingsBase.cs | 2 + Extensions/Cosmos/README.md | 16 ++++-- 6 files changed, 142 insertions(+), 10 deletions(-) diff --git a/ExampleConfigs.md b/ExampleConfigs.md index f74a931b..d67810c9 100644 --- a/ExampleConfigs.md +++ b/ExampleConfigs.md @@ -219,7 +219,9 @@ "PartitionKeyPath": "/id", "WriteMode": "Insert", "WebProxy": "http://yourproxy.server.com/", - "UseDefaultProxyCredentials": true + "UseDefaultProxyCredentials": true, + "UseDefaultCredentials": true, + "PreAuthenticate": true } } ``` diff --git a/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension.UnitTests/CosmosSinkSettingsTests.cs b/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension.UnitTests/CosmosSinkSettingsTests.cs index 251bf953..f724911d 100644 --- a/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension.UnitTests/CosmosSinkSettingsTests.cs +++ b/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension.UnitTests/CosmosSinkSettingsTests.cs @@ -236,4 +236,58 @@ public void UseDefaultProxyCredentials_CanBeSetToTrue() Assert.IsTrue(settings.UseDefaultProxyCredentials); } + + [TestMethod] + public void UseDefaultCredentials_DefaultsToFalse() + { + var settings = new CosmosSinkSettings + { + ConnectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=", + Database = "db", + Container = "container", + }; + + Assert.IsFalse(settings.UseDefaultCredentials); + } + + [TestMethod] + public void UseDefaultCredentials_CanBeSetToTrue() + { + var settings = new CosmosSinkSettings + { + ConnectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=", + Database = "db", + Container = "container", + UseDefaultCredentials = true, + }; + + Assert.IsTrue(settings.UseDefaultCredentials); + } + + [TestMethod] + public void PreAuthenticate_DefaultsToFalse() + { + var settings = new CosmosSinkSettings + { + ConnectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=", + Database = "db", + Container = "container", + }; + + Assert.IsFalse(settings.PreAuthenticate); + } + + [TestMethod] + public void PreAuthenticate_CanBeSetToTrue() + { + var settings = new CosmosSinkSettings + { + ConnectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=", + Database = "db", + Container = "container", + PreAuthenticate = true, + }; + + Assert.IsTrue(settings.PreAuthenticate); + } } \ No newline at end of file diff --git a/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension.UnitTests/CosmosSourceSettingsTests.cs b/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension.UnitTests/CosmosSourceSettingsTests.cs index ccf52605..1cb18f4a 100644 --- a/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension.UnitTests/CosmosSourceSettingsTests.cs +++ b/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension.UnitTests/CosmosSourceSettingsTests.cs @@ -87,4 +87,58 @@ public void UseDefaultProxyCredentials_CanBeSetToTrue() Assert.IsTrue(settings.UseDefaultProxyCredentials); } + + [TestMethod] + public void UseDefaultCredentials_DefaultsToFalse() + { + var settings = new CosmosSourceSettings + { + ConnectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=", + Database = "db", + Container = "container", + }; + + Assert.IsFalse(settings.UseDefaultCredentials); + } + + [TestMethod] + public void UseDefaultCredentials_CanBeSetToTrue() + { + var settings = new CosmosSourceSettings + { + ConnectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=", + Database = "db", + Container = "container", + UseDefaultCredentials = true, + }; + + Assert.IsTrue(settings.UseDefaultCredentials); + } + + [TestMethod] + public void PreAuthenticate_DefaultsToFalse() + { + var settings = new CosmosSourceSettings + { + ConnectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=", + Database = "db", + Container = "container", + }; + + Assert.IsFalse(settings.PreAuthenticate); + } + + [TestMethod] + public void PreAuthenticate_CanBeSetToTrue() + { + var settings = new CosmosSourceSettings + { + ConnectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=", + Database = "db", + Container = "container", + PreAuthenticate = true, + }; + + Assert.IsTrue(settings.PreAuthenticate); + } } \ No newline at end of file diff --git a/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosExtensionServices.cs b/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosExtensionServices.cs index 5f742f0d..ba585cae 100644 --- a/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosExtensionServices.cs +++ b/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosExtensionServices.cs @@ -15,9 +15,19 @@ namespace Cosmos.DataTransfer.CosmosExtension { public static class CosmosExtensionServices { - // Static HttpClient with default credentials for reuse across connections + // Static HttpClient instances with different configurations for reuse across connections // This avoids connection exhaustion and properly handles credentials private static readonly Lazy _httpClientWithDefaultCredentials = new Lazy(() => + { + var handler = new HttpClientHandler + { + Credentials = CredentialCache.DefaultNetworkCredentials, + PreAuthenticate = false + }; + return new HttpClient(handler); + }); + + private static readonly Lazy _httpClientWithDefaultCredentialsAndPreAuth = new Lazy(() => { var handler = new HttpClientHandler { @@ -58,11 +68,13 @@ public static CosmosClient CreateClient(CosmosSettingsBase settings, string disp clientOptions.WebProxy = webProxy; } - // When using default credentials, also configure the HttpClient with credentials - // This ensures authenticated proxy support for the underlying HTTP connections - if (settings.UseDefaultProxyCredentials) + // Configure the HttpClient with default credentials if requested + // This enables authenticated proxy support for the underlying HTTP connections + if (settings.UseDefaultCredentials) { - clientOptions.HttpClientFactory = () => _httpClientWithDefaultCredentials.Value; + clientOptions.HttpClientFactory = settings.PreAuthenticate + ? () => _httpClientWithDefaultCredentialsAndPreAuth.Value + : () => _httpClientWithDefaultCredentials.Value; } CosmosClient? cosmosClient; diff --git a/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosSettingsBase.cs b/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosSettingsBase.cs index 118d682f..c3814796 100644 --- a/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosSettingsBase.cs +++ b/Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosSettingsBase.cs @@ -13,6 +13,8 @@ public abstract class CosmosSettingsBase : IValidatableObject public ConnectionMode ConnectionMode { get; set; } = ConnectionMode.Gateway; public string? WebProxy { get; set; } public bool UseDefaultProxyCredentials { get; set; } = false; + public bool UseDefaultCredentials { get; set; } = false; + public bool PreAuthenticate { get; set; } = false; public bool UseRbacAuth { get; set; } public string? AccountEndpoint { get; set; } public bool EnableInteractiveCredentials { get; set; } diff --git a/Extensions/Cosmos/README.md b/Extensions/Cosmos/README.md index b72577b2..ba3d5194 100644 --- a/Extensions/Cosmos/README.md +++ b/Extensions/Cosmos/README.md @@ -21,7 +21,9 @@ Source supports the following optional parameters: - `PartitionKeyValue` - Allows for filtering to a single partition. - `Query` - Allows further filtering using a Cosmos SQL statement. - `WebProxy` (`null` by default) - Enables connections through a proxy. -- `UseDefaultProxyCredentials` (`false` by default) - When `true`, includes default credentials in both the WebProxy and the underlying HttpClient connection to CosmosDB. Use this when connecting through an authenticated proxy that returns [`407 Proxy Authentication Required`](https://learn.microsoft.com/dotnet/api/system.net.webproxy.credentials?view=net-10.0#remarks). +- `UseDefaultProxyCredentials` (`false` by default) - When `true`, includes default credentials in the WebProxy request. Use this when connecting through an authenticated proxy that returns [`407 Proxy Authentication Required`](https://learn.microsoft.com/dotnet/api/system.net.webproxy.credentials?view=net-10.0#remarks). +- `UseDefaultCredentials` (`false` by default) - When `true`, configures the underlying HttpClient with default network credentials. Use this when the connection to CosmosDB requires authentication through a proxy. +- `PreAuthenticate` (`false` by default) - When `true`, enables pre-authentication on the HttpClient, which sends credentials with the initial request rather than waiting for a 401/407 challenge. This can save extra round-trips but should only be used when the endpoint is trusted. ### Always Encrypted @@ -42,7 +44,9 @@ The extension will also automatically handle the encryption keys and encryption "PartitionKeyValue":"123", "Query":"SELECT * FROM c WHERE c.category='event'", "WebProxy":"http://yourproxy.server.com/", - "UseDefaultProxyCredentials": true + "UseDefaultProxyCredentials": true, + "UseDefaultCredentials": true, + "PreAuthenticate": true } ``` @@ -60,7 +64,9 @@ Or with RBAC: "Query":"SELECT * FROM c WHERE c.category='event'", "InitClientEncryption": false, "WebProxy":"http://yourproxy.server.com/", - "UseDefaultProxyCredentials": true + "UseDefaultProxyCredentials": true, + "UseDefaultCredentials": true, + "PreAuthenticate": true } ``` @@ -93,7 +99,9 @@ Or with RBAC: - `Direct` - **`WebProxy`**: Optional. Specifies the proxy server URL to use for connections (e.g., `http://yourproxy.server.com/`). -- **`UseDefaultProxyCredentials`**: Optional, defaults to `false`. When `true`, includes default credentials in both the WebProxy and the underlying HttpClient connection to CosmosDB. Use this when connecting through an authenticated proxy that returns [`407 Proxy Authentication Required`](https://learn.microsoft.com/dotnet/api/system.net.webproxy.credentials?view=net-10.0#remarks). +- **`UseDefaultProxyCredentials`**: Optional, defaults to `false`. When `true`, includes default credentials in the WebProxy request. Use this when connecting through an authenticated proxy that returns [`407 Proxy Authentication Required`](https://learn.microsoft.com/dotnet/api/system.net.webproxy.credentials?view=net-10.0#remarks). +- **`UseDefaultCredentials`**: Optional, defaults to `false`. When `true`, configures the underlying HttpClient with default network credentials. Use this when the connection to CosmosDB requires authentication through a proxy. +- **`PreAuthenticate`**: Optional, defaults to `false`. When `true`, enables pre-authentication on the HttpClient, which sends credentials with the initial request rather than waiting for a 401/407 challenge. This can save extra round-trips but should only be used when the endpoint is trusted. - **`LimitToEndpoint`**: Optional, defaults to `false`. When the value of this property is false, the Cosmos DB SDK will automatically discover write and read regions, and use them when the configured application region is not available.