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 Identity V2 Credentials #44736

Merged
merged 17 commits into from
Mar 25, 2025
Merged
2 changes: 1 addition & 1 deletion eng/versioning/version_client.txt
Original file line number Diff line number Diff line change
@@ -125,7 +125,7 @@ com.azure:azure-health-insights-clinicalmatching;1.0.0-beta.1;1.0.0-beta.2
com.azure:azure-health-insights-cancerprofiling;1.0.0-beta.1;1.0.0-beta.2
com.azure:azure-health-insights-radiologyinsights;1.0.0;1.1.0-beta.1
com.azure:azure-identity;1.15.4;1.16.0-beta.2
com.azure:azure-identity-v2;2.0.0-beta.1;2.0.0-beta.1
com.azure.v2:azure-identity-v2;2.0.0-beta.1;2.0.0-beta.1
com.azure:azure-identity-extensions;1.2.1;1.3.0-beta.1
com.azure:azure-identity-broker;1.1.13;1.2.0-beta.1
com.azure:azure-identity-broker-samples;1.0.0-beta.1;1.0.0-beta.1
25 changes: 25 additions & 0 deletions sdk/identity/azure-identity-v2/checkstyle-suppressions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suppressions PUBLIC "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN" "https://checkstyle.org/dtds/suppressions_1_2.dtd">
<!-- This file is generated by the /eng/scripts/linting_suppression_generator.py script. -->

<suppressions>
<suppress files="com.azure.v2.identity.implementation.client.AzureToolkitCacheAccessor.java" checks="IllegalImportCheck" />
<suppress files="com.azure.v2.identity.implementation.client.DevToolsClient.java" checks="IllegalImportCheck" />
<suppress files="com.azure.v2.identity.implementation.client.LinuxKeyRingAccessor.java" checks="IllegalImportCheck" />
<suppress files="com.azure.v2.identity.implementation.client.PowershellManager.java" checks="IllegalImportCheck" />
<suppress files="com.azure.v2.identity.implementation.client.PowershellManager.java" checks="MissingJavadocTypeCheck" />
<suppress files="com.azure.v2.identity.implementation.client.PowershellManager.java" checks="com.azure.tools.checkstyle.checks.JavadocThrowsChecks" />
<suppress files="com.azure.v2.identity.implementation.client.PowershellManager.java" checks="MissingJavadocMethodCheck" />
<suppress files="com.azure.v2.identity.implementation.client.LinuxKeyRingAccessor.java" checks="MissingJavadocMethodCheck" />
<suppress files="com.azure.v2.identity.implementation.client.LinuxKeyRingAccessor.java" checks="com.azure.tools.checkstyle.checks.EnforceFinalFieldsCheck" />
<suppress files="com.azure.v2.identity.implementation.client.DevToolsClient.java" checks="JavadocMethodCheck" />
<suppress files="com.azure.v2.identity.implementation.client.DevToolsClient.java" checks="MissingJavadocTypeCheck" />
<suppress files="com.azure.v2.identity.implementation.client.DevToolsClient.java" checks="com.azure.tools.checkstyle.checks.ThrowFromClientLoggerCheck" />
<suppress files="com.azure.v2.identity.implementation.client.AzureToolkitCacheAccessor.java" checks="MissingJavadocMethodCheck" />
<suppress files="com.azure.v2.identity.implementation.client.AzureToolkitCacheAccessor.java" checks="com.azure.tools.checkstyle.checks.JavadocThrowsChecks" />
<suppress files="com.azure.v2.identity.implementation.client.AzureToolkitCacheAccessor.java" checks="com.azure.tools.checkstyle.checks.ThrowFromClientLoggerCheck" />
<suppress files="com.azure.v2.identity.implementation.client.PersistentTokenCacheImpl.java" checks="MissingJavadocMethodCheck" />
<suppress files="com.azure.v2.identity.implementation.client.PersistentTokenCacheImpl.java" checks="MissingJavadocTypeCheck" />
<suppress files="com.azure.v2.identity.implementation.models.PublicClientOptions.java" checks="GoodLoggingCheck" />

</suppressions>
6 changes: 3 additions & 3 deletions sdk/identity/azure-identity-v2/pom.xml
Original file line number Diff line number Diff line change
@@ -9,10 +9,10 @@
<relativePath>../../parents/azure-client-sdk-parent-v2</relativePath>
</parent>

<groupId>com.azure</groupId>
<groupId>com.azure.v2</groupId>
<artifactId>azure-identity-v2</artifactId>
<packaging>jar</packaging>
<version>2.0.0-beta.1</version> <!-- {x-version-update;com.azure:azure-identity-v2;current} -->
<version>2.0.0-beta.1</version> <!-- {x-version-update;com.azure.v2:azure-identity-v2;current} -->

<name>Microsoft Azure Identity V2 Library</name>
<description>This package contains core types for Azure Java V2 clients.</description>
@@ -44,6 +44,7 @@
<legal><![CDATA[[INFO] Any downloads listed may be third party software. Microsoft grants you no rights for third party software.]]></legal>
<jacoco.min.linecoverage>0.10</jacoco.min.linecoverage> <!-- Temporary until full breadth of test coverage is added -->
<jacoco.min.branchcoverage>0.10</jacoco.min.branchcoverage> <!-- Temporary until full breadth of test coverage is added -->
<checkstyle.suppressionsLocation>checkstyle-suppressions.xml</checkstyle.suppressionsLocation>
</properties>

<developers>
@@ -112,7 +113,6 @@
<version>1.14.10</version><!-- {x-version-update;net.bytebuddy:byte-buddy;external_dependency} -->
<scope>test</scope>
</dependency>

</dependencies>

<build>

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.identity.v2;
package com.azure.v2.identity;

import com.azure.v2.core.credentials.TokenRequestContext;
import com.azure.v2.identity.exceptions.CredentialUnavailableException;

/**
* <p>The Authentication Required Exception is thrown by {@link InteractiveBrowserCredential}
* to indicate to the user that automatic authentication is disabled and authentication
* needs to be initiated via {@link InteractiveBrowserCredential#authenticate()} or
* {@link InteractiveBrowserCredential#authenticate()} APIs respectively before fetching an access token.</p>
*
* @see com.azure.identity
* @see com.azure.v2.identity
* @see InteractiveBrowserCredential
*/
public final class AuthenticationRequiredException extends CredentialUnavailableException {
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.v2.identity;

import com.azure.v2.identity.exceptions.CredentialAuthenticationException;
import com.azure.v2.identity.implementation.client.BrokeredAuthCache;
import com.azure.v2.identity.implementation.client.ConfidentialClient;
import com.azure.v2.identity.implementation.client.PublicClient;
import com.azure.v2.identity.implementation.models.ConfidentialClientOptions;
import com.azure.v2.identity.implementation.models.MsalToken;
import com.azure.v2.identity.implementation.models.PublicClientOptions;
import com.azure.v2.identity.implementation.util.LoggingUtil;
import com.azure.v2.core.credentials.TokenCredential;
import com.azure.v2.core.credentials.TokenRequestContext;
import io.clientcore.core.credentials.oauth.AccessToken;
import io.clientcore.core.instrumentation.logging.ClientLogger;
import io.clientcore.core.utils.CoreUtils;

/**
* <p>Authorization Code authentication in Azure is a type of authentication mechanism that allows users to
* authenticate with <a href="https://learn.microsoft.com/entra/fundamentals/">Microsoft Entra ID</a>
* and obtain an authorization code that can be used to request an access token to access
* Azure resources. It is a widely used authentication mechanism and is supported by a wide range of Azure services
* and applications. It provides a secure and scalable way to authenticate users and grant them access to Azure
* resources.
* The AuthorizationCodeCredential authenticates a user or an application and acquires a token with the configured
* authorization code and the redirectURL where authorization code was received.</p>
*
* <p><strong>Sample: Construct AuthorizationCodeCredential</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link AuthorizationCodeCredential},
* using the {@link AuthorizationCodeCredentialBuilder} to configure it.
* The {@code authorizationCode}, {@code redirectUrl} and {@code clientId} are required to be configured to create
* {@link AuthorizationCodeCredential}. Once this credential is created, it may be passed into the builder of many of
* the Azure SDK for Java client builders as the 'credential' parameter.</p>
*
* <pre>
* TokenCredential authorizationCodeCredential = new AuthorizationCodeCredentialBuilder&#40;&#41;.authorizationCode&#40;
* &quot;&#123;authorization-code-received-at-redirectURL&#125;&quot;&#41;
* .redirectUrl&#40;&quot;&#123;redirectUrl-where-authorization-code-is-received&#125;&quot;&#41;
* .clientId&#40;&quot;&#123;clientId-of-application-being-authenticated&quot;&#41;
* .build&#40;&#41;;
* </pre>
*
* @see com.azure.v2.identity
* @see AuthorizationCodeCredentialBuilder
*/
public class AuthorizationCodeCredential implements TokenCredential {
private static final ClientLogger LOGGER = new ClientLogger(AuthorizationCodeCredential.class);

private final ConfidentialClient confidentialClient;
private final PublicClient publicClient;
private final PublicClientOptions publicClientOptions;
private final BrokeredAuthCache cache;

/**
* Creates an AuthorizationCodeCredential with the given identity client options.
*
* @param clientSecret the client secret of the application
* @param publicClientOptions the options for configuring the identity client
*/
AuthorizationCodeCredential(String clientSecret, PublicClientOptions publicClientOptions) {
this.publicClient = new PublicClient(publicClientOptions);
this.publicClientOptions = publicClientOptions;
if (!CoreUtils.isNullOrEmpty(clientSecret)) {
confidentialClient = new ConfidentialClient(
(ConfidentialClientOptions) new ConfidentialClientOptions().setClientSecret(clientSecret)
.setClientId(publicClientOptions.getClientId())
.setTenantId(publicClientOptions.getTenantId()));
} else {
confidentialClient = null;
}
this.cache = new BrokeredAuthCache();
}

@Override
public AccessToken getToken(TokenRequestContext request) {
if (cache.isCachePopulated(request)) {
if (confidentialClient != null) {
return confidentialClient.authenticateWithCache(request, cache.getCachedAccount());
} else {
return publicClient.authenticateWithPublicClientCache(request, cache.getCachedAccount());
}
}

MsalToken accessToken;

try {
if (confidentialClient != null) {
accessToken = confidentialClient.authenticateWithAuthorizationCode(request,
publicClientOptions.getAuthCode(), publicClientOptions.getRedirectUri());
} else {
accessToken = publicClient.authenticateWithAuthorizationCode(request);
}
cache.updateCache(accessToken, publicClientOptions, request);
LoggingUtil.logTokenSuccess(LOGGER, request);
return accessToken;
} catch (Exception e) {
LoggingUtil.logTokenError(LOGGER, request, e);
throw LOGGER.logThrowableAsError(new CredentialAuthenticationException(e.getMessage(), e));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.v2.identity;

import com.azure.v2.identity.implementation.models.ClientOptions;
import com.azure.v2.identity.implementation.models.PublicClientOptions;
import com.azure.v2.identity.implementation.util.ValidationUtil;
import io.clientcore.core.instrumentation.logging.ClientLogger;

import java.net.URI;
import java.net.URISyntaxException;

/**
* <p>Fluent credential builder for instantiating a {@link AuthorizationCodeCredential}.</p>
*
* <p>Authorization Code authentication in Azure is a type of authentication mechanism that allows users to
* authenticate with <a href="https://learn.microsoft.com/entra/fundamentals/">Microsoft Entra ID</a>
* and obtain an authorization code that can be used to request an access token to access
* Azure resources. It is a widely used authentication mechanism and is supported by a wide range of Azure services
* and applications. It provides a secure and scalable way to authenticate users and grant them access to Azure
* resources.
* The AuthorizationCodeCredential authenticates a user or an application and acquires a token with the configured
* authorization code and the redirectURL where authorization code was received.</p>
*
* <p><strong>Sample: Construct AuthorizationCodeCredential</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link AuthorizationCodeCredential},
* using the {@link AuthorizationCodeCredentialBuilder} to configure it.
* The {@code authorizationCode}, {@code redirectUrl} and {@code clientId} are required to be configured to create
* {@link AuthorizationCodeCredential}. Once this credential is created, it may be passed into the builder of many of
* the Azure SDK for Java client builders as the 'credential' parameter.</p>
*
* <pre>
* TokenCredential authorizationCodeCredential = new AuthorizationCodeCredentialBuilder&#40;&#41;.authorizationCode&#40;
* &quot;&#123;authorization-code-received-at-redirectURL&#125;&quot;&#41;
* .redirectUrl&#40;&quot;&#123;redirectUrl-where-authorization-code-is-received&#125;&quot;&#41;
* .clientId&#40;&quot;&#123;clientId-of-application-being-authenticated&quot;&#41;
* .build&#40;&#41;;
* </pre>
*
* @see AuthorizationCodeCredential
*/
public class AuthorizationCodeCredentialBuilder
extends EntraIdCredentialBuilderBase<AuthorizationCodeCredentialBuilder> {
private static final ClientLogger LOGGER = new ClientLogger(AuthorizationCodeCredentialBuilder.class);
private static final String CLASS_NAME = AuthorizationCodeCredentialBuilder.class.getSimpleName();
private String clientSecret;
private final PublicClientOptions publicClientOptions;

/**
* Constructs an instance of AuthorizationCodeCredentialBuilder.
*/
public AuthorizationCodeCredentialBuilder() {
super();
publicClientOptions = new PublicClientOptions();
}

/**
* Sets the authorization code on the builder.
*
* @param authCode the authorization code acquired from user login
* @return the AuthorizationCodeCredentialBuilder itself
*/
public AuthorizationCodeCredentialBuilder authorizationCode(String authCode) {
this.publicClientOptions.setAuthCode(authCode);
return this;
}

/**
* Sets redirect URL for the OAuth 2.0 login request, which must be
* registered as a valid redirect URL on the application. The authorization code
* will be sent to this URL, so it must be listening on this server and is able
* to complete the {@link AuthorizationCodeCredential} construction from there.
* This is also called Reply URLs in some contexts.
*
* @param redirectUrl the redirect URL to send the authorization code
* @return the AuthorizationCodeCredentialBuilder itself
*/
public AuthorizationCodeCredentialBuilder redirectUrl(String redirectUrl) {
try {
this.publicClientOptions.setRedirectUri(new URI(redirectUrl));
} catch (URISyntaxException e) {
throw LOGGER.logThrowableAsError(new IllegalArgumentException(e));
}
return this;
}

/**
* <p>Sets the client secret for the authentication. This is required for Microsoft Entra web apps.</p>
*
* <p>Do not set this for Microsoft Entra native apps.</p>
*
* @param clientSecret the secret value of the Microsoft Entra application.
* @return An updated instance of this builder.
*/
public AuthorizationCodeCredentialBuilder clientSecret(String clientSecret) {
this.clientSecret = clientSecret;
return this;
}

/**
* Creates a new {@link AuthorizationCodeCredential} with the current configurations.
*
* @return a {@link AuthorizationCodeCredential} with the current configurations.
*/
public AuthorizationCodeCredential build() {
ValidationUtil.validate(CLASS_NAME, LOGGER, "clientId", publicClientOptions.getClientId(), "authorizationCode",
publicClientOptions.getAuthCode(), "redirectUrl", publicClientOptions.getRedirectUri());

return new AuthorizationCodeCredential(clientSecret, publicClientOptions);
}

@Override
ClientOptions getClientOptions() {
return publicClientOptions;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.identity.v2;
package com.azure.v2.identity;

/**
* <p>Defines fields exposing the well known authority hosts for the Azure Public Cloud and sovereign clouds.</p>
@@ -28,14 +28,6 @@ private AzureAuthorityHosts() {
*/
public static final String AZURE_CHINA = "https://login.chinacloudapi.cn/";

/**
* The host of the Microsoft Entra authority for tenants in the Azure German Cloud.
*
* @deprecated Microsoft Cloud Germany was closed on October 29th, 2021.
*/
@Deprecated
public static final String AZURE_GERMANY = "https://login.microsoftonline.de/";

/**
* The host of the Microsoft Entra authority for tenants in the Azure US Government Cloud.
*/
@@ -49,9 +41,6 @@ static String getDefaultScope(String authorityHost) {
case AZURE_CHINA:
return "https://management.core.chinacloudapi.cn//.default";

case AZURE_GERMANY:
return "https://management.core.cloudapi.de//.default";

case AZURE_GOVERNMENT:
return "https://management.core.usgovcloudapi.net//.default";

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

package com.azure.v2.identity;

import com.azure.v2.identity.exceptions.CredentialAuthenticationException;
import com.azure.v2.identity.exceptions.CredentialUnavailableException;
import com.azure.v2.identity.implementation.client.DevToolsClient;
import com.azure.v2.identity.implementation.models.DevToolsClientOptions;
import com.azure.v2.identity.implementation.util.LoggingUtil;
import com.azure.v2.core.credentials.TokenCredential;
import com.azure.v2.core.credentials.TokenRequestContext;
import io.clientcore.core.credentials.oauth.AccessToken;
import io.clientcore.core.instrumentation.logging.ClientLogger;

/**
* <p>The Azure CLI is a command-line tool that allows users to manage Azure resources from their local machine or
* terminal. It allows users to
* <a href="https://learn.microsoft.com/cli/azure/authenticate-azure-cli">authenticate interactively</a> as a
* user and/or a service principal against
* <a href="https://learn.microsoft.com/entra/fundamentals/">Microsoft Entra ID</a>.
* The AzureCliCredential authenticates in a development environment and acquires a token on behalf of the
* logged-in user or service principal in Azure CLI.</p>
*
* <h2>Configure AzureCliCredential</h2>
*
* <p> To use this credential, the developer needs to authenticate locally in Azure CLI using one of the commands
* below:</p>
*
* <ol>
* <li>Run "az login" in Azure CLI to authenticate as a user.</li>
* <li>Run "az login --service-principal --username {client ID} --password {client secret} --tenant {tenant ID}"
* to authenticate as a service principal.</li>
* </ol>
*
* <p>You may need to repeat this process after a certain time period, depending on the refresh token policy in your
* organization. AzureCliCredential will prompt you to sign in again when your token expires.</p>
*
* <p><strong>Sample: Construct AzureCliCredential</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link AzureCliCredential},
* using the {@link AzureCliCredentialBuilder} to configure it. Once this credential is
* created, it may be passed into the builder of many of the Azure SDK for Java client builders as the 'credential'
* parameter.</p>
*
* <pre>
* TokenCredential azureCliCredential = new AzureCliCredentialBuilder&#40;&#41;.build&#40;&#41;;
* </pre>
*
* @see com.azure.v2.identity
* @see AzureCliCredentialBuilder
*/
public class AzureCliCredential implements TokenCredential {
private static final ClientLogger LOGGER = new ClientLogger(AzureCliCredential.class);

private final DevToolsClient devToolslClient;

/**
* Creates an AzureCliCredential with the given dev tools client options.
* @param devToolsClientOptions the options to configure the dev tools client
*/
AzureCliCredential(DevToolsClientOptions devToolsClientOptions) {
devToolslClient = new DevToolsClient(devToolsClientOptions);
}

@Override
public AccessToken getToken(TokenRequestContext request) {
try {
AccessToken accessToken = devToolslClient.authenticateWithAzureCli(request);
LoggingUtil.logTokenSuccess(LOGGER, request);
return accessToken;
} catch (Exception ex) {
LoggingUtil.logTokenError(LOGGER, request, ex);
if (devToolslClient.getClientOptions().isChained()) {
throw LOGGER.logThrowableAsError(new CredentialUnavailableException(ex.getMessage(), ex));
}
throw LOGGER.logThrowableAsError(new CredentialAuthenticationException(ex.getMessage(), ex));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.v2.identity;

import com.azure.v2.identity.implementation.models.ClientOptions;
import com.azure.v2.identity.implementation.models.DevToolsClientOptions;
import com.azure.v2.identity.implementation.util.IdentityUtil;
import com.azure.v2.identity.implementation.util.ValidationUtil;
import io.clientcore.core.instrumentation.logging.ClientLogger;

import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
* Fluent credential builder for instantiating a {@link AzureCliCredential}.
*
* <p>The Azure CLI is a command-line tool that allows users to manage Azure resources from their local machine or
* terminal. It allows users to
* <a href="https://learn.microsoft.com/cli/azure/authenticate-azure-cli">authenticate interactively</a> as a
* user and/or a service principal against
* <a href="https://learn.microsoft.com/entra/fundamentals/">Microsoft Entra ID</a>.
* The AzureCliCredential authenticates in a development environment and acquires a token on behalf of the
* logged-in user or service principal in Azure CLI.</p>
*
* <p><strong>Sample: Construct AzureCliCredential</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link AzureCliCredential},
* using the {@link AzureCliCredentialBuilder} to configure it. Once this credential is
* created, it may be passed into the builder of many of the Azure SDK for Java client builders as the 'credential'
* parameter.</p>
*
* <pre>
* TokenCredential azureCliCredential = new AzureCliCredentialBuilder&#40;&#41;.build&#40;&#41;;
* </pre>
*
* @see AzureCliCredential
*/
public class AzureCliCredentialBuilder extends CredentialBuilderBase<AzureCliCredentialBuilder> {
private static final ClientLogger LOGGER = new ClientLogger(AzureCliCredentialBuilder.class);

private final DevToolsClientOptions clientOptions;

/**
* Constructs an instance of AzureCliCredentialBuilder.
*/
public AzureCliCredentialBuilder() {
super();
clientOptions = new DevToolsClientOptions();
}

/**
* Sets the tenant ID of the application.
*
* @param tenantId the tenant ID of the application.
* @return An updated instance of this builder with the tenant id set as specified.
*/
public AzureCliCredentialBuilder tenantId(String tenantId) {
ValidationUtil.validateTenantIdCharacterRange(tenantId, LOGGER);
this.clientOptions.setTenantId(tenantId);
return this;
}

@Override
ClientOptions getClientOptions() {
return clientOptions;
}

/**
* Specifies a {@link Duration} timeout for calling the Azure CLI. The timeout period is applied on the Azure CLI
* command execution process invoked by the credential
* @param processTimeout The {@link Duration} to wait.
* @return An updated instance of this builder with the timeout specified.
*/
public AzureCliCredentialBuilder processTimeout(Duration processTimeout) {
Objects.requireNonNull(processTimeout);
this.clientOptions.setProcessTimeout(processTimeout);
return this;
}

/**
* Creates a new {@link AzureCliCredential} with the current configurations.
*
* @return a {@link AzureCliCredential} with the current configurations.
*/
public AzureCliCredential build() {
return new AzureCliCredential(clientOptions);
}

/**
* Specifies tenants in addition to the specified tenantId for which the credential may acquire tokens.
* Add the wildcard value "*" to allow the credential to acquire tokens for any tenant the logged in account can access.
* If no value is specified for tenantId this option will have no effect, and the credential will acquire tokens
* for any requested tenant.
*
* @param additionallyAllowedTenants the additionally allowed tenants.
* @return An updated instance of this builder with the additional tenants configured.
*/
@SuppressWarnings("unchecked")
public AzureCliCredentialBuilder additionallyAllowedTenants(String... additionallyAllowedTenants) {
clientOptions.setAdditionallyAllowedTenants(
IdentityUtil.resolveAdditionalTenants(Arrays.asList(additionallyAllowedTenants)));
return this;
}

/**
* Specifies tenants in addition to the specified tenantId for which the credential may acquire tokens.
* Add the wildcard value "*" to allow the credential to acquire tokens for any tenant the logged in account can access.
* If no value is specified for tenantId this option will have no effect, and the credential will acquire tokens
* for any requested tenant.
*
* @param additionallyAllowedTenants the additionally allowed tenants.
* @return An updated instance of this builder with the additional tenants configured.
*/
@SuppressWarnings("unchecked")
public AzureCliCredentialBuilder additionallyAllowedTenants(List<String> additionallyAllowedTenants) {
clientOptions.setAdditionallyAllowedTenants(IdentityUtil.resolveAdditionalTenants(additionallyAllowedTenants));
return this;
}

/**
* Specifies the name or ID of a subscription. This is used to acquire tokens for a specific
* Azure subscription when using Azure CLI authentication.
*
* @param subscription The subscription name or ID.
* @return An updated instance of this builder with the subscription configured.
*/
public AzureCliCredentialBuilder subscription(String subscription) {
ValidationUtil.validateSubscriptionCharacterRange(subscription, LOGGER);
this.clientOptions.setSubscription(subscription);
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.v2.identity;

import com.azure.v2.identity.exceptions.CredentialAuthenticationException;
import com.azure.v2.identity.exceptions.CredentialUnavailableException;
import com.azure.v2.identity.implementation.client.DevToolsClient;
import com.azure.v2.identity.implementation.models.DevToolsClientOptions;
import com.azure.v2.identity.implementation.util.LoggingUtil;
import com.azure.v2.core.credentials.TokenCredential;
import com.azure.v2.core.credentials.TokenRequestContext;
import io.clientcore.core.credentials.oauth.AccessToken;
import io.clientcore.core.instrumentation.logging.ClientLogger;

/**
* <p>Azure Developer CLI is a command-line interface tool that allows developers to create, manage, and deploy
* resources in Azure. It's built on top of the Azure CLI and provides additional functionality specific
* to Azure developers. It allows users to authenticate as a user and/or a service principal against
* <a href="https://learn.microsoft.com/entra/fundamentals/">Microsoft Entra ID</a>.
* The AzureDeveloperCliCredential authenticates in a development environment and acquires a token on behalf of
* the logged-in user or service principal in Azure Developer CLI. It acts as the Azure Developer CLI logged in user or
* service principal and executes an Azure CLI command underneath to authenticate the application against
* Microsoft Entra ID.</p>
*
* <h2>Configure AzureDeveloperCliCredential</h2>
*
* <p> To use this credential, the developer needs to authenticate locally in Azure Developer CLI using one of the
* commands below:</p>
*
* <ol>
* <li>Run "azd auth login" in Azure Developer CLI to authenticate interactively as a user.</li>
* <li>Run "azd auth login --client-id {@code clientID} --client-secret {@code clientSecret}
* --tenant-id {@code tenantID}" to authenticate as a service principal.</li>
* </ol>
*
* <p>You may need to repeat this process after a certain time period, depending on the refresh token validity in your
* organization. Generally, the refresh token validity period is a few weeks to a few months.
* AzureDeveloperCliCredential will prompt you to sign in again.</p>
*
* <p><strong>Sample: Construct AzureDeveloperCliCredential</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link AzureDeveloperCliCredential},
* using the {@link AzureDeveloperCliCredentialBuilder} to configure it. Once this credential is
* created, it may be passed into the builder of many of the Azure SDK for Java client builders as the 'credential'
* parameter.</p>
*
* <pre>
* TokenCredential azureDevCliCredential = new AzureDeveloperCliCredentialBuilder&#40;&#41;.build&#40;&#41;;
* </pre>
*
* @see com.azure.v2.identity
* @see AzureDeveloperCliCredentialBuilder
*/
public class AzureDeveloperCliCredential implements TokenCredential {
private static final ClientLogger LOGGER = new ClientLogger(AzureDeveloperCliCredential.class);

private final DevToolsClient devToolslClient;

/**
* Creates an AzureDeveloperClCredential with given dev tools client options.
*
* @param clientOptions the options to configure the dev tools client
*/
AzureDeveloperCliCredential(DevToolsClientOptions clientOptions) {
devToolslClient = new DevToolsClient(clientOptions);
}

@Override
public AccessToken getToken(TokenRequestContext request) {
try {
AccessToken accessToken = devToolslClient.authenticateWithAzureDeveloperCli(request);
LoggingUtil.logTokenSuccess(LOGGER, request);
return accessToken;
} catch (Exception ex) {
LoggingUtil.logTokenError(LOGGER, request, ex);
if (devToolslClient.getClientOptions().isChained()) {
throw LOGGER.logThrowableAsError(new CredentialUnavailableException(ex.getMessage(), ex));
}
throw LOGGER.logThrowableAsError(new CredentialAuthenticationException(ex.getMessage(), ex));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.v2.identity;

import com.azure.v2.identity.implementation.models.ClientOptions;
import com.azure.v2.identity.implementation.models.DevToolsClientOptions;
import com.azure.v2.identity.implementation.util.IdentityUtil;
import com.azure.v2.identity.implementation.util.ValidationUtil;
import io.clientcore.core.instrumentation.logging.ClientLogger;

import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
* Fluent credential builder for instantiating a {@link AzureDeveloperCliCredential}.
*
* <p>Azure Developer CLI is a command-line interface tool that allows developers to create, manage, and deploy
* resources in Azure. It's built on top of the Azure CLI and provides additional functionality specific
* to Azure developers. It allows users to authenticate as a user and/or a service principal against
* <a href="https://learn.microsoft.com/entra/fundamentals/">Microsoft Entra ID</a>.
* The AzureDeveloperCliCredential authenticates in a development environment and acquires a token on behalf of
* the logged-in user or service principal in Azure Developer CLI. It acts as the Azure Developer CLI logged in user or
* service principal and executes an Azure CLI command underneath to authenticate the application against
* Microsoft Entra ID.</p>
*
* <p><strong>Sample: Construct AzureDeveloperCliCredential</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link AzureDeveloperCliCredential},
* using the {@link AzureDeveloperCliCredentialBuilder} to configure it. Once this credential is
* created, it may be passed into the builder of many of the Azure SDK for Java client builders as the 'credential'
* parameter.</p>
*
* <pre>
* TokenCredential azureDevCliCredential = new AzureDeveloperCliCredentialBuilder&#40;&#41;.build&#40;&#41;;
* </pre>
*
* @see AzureDeveloperCliCredential
*/
public class AzureDeveloperCliCredentialBuilder extends CredentialBuilderBase<AzureDeveloperCliCredentialBuilder> {
private static final ClientLogger LOGGER = new ClientLogger(AzureDeveloperCliCredentialBuilder.class);

private final DevToolsClientOptions clientOptions;

/**
* Constructs an instance of AzureDeveloperCliCredentialBuilder.
*/
public AzureDeveloperCliCredentialBuilder() {
super();
this.clientOptions = new DevToolsClientOptions();
}

/**
* Sets the tenant ID of the application.
*
* @param tenantId the tenant ID of the application.
* @return An updated instance of this builder with the tenant id set as specified.
*/
public AzureDeveloperCliCredentialBuilder tenantId(String tenantId) {
ValidationUtil.validateTenantIdCharacterRange(tenantId, LOGGER);
this.clientOptions.setTenantId(tenantId);
return this;
}

/**
* Specifies a {@link Duration} timeout for calling the Azure Developer CLI. The timeout period is applied on
* the Azure Developer CLI command execution process invoked by the credential.
* @param processTimeout The {@link Duration} to wait.
* @return An updated instance of this builder with the timeout specified.
*/
public AzureDeveloperCliCredentialBuilder processTimeout(Duration processTimeout) {
Objects.requireNonNull(processTimeout);
this.clientOptions.setProcessTimeout(processTimeout);
return this;
}

/**
* Creates a new {@link AzureDeveloperCliCredential} with the current configurations.
*
* @return a {@link AzureDeveloperCliCredential} with the current configurations.
*/
public AzureDeveloperCliCredential build() {
return new AzureDeveloperCliCredential(clientOptions);
}

/**
* Specifies tenants in addition to the specified tenantId for which the credential may acquire tokens.
* Add the wildcard value "*" to allow the credential to acquire tokens for any tenant the logged in account can access.
* If no value is specified for tenantId this option will have no effect, and the credential will acquire tokens
* for any requested tenant.
*
* @param additionallyAllowedTenants the additionally allowed tenants.
* @return An updated instance of this builder with the additional tenants configured.
*/
@SuppressWarnings("unchecked")
public AzureDeveloperCliCredentialBuilder additionallyAllowedTenants(String... additionallyAllowedTenants) {
clientOptions.setAdditionallyAllowedTenants(
IdentityUtil.resolveAdditionalTenants(Arrays.asList(additionallyAllowedTenants)));
return this;
}

/**
* Specifies tenants in addition to the specified tenantId for which the credential may acquire tokens.
* Add the wildcard value "*" to allow the credential to acquire tokens for any tenant the logged in account can access.
* If no value is specified for tenantId this option will have no effect, and the credential will acquire tokens
* for any requested tenant.
*
* @param additionallyAllowedTenants the additionally allowed tenants.
* @return An updated instance of this builder with the additional tenants configured.
*/
@SuppressWarnings("unchecked")
public AzureDeveloperCliCredentialBuilder additionallyAllowedTenants(List<String> additionallyAllowedTenants) {
clientOptions.setAdditionallyAllowedTenants(IdentityUtil.resolveAdditionalTenants(additionallyAllowedTenants));
return this;
}

@Override
ClientOptions getClientOptions() {
return clientOptions;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.identity.v2;
package com.azure.v2.identity;

import com.azure.identity.v2.implementation.client.ConfidentialClient;
import com.azure.identity.v2.implementation.models.ConfidentialClientOptions;
import com.azure.identity.v2.implementation.models.OidcTokenResponse;
import com.azure.identity.v2.implementation.util.IdentityUtil;
import com.azure.identity.v2.implementation.util.LoggingUtil;
import com.azure.v2.identity.exceptions.CredentialAuthenticationException;
import com.azure.v2.identity.implementation.client.ConfidentialClient;
import com.azure.v2.identity.implementation.models.ConfidentialClientOptions;
import com.azure.v2.identity.implementation.models.OidcTokenResponse;
import com.azure.v2.identity.implementation.util.IdentityUtil;
import com.azure.v2.identity.implementation.util.LoggingUtil;
import com.azure.v2.core.credentials.TokenCredential;
import com.azure.v2.core.credentials.TokenRequestContext;
import io.clientcore.core.credentials.oauth.AccessToken;
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.identity.v2;
package com.azure.v2.identity;

import com.azure.identity.v2.implementation.models.ClientOptions;
import com.azure.identity.v2.implementation.models.ConfidentialClientOptions;
import com.azure.identity.v2.implementation.util.ValidationUtil;
import com.azure.v2.identity.implementation.models.ClientOptions;
import com.azure.v2.identity.implementation.models.ConfidentialClientOptions;
import com.azure.v2.identity.implementation.util.ValidationUtil;
import com.azure.v2.identity.models.TokenCachePersistenceOptions;
import io.clientcore.core.http.models.HttpHeaderName;
import io.clientcore.core.http.pipeline.HttpInstrumentationOptions;
import io.clientcore.core.instrumentation.logging.ClientLogger;
@@ -133,6 +134,7 @@ public AzurePipelinesCredential build() {
options.addAllowedHeaderName(HttpHeaderName.fromString("x-msedge-ref"));
confidentialClientOptions.getHttpPipelineOptions().setHttpInstrumentationOptions(options);
}
return new AzurePipelinesCredential(requestUrl, systemAccessToken, confidentialClientOptions.clone());
return new AzurePipelinesCredential(requestUrl, systemAccessToken,
new ConfidentialClientOptions(confidentialClientOptions));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.v2.identity;

import com.azure.v2.identity.exceptions.CredentialAuthenticationException;
import com.azure.v2.identity.exceptions.CredentialUnavailableException;
import com.azure.v2.identity.implementation.client.DevToolsClient;
import com.azure.v2.identity.implementation.models.DevToolsClientOptions;
import com.azure.v2.identity.implementation.util.LoggingUtil;
import com.azure.v2.core.credentials.TokenCredential;
import com.azure.v2.core.credentials.TokenRequestContext;
import io.clientcore.core.credentials.oauth.AccessToken;
import io.clientcore.core.instrumentation.logging.ClientLogger;

/**
* <p>The Azure Powershell is a command-line tool that allows users to manage Azure resources from their local machine
* or terminal. It allows users to
* <a href="https://learn.microsoft.com/powershell/azure/authenticate-azureps">authenticate interactively</a>
* as a user and/or a service principal against
* <a href="https://learn.microsoft.com/entra/fundamentals/">Microsoft Entra ID</a>.
* The AzurePowershellCredential authenticates in a development environment and acquires a token on behalf of the
* logged-in user or service principal in Azure Powershell. It acts as the Azure Powershell logged in user or
* service principal and executes an Azure Powershell command underneath to authenticate the application against
* Microsoft Entra ID.</p>
*
* <h2>Configure AzurePowershellCredential</h2>
*
* <p> To use this credential, the developer needs to authenticate locally in Azure Powershell using one of the
* commands below:</p>
*
* <ol>
* <li>Run "Connect-AzAccount" in Azure Powershell to authenticate as a user.</li>
* <li>Run "Connect-AzAccount -ServicePrincipal -ApplicationId {servicePrincipalId} -Tenant {tenantId}
* -CertificateThumbprint {thumbprint} to authenticate as a service principal."</li>
* </ol>
*
* <p>You may need to repeat this process after a certain time period, depending on the refresh token validity in your
* organization. Generally, the refresh token validity period is a few weeks to a few months. AzurePowershellCredential
* will prompt you to sign in again.</p>
*
* <p><strong>Sample: Construct AzurePowershellCredential</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link AzurePowerShellCredential},
* using the {@link AzurePowerShellCredentialBuilder} to configure it. Once this credential is
* created, it may be passed into the builder of many of the Azure SDK for Java client builders as the 'credential'
* parameter.</p>
*
* <pre>
* TokenCredential powerShellCredential = new AzurePowerShellCredentialBuilder&#40;&#41;.build&#40;&#41;;
* </pre>
*
* @see com.azure.v2.identity
* @see AzurePowerShellCredentialBuilder
*/
public class AzurePowerShellCredential implements TokenCredential {
private static final ClientLogger LOGGER = new ClientLogger(AzurePowerShellCredential.class);

private final DevToolsClient devToolslClient;

AzurePowerShellCredential(DevToolsClientOptions options) {
devToolslClient = new DevToolsClient(options);
}

@Override
public AccessToken getToken(TokenRequestContext request) {
try {
AccessToken accessToken = devToolslClient.authenticateWithAzurePowerShell(request);
LoggingUtil.logTokenSuccess(LOGGER, request);
return accessToken;
} catch (Exception ex) {
LoggingUtil.logTokenError(LOGGER, request, ex);
if (devToolslClient.getClientOptions().isChained()) {
throw LOGGER.logThrowableAsError(new CredentialUnavailableException(ex.getMessage(), ex));
}
throw LOGGER.logThrowableAsError(new CredentialAuthenticationException(ex.getMessage(), ex));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.v2.identity;

import com.azure.v2.identity.implementation.models.ClientOptions;
import com.azure.v2.identity.implementation.models.DevToolsClientOptions;
import com.azure.v2.identity.implementation.util.IdentityUtil;
import com.azure.v2.identity.implementation.util.ValidationUtil;
import io.clientcore.core.instrumentation.logging.ClientLogger;

import java.util.Arrays;
import java.util.List;

/**
* Fluent credential builder for instantiating a {@link AzurePowerShellCredential}.
*
* <p>The Azure Powershell is a command-line tool that allows users to manage Azure resources from their local machine
* or terminal. It allows users to
* <a href="https://learn.microsoft.com/powershell/azure/authenticate-azureps">authenticate interactively</a>
* as a user and/or a service principal against
* <a href="https://learn.microsoft.com/entra/fundamentals/">Microsoft Entra ID</a>.
* The {@link AzurePowerShellCredential} authenticates in a development environment and acquires a token on
* behalf of the logged-in user or service principal in Azure Powershell. It acts as the Azure Powershell logged in
* user or service principal and executes an Azure Powershell command underneath to authenticate the application
* against Microsoft Entra ID.</p>
*
* <p><strong>Sample: Construct AzurePowershellCredential</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link AzurePowerShellCredential},
* using the {@link AzurePowerShellCredentialBuilder} to configure it. Once this credential is
* created, it may be passed into the builder of many of the Azure SDK for Java client builders as the 'credential'
* parameter.</p>
*
* <pre>
* TokenCredential powerShellCredential = new AzurePowerShellCredentialBuilder&#40;&#41;.build&#40;&#41;;
* </pre>
*
* @see AzurePowerShellCredential
*/
public class AzurePowerShellCredentialBuilder extends CredentialBuilderBase<AzurePowerShellCredentialBuilder> {
private static final ClientLogger LOGGER = new ClientLogger(AzurePowerShellCredentialBuilder.class);

private final DevToolsClientOptions clientOptions;

/**
* Constructs an instance of AzurePowerShellCredentialBuilder.
*/
public AzurePowerShellCredentialBuilder() {
super();
clientOptions = new DevToolsClientOptions();
}

/**
* Sets the tenant ID of the application.
*
* @param tenantId the tenant ID of the application.
* @return An updated instance of this builder with the tenant id set as specified.
*/
public AzurePowerShellCredentialBuilder tenantId(String tenantId) {
ValidationUtil.validateTenantIdCharacterRange(tenantId, LOGGER);
this.clientOptions.setTenantId(tenantId);
return this;
}

/**
* Specifies tenants in addition to the specified tenantId for which the credential may acquire tokens.
* Add the wildcard value "*" to allow the credential to acquire tokens for any tenant the logged in account can access.
* If no value is specified for tenantId this option will have no effect, and the credential will acquire tokens
* for any requested tenant.
*
* @param additionallyAllowedTenants the additionally allowed tenants.
* @return An updated instance of this builder with the additional tenants configured.
*/
@SuppressWarnings("unchecked")
public AzurePowerShellCredentialBuilder additionallyAllowedTenants(String... additionallyAllowedTenants) {
clientOptions.setAdditionallyAllowedTenants(
IdentityUtil.resolveAdditionalTenants(Arrays.asList(additionallyAllowedTenants)));
return this;
}

/**
* Specifies tenants in addition to the specified tenantId for which the credential may acquire tokens.
* Add the wildcard value "*" to allow the credential to acquire tokens for any tenant the logged in account can access.
* If no value is specified for tenantId this option will have no effect, and the credential will acquire tokens
* for any requested tenant.
*
* @param additionallyAllowedTenants the additionally allowed tenants.
* @return An updated instance of this builder with the additional tenants configured.
*/
@SuppressWarnings("unchecked")
public AzurePowerShellCredentialBuilder additionallyAllowedTenants(List<String> additionallyAllowedTenants) {
clientOptions.setAdditionallyAllowedTenants(IdentityUtil.resolveAdditionalTenants(additionallyAllowedTenants));
return this;
}

/**
* Creates a new {@link AzurePowerShellCredential} with the current configurations.
*
* @return a {@link AzurePowerShellCredential} with the current configurations.
*/
public AzurePowerShellCredential build() {
return new AzurePowerShellCredential(clientOptions);
}

@Override
ClientOptions getClientOptions() {
return clientOptions;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.v2.identity;

import com.azure.v2.identity.exceptions.CredentialAuthenticationException;
import com.azure.v2.identity.exceptions.CredentialUnavailableException;
import com.azure.v2.identity.implementation.client.PublicClient;
import com.azure.v2.identity.implementation.models.MsalToken;
import com.azure.v2.identity.implementation.models.PublicClientOptions;
import com.azure.v2.identity.implementation.util.IdentityConstants;
import com.azure.v2.identity.implementation.util.LoggingUtil;
import com.azure.v2.core.credentials.TokenCredential;
import com.azure.v2.core.credentials.TokenRequestContext;
import io.clientcore.core.credentials.oauth.AccessToken;
import io.clientcore.core.instrumentation.logging.ClientLogger;

import java.util.concurrent.atomic.AtomicReference;

/**
* <p>
* The AzureToolkitCredential authenticates in a development environment and acquires a token on behalf of the
* logged-in account in Azure Toolkit for IntelliJ/Eclipse. It uses the logged in user information on the
* IntelliJ/Eclipse IDE and uses it to authenticate the application against Microsoft Entra ID.</p>
*
* <h2>Configure AzureToolkitCredential</h2>
*
* <p>Follow the steps outlined below, if using IntelliJ:</p>
*
* <ol>
* <li>In your IntelliJ window, open File > Settings > Plugins.</li>
* <li>Search for "Azure Toolkit for IntelliJ" in the marketplace. Install and restart IDE.</li>
* <li>Find the new menu item Tools > Azure > Azure Sign In.</li>
* <li>Device Login will help you log in as a user account. Follow the instructions to log in on the
* login.microsoftonline.com website with the device code. IntelliJ will prompt you to select your subscriptions.
* Select the subscription with the resources that you want to access.</li>
* </ol>
*
* TODO: Add similar instructions for Eclipse IDE
*
* <p> Once the developer has followed the steps above and authenticated successfully with
* Azure Tools for IntelliJ/Eclipse plugin in the IntelliJ/Eclipse IDE then this credential can be used in the
* development code to reuse the cached plugin credentials.</p>
*
* <p><strong>Sample: Construct AzureToolkitCredential</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link AzureToolkitCredential},
* using the {@link AzureToolkitCredentialBuilder} to configure it. Once this credential is
* created, it may be passed into the builder of many of the Azure SDK for Java client builders as the 'credential'
* parameter.</p>
*
*
* <pre>
* TokenCredential azureToolkitCredential = new AzureToolkitCredentialBuilder&#40;&#41;.build&#40;&#41;;
* </pre>
*
* @see com.azure.v2.identity
* @see AzureToolkitCredentialBuilder
*/
public class AzureToolkitCredential implements TokenCredential {
private static final ClientLogger LOGGER = new ClientLogger(AzureToolkitCredential.class);
private static final String AZURE_TOOLKIT_CLIENT_ID = "61d65f5a-6e3b-468b-af73-a033f5098c5c";
private final PublicClient publicClient;
private final AtomicReference<MsalToken> cachedToken;

/**
* Creates an {@link AzureToolkitCredential} with the given public client options.
* @param publicClientOptions the options to configure the public client
*/
AzureToolkitCredential(PublicClientOptions publicClientOptions) {
String tenant = publicClientOptions.getTenantId();

if (tenant == null) {
publicClientOptions.setTenantId("common");
}

publicClientOptions.setClientId(IdentityConstants.DEVELOPER_SINGLE_SIGN_ON_ID);

publicClient = new PublicClient(publicClientOptions);

this.cachedToken = new AtomicReference<>();
}

@Override
public AccessToken getToken(TokenRequestContext request) {
try {
if (cachedToken.get() != null) {
return publicClient.authenticateWithPublicClientCache(request, cachedToken.get().getAccount());
}
} catch (Exception ex) {
}

try {
MsalToken msalToken = publicClient.authenticateWithAzureToolkit(request);
cachedToken.set(msalToken);
LoggingUtil.logTokenSuccess(LOGGER, request);
return msalToken;
} catch (Exception ex) {
LoggingUtil.logTokenError(LOGGER, request, ex);
if (publicClient.getClientOptions().isChained()) {
throw LOGGER.logThrowableAsError(new CredentialUnavailableException(ex.getMessage(), ex));
}
throw LOGGER.logThrowableAsError(new CredentialAuthenticationException(ex.getMessage(), ex));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.v2.identity;

import com.azure.v2.identity.implementation.models.ClientOptions;
import com.azure.v2.identity.implementation.models.PublicClientOptions;
import com.azure.v2.identity.implementation.util.IdentityUtil;
import com.azure.v2.identity.implementation.util.ValidationUtil;
import io.clientcore.core.instrumentation.logging.ClientLogger;

import java.util.Arrays;
import java.util.List;

/**
* Fluent credential builder for instantiating a {@link AzureToolkitCredential}.
*
* <p>IntelliJ IDEA is an integrated development environment (IDE) developed by JetBrains, which provides a variety of
* features to support software development, such as code completion, debugging, and testing.
* Azure offers <a href="https://learn.microsoft.com/azure/developer/java/toolkit-for-intellij/">Azure Toolkit
* for IntelliJ plugin</a> for the IntelliJ IDEA development environment. It enables developers to create, test, and
* deploy Java applications to the Azure cloud platform. In order to use the plugin authentication as a user or
* service principal against
* <a href="https://learn.microsoft.com/entra/fundamentals/">Microsoft Entra ID</a> is required.
* The {@link AzureToolkitCredential} authenticates in a development environment and acquires a token on behalf of the
* logged-in account in Azure Toolkit for IntelliJ. It uses the logged in user information on the IntelliJ IDE and uses
* it to authenticate the application against Microsoft Entra ID.</p>
*
* <p><strong>Sample: Construct IntelliJCredential</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link AzureToolkitCredential},
* using the {@link AzureToolkitCredentialBuilder} to configure it. Once this credential is
* created, it may be passed into the builder of many of the Azure SDK for Java client builders as the 'credential'
* parameter.</p>
*
* <pre>
* TokenCredential intelliJCredential = new IntelliJCredentialBuilder&#40;&#41;.build&#40;&#41;;
* </pre>
*
* @see AzureToolkitCredential
*/
public class AzureToolkitCredentialBuilder extends CredentialBuilderBase<AzureToolkitCredentialBuilder> {
private static final ClientLogger LOGGER = new ClientLogger(AzureToolkitCredentialBuilder.class);

private String tenantId;
private final PublicClientOptions publicClientOptions;

/**
* Constructs an instance of IntelliJCredentialBuilder.
*/
public AzureToolkitCredentialBuilder() {
super();
publicClientOptions = new PublicClientOptions();
}

/**
* Sets the tenant id of the user to authenticate through the {@link AzureToolkitCredential}. The default is
* the tenant the user originally authenticated to via the Azure Toolkit for IntelliJ plugin.
*
* @param tenantId the tenant ID to set.
* @return An updated instance of this builder with the tenant id set as specified.
*/
public AzureToolkitCredentialBuilder tenantId(String tenantId) {
ValidationUtil.validateTenantIdCharacterRange(tenantId, LOGGER);
this.tenantId = tenantId;
return this;
}

/**
* Specifies tenants in addition to the specified tenantId for which the credential may acquire tokens.
* Add the wildcard value "*" to allow the credential to acquire tokens for any tenant the logged in account can access.
* If no value is specified for tenantId this option will have no effect, and the credential will acquire tokens
* for any requested tenant.
*
* @param additionallyAllowedTenants the additionally allowed tenants.
* @return An updated instance of this builder with the additional tenants configured.
*/
public AzureToolkitCredentialBuilder additionallyAllowedTenants(String... additionallyAllowedTenants) {
publicClientOptions.setAdditionallyAllowedTenants(
IdentityUtil.resolveAdditionalTenants(Arrays.asList(additionallyAllowedTenants)));
return this;
}

/**
* Specifies tenants in addition to the specified tenantId for which the credential may acquire tokens.
* Add the wildcard value "*" to allow the credential to acquire tokens for any tenant the logged in account can access.
* If no value is specified for tenantId this option will have no effect, and the credential will acquire tokens
* for any requested tenant.
*
* @param additionallyAllowedTenants the additionally allowed tenants.
* @return An updated instance of this builder with the additional tenants configured.
*/
public AzureToolkitCredentialBuilder additionallyAllowedTenants(List<String> additionallyAllowedTenants) {
publicClientOptions
.setAdditionallyAllowedTenants(IdentityUtil.resolveAdditionalTenants(additionallyAllowedTenants));
return this;
}

/**
* Creates a new {@link AzureToolkitCredential} with the current configurations.
*
* @return a {@link AzureToolkitCredential} with the current configurations.
*/
public AzureToolkitCredential build() {
return new AzureToolkitCredential(publicClientOptions);
}

@Override
ClientOptions getClientOptions() {
return publicClientOptions;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.v2.identity;

import com.azure.v2.core.credentials.TokenCredential;
import com.azure.v2.core.credentials.TokenRequestContext;
import com.azure.v2.identity.exceptions.CredentialAuthenticationException;
import com.azure.v2.identity.exceptions.CredentialUnavailableException;
import io.clientcore.core.credentials.oauth.AccessToken;
import io.clientcore.core.instrumentation.logging.ClientLogger;
import io.clientcore.core.instrumentation.logging.LogLevel;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
* <p>ChainedTokenCredential allows you to chain together a set of TokenCredential instances. Each credential in the chain is attempted
* sequentially. The token from the first credential that successfully authenticates is returned. For more information, see
* <a href="https://aka.ms/azsdk/java/identity/credential-chains#chainedtokencredential-overview">ChainedTokenCredential overview</a>.</p>
*
* <p><strong>Sample: Construct a ChainedTokenCredential.</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link ChainedTokenCredential},
* using the {@link ChainedTokenCredentialBuilder} to configure it. The sample below
* tries silent username+password login tried first, then interactive browser login as needed
* (e.g. when 2FA is turned on in the directory). Once this credential is created, it may be passed into the builder
* of many of the Azure SDK for Java client builders as the 'credential' parameter.</p>
*
* <pre>
* TokenCredential usernamePasswordCredential = new UsernamePasswordCredentialBuilder&#40;&#41;.clientId&#40;clientId&#41;
* .username&#40;fakeUsernamePlaceholder&#41;
* .password&#40;fakePasswordPlaceholder&#41;
* .build&#40;&#41;;
* TokenCredential interactiveBrowserCredential = new InteractiveBrowserCredentialBuilder&#40;&#41;.clientId&#40;clientId&#41;
* .port&#40;8765&#41;
* .build&#40;&#41;;
* TokenCredential credential = new ChainedTokenCredentialBuilder&#40;&#41;.addLast&#40;usernamePasswordCredential&#41;
* .addLast&#40;interactiveBrowserCredential&#41;
* .build&#40;&#41;;
* </pre>
*
* @see com.azure.v2.identity
* @see ChainedTokenCredentialBuilder
*/
public class ChainedTokenCredential implements TokenCredential {
private static final ClientLogger LOGGER = new ClientLogger(ChainedTokenCredential.class);
private final List<TokenCredential> credentials;
private final String unavailableError = this.getClass().getSimpleName() + " authentication failed. ---> ";
private final AtomicReference<TokenCredential> selectedCredential;
private boolean useCachedWorkingCredential = false;

/**
* Create an instance of chained token credential that aggregates a list of token
* credentials.
*/
ChainedTokenCredential(List<TokenCredential> credentials) {
this.credentials = Collections.unmodifiableList(credentials);
selectedCredential = new AtomicReference<>();
}

@Override
public AccessToken getToken(TokenRequestContext request) {
List<CredentialUnavailableException> exceptions = new ArrayList<>(4);

if (selectedCredential.get() != null && useCachedWorkingCredential) {
try {
AccessToken accessToken = selectedCredential.get().getToken(request);
logTokenMessage("Azure Identity => Returning token from cached credential {}",
selectedCredential.get());
return accessToken;
} catch (Exception e) {
handleException(e, selectedCredential.get(), exceptions,
"Azure Identity => Cached credential {} is unavailable.", selectedCredential.get());
}
} else {
for (TokenCredential credential : credentials) {
try {
AccessToken accessToken = credential.getToken(request);
logTokenMessage("Azure Identity => Attempted credential {} returns a token", credential);
selectedCredential.set(credential);
return accessToken;

} catch (Exception e) {
handleException(e, credential, exceptions,
"Azure Identity => Attempted credential {} is unavailable.", credential);
}
}
}

CredentialUnavailableException last = exceptions.get(exceptions.size() - 1);
for (int z = exceptions.size() - 2; z >= 0; z--) {
CredentialUnavailableException current = exceptions.get(z);
last = new CredentialUnavailableException(current.getMessage() + "\r\n" + last.getMessage()
+ (z == 0
? "To mitigate this issue, please refer to the troubleshooting guidelines here at "
+ "https://aka.ms/azure-identity-java-default-azure-credential-troubleshoot"
: ""));
}
throw LOGGER.logThrowableAsError(last);
}

private void logTokenMessage(String format, TokenCredential selectedCredential) {
LOGGER.atLevel(LogLevel.INFORMATIONAL)
.log(String.format(format, selectedCredential.getClass().getSimpleName()));
}

private String getCredUnavailableMessage(TokenCredential p, Exception t) {
return unavailableError + p.getClass().getSimpleName() + " authentication failed. Error Details: "
+ t.getMessage();
}

private void handleException(Exception e, TokenCredential selectedCredential,
List<CredentialUnavailableException> exceptions, String logMessage, TokenCredential selectedCredential1) {
if (e.getClass() != CredentialUnavailableException.class) {
throw LOGGER.logThrowableAsError(
new CredentialAuthenticationException(getCredUnavailableMessage(selectedCredential, e), e));
} else {
if (e instanceof CredentialUnavailableException) {
exceptions.add((CredentialUnavailableException) e);
}
}
logTokenMessage(logMessage, selectedCredential1);
}

WorkloadIdentityCredential getWorkloadIdentityCredentialIfPresent() {
List<TokenCredential> tokenCredentials = this.credentials.stream()
.filter(tokenCredential -> tokenCredential instanceof WorkloadIdentityCredential)
.collect(Collectors.toList());
if (tokenCredentials.size() == 1) {
return (WorkloadIdentityCredential) tokenCredentials.get(0);
} else {
return null;
}
}

void enableUseCachedWorkingCredential() {
this.useCachedWorkingCredential = true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.v2.identity;

import com.azure.v2.core.credentials.TokenCredential;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;

/**
* <p>Fluent credential builder for instantiating {@link ChainedTokenCredential}.</p>
*
* <p><strong>Sample: Construct a ChainedTokenCredential.</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link ChainedTokenCredential},
* using the {@link ChainedTokenCredentialBuilder} to configure it. The sample below
* tries silent username+password login tried first, then interactive browser login as needed
* (e.g. when 2FA is turned on in the directory). Once this credential is created, it may be passed into the builder
* of many of the Azure SDK for Java client builders as the 'credential' parameter.</p>
*
* <pre>
* TokenCredential usernamePasswordCredential = new UsernamePasswordCredentialBuilder&#40;&#41;.clientId&#40;clientId&#41;
* .username&#40;fakeUsernamePlaceholder&#41;
* .password&#40;fakePasswordPlaceholder&#41;
* .build&#40;&#41;;
* TokenCredential interactiveBrowserCredential = new InteractiveBrowserCredentialBuilder&#40;&#41;.clientId&#40;clientId&#41;
* .port&#40;8765&#41;
* .build&#40;&#41;;
* TokenCredential credential = new ChainedTokenCredentialBuilder&#40;&#41;.addLast&#40;usernamePasswordCredential&#41;
* .addLast&#40;interactiveBrowserCredential&#41;
* .build&#40;&#41;;
* </pre>
*
* @see ChainedTokenCredential
*/
public class ChainedTokenCredentialBuilder {
private final Deque<TokenCredential> credentials;

/**
* Creates an instance of the builder to config the credential.
*/
public ChainedTokenCredentialBuilder() {
this.credentials = new ArrayDeque<>();
}

/**
* Adds a credential to try to authenticate at the front of the chain.
*
* @param credential the credential to be added to the front of chain
* @return the ChainedTokenCredential itself
*/
public ChainedTokenCredentialBuilder addFirst(TokenCredential credential) {
credentials.addFirst(credential);
return this;
}

/**
* Adds a credential to try to authenticate at the last of the chain.
* @param credential the credential to be added to the end of chain
* @return the ChainedTokenCredential itself
*/
public ChainedTokenCredentialBuilder addLast(TokenCredential credential) {
credentials.addLast(credential);
return this;
}

/**
* Adds all of the credentials in the specified collection at the end
* of this chain, as if by calling {@link ChainedTokenCredentialBuilder#addLast(TokenCredential)} on each one,
* in the order that they are returned by the collection's iterator.
*
* @param credentials the collection of credentials to be appended to the chain.
* @return An updated instance of the builder.
*/
public ChainedTokenCredentialBuilder addAll(Collection<? extends TokenCredential> credentials) {
this.credentials.addAll(credentials);
return this;
}

/**
* Creates a new {@link ChainedTokenCredential} with the current configurations.
*
* @return a {@link ChainedTokenCredential} with the current configurations.
*/
public ChainedTokenCredential build() {
return new ChainedTokenCredential(new ArrayList<TokenCredential>(credentials));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.v2.identity;

import com.azure.v2.identity.implementation.client.ConfidentialClient;
import com.azure.v2.identity.implementation.models.ConfidentialClientOptions;
import com.azure.v2.identity.implementation.util.LoggingUtil;
import com.azure.v2.core.credentials.TokenCredential;
import com.azure.v2.core.credentials.TokenRequestContext;
import io.clientcore.core.credentials.oauth.AccessToken;
import io.clientcore.core.instrumentation.logging.ClientLogger;

/**
* <p>The ClientAssertionCredential acquires a token via client assertion and service principal authentication.
* This authentication method provides a secure and scalable way for client applications to access Azure resources
* without the need for users to provide their credentials. It is often used in scenarios where a client application
* needs to access Azure resources on behalf of a user, such as in a multi-tier application architecture.
* In this authentication method, the client application creates a JSON Web Token (JWT) that includes information about
* the service principal (such as its client ID and tenant ID) and signs it using a client secret. The client then
* sends this token to
* <a href="https://learn.microsoft.com/entra/fundamentals/">Microsoft Entra ID</a> as proof of its
* identity. Microsoft Entra ID verifies the token signature and checks that the service principal has
* the necessary permissions to access the requested Azure resource. If the token is valid and the service principal is
* authorized, Microsoft Entra ID issues an access token that the client application can use to access the requested resource.
* The ClientAssertionCredential acquires an access token with a client client assertion for a
* service principal/registered Microsoft Entra application. The tenantId, clientId, and clientAssertion of the service principal
* are required for this credential to acquire an access token. It can be used both in Azure-hosted and local
* development environments for authentication.</p>
*
* <p>As a pre-requisite, a service principal is required to use this authentication mechanism. If you don't have a
* service principal, refer to
* <a href="https://aka.ms/azsdk/java/identity/serviceprincipal/create/docs">create a service principal with Azure CLI.
* </a></p>
*
* <p><strong>Sample: Construct a simple ClientAssertionCredential</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link ClientAssertionCredential},
* using the {@link ClientAssertionCredentialBuilder} to configure it. The {@code tenantId},
* {@code clientId} and {@code certificate} parameters are required to create
* {@link ClientAssertionCredential}. Once this credential is created, it may be passed into the
* builder of many of the Azure SDK for Java client builders as the 'credential' parameter.</p>
*
* <pre>
* TokenCredential clientAssertionCredential = new ClientAssertionCredentialBuilder&#40;&#41;.tenantId&#40;tenantId&#41;
* .clientId&#40;clientId&#41;
* .clientAssertion&#40;&#40;&#41; -&gt; &quot;&lt;Client-Assertion&gt;&quot;&#41;
* .build&#40;&#41;;
* </pre>
*
* @see com.azure.v2.identity
* @see ClientCertificateCredentialBuilder
*/
public class ClientAssertionCredential implements TokenCredential {
private static final ClientLogger LOGGER = new ClientLogger(ClientAssertionCredential.class);
private final ConfidentialClient confidentialClient;

/**
* Creates an instance of ClientAssertionCredential.
*
* @param confidentialClientOptions the options to configure the confidential client
*/
ClientAssertionCredential(ConfidentialClientOptions confidentialClientOptions) {
confidentialClient = new ConfidentialClient(confidentialClientOptions);
}

@Override
public AccessToken getToken(TokenRequestContext request) {
try {
AccessToken token = confidentialClient.authenticateWithCache(request);
if (token != null) {
LoggingUtil.logTokenSuccess(LOGGER, request);
return token;
}
} catch (Exception ignored) {
}

try {
AccessToken token = confidentialClient.authenticate(request);
LoggingUtil.logTokenSuccess(LOGGER, request);
return token;
} catch (Exception e) {
LoggingUtil.logTokenError(LOGGER, request, e);
// wrap the exception in a RuntimeException to avoid checked exception problems.
throw LOGGER.logThrowableAsError(new RuntimeException(e));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.v2.identity;

import com.azure.v2.identity.implementation.models.ClientOptions;
import com.azure.v2.identity.implementation.models.ConfidentialClientOptions;
import com.azure.v2.identity.implementation.util.ValidationUtil;
import com.azure.v2.identity.models.TokenCachePersistenceOptions;
import io.clientcore.core.instrumentation.logging.ClientLogger;

import java.util.function.Supplier;

/**
* Fluent credential builder for instantiating a {@link ClientAssertionCredential}.
*
* <p>The {@link ClientAssertionCredential} acquires a token via client assertion and service principal authentication.
* This authentication method provides a secure and scalable way for client applications to access Azure resources
* without the need for users to provide their credentials. It is often used in scenarios where a client application
* needs to access Azure resources on behalf of a user, such as in a multi-tier application architecture.
* In this authentication method, the client application creates a JSON Web Token (JWT) that includes information about
* the service principal (such as its client ID and tenant ID) and signs it using a client secret. The client then
* sends this token to
* <a href="https://learn.microsoft.com/entra/fundamentals/">Microsoft Entra ID</a> as proof of its
* identity. Microsoft Entra ID verifies the token signature and checks that the service principal has
* the necessary permissions to access the requested Azure resource. If the token is valid and the service principal is
* authorized, Microsoft Entra ID issues an access token that the client application can use to access the requested resource.
* The {@link ClientAssertionCredential} acquires an access token with a client client assertion for a
* service principal/registered Microsoft Entra application. The tenantId, clientId and clientAssertion of the service principal
* are required for this credential to acquire an access token. It can be used both in Azure hosted and local
* development environments for authentication.</p>
*
* <p><strong>Sample: Construct a simple ClientAssertionCredential</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link ClientAssertionCredential},
* using the {@link ClientAssertionCredentialBuilder} to configure it. The {@code tenantId},
* {@code clientId} and {@code certificate} parameters are required to create
* {@link ClientAssertionCredential}. Once this credential is created, it may be passed into the
* builder of many of the Azure SDK for Java client builders as the 'credential' parameter.</p>
*
* <pre>
* TokenCredential clientAssertionCredential = new ClientAssertionCredentialBuilder&#40;&#41;.tenantId&#40;tenantId&#41;
* .clientId&#40;clientId&#41;
* .clientAssertion&#40;&#40;&#41; -&gt; &quot;&lt;Client-Assertion&gt;&quot;&#41;
* .build&#40;&#41;;
* </pre>
*
* @see ClientAssertionCredential
*/
public class ClientAssertionCredentialBuilder extends EntraIdCredentialBuilderBase<ClientAssertionCredentialBuilder> {
private static final ClientLogger LOGGER = new ClientLogger(ClientAssertionCredentialBuilder.class);
private static final String CLASS_NAME = ClientAssertionCredentialBuilder.class.getSimpleName();

private final ConfidentialClientOptions confidentialClientOptions;

/**
* Constructs an instance of ClientAssertionCredentialBuilder.
*/
public ClientAssertionCredentialBuilder() {
super();
confidentialClientOptions = new ConfidentialClientOptions();
}

/**
* Sets the supplier containing the logic to supply the client assertion when invoked.
*
* @param clientAssertionSupplier the supplier supplying client assertion.
* @return An updated instance of this builder.
*/
public ClientAssertionCredentialBuilder clientAssertion(Supplier<String> clientAssertionSupplier) {
this.confidentialClientOptions.setClientAssertionSupplier(clientAssertionSupplier);
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 ClientAssertionCredentialBuilder
tokenCachePersistenceOptions(TokenCachePersistenceOptions tokenCachePersistenceOptions) {
this.confidentialClientOptions.setTokenCacheOptions(tokenCachePersistenceOptions);
return this;
}

/**
* Creates a new {@link ClientAssertionCredential} with the current configurations.
*
* @return a {@link ClientAssertionCredential} with the current configurations.
* @throws IllegalArgumentException if either of clientId, tenantId or clientAssertion is not present.
*/
public ClientAssertionCredential build() {
ValidationUtil.validate(CLASS_NAME, LOGGER, "clientId", confidentialClientOptions.getClientId(), "tenantId",
confidentialClientOptions.getTenantId(), "clientAssertion",
confidentialClientOptions.getClientAssertionSupplier());

return new ClientAssertionCredential(confidentialClientOptions);
}

@Override
ClientOptions getClientOptions() {
return confidentialClientOptions;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.v2.identity;

import com.azure.v2.identity.exceptions.CredentialAuthenticationException;
import com.azure.v2.identity.implementation.client.ConfidentialClient;
import com.azure.v2.identity.implementation.models.ConfidentialClientOptions;
import com.azure.v2.identity.implementation.util.LoggingUtil;
import com.azure.v2.core.credentials.TokenCredential;
import com.azure.v2.core.credentials.TokenRequestContext;
import io.clientcore.core.credentials.oauth.AccessToken;
import io.clientcore.core.instrumentation.logging.ClientLogger;

import java.io.ByteArrayInputStream;
import java.util.Objects;

/**
* <p>The ClientCertificateCredential acquires a token via service principal authentication. It is a type of
* authentication in Azure that enables a non-interactive login to
* <a href="https://learn.microsoft.com/entra/fundamentals/">Microsoft Entra ID</a>, allowing
* an application or service to authenticate itself with Azure resources.
* A Service Principal is essentially an identity created for an application in Microsoft Entra ID that can be used to
* authenticate with Azure resources. It's like a "user identity" for the application or service, and it provides
* a way for the application to authenticate itself with Azure resources without needing to use a user's credentials.
* <a href="https://learn.microsoft.com/entra/fundamentals/">Microsoft Entra ID</a> allows users
* to register service principals which can be used as an identity for authentication.
* A client certificate associated with the registered service principal is used as the password when authenticating
* the service principal.
* The ClientCertificateCredential acquires an access token with a client certificate for a service principal/registered
* Microsoft Entra application. The tenantId, clientId and clientCertificate of the service principal are required for this
* credential to acquire an access token. It can be used both in Azure hosted and local development environments for
* authentication. For more information refer to the
* <a href="https://aka.ms/azsdk/java/identity/clientcertificatecredential/docs">conceptual knowledge and configuration
* details</a>.</p>
*
* <p>As a pre-requisite, a service principal is required to use this authentication mechanism. If you don't have a
* service principal, refer to
* <a href="https://aka.ms/azsdk/java/identity/serviceprincipal/create/docs">create a service principal with Azure CLI.
* </a></p>
*
* <p><strong>Sample: Construct a simple ClientCertificateCredential</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link ClientCertificateCredential},
* using the {@link ClientCertificateCredentialBuilder} to configure it. The {@code tenantId},
* {@code clientId} and {@code certificate} parameters are required to create
* {@link ClientCertificateCredential}. Once this credential is created, it may be passed into the
* builder of many of the Azure SDK for Java client builders as the 'credential' parameter.</p>
*
* <pre>
* TokenCredential clientCertificateCredential = new ClientCertificateCredentialBuilder&#40;&#41;.tenantId&#40;tenantId&#41;
* .clientId&#40;clientId&#41;
* .pemCertificate&#40;&quot;&lt;PATH-TO-PEM-CERTIFICATE&gt;&quot;&#41;
* .build&#40;&#41;;
* </pre>
*
* <p><strong>Sample: Construct a ClientCertificateCredential using {@link ByteArrayInputStream}</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link ClientCertificateCredential},
* using the {@link ClientCertificateCredentialBuilder} to configure it. The {@code tenantId},
* {@code clientId} and {@code certificate} parameters are required to create
* {@link ClientSecretCredential}. The {@code certificate} in this example is configured as
* a {@link ByteArrayInputStream}. This is helpful if the certificate is available in memory via a cert store.</p>
*
* <pre>
* ByteArrayInputStream certificateStream = new ByteArrayInputStream&#40;certificateBytes&#41;;
* TokenCredential certificateCredentialWithStream = new ClientCertificateCredentialBuilder&#40;&#41;.tenantId&#40;tenantId&#41;
* .clientId&#40;clientId&#41;
* .pemCertificate&#40;certificateStream&#41;
* .build&#40;&#41;;
* </pre>
*
* @see com.azure.v2.identity
* @see ClientCertificateCredentialBuilder
*/
public class ClientCertificateCredential implements TokenCredential {
private static final ClientLogger LOGGER = new ClientLogger(ClientCertificateCredential.class);

private final ConfidentialClient confidentialClient;

/**
* Creates a ClientCertificateCredential with the given confidential client options.
*
* @param confidentialClientOptions the options to configure the confidential client
*/
ClientCertificateCredential(ConfidentialClientOptions confidentialClientOptions) {
Objects.requireNonNull(
confidentialClientOptions.getCertificatePath() == null
? confidentialClientOptions.getCertificateBytes()
: confidentialClientOptions.getCertificatePath(),
"'certificate' and 'certificatePath' cannot both be null.");

confidentialClient = new ConfidentialClient(confidentialClientOptions);
}

@Override
public AccessToken getToken(TokenRequestContext request) {
try {
AccessToken token = confidentialClient.authenticateWithCache(request);
if (token != null) {
LoggingUtil.logTokenSuccess(LOGGER, request);
return token;
}
} catch (Exception e) {
}

try {
AccessToken token = confidentialClient.authenticate(request);
LoggingUtil.logTokenSuccess(LOGGER, request);
return token;
} catch (Exception e) {
LoggingUtil.logTokenError(LOGGER, request, e);
throw LOGGER.logThrowableAsError(new CredentialAuthenticationException(e.getMessage(), e));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.v2.identity;

import com.azure.v2.identity.implementation.models.ClientOptions;
import com.azure.v2.identity.implementation.models.ConfidentialClientOptions;
import com.azure.v2.identity.implementation.util.IdentityUtil;
import com.azure.v2.identity.implementation.util.ValidationUtil;
import com.azure.v2.identity.models.TokenCachePersistenceOptions;
import io.clientcore.core.instrumentation.logging.ClientLogger;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Arrays;

/**
* Fluent credential builder for instantiating a {@link ClientCertificateCredential}.
*
* <p>The ClientCertificateCredential acquires a token via service principal authentication. It is a type of
* authentication in Azure that enables a non-interactive login to
* <a href="https://learn.microsoft.com/entra/fundamentals/">Microsoft Entra ID</a>, allowing an
* application or service to authenticate itself with Azure resources.
* A Service Principal is essentially an identity created for an application in Microsoft Entra ID that can be used to
* authenticate with Azure resources. It's like a "user identity" for the application or service, and it provides
* a way for the application to authenticate itself with Azure resources without needing to use a user's credentials.
* <a href="https://learn.microsoft.com/entra/fundamentals/">Microsoft Entra ID</a> allows users to
* register service principals which can be used as an identity for authentication.
* A client certificate associated with the registered service principal is used as the password when authenticating
* the service principal.
* The {@link ClientCertificateCredentialBuilder} acquires an access token with a client certificate for a service
* principal/registered Microsoft Entra application. The tenantId, clientId and clientCertificate of the service principal are
* required for this credential to acquire an access token. It can be used both in Azure hosted and local development
* environments for authentication. For more information refer to the
* <a href="https://aka.ms/azsdk/java/identity/clientcertificatecredential/docs">conceptual knowledge and configuration
* details</a>.</p>
*
* <p><strong>Sample: Construct a simple ClientCertificateCredential</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link ClientCertificateCredential},
* using the {@link ClientCertificateCredentialBuilder} to configure it. The {@code tenantId},
* {@code clientId} and {@code certificate} parameters are required to create
* {@link ClientCertificateCredential}. Once this credential is created, it may be passed into the
* builder of many of the Azure SDK for Java client builders as the 'credential' parameter.</p>
*
* <pre>
* TokenCredential clientCertificateCredential = new ClientCertificateCredentialBuilder&#40;&#41;.tenantId&#40;tenantId&#41;
* .clientId&#40;clientId&#41;
* .pemCertificate&#40;&quot;&lt;PATH-TO-PEM-CERTIFICATE&gt;&quot;&#41;
* .build&#40;&#41;;
* </pre>
*
* <p><strong>Sample: Construct a ClientCertificateCredential using {@link ByteArrayInputStream}</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link ClientCertificateCredential},
* using the {@link ClientCertificateCredentialBuilder} to configure it. The {@code tenantId},
* {@code clientId} and {@code certificate} parameters are required to create
* {@link ClientSecretCredential}. The {@code certificate} in this example is configured as
* a {@link ByteArrayInputStream}. This is helpful if the certificate is available in memory via a cert store.</p>
*
* <pre>
* ByteArrayInputStream certificateStream = new ByteArrayInputStream&#40;certificateBytes&#41;;
* TokenCredential certificateCredentialWithStream = new ClientCertificateCredentialBuilder&#40;&#41;.tenantId&#40;tenantId&#41;
* .clientId&#40;clientId&#41;
* .pemCertificate&#40;certificateStream&#41;
* .build&#40;&#41;;
* </pre>
*
* @see ClientCertificateCredential
*/
public class ClientCertificateCredentialBuilder
extends EntraIdCredentialBuilderBase<ClientCertificateCredentialBuilder> {
private static final ClientLogger LOGGER = new ClientLogger(ClientCertificateCredentialBuilder.class);
private static final String CLASS_NAME = ClientCertificateCredentialBuilder.class.getSimpleName();
private final ConfidentialClientOptions confidentialClientOptions;

/**
* Constructs an instance of ClientCertificateCredentialBuilder.
*/
public ClientCertificateCredentialBuilder() {
super();
confidentialClientOptions = new ConfidentialClientOptions();
}

/**
* Sets the path of the PFX certificate for authenticating to Microsoft Entra ID.
*
* @param certificatePath the password protected PFX file containing the certificate
* @return An updated instance of this builder.
*/
public ClientCertificateCredentialBuilder clientCertificatePath(String certificatePath) {
this.confidentialClientOptions.setCertificatePath(certificatePath);
return this;
}

/**
* Sets the input stream holding the PFX/PEM certificate for authenticating to Microsoft Entra ID.
*
* @param certificate the input stream containing the PFX/PEM certificate
* @return An updated instance of this builder.
*/
public ClientCertificateCredentialBuilder clientCertificate(InputStream certificate) {
this.confidentialClientOptions.setCertificateBytes(IdentityUtil.convertInputStreamToByteArray(certificate));
return this;
}

/**
* Sets the input stream holding the PFX certificate for authenticating to Microsoft Entra ID.
*
* @param certificate the input stream containing the PFX/PEM certificate
* @return An updated instance of this builder.
*/
public ClientCertificateCredentialBuilder clientCertificate(byte[] certificate) {
this.confidentialClientOptions.setCertificateBytes(Arrays.copyOf(certificate, certificate.length));
return this;
}

/**
* Sets the password of the client certificate for authenticating to Microsoft Entra ID.
*
* @param clientCertificatePassword the password protecting the certificate
* @return An updated instance of this builder.
*/
public ClientCertificateCredentialBuilder clientCertificatePassword(String clientCertificatePassword) {
this.confidentialClientOptions.setCertificatePassword(clientCertificatePassword);
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 ClientCertificateCredentialBuilder
tokenCachePersistenceOptions(TokenCachePersistenceOptions tokenCachePersistenceOptions) {
this.confidentialClientOptions.setTokenCacheOptions(tokenCachePersistenceOptions);
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 ClientCertificateCredentialBuilder sendCertificateChain(boolean sendCertificateChain) {
this.confidentialClientOptions.setIncludeX5c(sendCertificateChain);
return this;
}

/**
* Creates a new {@link ClientCertificateCredential} with the current configurations.
*
* @return a {@link ClientCertificateCredential} with the current configurations.
*/
public ClientCertificateCredential build() {
ValidationUtil.validate(CLASS_NAME, LOGGER, "clientId", confidentialClientOptions.getClientId(), "tenantId",
confidentialClientOptions.getTenantId(), "clientCertificate",
(confidentialClientOptions.getCertificateBytes() == null
|| confidentialClientOptions.getCertificateBytes().length == 0)
? confidentialClientOptions.getCertificatePath()
: confidentialClientOptions.getCertificateBytes());

if (confidentialClientOptions.getCertificateBytes() != null
&& confidentialClientOptions.getCertificatePath() != null) {
throw LOGGER.logThrowableAsWarning(new IllegalArgumentException("Both certificate input stream and "
+ "certificate path/bytes are provided in ClientCertificateCredentialBuilder. Only one of them should "
+ "be provided."));
}
return new ClientCertificateCredential(confidentialClientOptions);
}

@Override
ClientOptions getClientOptions() {
return confidentialClientOptions;
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.identity.v2;
package com.azure.v2.identity;

import com.azure.identity.v2.implementation.client.ConfidentialClient;
import com.azure.identity.v2.implementation.models.ConfidentialClientOptions;
import com.azure.identity.v2.implementation.util.LoggingUtil;
import com.azure.v2.identity.exceptions.CredentialAuthenticationException;
import com.azure.v2.identity.implementation.client.ConfidentialClient;
import com.azure.v2.identity.implementation.models.ConfidentialClientOptions;
import com.azure.v2.identity.implementation.util.LoggingUtil;
import com.azure.v2.core.credentials.TokenCredential;
import com.azure.v2.core.credentials.TokenRequestContext;
import io.clientcore.core.credentials.oauth.AccessToken;
@@ -39,10 +40,10 @@
*
* <p><strong>Sample: Construct a simple ClientSecretCredential</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link com.azure.identity.v2.ClientSecretCredential},
* <p>The following code sample demonstrates the creation of a {@link ClientSecretCredential},
* using the {@link ClientSecretCredentialBuilder} to configure it. The {@code tenantId},
* {@code clientId} and {@code clientSecret} parameters are required to create
* {@link com.azure.identity.v2.ClientSecretCredential} .Once this credential is created, it may be passed into the
* {@link ClientSecretCredential} .Once this credential is created, it may be passed into the
* builder of many of the Azure SDK for Java client builders as the 'credential' parameter.</p>
*
* <pre>
@@ -52,7 +53,7 @@
* .build&#40;&#41;;
* </pre>
*
* @see com.azure.identity.v2
* @see com.azure.v2.identity
* @see ClientSecretCredentialBuilder
*/
public class ClientSecretCredential implements TokenCredential {
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.identity.v2;
package com.azure.v2.identity;

import com.azure.identity.v2.implementation.models.ClientOptions;
import com.azure.identity.v2.implementation.models.ConfidentialClientOptions;
import com.azure.identity.v2.implementation.util.ValidationUtil;
import com.azure.v2.identity.implementation.models.ClientOptions;
import com.azure.v2.identity.implementation.models.ConfidentialClientOptions;
import com.azure.v2.identity.implementation.util.ValidationUtil;
import com.azure.v2.identity.models.TokenCachePersistenceOptions;
import io.clientcore.core.instrumentation.logging.ClientLogger;

/**
@@ -32,9 +33,9 @@
* <p><strong>Sample: Construct a simple ClientSecretCredential</strong></p>
*
* <p>The following code sample demonstrates the creation of a {@link ClientSecretCredential},
* using the {@link com.azure.identity.v2.ClientSecretCredentialBuilder} to configure it. The {@code tenantId},
* using the {@link ClientSecretCredentialBuilder} to configure it. The {@code tenantId},
* {@code clientId} and {@code clientSecret} parameters are required to create
* {@link com.azure.identity.v2.ClientSecretCredential} .Once this credential is created, it may be passed into the
* {@link ClientSecretCredential} .Once this credential is created, it may be passed into the
* builder of many of the Azure SDK for Java client builders as the 'credential' parameter.</p>
*
* <pre>
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.identity.v2;
package com.azure.v2.identity;

import com.azure.identity.v2.implementation.models.ClientOptions;
import com.azure.identity.v2.implementation.models.HttpPipelineOptions;
import com.azure.identity.v2.implementation.util.ValidationUtil;
import com.azure.v2.identity.implementation.models.ClientOptions;
import com.azure.v2.identity.implementation.models.HttpPipelineOptions;
import com.azure.v2.identity.implementation.util.ValidationUtil;
import io.clientcore.core.instrumentation.logging.ClientLogger;
import io.clientcore.core.utils.configuration.Configuration;

Loading