diff --git a/eng/versioning/external_dependencies.txt b/eng/versioning/external_dependencies.txt
index 2ca41d0af56e..91c8c5daf8c1 100644
--- a/eng/versioning/external_dependencies.txt
+++ b/eng/versioning/external_dependencies.txt
@@ -199,7 +199,7 @@ com.microsoft.azure:azure-mgmt-resources;1.3.0
com.microsoft.azure:azure-mgmt-search;1.24.1
com.microsoft.azure:azure-mgmt-storage;1.3.0
com.microsoft.azure:azure-storage;8.0.0
-com.microsoft.azure:msal4j;1.12.0
+com.microsoft.azure:msal4j;1.13.0
com.microsoft.azure:msal4j-persistence-extension;1.1.0
com.sun.activation:jakarta.activation;1.2.2
io.opentelemetry:opentelemetry-api;1.14.0
diff --git a/sdk/eventhubs/microsoft-azure-eventhubs-eph/pom.xml b/sdk/eventhubs/microsoft-azure-eventhubs-eph/pom.xml
index 5487d19589cb..c13ec3f9c2d2 100644
--- a/sdk/eventhubs/microsoft-azure-eventhubs-eph/pom.xml
+++ b/sdk/eventhubs/microsoft-azure-eventhubs-eph/pom.xml
@@ -64,7 +64,7 @@
com.microsoft.azure
msal4j
- 1.12.0
+ 1.13.0
test
diff --git a/sdk/eventhubs/microsoft-azure-eventhubs-extensions/pom.xml b/sdk/eventhubs/microsoft-azure-eventhubs-extensions/pom.xml
index 81726ca0b50a..51f36e90bfec 100644
--- a/sdk/eventhubs/microsoft-azure-eventhubs-extensions/pom.xml
+++ b/sdk/eventhubs/microsoft-azure-eventhubs-extensions/pom.xml
@@ -68,7 +68,7 @@
com.microsoft.azure
msal4j
- 1.12.0
+ 1.13.0
test
diff --git a/sdk/eventhubs/microsoft-azure-eventhubs/pom.xml b/sdk/eventhubs/microsoft-azure-eventhubs/pom.xml
index bb4d43b79b1e..54b903680e3b 100644
--- a/sdk/eventhubs/microsoft-azure-eventhubs/pom.xml
+++ b/sdk/eventhubs/microsoft-azure-eventhubs/pom.xml
@@ -77,7 +77,7 @@
com.microsoft.azure
msal4j
- 1.12.0
+ 1.13.0
test
diff --git a/sdk/identity/azure-identity/CHANGELOG.md b/sdk/identity/azure-identity/CHANGELOG.md
index 713c96d4e8e3..7a194e8d0f88 100644
--- a/sdk/identity/azure-identity/CHANGELOG.md
+++ b/sdk/identity/azure-identity/CHANGELOG.md
@@ -1,17 +1,18 @@
# Release History
-## 1.6.0-beta.1 (Unreleased)
+## 1.6.0-beta.1 (2022-08-12)
### Features Added
- `EnvironmentCredential` will read the environment variable `AZURE_CLIENT_CERTIFICATE_PASSWORD` for a `pem`/`pfx` certificate specified by `AZURE_CLIENT_CERTIFICATE_PATH`.
+- Added support for in-memory token caching in `ManagedIdentityCredential`.
### Breaking Changes
- Removed `VisualStudioCodeCredential` from `DefaultAzureCredential` token chain. [Issue 27364](https://github.com/Azure/azure-sdk-for-java/issues/27364) tracks this.
-### Bugs Fixed
-
### Other Changes
+#### Dependency Updates
+- Upgraded `msal4j` from `1.12.0` to version `1.13.0`.
## 1.5.4 (2022-08-08)
diff --git a/sdk/identity/azure-identity/pom.xml b/sdk/identity/azure-identity/pom.xml
index 5f37e9bc11e7..f0d6964a31e3 100644
--- a/sdk/identity/azure-identity/pom.xml
+++ b/sdk/identity/azure-identity/pom.xml
@@ -43,7 +43,7 @@
com.microsoft.azure
msal4j
- 1.12.0
+ 1.13.0
com.microsoft.azure
@@ -122,7 +122,7 @@
- com.microsoft.azure:msal4j:[1.12.0]
+ com.microsoft.azure:msal4j:[1.13.0]
com.microsoft.azure:msal4j-persistence-extension:[1.1.0]
net.java.dev.jna:jna-platform:[5.6.0]
org.linguafranca.pwdb:KeePassJava2:[2.1.4]
diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/AksExchangeTokenCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/AksExchangeTokenCredential.java
index c133f3be400d..fbdcd006c9c2 100644
--- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/AksExchangeTokenCredential.java
+++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/AksExchangeTokenCredential.java
@@ -33,6 +33,6 @@ public Mono authenticate(TokenRequestContext request) {
+ " 'AZURE_CLIENT_ID' environment variable or through the credential builder."
+ " Please ensure client id is provided to authenticate via token exchange in AKS environment.")));
}
- return identityClient.authenticateWithExchangeToken(request);
+ return identityClient.authenticateWithManagedIdentityConfidentialClient(request);
}
}
diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/AppServiceMsiCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/AppServiceMsiCredential.java
index 187df10b2efd..0d8f9a8e0e51 100644
--- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/AppServiceMsiCredential.java
+++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/AppServiceMsiCredential.java
@@ -51,8 +51,6 @@ class AppServiceMsiCredential extends ManagedIdentityServiceCredential {
* @return A publisher that emits an {@link AccessToken}.
*/
public Mono authenticate(TokenRequestContext request) {
- return identityClient.authenticateToManagedIdentityEndpoint(identityEndpoint, identityHeader,
- msiEndpoint, msiSecret,
- request);
+ return identityClient.authenticateWithManagedIdentityConfidentialClient(request);
}
}
diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/ArcIdentityCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/ArcIdentityCredential.java
index a1597be83983..b1a5ce2d5630 100644
--- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/ArcIdentityCredential.java
+++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/ArcIdentityCredential.java
@@ -49,6 +49,6 @@ public Mono authenticate(TokenRequestContext request) {
+ "with the system assigned identity omit the client id when constructing the"
+ " ManagedIdentityCredential.", null)));
}
- return identityClient.authenticateToArcManagedIdentityEndpoint(identityEndpoint, request);
+ return identityClient.authenticateWithManagedIdentityConfidentialClient(request);
}
}
diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/ManagedIdentityCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/ManagedIdentityCredential.java
index 2b2f1da7344c..5a9b4d1401d5 100644
--- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/ManagedIdentityCredential.java
+++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/ManagedIdentityCredential.java
@@ -11,6 +11,8 @@
import com.azure.core.util.logging.ClientLogger;
import com.azure.identity.implementation.IdentityClientBuilder;
import com.azure.identity.implementation.IdentityClientOptions;
+import com.azure.identity.implementation.ManagedIdentityParameters;
+import com.azure.identity.implementation.ManagedIdentityType;
import com.azure.identity.implementation.util.LoggingUtil;
import reactor.core.publisher.Mono;
@@ -48,7 +50,6 @@ public final class ManagedIdentityCredential implements TokenCredential {
Configuration configuration = identityClientOptions.getConfiguration() == null
? Configuration.getGlobalConfiguration().clone() : identityClientOptions.getConfiguration();
-
/*
* Choose credential based on available environment variables in this order:
*
@@ -62,18 +63,33 @@ public final class ManagedIdentityCredential implements TokenCredential {
*/
if (configuration.contains(Configuration.PROPERTY_MSI_ENDPOINT)) {
- managedIdentityServiceCredential = new AppServiceMsiCredential(clientId, clientBuilder.build());
+ managedIdentityServiceCredential = new AppServiceMsiCredential(clientId, clientBuilder
+ .identityClientOptions(updateIdentityClientOptions(ManagedIdentityType.APP_SERVICE,
+ identityClientOptions, configuration))
+ .build());
} else if (configuration.contains(Configuration.PROPERTY_IDENTITY_ENDPOINT)) {
if (configuration.contains(Configuration.PROPERTY_IDENTITY_HEADER)) {
if (configuration.get(PROPERTY_IDENTITY_SERVER_THUMBPRINT) != null) {
- managedIdentityServiceCredential = new ServiceFabricMsiCredential(clientId, clientBuilder.build());
+ managedIdentityServiceCredential = new ServiceFabricMsiCredential(clientId, clientBuilder
+ .identityClientOptions(updateIdentityClientOptions(ManagedIdentityType.SERVICE_FABRIC,
+ identityClientOptions, configuration))
+ .build());
} else {
- managedIdentityServiceCredential = new AppServiceMsiCredential(clientId, clientBuilder.build());
+ managedIdentityServiceCredential = new AppServiceMsiCredential(clientId, clientBuilder
+ .identityClientOptions(updateIdentityClientOptions(ManagedIdentityType.APP_SERVICE,
+ identityClientOptions, configuration))
+ .build());
}
} else if (configuration.get(PROPERTY_IMDS_ENDPOINT) != null) {
- managedIdentityServiceCredential = new ArcIdentityCredential(clientId, clientBuilder.build());
+ managedIdentityServiceCredential = new ArcIdentityCredential(clientId, clientBuilder
+ .identityClientOptions(updateIdentityClientOptions(ManagedIdentityType.ARC,
+ identityClientOptions, configuration))
+ .build());
} else {
- managedIdentityServiceCredential = new VirtualMachineMsiCredential(clientId, clientBuilder.build());
+ managedIdentityServiceCredential = new VirtualMachineMsiCredential(clientId, clientBuilder
+ .identityClientOptions(updateIdentityClientOptions(ManagedIdentityType.VM,
+ identityClientOptions, configuration))
+ .build());
}
} else if (configuration.contains(Configuration.PROPERTY_AZURE_TENANT_ID)
&& configuration.get(AZURE_FEDERATED_TOKEN_FILE) != null) {
@@ -83,13 +99,51 @@ public final class ManagedIdentityCredential implements TokenCredential {
clientBuilder.tenantId(configuration.get(Configuration.PROPERTY_AZURE_TENANT_ID));
clientBuilder.clientAssertionPath(configuration.get(AZURE_FEDERATED_TOKEN_FILE));
clientBuilder.clientAssertionTimeout(Duration.ofMinutes(5));
- managedIdentityServiceCredential = new AksExchangeTokenCredential(clientIdentifier, clientBuilder.build());
+ managedIdentityServiceCredential = new AksExchangeTokenCredential(clientIdentifier, clientBuilder
+ .identityClientOptions(updateIdentityClientOptions(ManagedIdentityType.AKS,
+ identityClientOptions, configuration))
+ .build());
} else {
- managedIdentityServiceCredential = new VirtualMachineMsiCredential(clientId, clientBuilder.build());
+ managedIdentityServiceCredential = new VirtualMachineMsiCredential(clientId, clientBuilder
+ .identityClientOptions(updateIdentityClientOptions(ManagedIdentityType.VM,
+ identityClientOptions, configuration))
+ .build());
}
LoggingUtil.logAvailableEnvironmentVariables(LOGGER, configuration);
}
+ private IdentityClientOptions updateIdentityClientOptions(ManagedIdentityType managedIdentityType,
+ IdentityClientOptions clientOptions, Configuration configuration) {
+ switch (managedIdentityType) {
+ case APP_SERVICE:
+ return clientOptions
+ .setManagedIdentityType(ManagedIdentityType.APP_SERVICE)
+ .setManagedIdentityParameters(new ManagedIdentityParameters()
+ .setMsiEndpoint(configuration.get(Configuration.PROPERTY_MSI_ENDPOINT))
+ .setMsiSecret(configuration.get(Configuration.PROPERTY_MSI_SECRET))
+ .setIdentityEndpoint(configuration.get(Configuration.PROPERTY_IDENTITY_ENDPOINT))
+ .setIdentityHeader(configuration.get(Configuration.PROPERTY_IDENTITY_HEADER)));
+ case SERVICE_FABRIC:
+ return clientOptions
+ .setManagedIdentityType(ManagedIdentityType.SERVICE_FABRIC)
+ .setManagedIdentityParameters(new ManagedIdentityParameters()
+ .setIdentityServerThumbprint(configuration.get(PROPERTY_IDENTITY_SERVER_THUMBPRINT))
+ .setIdentityEndpoint(configuration.get(Configuration.PROPERTY_IDENTITY_ENDPOINT))
+ .setIdentityHeader(configuration.get(Configuration.PROPERTY_IDENTITY_HEADER)));
+ case ARC:
+ return clientOptions
+ .setManagedIdentityType(ManagedIdentityType.ARC)
+ .setManagedIdentityParameters(new ManagedIdentityParameters()
+ .setIdentityEndpoint(configuration.get(Configuration.PROPERTY_IDENTITY_ENDPOINT)));
+ case VM:
+ return clientOptions.setManagedIdentityType(ManagedIdentityType.VM);
+ case AKS:
+ return clientOptions.setManagedIdentityType(ManagedIdentityType.AKS);
+ default:
+ return clientOptions;
+ }
+ }
+
/**
* Gets the client ID of user assigned or system assigned identity.
* @return the client ID of user assigned or system assigned identity.
diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/ServiceFabricMsiCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/ServiceFabricMsiCredential.java
index 9b2468855e89..a02b354fb64c 100644
--- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/ServiceFabricMsiCredential.java
+++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/ServiceFabricMsiCredential.java
@@ -49,7 +49,6 @@ class ServiceFabricMsiCredential extends ManagedIdentityServiceCredential {
* @return A publisher that emits an {@link AccessToken}.
*/
public Mono authenticate(TokenRequestContext request) {
- return identityClient.authenticateToServiceFabricManagedIdentityEndpoint(identityEndpoint, identityHeader,
- identityServerThumbprint, request);
+ return identityClient.authenticateWithManagedIdentityConfidentialClient(request);
}
}
diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/VirtualMachineMsiCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/VirtualMachineMsiCredential.java
index 30fa1e37d160..fde04fe7b878 100644
--- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/VirtualMachineMsiCredential.java
+++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/VirtualMachineMsiCredential.java
@@ -31,6 +31,6 @@ class VirtualMachineMsiCredential extends ManagedIdentityServiceCredential {
* @return A publisher that emits an {@link AccessToken}.
*/
public Mono authenticate(TokenRequestContext request) {
- return identityClient.authenticateToIMDSEndpoint(request);
+ return identityClient.authenticateWithManagedIdentityConfidentialClient(request);
}
}
diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClient.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClient.java
index 7ca2288bcdb8..08c7cd4cbd59 100644
--- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClient.java
+++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClient.java
@@ -47,6 +47,7 @@
import com.microsoft.aad.msal4j.RefreshTokenParameters;
import com.microsoft.aad.msal4j.SilentParameters;
import com.microsoft.aad.msal4j.UserNamePasswordParameters;
+import com.microsoft.aad.msal4j.TokenProviderResult;
import com.sun.jna.Platform;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -137,6 +138,7 @@ public class IdentityClient {
private HttpPipelineAdapter httpPipelineAdapter;
private final SynchronizedAccessor publicClientApplicationAccessor;
private final SynchronizedAccessor confidentialClientApplicationAccessor;
+ private final SynchronizedAccessor managedIdentityConfidentialClientApplicationAccessor;
private final SynchronizedAccessor clientAssertionAccessor;
@@ -182,6 +184,9 @@ public class IdentityClient {
this.confidentialClientApplicationAccessor = new SynchronizedAccessor<>(() ->
getConfidentialClientApplication());
+ this.managedIdentityConfidentialClientApplicationAccessor = new SynchronizedAccessor<>(() ->
+ getManagedIdentityConfidentialClient());
+
this.clientAssertionAccessor = clientAssertionTimeout == null
? new SynchronizedAccessor<>(() -> parseClientAssertion(), Duration.ofMinutes(5))
: new SynchronizedAccessor<>(() -> parseClientAssertion(), clientAssertionTimeout);
@@ -277,6 +282,84 @@ private Mono getConfidentialClientApplication() {
});
}
+ private Mono getManagedIdentityConfidentialClient() {
+ return Mono.defer(() -> {
+ String authorityUrl = TRAILING_FORWARD_SLASHES.matcher(options.getAuthorityHost()).replaceAll("")
+ + "/" + tenantId;
+
+ // Temporarily pass in Dummy Client secret and Client ID. until MSal removes its requirements.
+ IClientCredential credential = ClientCredentialFactory
+ .createFromSecret(clientSecret != null ? clientSecret : "dummy-secret");
+ ConfidentialClientApplication.Builder applicationBuilder =
+ ConfidentialClientApplication.builder(clientId == null ? "SYSTEM-ASSIGNED-MANAGED-IDENTITY"
+ : clientId, credential);
+ try {
+ applicationBuilder = applicationBuilder.authority(authorityUrl);
+ } catch (MalformedURLException e) {
+ return Mono.error(LOGGER.logExceptionAsWarning(new IllegalStateException(e)));
+ }
+
+ if (options.getManagedIdentityType() == null) {
+ return Mono.error(LOGGER.logExceptionAsError(
+ new CredentialUnavailableException("Managed Identity type not configured, authentication not available.")));
+ }
+ applicationBuilder.appTokenProvider(appTokenProviderParameters -> {
+ TokenRequestContext trc = new TokenRequestContext()
+ .setScopes(new ArrayList<>(appTokenProviderParameters.scopes))
+ .setClaims(appTokenProviderParameters.claims)
+ .setTenantId(appTokenProviderParameters.tenantId);
+
+ Mono accessTokenAsync = getTokenFromTargetManagedIdentity(trc);
+
+ return accessTokenAsync.map(accessToken -> {
+ TokenProviderResult result = new TokenProviderResult();
+ result.setAccessToken(accessToken.getToken());
+ result.setTenantId(trc.getTenantId());
+ result.setExpiresInSeconds(accessToken.getExpiresAt().toEpochSecond());
+ return result;
+ }).toFuture();
+ });
+
+
+ initializeHttpPipelineAdapter();
+ if (httpPipelineAdapter != null) {
+ applicationBuilder.httpClient(httpPipelineAdapter);
+ } else {
+ applicationBuilder.proxy(proxyOptionsToJavaNetProxy(options.getProxyOptions()));
+ }
+
+ if (options.getExecutorService() != null) {
+ applicationBuilder.executorService(options.getExecutorService());
+ }
+
+ ConfidentialClientApplication confidentialClientApplication = applicationBuilder.build();
+ return Mono.just(confidentialClientApplication);
+ });
+ }
+
+ Mono getTokenFromTargetManagedIdentity(TokenRequestContext tokenRequestContext) {
+ ManagedIdentityParameters parameters = options.getManagedIdentityParameters();
+ ManagedIdentityType managedIdentityType = options.getManagedIdentityType();
+ switch (managedIdentityType) {
+ case APP_SERVICE:
+ return authenticateToManagedIdentityEndpoint(parameters.getIdentityEndpoint(),
+ parameters.getIdentityHeader(), parameters.getMsiEndpoint(), parameters.getMsiSecret(),
+ tokenRequestContext);
+ case SERVICE_FABRIC:
+ return authenticateToServiceFabricManagedIdentityEndpoint(parameters.getIdentityEndpoint(),
+ parameters.getIdentityHeader(), parameters.getIdentityServerThumbprint(), tokenRequestContext);
+ case ARC:
+ return authenticateToArcManagedIdentityEndpoint(parameters.getIdentityEndpoint(), tokenRequestContext);
+ case AKS:
+ return authenticateWithExchangeToken(tokenRequestContext);
+ case VM:
+ return authenticateToIMDSEndpoint(tokenRequestContext);
+ default:
+ return Mono.error(LOGGER.logExceptionAsError(
+ new CredentialUnavailableException("Unknown Managed Identity type, authentication not available.")));
+ }
+ }
+
private Mono parseClientAssertion() {
return Mono.fromCallable(() -> {
if (clientAssertionFilePath != null) {
@@ -288,7 +371,6 @@ private Mono parseClientAssertion() {
+ " It should be provided to authenticate with client assertion."
));
}
-
});
}
@@ -632,7 +714,6 @@ public Mono authenticateWithOBO(TokenRequestContext request) {
.map(MsalToken::new));
}
-
private Mono getAccessTokenFromPowerShell(TokenRequestContext request,
PowershellManager powershellManager) {
return powershellManager.initSession()
@@ -705,6 +786,18 @@ public Mono authenticateWithConfidentialClient(TokenRequestContext
)).map(MsalToken::new);
}
+ public Mono authenticateWithManagedIdentityConfidentialClient(TokenRequestContext request) {
+ return managedIdentityConfidentialClientApplicationAccessor.getValue()
+ .flatMap(confidentialClient -> Mono.fromFuture(() -> {
+ ClientCredentialParameters.ClientCredentialParametersBuilder builder =
+ ClientCredentialParameters.builder(new HashSet<>(request.getScopes()))
+ .tenant(IdentityUtil
+ .resolveTenantId(tenantId, request, options));
+ return confidentialClient.acquireToken(builder.build());
+ }
+ )).map(MsalToken::new);
+ }
+
private HttpPipeline setupPipeline(HttpClient httpClient) {
List policies = new ArrayList<>();
HttpLogOptions httpLogOptions = new HttpLogOptions();
@@ -1047,7 +1140,7 @@ public Mono authenticateWithSharedTokenCache(TokenRequestContext requ
* @param request the details of the token request
* @return a Publisher that emits an AccessToken
*/
- public Mono authenticateToArcManagedIdentityEndpoint(String identityEndpoint,
+ private Mono authenticateToArcManagedIdentityEndpoint(String identityEndpoint,
TokenRequestContext request) {
return Mono.fromCallable(() -> {
HttpURLConnection connection = null;
@@ -1142,7 +1235,7 @@ public Mono authenticateToArcManagedIdentityEndpoint(String identit
* @param request the details of the token request
* @return a Publisher that emits an AccessToken
*/
- public Mono authenticateWithExchangeToken(TokenRequestContext request) {
+ private Mono authenticateWithExchangeToken(TokenRequestContext request) {
return clientAssertionAccessor.getValue()
.flatMap(assertionToken -> Mono.fromCallable(() -> {
@@ -1201,7 +1294,7 @@ public Mono authenticateWithExchangeToken(TokenRequestContext reque
* @param request the details of the token request
* @return a Publisher that emits an AccessToken
*/
- public Mono authenticateToServiceFabricManagedIdentityEndpoint(String identityEndpoint,
+ private Mono authenticateToServiceFabricManagedIdentityEndpoint(String identityEndpoint,
String identityHeader,
String thumbprint,
TokenRequestContext request) {
@@ -1271,7 +1364,7 @@ public Mono authenticateToServiceFabricManagedIdentityEndpoint(Stri
* @param request the details of the token request
* @return a Publisher that emits an AccessToken
*/
- public Mono authenticateToManagedIdentityEndpoint(String identityEndpoint, String identityHeader,
+ private Mono authenticateToManagedIdentityEndpoint(String identityEndpoint, String identityHeader,
String msiEndpoint, String msiSecret,
TokenRequestContext request) {
return Mono.fromCallable(() -> {
@@ -1357,7 +1450,7 @@ static URL getUrl(String uri) throws MalformedURLException {
* @param request the details of the token request
* @return a Publisher that emits an AccessToken
*/
- public Mono authenticateToIMDSEndpoint(TokenRequestContext request) {
+ private Mono authenticateToIMDSEndpoint(TokenRequestContext request) {
String resource = ScopeUtil.scopesToResource(request.getScopes());
StringBuilder payload = new StringBuilder();
final int imdsUpgradeTimeInMs = 70 * 1000;
diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClientOptions.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClientOptions.java
index d347f511d12b..a924c77ee7dd 100644
--- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClientOptions.java
+++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClientOptions.java
@@ -50,6 +50,8 @@ public final class IdentityClientOptions {
private Configuration configuration;
private IdentityLogOptionsImpl identityLogOptionsImpl;
private boolean accountIdentifierLogging;
+ private ManagedIdentityType managedIdentityType;
+ private ManagedIdentityParameters managedIdentityParameters;
/**
* Creates an instance of IdentityClientOptions with default settings.
@@ -424,6 +426,43 @@ public IdentityClientOptions setIdentityLogOptionsImpl(IdentityLogOptionsImpl id
return this;
}
+ /**
+ * Set the Managed Identity Type
+ * @param managedIdentityType the Managed Identity Type
+ * @return the updated identity client options
+ */
+ public IdentityClientOptions setManagedIdentityType(ManagedIdentityType managedIdentityType) {
+ this.managedIdentityType = managedIdentityType;
+ return this;
+ }
+
+ /**
+ * Get the Managed Identity Type
+ * @return the Managed Identity Type
+ */
+ public ManagedIdentityType getManagedIdentityType() {
+ return managedIdentityType;
+ }
+
+ /**
+ * Get the Managed Identity parameters
+ * @return the Managed Identity Parameters
+ */
+ public ManagedIdentityParameters getManagedIdentityParameters() {
+ return managedIdentityParameters;
+ }
+
+ /**
+ * Configure the managed identity parameters.
+ *
+ * @param managedIdentityParameters the managed identity parameters to use for authentication.
+ * @return the updated identity client options
+ */
+ public IdentityClientOptions setManagedIdentityParameters(ManagedIdentityParameters managedIdentityParameters) {
+ this.managedIdentityParameters = managedIdentityParameters;
+ return this;
+ }
+
/**
* Loads the details from the specified Configuration Store.
*/
diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/ManagedIdentityParameters.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/ManagedIdentityParameters.java
new file mode 100644
index 000000000000..3e2ea918ba3e
--- /dev/null
+++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/ManagedIdentityParameters.java
@@ -0,0 +1,110 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.identity.implementation;
+
+/**
+ * Wrapper Class for Managed Identity Parameters.
+ */
+public class ManagedIdentityParameters {
+ private String identityEndpoint;
+ private String identityHeader;
+ private String msiEndpoint;
+ private String msiSecret;
+ private String identityServerThumbprint;
+
+ /**
+ * Creates an Instance of ManagedIdentityParameters.
+ */
+ public ManagedIdentityParameters() { }
+
+ /**
+ * Get the Identity Endpoint.
+ * @return the Identity Endpoint.
+ */
+ public String getIdentityEndpoint() {
+ return identityEndpoint;
+ }
+
+ /**
+ * Set the Identity Endpoint.
+ * @param identityEndpoint the Identity Endpoint.
+ * @return the {@link ManagedIdentityParameters}
+ */
+ public ManagedIdentityParameters setIdentityEndpoint(String identityEndpoint) {
+ this.identityEndpoint = identityEndpoint;
+ return this;
+ }
+
+ /**
+ * Get the Identity Header.
+ * @return the Identity Header.
+ */
+ public String getIdentityHeader() {
+ return identityHeader;
+ }
+
+ /**
+ * Set the Identity Header.
+ * @param identityHeader the Identity Header.
+ * @return the Identity Header
+ */
+ public ManagedIdentityParameters setIdentityHeader(String identityHeader) {
+ this.identityHeader = identityHeader;
+ return this;
+ }
+
+ /**
+ * Get the MSI Endpoint.
+ * @return the MSI Endpoint.
+ */
+ public String getMsiEndpoint() {
+ return msiEndpoint;
+ }
+
+ /**
+ * Set the MSI Endpoint
+ * @param msiEndpoint the MSI Endpoint
+ * @return the MSI endpoint.
+ */
+ public ManagedIdentityParameters setMsiEndpoint(String msiEndpoint) {
+ this.msiEndpoint = msiEndpoint;
+ return this;
+ }
+
+ /**
+ * Get the MSI Secret.
+ * @return the MSI Secret.
+ */
+ public String getMsiSecret() {
+ return msiSecret;
+ }
+
+ /**
+ * Set the MSI Secret
+ * @param msiSecret the MSI Secret
+ * @return the MSI Secret
+ */
+ public ManagedIdentityParameters setMsiSecret(String msiSecret) {
+ this.msiSecret = msiSecret;
+ return this;
+ }
+
+ /**
+ * Get the Identity Server Thumbprint
+ * @return the Identity Server Thumbprint
+ */
+ public String getIdentityServerThumbprint() {
+ return identityServerThumbprint;
+ }
+
+ /**
+ * Set the Identity Server Thumbprint
+ * @param identityServerThumbprint the Identity Server Thumbprint
+ * @return the Identity Server Thumbprint
+ */
+ public ManagedIdentityParameters setIdentityServerThumbprint(String identityServerThumbprint) {
+ this.identityServerThumbprint = identityServerThumbprint;
+ return this;
+ }
+}
diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/ManagedIdentityType.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/ManagedIdentityType.java
new file mode 100644
index 000000000000..bde15ec32f3f
--- /dev/null
+++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/ManagedIdentityType.java
@@ -0,0 +1,15 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.identity.implementation;
+
+/**
+ * Enum used to represent different Managed Identity platforms.
+ */
+public enum ManagedIdentityType {
+ VM,
+ APP_SERVICE,
+ SERVICE_FABRIC,
+ ARC,
+ AKS;
+}
diff --git a/sdk/identity/azure-identity/src/test/java/com/azure/identity/AzureApplicationCredentialTest.java b/sdk/identity/azure-identity/src/test/java/com/azure/identity/AzureApplicationCredentialTest.java
index 92823bde921f..2832ea86f7c8 100644
--- a/sdk/identity/azure-identity/src/test/java/com/azure/identity/AzureApplicationCredentialTest.java
+++ b/sdk/identity/azure-identity/src/test/java/com/azure/identity/AzureApplicationCredentialTest.java
@@ -67,7 +67,7 @@ public void testUseManagedIdentityCredential() throws Exception {
// mock
try (MockedConstruction identityClientMock = mockConstruction(IdentityClient.class, (identityClient, context) -> {
- when(identityClient.authenticateToIMDSEndpoint(request)).thenReturn(TestUtils.getMockAccessToken(token1, expiresAt));
+ when(identityClient.authenticateWithManagedIdentityConfidentialClient(request)).thenReturn(TestUtils.getMockAccessToken(token1, expiresAt));
}); MockedConstruction intelliCredentialMock = mockConstruction(IntelliJCredential.class, (intelliJCredential, context) -> {
when(intelliJCredential.getToken(request)).thenReturn(Mono.empty());
@@ -91,7 +91,7 @@ public void testNoCredentialWorks() throws Exception {
Configuration configuration = new ConfigurationBuilder(source, source, source).build();
// mock
try (MockedConstruction identityClientMock = mockConstruction(IdentityClient.class, (identityClient, context) -> {
- when(identityClient.authenticateToIMDSEndpoint(request))
+ when(identityClient.authenticateWithManagedIdentityConfidentialClient(request))
.thenReturn(Mono.error(new CredentialUnavailableException("Cannot get token from managed identity")));
})) {
// test
diff --git a/sdk/identity/azure-identity/src/test/java/com/azure/identity/DefaultAzureCredentialTest.java b/sdk/identity/azure-identity/src/test/java/com/azure/identity/DefaultAzureCredentialTest.java
index f7862d222745..b70302fd5751 100644
--- a/sdk/identity/azure-identity/src/test/java/com/azure/identity/DefaultAzureCredentialTest.java
+++ b/sdk/identity/azure-identity/src/test/java/com/azure/identity/DefaultAzureCredentialTest.java
@@ -70,7 +70,7 @@ public void testUseManagedIdentityCredential() throws Exception {
// mock
try (MockedConstruction mocked = mockConstruction(IdentityClient.class, (identityClient, context) -> {
- when(identityClient.authenticateToIMDSEndpoint(request)).thenReturn(TestUtils.getMockAccessToken(token1, expiresAt));
+ when(identityClient.authenticateWithManagedIdentityConfidentialClient(request)).thenReturn(TestUtils.getMockAccessToken(token1, expiresAt));
}); MockedConstruction ijcredential = mockConstruction(IntelliJCredential.class, (intelliJCredential, context) -> {
when(intelliJCredential.getToken(request)).thenReturn(Mono.empty());
})) {
@@ -96,7 +96,7 @@ public void testUseAzureCliCredential() throws Exception {
// mock
try (MockedConstruction mocked = mockConstruction(IdentityClient.class, (identityClient, context) -> {
when(identityClient.authenticateWithAzureCli(request)).thenReturn(TestUtils.getMockAccessToken(token1, expiresAt));
- when(identityClient.authenticateToIMDSEndpoint(request)).thenReturn(Mono.empty());
+ when(identityClient.authenticateWithManagedIdentityConfidentialClient(request)).thenReturn(Mono.empty());
when(identityClient.authenticateWithSharedTokenCache(request, null)).thenReturn(Mono.empty());
when(identityClient.authenticateWithIntelliJ(request)).thenReturn(Mono.empty());
when(identityClient.authenticateWithVsCodeCredential(any(), any())).thenReturn(Mono.empty());
@@ -121,7 +121,7 @@ public void testNoCredentialWorks() throws Exception {
// mock
try (MockedConstruction identityClientMock = mockConstruction(IdentityClient.class, (identityClient, context) -> {
- when(identityClient.authenticateToIMDSEndpoint(request)).thenReturn(Mono.error(new CredentialUnavailableException("Cannot get token from managed identity")));
+ when(identityClient.authenticateWithManagedIdentityConfidentialClient(request)).thenReturn(Mono.error(new CredentialUnavailableException("Cannot get token from managed identity")));
}); MockedConstruction sharedTokenCacheCredentialMock = mockConstruction(SharedTokenCacheCredential.class, (sharedTokenCacheCredential, context) -> {
when(sharedTokenCacheCredential.getToken(request)).thenReturn(Mono.error(new CredentialUnavailableException("Cannot get token from shared token cache")));
}); MockedConstruction azureCliCredentialMock = mockConstruction(AzureCliCredential.class, (azureCliCredential, context) -> {
diff --git a/sdk/identity/azure-identity/src/test/java/com/azure/identity/ManagedIdentityCredentialTest.java b/sdk/identity/azure-identity/src/test/java/com/azure/identity/ManagedIdentityCredentialTest.java
index 6bc79e93738b..46ea04022e78 100644
--- a/sdk/identity/azure-identity/src/test/java/com/azure/identity/ManagedIdentityCredentialTest.java
+++ b/sdk/identity/azure-identity/src/test/java/com/azure/identity/ManagedIdentityCredentialTest.java
@@ -50,7 +50,7 @@ public void testMSIEndpoint() throws Exception {
// mock
try (MockedConstruction identityClientMock = mockConstruction(IdentityClient.class, (identityClient, context) -> {
- when(identityClient.authenticateToManagedIdentityEndpoint(endpoint, secret, endpoint, secret, request1)).thenReturn(TestUtils.getMockAccessToken(token1, expiresAt));
+ when(identityClient.authenticateWithManagedIdentityConfidentialClient(request1)).thenReturn(TestUtils.getMockAccessToken(token1, expiresAt));
})) {
// test
ManagedIdentityCredential credential = new ManagedIdentityCredentialBuilder().configuration(configuration).clientId(CLIENT_ID).build();
@@ -78,7 +78,7 @@ public void testIMDS() throws Exception {
// mock
try (MockedConstruction identityClientMock = mockConstruction(IdentityClient.class, (identityClient, context) -> {
- when(identityClient.authenticateToIMDSEndpoint(request)).thenReturn(TestUtils.getMockAccessToken(token1, expiresOn));
+ when(identityClient.authenticateWithManagedIdentityConfidentialClient(request)).thenReturn(TestUtils.getMockAccessToken(token1, expiresOn));
})) {
// test
diff --git a/sdk/identity/azure-identity/src/test/java/com/azure/identity/implementation/IdentityClientTests.java b/sdk/identity/azure-identity/src/test/java/com/azure/identity/implementation/IdentityClientTests.java
index 29619d5d37b8..ac5983770b94 100644
--- a/sdk/identity/azure-identity/src/test/java/com/azure/identity/implementation/IdentityClientTests.java
+++ b/sdk/identity/azure-identity/src/test/java/com/azure/identity/implementation/IdentityClientTests.java
@@ -189,11 +189,16 @@ public void testValidServiceFabricCodeFlow() throws Exception {
String tokenJson = "{ \"access_token\" : \"token1\", \"expires_on\" : \"" + expiresOn.toEpochSecond() + "\" }";
// mock
- IdentityClient client = new IdentityClientBuilder().build();
+ IdentityClientOptions options = new IdentityClientOptions()
+ .setManagedIdentityType(ManagedIdentityType.SERVICE_FABRIC)
+ .setManagedIdentityParameters(new ManagedIdentityParameters()
+ .setIdentityEndpoint(endpoint)
+ .setIdentityHeader(secret)
+ .setIdentityServerThumbprint(thumbprint));
+ IdentityClient client = new IdentityClientBuilder().identityClientOptions(options).build();
mockForServiceFabricCodeFlow(tokenJson, () -> {
// test
- AccessToken token = client.authenticateToServiceFabricManagedIdentityEndpoint(endpoint, secret,
- thumbprint, request).block();
+ AccessToken token = client.getTokenFromTargetManagedIdentity(request).block();
Assert.assertEquals("token1", token.getToken());
Assert.assertEquals(expiresOn.getSecond(), token.getExpiresAt().getSecond());
});
@@ -213,10 +218,15 @@ public void testValidIdentityEndpointMSICodeFlow() throws Exception {
String tokenJson = "{ \"access_token\" : \"token1\", \"expires_on\" : \"" + expiresOn.format(dtf) + "\" }";
// mock
- IdentityClient client = new IdentityClientBuilder().build();
+ IdentityClientOptions options = new IdentityClientOptions()
+ .setManagedIdentityType(ManagedIdentityType.APP_SERVICE)
+ .setManagedIdentityParameters(new ManagedIdentityParameters()
+ .setIdentityEndpoint(endpoint)
+ .setIdentityHeader(secret));
+ IdentityClient client = new IdentityClientBuilder().identityClientOptions(options).build();
mockForMSICodeFlow(tokenJson, () -> {
// test
- AccessToken token = client.authenticateToManagedIdentityEndpoint(endpoint, secret, null, null, request).block();
+ AccessToken token = client.getTokenFromTargetManagedIdentity(request).block();
Assert.assertEquals("token1", token.getToken());
Assert.assertEquals(expiresOn.getSecond(), token.getExpiresAt().getSecond());
});
@@ -230,9 +240,13 @@ public void testInValidIdentityEndpointSecretArcCodeFlow() throws Exception {
TokenRequestContext request = new TokenRequestContext().addScopes("https://management.azure.com");
configuration.put("IDENTITY_ENDPOINT", endpoint);
// mock
- IdentityClient client = new IdentityClientBuilder().build();
+ IdentityClientOptions options = new IdentityClientOptions()
+ .setManagedIdentityType(ManagedIdentityType.ARC)
+ .setManagedIdentityParameters(new ManagedIdentityParameters()
+ .setIdentityEndpoint(endpoint));
+ IdentityClient client = new IdentityClientBuilder().identityClientOptions(options).build();
mockForArcCodeFlow(401, () -> {
- client.authenticateToArcManagedIdentityEndpoint(endpoint, request).block();
+ client.getTokenFromTargetManagedIdentity(request).block();
});
}
@@ -243,10 +257,14 @@ public void testInValidIdentityEndpointResponseCodeArcCodeFlow() throws Exceptio
String endpoint = "http://localhost";
TokenRequestContext request = new TokenRequestContext().addScopes("https://management.azure.com");
configuration.put("IDENTITY_ENDPOINT", endpoint);
- IdentityClient client = new IdentityClientBuilder().build();
+ IdentityClientOptions options = new IdentityClientOptions()
+ .setManagedIdentityType(ManagedIdentityType.ARC)
+ .setManagedIdentityParameters(new ManagedIdentityParameters()
+ .setIdentityEndpoint(endpoint));
+ IdentityClient client = new IdentityClientBuilder().identityClientOptions(options).build();
// mock
mockForArcCodeFlow(200, () -> {
- client.authenticateToArcManagedIdentityEndpoint(endpoint, request).block();
+ client.getTokenFromTargetManagedIdentity(request).block();
});
}
@@ -264,11 +282,13 @@ public void testValidIMDSCodeFlow() throws Exception {
String tokenJson = "{ \"access_token\" : \"token1\", \"expires_on\" : \"" + expiresOn.format(dtf) + "\" }";
- IdentityClient client = new IdentityClientBuilder().build();
+ IdentityClientOptions options = new IdentityClientOptions()
+ .setManagedIdentityType(ManagedIdentityType.VM);
+ IdentityClient client = new IdentityClientBuilder().identityClientOptions(options).build();
// mock
mockForIMDSCodeFlow(IdentityConstants.DEFAULT_IMDS_ENDPOINT, tokenJson, () -> {
// test
- AccessToken token = client.authenticateToIMDSEndpoint(request).block();
+ AccessToken token = client.getTokenFromTargetManagedIdentity(request).block();
Assert.assertEquals("token1", token.getToken());
Assert.assertEquals(expiresOn.getSecond(), token.getExpiresAt().getSecond());
});
@@ -286,11 +306,13 @@ public void testCustomIMDSCodeFlow() throws Exception {
String tokenJson = "{ \"access_token\" : \"token1\", \"expires_on\" : \"" + expiresOn.format(dtf) + "\" }";
- IdentityClient client = new IdentityClientBuilder().build();
+ IdentityClientOptions options = new IdentityClientOptions()
+ .setManagedIdentityType(ManagedIdentityType.VM);
+ IdentityClient client = new IdentityClientBuilder().identityClientOptions(options).build();
// mock
mockForIMDSCodeFlow(endpoint, tokenJson, () -> {
// test
- AccessToken token = client.authenticateToIMDSEndpoint(request).block();
+ AccessToken token = client.getTokenFromTargetManagedIdentity(request).block();
Assert.assertEquals("token1", token.getToken());
Assert.assertEquals(expiresOn.getSecond(), token.getExpiresAt().getSecond());
});
@@ -393,7 +415,61 @@ public void testOpenUrl() throws Exception {
}
}
+ @Test
+ public void testAuthWithManagedIdentityFlow() throws Exception {
+ // setup
+ String secret = "SYSTEM-ASSIGNED-CLIENT-SECRET";
+ String clientId = "SYSTEM-ASSIGNED-CLIENT-ID";
+ String accessToken = "token";
+ TokenRequestContext request = new TokenRequestContext().addScopes("https://management.azure.com");
+ OffsetDateTime expiresOn = OffsetDateTime.now(ZoneOffset.UTC).plusHours(1);
+
+ // mock
+ mockForManagedIdentityFlow(secret, clientId, request, accessToken, expiresOn, () -> {
+ // test
+ IdentityClient client = new IdentityClientBuilder()
+ .tenantId(TENANT_ID)
+ .clientId(clientId)
+ .clientSecret(secret)
+ .identityClientOptions(new IdentityClientOptions()
+ .setManagedIdentityType(ManagedIdentityType.VM))
+ .build();
+ AccessToken token = client.authenticateWithManagedIdentityConfidentialClient(request).block();
+ Assert.assertEquals(accessToken, token.getToken());
+ Assert.assertEquals(expiresOn.getSecond(), token.getExpiresAt().getSecond());
+ });
+ }
+
/****** mocks ******/
+ private void mockForManagedIdentityFlow(String secret, String clientId, TokenRequestContext request, String accessToken, OffsetDateTime expiresOn, Runnable test) throws Exception {
+
+ try (MockedStatic staticConfidentialClientApplicationMock = mockStatic(ConfidentialClientApplication.class); MockedConstruction confidentialClientApplicationBuilderMock = mockConstruction(ConfidentialClientApplication.Builder.class, (builder, context) -> {
+
+ when(builder.authority(any())).thenReturn(builder);
+ when(builder.httpClient(any())).thenReturn(builder);
+ when(builder.appTokenProvider(any())).thenReturn(builder);
+ ConfidentialClientApplication application = Mockito.mock(ConfidentialClientApplication.class);
+ when(application.acquireToken(any(ClientCredentialParameters.class))).thenAnswer(invocation -> {
+ ClientCredentialParameters argument = (ClientCredentialParameters) invocation.getArguments()[0];
+ if (argument.scopes().size() == 1 && request.getScopes().get(0).equals(argument.scopes().iterator().next())) {
+ return TestUtils.getMockAuthenticationResult(accessToken, expiresOn);
+ } else {
+ return CompletableFuture.runAsync(() -> {
+ throw new MsalServiceException("Invalid request", "InvalidScopes");
+ });
+ }
+ });
+ when(builder.build()).thenReturn(application);
+ })) {
+ // Mocking the static builder to ensure we pass the right thing to it.
+ staticConfidentialClientApplicationMock.when(() -> ConfidentialClientApplication.builder(eq(clientId), argThat(cred -> ((IClientSecret) cred).clientSecret().equals(secret)))).thenCallRealMethod();
+ staticConfidentialClientApplicationMock.when(() -> ConfidentialClientApplication.builder(anyString(), argThat(cred -> !((IClientSecret) cred).clientSecret().equals(secret)))).thenThrow(new MsalServiceException("Invalid clientSecret", "InvalidClientSecret"));
+ staticConfidentialClientApplicationMock.when(() -> ConfidentialClientApplication.builder(AdditionalMatchers.not(eq(clientId)), any(IClientSecret.class))).thenThrow(new MsalServiceException("Invalid CLIENT_ID", "InvalidClientId"));
+
+ test.run();
+ Assert.assertNotNull(confidentialClientApplicationBuilderMock);
+ }
+ }
private void mockForClientSecret(String secret, TokenRequestContext request, String accessToken, OffsetDateTime expiresOn, Runnable test) throws Exception {