Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.identity;

import com.azure.core.credential.AccessToken;
import com.azure.core.credential.TokenRequestContext;
import com.azure.identity.implementation.IdentityClient;

import reactor.core.publisher.Mono;

/**
* Authenticates a service principal with AAD using a client assertion.
*/
class ClientAssertionCredential extends ManagedIdentityServiceCredential {

/**
* Creates an instance of ClientAssertionCredential.
*
* @param clientId the client id of user assigned or system assigned identity.
* @param identityClient the identity client to acquire a token with.
*/
ClientAssertionCredential(String clientId, IdentityClient identityClient) {
super(clientId, identityClient, "AZURE AKS TOKEN EXCHANGE");
}

@Override
public Mono<AccessToken> authenticate(TokenRequestContext request) {
return identityClient.authenticatewithExchangeToken(request);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import com.azure.core.credential.TokenRequestContext;
import com.azure.core.util.Configuration;
import com.azure.core.util.logging.ClientLogger;
import com.azure.identity.implementation.IdentityClient;
import com.azure.identity.implementation.IdentityClientBuilder;
import com.azure.identity.implementation.IdentityClientOptions;
import com.azure.identity.implementation.util.LoggingUtil;
Expand All @@ -25,6 +24,7 @@ public final class ManagedIdentityCredential implements TokenCredential {

static final String PROPERTY_IMDS_ENDPOINT = "IMDS_ENDPOINT";
static final String PROPERTY_IDENTITY_SERVER_THUMBPRINT = "IDENTITY_SERVER_THUMBPRINT";
static final String TOKEN_FILE_PATH = "TOKEN_FILE_PATH";


/**
Expand All @@ -33,28 +33,35 @@ public final class ManagedIdentityCredential implements TokenCredential {
* @param identityClientOptions the options for configuring the identity client.
*/
ManagedIdentityCredential(String clientId, IdentityClientOptions identityClientOptions) {
IdentityClient identityClient = new IdentityClientBuilder()
IdentityClientBuilder clientBuilder = new IdentityClientBuilder()
.clientId(clientId)
.identityClientOptions(identityClientOptions)
.build();
.identityClientOptions(identityClientOptions);

Configuration configuration = Configuration.getGlobalConfiguration().clone();

if (configuration.contains(Configuration.PROPERTY_MSI_ENDPOINT)) {
managedIdentityServiceCredential = new AppServiceMsiCredential(clientId, identityClient);
managedIdentityServiceCredential = new AppServiceMsiCredential(clientId, clientBuilder.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, identityClient);
managedIdentityServiceCredential = new ServiceFabricMsiCredential(clientId, clientBuilder.build());
} else {
managedIdentityServiceCredential = new VirtualMachineMsiCredential(clientId, identityClient);
managedIdentityServiceCredential = new VirtualMachineMsiCredential(clientId, clientBuilder.build());
}
} else if (configuration.get(PROPERTY_IMDS_ENDPOINT) != null) {
managedIdentityServiceCredential = new ArcIdentityCredential(clientId, identityClient);
managedIdentityServiceCredential = new ArcIdentityCredential(clientId, clientBuilder.build());
} else {
managedIdentityServiceCredential = new VirtualMachineMsiCredential(clientId, identityClient);
managedIdentityServiceCredential = new VirtualMachineMsiCredential(clientId, clientBuilder.build());
}
} else if (configuration.contains(Configuration.PROPERTY_AZURE_CLIENT_ID)
&& configuration.contains(Configuration.PROPERTY_AZURE_TENANT_ID)
&& configuration.get(TOKEN_FILE_PATH) != null) {
clientBuilder.tenantId(configuration.get(Configuration.PROPERTY_AZURE_TENANT_ID));
clientBuilder.clientAssertionPath(configuration.get(TOKEN_FILE_PATH));
managedIdentityServiceCredential = new ClientAssertionCredential(clientId, clientBuilder.build());

} else {
managedIdentityServiceCredential = new VirtualMachineMsiCredential(clientId, identityClient);
managedIdentityServiceCredential = new VirtualMachineMsiCredential(clientId, clientBuilder.build());
}
LoggingUtil.logAvailableEnvironmentVariables(logger, configuration);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.identity;

import com.azure.core.credential.AccessToken;
import com.azure.core.credential.TokenCredential;
import com.azure.core.credential.TokenRequestContext;
import com.azure.core.util.logging.ClientLogger;
import com.azure.identity.implementation.IdentityClient;
import com.azure.identity.implementation.IdentityClientBuilder;
import com.azure.identity.implementation.IdentityClientOptions;
import com.azure.identity.implementation.util.LoggingUtil;
import reactor.core.publisher.Mono;

import java.time.Duration;

/**
* An AAD credential that acquires a token with a client secret and user assertion for an AAD application
* on behalf of a user principal.
*/
public class OnBehalfOfCredential implements TokenCredential {
private final IdentityClient identityClient;
private final ClientLogger logger = new ClientLogger(OnBehalfOfCredential.class);


/**
* Creates OnBehalfOfCredential with the specified AAD application details and client options.
*
* @param tenantId the tenant ID of the application
* @param clientId the client ID of the application
* @param clientSecret the secret value of the AAD application.
* @param certificatePath the PEM file or PFX file containing the certificate
* @param certificatePassword the password protecting the PFX file
* @param identityClientOptions the options for configuring the identity client
*/
public OnBehalfOfCredential(String clientId, String tenantId, String clientSecret, String certificatePath,
String certificatePassword, IdentityClientOptions identityClientOptions) {
this.identityClient = new IdentityClientBuilder()
.tenantId(tenantId)
.clientId(clientId)
.clientSecret(clientSecret)
.certificatePath(certificatePath)
.certificatePassword(certificatePassword)
.identityClientOptions(identityClientOptions)
.confidentialClientCacheTimeout(Duration.ofMinutes(5))
.build();
}

@Override
public Mono<AccessToken> getToken(TokenRequestContext request) {
return Mono.deferContextual(ctx -> identityClient.authenticateWithConfidentialClientCache(request)
.onErrorResume(t -> Mono.empty())
.switchIfEmpty(Mono.defer(() -> identityClient.authenticateWithOBO(request)))
.doOnNext(token -> LoggingUtil.logTokenSuccess(logger, request))
.doOnError(error -> LoggingUtil.logTokenError(logger, request, error)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.identity;

import com.azure.core.util.logging.ClientLogger;
import com.azure.identity.implementation.util.ValidationUtil;

import java.util.HashMap;

/**
* Fluent credential builder for instantiating a {@link OnBehalfOfCredential}.
*
* @see OnBehalfOfCredential
*/
public class OnBehalfOfCredentialBuilder extends AadCredentialBuilderBase<OnBehalfOfCredentialBuilder> {
Comment thread
g2vinay marked this conversation as resolved.
private String clientSecret;
private String clientCertificatePath;
private String clientCertificatePassword;
private final ClientLogger logger = new ClientLogger(OnBehalfOfCredentialBuilder.class);

/**
* Sets the client secret for the authentication.
* @param clientSecret the secret value of the AAD application.
* @return An updated instance of this builder.
*/
public OnBehalfOfCredentialBuilder clientSecret(String clientSecret) {
this.clientSecret = clientSecret;
return this;
}

/**
* Configures the persistent shared token cache options and enables the persistent token cache which is disabled
* by default. If configured, the credential will store tokens in a cache persisted to the machine, protected to
* the current user, which can be shared by other credentials and processes.
*
* @param tokenCachePersistenceOptions the token cache configuration options
* @return An updated instance of this builder with the token cache options configured.
*/
public OnBehalfOfCredentialBuilder tokenCachePersistenceOptions(TokenCachePersistenceOptions
tokenCachePersistenceOptions) {
this.identityClientOptions.setTokenCacheOptions(tokenCachePersistenceOptions);
return this;
}

/**
* Sets the path and password of the PFX certificate for authenticating to AAD.
*
* @param certificatePath the password protected PFX file containing the certificate
* @param clientCertificatePassword the password protecting the PFX file
* @return An updated instance of this builder.
*/
public OnBehalfOfCredentialBuilder pfxCertificate(String certificatePath,
String clientCertificatePassword) {
this.clientCertificatePath = certificatePath;
this.clientCertificatePassword = clientCertificatePassword;
return this;
}

/**
* Specifies if the x5c claim (public key of the certificate) should be sent as part of the authentication request
* and enable subject name / issuer based authentication. The default value is false.
*
* @param sendCertificateChain the flag to indicate if certificate chain should be sent as part of authentication
* request.
* @return An updated instance of this builder.
*/
public OnBehalfOfCredentialBuilder sendCertificateChain(boolean sendCertificateChain) {
this.identityClientOptions.setIncludeX5c(sendCertificateChain);
return this;
}

/**
* Specifies either the specific regional authority, or use {@link RegionalAuthority#AUTO_DISCOVER_REGION} to
* attempt to auto-detect the region. If unset, a non-regional authority will be used. This argument should be used
* only by applications deployed to Azure VMs.
*
* @param regionalAuthority the regional authority
* @return An updated instance of this builder with the regional authority configured.
*/
public OnBehalfOfCredentialBuilder regionalAuthority(RegionalAuthority regionalAuthority) {
this.identityClientOptions.setRegionalAuthority(regionalAuthority);
return this;
}

/**
* Configure the User Assertion Scope to be used for OnBehalfOf Authentication request.
*
* @param userAssertion the user assertion access token to be used for On behalf Of authentication flow
* @return An updated instance of this builder with the user assertion scope configured.
*/
public OnBehalfOfCredentialBuilder userAssertion(String userAssertion) {
this.identityClientOptions.userAssertion(userAssertion);
return this;
}
Comment thread
g2vinay marked this conversation as resolved.

/**
* Creates a new {@link OnBehalfOfCredential} with the current configurations.
*
* @return a {@link OnBehalfOfCredential} with the current configurations.
* @throws IllegalArgumentException if eiter both the client secret and certificate are configured or none of them
* are configured.
*/
public OnBehalfOfCredential build() {
ValidationUtil.validate(getClass().getSimpleName(), new HashMap<String, Object>() {
{
put("clientId", clientId);
put("tenantId", tenantId);
}
});

if (clientSecret == null && clientCertificatePath == null) {
throw logger.logExceptionAsWarning(new IllegalArgumentException("Atleast client secret or certificate "
+ "path should provided in OnBhealfOfCredentialBuilder. Only one of them should "
+ "be provided."));
}

if (clientCertificatePath != null && clientSecret != null) {
throw logger.logExceptionAsWarning(new IllegalArgumentException("Both client secret and certificate "
+ "path are provided in OnBhealfCredentialBuilder. Only one of them should "
+ "be provided."));
}

return new OnBehalfOfCredential(clientId, tenantId, clientSecret, clientCertificatePath,
clientCertificatePassword, identityClientOptions);
}
}
Loading