Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Token Caching Support For Managed Identity #30282

Merged
merged 13 commits into from
Aug 12, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eng/versioning/external_dependencies.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion sdk/eventhubs/microsoft-azure-eventhubs-eph/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>msal4j</artifactId>
<version>1.12.0</version> <!-- {x-version-update;com.microsoft.azure:msal4j;external_dependency} -->
<version>1.13.0</version> <!-- {x-version-update;com.microsoft.azure:msal4j;external_dependency} -->
<scope>test</scope>
</dependency>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>msal4j</artifactId>
<version>1.12.0</version> <!-- {x-version-update;com.microsoft.azure:msal4j;external_dependency} -->
<version>1.13.0</version> <!-- {x-version-update;com.microsoft.azure:msal4j;external_dependency} -->
<scope>test</scope>
</dependency>
<dependency>
Expand Down
2 changes: 1 addition & 1 deletion sdk/eventhubs/microsoft-azure-eventhubs/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>msal4j</artifactId>
<version>1.12.0</version> <!-- {x-version-update;com.microsoft.azure:msal4j;external_dependency} -->
<version>1.13.0</version> <!-- {x-version-update;com.microsoft.azure:msal4j;external_dependency} -->
<scope>test</scope>
</dependency>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ private IdentityClientOptions updateIdentityClientOptions(ManagedIdentityType ma
case AKS:
return clientOptions.setManagedIdentityType(ManagedIdentityType.AKS);
default:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is NONE really a valid value? should this be an error?

Copy link
Member Author

@g2vinay g2vinay Aug 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed NONE on second design iteration.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I'm not sure that's entirely right either - now won't the default value be VM? We probably need a NONE or DEFAULT to indicate the unchosen value?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah in our code flow we default to VM, that's our default based on the logic we follow.
None or Unchosen are invalid states to have and are equivalent to the value being null.

return clientOptions.setManagedIdentityType(ManagedIdentityType.NONE);
return clientOptions;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,31 +288,36 @@ private Mono<ConfidentialClientApplication> getManagedIdentityConfidentialClient
+ "/" + tenantId;

// Temporarily pass in Dummy Client secret and Client ID. until MSal removes its requirements.
g2vinay marked this conversation as resolved.
Show resolved Hide resolved
IClientCredential credential = ClientCredentialFactory.createFromSecret("dummy-secret");
IClientCredential credential = ClientCredentialFactory
.createFromSecret(clientSecret != null ? clientSecret : "dummy-secret");
ConfidentialClientApplication.Builder applicationBuilder =
ConfidentialClientApplication.builder(clientId == null ? IdentityConstants.DEVELOPER_SINGLE_SIGN_ON_ID
ConfidentialClientApplication.builder(clientId == null ? "SYSTEM-ASSIGNED-MANAGED-IDENTITY"
billwert marked this conversation as resolved.
Show resolved Hide resolved
: 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<AccessToken> accessTokenMono = getTokenFromTargetManagedIdentity(options.getManagedIdentityType(), trc);
Mono<AccessToken> accessTokenAsync = getTokenFromTargetManagedIdentity(trc);

return accessTokenMono.toFuture().thenApply(accessToken -> {
return accessTokenAsync.map(accessToken -> {
TokenProviderResult result = new TokenProviderResult();
result.setAccessToken(accessToken.getToken());
result.setTenantId(trc.getTenantId());
result.setExpiresInSeconds(accessToken.getExpiresAt().toEpochSecond());
return result;
});
}).toFuture();
});


Expand All @@ -332,21 +337,26 @@ private Mono<ConfidentialClientApplication> getManagedIdentityConfidentialClient
});
}

private Mono<AccessToken> getTokenFromTargetManagedIdentity(ManagedIdentityType managedIdentityType, TokenRequestContext trc) {
Mono<AccessToken> 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(), trc);
parameters.getIdentityHeader(), parameters.getMsiEndpoint(), parameters.getMsiSecret(),
tokenRequestContext);
case SERVICE_FABRIC:
return authenticateToServiceFabricManagedIdentityEndpoint(parameters.getIdentityEndpoint(),
parameters.getIdentityHeader(), parameters.getIdentityServerThumbprint(), trc);
parameters.getIdentityHeader(), parameters.getIdentityServerThumbprint(), tokenRequestContext);
case ARC:
return authenticateToArcManagedIdentityEndpoint(parameters.getIdentityEndpoint(), trc);
return authenticateToArcManagedIdentityEndpoint(parameters.getIdentityEndpoint(), tokenRequestContext);
case AKS:
return authenticateWithExchangeToken(trc);
return authenticateWithExchangeToken(tokenRequestContext);
case VM:
return authenticateToIMDSEndpoint(tokenRequestContext);
default:
return authenticateToIMDSEndpoint(trc);
return Mono.error(LOGGER.logExceptionAsError(
new CredentialUnavailableException("Unknown Managed Identity type, authentication not available.")));
}
}

Expand Down Expand Up @@ -1131,7 +1141,7 @@ public Mono<MsalToken> authenticateWithSharedTokenCache(TokenRequestContext requ
* @param request the details of the token request
* @return a Publisher that emits an AccessToken
*/
public Mono<AccessToken> authenticateToArcManagedIdentityEndpoint(String identityEndpoint,
Mono<AccessToken> authenticateToArcManagedIdentityEndpoint(String identityEndpoint,
g2vinay marked this conversation as resolved.
Show resolved Hide resolved
TokenRequestContext request) {
return Mono.fromCallable(() -> {
HttpURLConnection connection = null;
Expand Down Expand Up @@ -1285,7 +1295,7 @@ public Mono<AccessToken> authenticateWithExchangeToken(TokenRequestContext reque
* @param request the details of the token request
* @return a Publisher that emits an AccessToken
*/
public Mono<AccessToken> authenticateToServiceFabricManagedIdentityEndpoint(String identityEndpoint,
Mono<AccessToken> authenticateToServiceFabricManagedIdentityEndpoint(String identityEndpoint,
String identityHeader,
String thumbprint,
TokenRequestContext request) {
Expand Down Expand Up @@ -1355,7 +1365,7 @@ public Mono<AccessToken> authenticateToServiceFabricManagedIdentityEndpoint(Stri
* @param request the details of the token request
* @return a Publisher that emits an AccessToken
*/
public Mono<AccessToken> authenticateToManagedIdentityEndpoint(String identityEndpoint, String identityHeader,
Mono<AccessToken> authenticateToManagedIdentityEndpoint(String identityEndpoint, String identityHeader,
String msiEndpoint, String msiSecret,
TokenRequestContext request) {
return Mono.fromCallable(() -> {
Expand Down Expand Up @@ -1441,7 +1451,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<AccessToken> authenticateToIMDSEndpoint(TokenRequestContext request) {
Mono<AccessToken> authenticateToIMDSEndpoint(TokenRequestContext request) {
String resource = ScopeUtil.scopesToResource(request.getScopes());
StringBuilder payload = new StringBuilder();
final int imdsUpgradeTimeInMs = 70 * 1000;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ public final class IdentityClientOptions {
public IdentityClientOptions() {
Configuration configuration = Configuration.getGlobalConfiguration().clone();
loadFromConfiguration(configuration);
managedIdentityType = ManagedIdentityType.NONE;
identityLogOptionsImpl = new IdentityLogOptionsImpl();
maxRetry = MAX_RETRY_DEFAULT_LIMIT;
retryTimeout = i -> Duration.ofSeconds((long) Math.pow(2, i.getSeconds() - 1));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,55 +1,108 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.identity.implementation;
g2vinay marked this conversation as resolved.
Show resolved Hide resolved

/**
* 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() { }
g2vinay marked this conversation as resolved.
Show resolved Hide resolved


/**
* 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;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +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 {
g2vinay marked this conversation as resolved.
Show resolved Hide resolved
VM, APP_SERVICE, SERVICE_FABRIC, ARC, AKS, NONE;
VM,
APP_SERVICE,
SERVICE_FABRIC,
ARC,
AKS;
}
Loading