Skip to content

Commit 4882842

Browse files
Allow override aad auth endpoint of cosmos db's auth config (Azure#45016)
* Override aad auth endpoint cosmos db kafka connector * Update unit test * Update information of config key * Refactor & add unit tests for config * Fix * Revert the wrong change * Update changelog & doc * Remove azure.cosmos.throughputControl.auth.aad.authEndpointOverride * Remove * Remove unused method & update doc * Update sdk/cosmos/azure-cosmos-kafka-connect/CHANGELOG.md --------- Co-authored-by: Fabian Meiswinkel <[email protected]>
1 parent 6c430ad commit 4882842

File tree

7 files changed

+129
-32
lines changed

7 files changed

+129
-32
lines changed

sdk/cosmos/azure-cosmos-kafka-connect/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#### Bugs Fixed
1010

1111
#### Other Changes
12+
* Added `authEndpointOverride` option for all AAD authentication types - See [PR 45016](https://github.com/Azure/azure-sdk-for-java/pull/45016)
1213

1314
### 2.2.0 (2025-02-20)
1415

sdk/cosmos/azure-cosmos-kafka-connect/docs/configuration-reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
| `azure.cosmos.account.key` | `""` | Cosmos DB Account Key (only required in case of `auth.type` as `MasterKey`) |
1212
| `azure.cosmos.auth.aad.clientId` | `""` | The clientId/ApplicationId of the service principal. Required for `ServicePrincipal` authentication. |
1313
| `azure.cosmos.auth.aad.clientSecret` | `""` | The client secret/password of the service principal. |
14+
| `azure.cosmos.auth.aad.authEndpointOverride` | `""` | Overrides the Azure Active Directory (AAD) authentication endpoint. This is useful when the Cosmos DB account resides in a non-public Azure cloud environment such as Azure China, Azure US Government, or Azure Germany. By default, the SDK uses the standard AAD endpoint for the public Azure cloud. Set this value if your deployment requires a custom authority URI (e.g., https://login.chinacloudapi.cn/). |
1415
| `azure.cosmos.mode.gateway` | `false` | Flag to indicate whether to use gateway mode. By default it is false, means SDK uses direct mode. https://learn.microsoft.com/azure/cosmos-db/nosql/sdk-connection-modes |
1516
| `azure.cosmos.preferredRegionList` | `[]` | Preferred regions list to be used for a multi region Cosmos DB account. This is a comma separated value (e.g., `[East US, West US]` or `East US, West US`) provided preferred regions will be used as hint. You should use a collocated kafka cluster with your Cosmos DB account and pass the kafka cluster region as preferred region. See list of azure regions [here](https://docs.microsoft.com/dotnet/api/microsoft.azure.documents.locationnames?view=azure-dotnet&preserve-view=true). |
1617
| `azure.cosmos.application.name` | `""` | Application name. Will be added as the userAgent suffix. |

sdk/cosmos/azure-cosmos-kafka-connect/src/main/java/com/azure/cosmos/kafka/connect/implementation/CosmosAadAuthConfig.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,36 @@
55

66
import com.azure.cosmos.implementation.apachecommons.lang.StringUtils;
77

8+
import java.util.HashMap;
9+
import java.util.Map;
10+
811
import static com.azure.cosmos.implementation.guava25.base.Preconditions.checkArgument;
912

1013
public class CosmosAadAuthConfig implements CosmosAuthConfig {
14+
private static final Map<CosmosAzureEnvironment, String> ACTIVE_DIRECTORY_ENDPOINT_MAP;
15+
static {
16+
// for now we maintain a static list within the SDK these values do not change very frequently
17+
ACTIVE_DIRECTORY_ENDPOINT_MAP = new HashMap<>();
18+
ACTIVE_DIRECTORY_ENDPOINT_MAP.put(CosmosAzureEnvironment.AZURE, "https://login.microsoftonline.com/");
19+
ACTIVE_DIRECTORY_ENDPOINT_MAP.put(CosmosAzureEnvironment.AZURE_CHINA, "https://login.chinacloudapi.cn/");
20+
ACTIVE_DIRECTORY_ENDPOINT_MAP.put(CosmosAzureEnvironment.AZURE_US_GOVERNMENT, "https://login.microsoftonline.us/");
21+
ACTIVE_DIRECTORY_ENDPOINT_MAP.put(CosmosAzureEnvironment.AZURE_GERMANY, "https://login.microsoftonline.de/");
22+
}
23+
1124
private final String clientId;
1225
private final String clientSecret;
26+
private final String authEndpointOverride;
1327
private final String tenantId;
1428
private final CosmosAzureEnvironment azureEnvironment;
1529

16-
public CosmosAadAuthConfig(String clientId, String clientSecret, String tenantId, CosmosAzureEnvironment azureEnvironment) {
30+
public CosmosAadAuthConfig(String clientId, String clientSecret, String authEndpointOverride, String tenantId, CosmosAzureEnvironment azureEnvironment) {
1731
checkArgument(StringUtils.isNotEmpty(clientId), "Argument 'clientId' should not be null");
1832
checkArgument(StringUtils.isNotEmpty(clientSecret), "Argument 'clientSecret' should not be null");
1933
checkArgument(StringUtils.isNotEmpty(tenantId), "Argument 'tenantId' should not be null");
2034

2135
this.clientId = clientId;
2236
this.clientSecret = clientSecret;
37+
this.authEndpointOverride = authEndpointOverride;
2338
this.tenantId = tenantId;
2439
this.azureEnvironment = azureEnvironment;
2540
}
@@ -36,7 +51,9 @@ public String getTenantId() {
3651
return tenantId;
3752
}
3853

39-
public CosmosAzureEnvironment getAzureEnvironment() {
40-
return azureEnvironment;
54+
public String getAuthEndpoint() {
55+
String defaultAuthEndpoint = ACTIVE_DIRECTORY_ENDPOINT_MAP.get(azureEnvironment);
56+
String authEndpoint = StringUtils.isNotEmpty(authEndpointOverride) ? authEndpointOverride : defaultAuthEndpoint;
57+
return authEndpoint.replaceAll("/$", "") + "/";
4158
}
4259
}

sdk/cosmos/azure-cosmos-kafka-connect/src/main/java/com/azure/cosmos/kafka/connect/implementation/CosmosClientStore.java

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,8 @@
1414
import com.azure.identity.ClientSecretCredentialBuilder;
1515

1616
import java.time.Duration;
17-
import java.util.HashMap;
18-
import java.util.Map;
1917

2018
public class CosmosClientStore {
21-
private static final Map<CosmosAzureEnvironment, String> ACTIVE_DIRECTORY_ENDPOINT_MAP;
22-
static {
23-
// for now we maintain a static list within the SDK these values do not change very frequently
24-
ACTIVE_DIRECTORY_ENDPOINT_MAP = new HashMap<>();
25-
ACTIVE_DIRECTORY_ENDPOINT_MAP.put(CosmosAzureEnvironment.AZURE, "https://login.microsoftonline.com/");
26-
ACTIVE_DIRECTORY_ENDPOINT_MAP.put(CosmosAzureEnvironment.AZURE_CHINA, "https://login.chinacloudapi.cn/");
27-
ACTIVE_DIRECTORY_ENDPOINT_MAP.put(CosmosAzureEnvironment.AZURE_US_GOVERNMENT, "https://login.microsoftonline.us/");
28-
ACTIVE_DIRECTORY_ENDPOINT_MAP.put(CosmosAzureEnvironment.AZURE_GERMANY, "https://login.microsoftonline.de/");
29-
}
30-
3119
public static CosmosAsyncClient getCosmosClient(
3220
CosmosAccountConfig accountConfig,
3321
String sourceName) {
@@ -59,10 +47,9 @@ public static CosmosAsyncClient getCosmosClient(
5947
if (accountConfig.getCosmosAuthConfig() instanceof CosmosMasterKeyAuthConfig) {
6048
cosmosClientBuilder.key(((CosmosMasterKeyAuthConfig) accountConfig.getCosmosAuthConfig()).getMasterKey());
6149
} else if (accountConfig.getCosmosAuthConfig() instanceof CosmosAadAuthConfig) {
62-
6350
CosmosAadAuthConfig aadAuthConfig = (CosmosAadAuthConfig) accountConfig.getCosmosAuthConfig();
6451
ClientSecretCredential tokenCredential = new ClientSecretCredentialBuilder()
65-
.authorityHost(ACTIVE_DIRECTORY_ENDPOINT_MAP.get(aadAuthConfig.getAzureEnvironment()).replaceAll("/$", "") + "/")
52+
.authorityHost(aadAuthConfig.getAuthEndpoint())
6653
.tenantId(aadAuthConfig.getTenantId())
6754
.clientId(aadAuthConfig.getClientId())
6855
.clientSecret(aadAuthConfig.getClientSecret())

sdk/cosmos/azure-cosmos-kafka-connect/src/main/java/com/azure/cosmos/kafka/connect/implementation/KafkaCosmosConfig.java

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ public class KafkaCosmosConfig extends AbstractConfig {
6868
private static final String AAD_CLIENT_SECRET_DISPLAY = "The client secret/password of the service principal.";
6969
private static final String DEFAULT_AAD_CLIENT_SECRET = Strings.Emtpy;
7070

71+
private static final String AAD_AUTH_ENDPOINT_OVERRIDE = "azure.cosmos.auth.aad.authEndpointOverride";
72+
private static final String AAD_AUTH_ENDPOINT_OVERRIDE_DOC = "Overrides the Azure Active Directory (AAD) authentication endpoint. "
73+
+ "This is useful when the Cosmos DB account resides in a non-public Azure cloud environment such as Azure Air-Gapped Environment. "
74+
+ "By default, the SDK uses the standard AAD endpoint for the public Azure cloud. Set this value if your deployment requires a custom authority URI.";
75+
private static final String AAD_AUTH_ENDPOINT_OVERRIDE_DISPLAY =
76+
"The Azure Active Directory (AAD) authentication endpoint override. Set this if you are using a cloud environment other than public Azure.";
77+
private static final String DEFAULT_AAD_AUTH_ENDPOINT_OVERRIDE = Strings.Emtpy;
78+
7179
private static final String USE_GATEWAY_MODE = "azure.cosmos.mode.gateway";
7280
private static final String USE_GATEWAY_MODE_DOC = "Flag to indicate whether to use gateway mode. By default it is false, means SDK uses direct mode. https://learn.microsoft.com/azure/cosmos-db/nosql/sdk-connection-modes";
7381
private static final String USE_GATEWAY_MODE_DISPLAY = "Use gateway mode.";
@@ -206,6 +214,7 @@ private CosmosAccountConfig parseAccountConfig() {
206214
ACCOUNT_KEY,
207215
AAD_CLIENT_ID,
208216
AAD_CLIENT_SECRET,
217+
AAD_AUTH_ENDPOINT_OVERRIDE,
209218
APPLICATION_NAME,
210219
USE_GATEWAY_MODE,
211220
PREFERRED_REGIONS_LIST);
@@ -219,6 +228,7 @@ private CosmosAccountConfig parseAccountConfigCore(
219228
String accountKeyConfig,
220229
String clientIdConfig,
221230
String clientSecretConfig,
231+
String authEndpointOverrideConfig,
222232
String applicationNameConfig,
223233
String useGatewayModeConfig,
224234
String preferredRegionListConfig) {
@@ -230,7 +240,8 @@ private CosmosAccountConfig parseAccountConfigCore(
230240
String masterKey = this.getPassword(accountKeyConfig).value();
231241
String clientId = this.getString(clientIdConfig);
232242
String clientSecret = this.getPassword(clientSecretConfig).value();
233-
CosmosAuthConfig authConfig = getAuthConfig(azureEnvironment, tenantId, authType, masterKey, clientId, clientSecret);
243+
String authEndpointOverride = this.getString(authEndpointOverrideConfig);
244+
CosmosAuthConfig authConfig = getAuthConfig(azureEnvironment, tenantId, authType, masterKey, clientId, clientSecret, authEndpointOverride);
234245

235246
String applicationName = this.getString(applicationNameConfig);
236247
boolean useGatewayMode = this.getBoolean(useGatewayModeConfig);
@@ -250,13 +261,13 @@ private CosmosAuthConfig getAuthConfig(
250261
CosmosAuthType authType,
251262
String masterKey,
252263
String clientId,
253-
String clientSecret) {
254-
264+
String clientSecret,
265+
String authEndpointOverride) {
255266
switch (authType) {
256267
case MASTER_KEY:
257268
return new CosmosMasterKeyAuthConfig(masterKey);
258269
case SERVICE_PRINCIPAL:
259-
return new CosmosAadAuthConfig(clientId, clientSecret, tenantId, azureEnvironment);
270+
return new CosmosAadAuthConfig(clientId, clientSecret, authEndpointOverride, tenantId, azureEnvironment);
260271
default:
261272
throw new IllegalArgumentException("AuthType " + authType + " is not supported");
262273
}
@@ -286,6 +297,7 @@ private CosmosThroughputControlConfig parseThroughputControlConfig() {
286297
THROUGHPUT_CONTROL_ACCOUNT_KEY,
287298
THROUGHPUT_CONTROL_AAD_CLIENT_ID,
288299
THROUGHPUT_CONTROL_AAD_CLIENT_SECRET,
300+
AAD_AUTH_ENDPOINT_OVERRIDE,
289301
APPLICATION_NAME,
290302
THROUGHPUT_CONTROL_USE_GATEWAY_MODE,
291303
THROUGHPUT_CONTROL_PREFERRED_REGIONS_LIST);
@@ -417,6 +429,17 @@ private static void defineAccountConfig(ConfigDef result) {
417429
ConfigDef.Width.MEDIUM,
418430
AAD_CLIENT_SECRET_DISPLAY
419431
)
432+
.define(
433+
AAD_AUTH_ENDPOINT_OVERRIDE,
434+
ConfigDef.Type.STRING,
435+
DEFAULT_AAD_AUTH_ENDPOINT_OVERRIDE,
436+
ConfigDef.Importance.MEDIUM,
437+
AAD_AUTH_ENDPOINT_OVERRIDE_DOC,
438+
accountGroupName,
439+
accountGroupOrder++,
440+
ConfigDef.Width.LONG,
441+
AAD_AUTH_ENDPOINT_OVERRIDE_DISPLAY
442+
)
420443
.define(
421444
APPLICATION_NAME,
422445
ConfigDef.Type.STRING,
@@ -738,7 +761,8 @@ public static void validateThroughputControlConfig(Map<String, ConfigValue> conf
738761
THROUGHPUT_CONTROL_AUTH_TYPE,
739762
THROUGHPUT_CONTROL_ACCOUNT_KEY,
740763
THROUGHPUT_CONTROL_AAD_CLIENT_ID,
741-
THROUGHPUT_CONTROL_AAD_CLIENT_SECRET);
764+
THROUGHPUT_CONTROL_AAD_CLIENT_SECRET,
765+
AAD_AUTH_ENDPOINT_OVERRIDE);
742766
}
743767

744768
// if throughput control is using aad auth, then only targetThroughput is supported
@@ -763,7 +787,8 @@ public static void validateCosmosAccountAuthConfig(Map<String, ConfigValue> conf
763787
AUTH_TYPE,
764788
ACCOUNT_KEY,
765789
AAD_CLIENT_ID,
766-
AAD_CLIENT_SECRET);
790+
AAD_CLIENT_SECRET,
791+
AAD_AUTH_ENDPOINT_OVERRIDE);
767792
}
768793

769794
public static void validateAccountAuthConfigCore(
@@ -772,7 +797,8 @@ public static void validateAccountAuthConfigCore(
772797
String authTypeConfig,
773798
String accountKeyConfig,
774799
String clientIdConfig,
775-
String clientSecretConfig) {
800+
String clientSecretConfig,
801+
String authEndpointOverrideConfig) {
776802

777803
CosmosAuthType authType = CosmosAuthType.fromName(configValueMap.get(authTypeConfig).value().toString());
778804
switch (authType) {
@@ -805,6 +831,18 @@ public static void validateAccountAuthConfigCore(
805831
.get(clientSecretConfig)
806832
.addErrorMessage("ClientSecret is required for Service Principal auth type");
807833
}
834+
835+
String authEndpointOverride = configValueMap.get(authEndpointOverrideConfig).value().toString();
836+
if (StringUtils.isNotEmpty(authEndpointOverride)) {
837+
try {
838+
new URL(authEndpointOverride);
839+
} catch (MalformedURLException e) {
840+
configValueMap
841+
.get(authEndpointOverrideConfig)
842+
.addErrorMessage("AuthEndpointOverride need to be valid URI format for Service Principal auth type");
843+
}
844+
}
845+
808846
break;
809847
default:
810848
throw new IllegalArgumentException("AuthType " + authType + " is not supported");

0 commit comments

Comments
 (0)