diff --git a/sdk/identity/azure-identity/CHANGELOG.md b/sdk/identity/azure-identity/CHANGELOG.md index 90416c1b4f582..d040b5b05781b 100644 --- a/sdk/identity/azure-identity/CHANGELOG.md +++ b/sdk/identity/azure-identity/CHANGELOG.md @@ -3,8 +3,15 @@ ## 1.4.0-beta.2 (Unreleased) ### Features Added +- Added `tenantId` setter on `AzurePowerShellCredential` and `AzureCliCredential` -### Breaking Changes +### Breaking Changes from 1.4.0-beta.1 +Note the breaking changes below don't apply if you're upgrading from a previous released stable version. + +- Removed 'AzureApplicationCredential' and 'AzureApplicationCredentialBuilder' +- Removed 'regionalAuthority' setter on `ClientSecretCredentialBuilder` and `ClientCertificateCredentialBuilder` +- Removed `RegionalAuthority` enum class. +- Removed `allowMultiTenantAuthentication` method from Credential Builders. The Multi Tenant Authentication is enabled by default now. ### Bugs Fixed diff --git a/sdk/identity/azure-identity/README.md b/sdk/identity/azure-identity/README.md index b973514fdc8a4..f28c1e4f0d3b0 100644 --- a/sdk/identity/azure-identity/README.md +++ b/sdk/identity/azure-identity/README.md @@ -1,5 +1,5 @@ # Azure Identity client library for Java -The Azure Identity library provides [Azure Active Directory (AAD)](https://docs.microsoft.com/azure/active-directory/fundamentals/active-directory-whatis) token authentication through a set of convenient [TokenCredential](https://docs.microsoft.com/java/api/com.azure.core.credential.tokencredential?view=azure-java-stable) implementations. It enables Azure SDK clients to authenticate with AAD, while also allowing other Java apps to authenticate with AAD work and school accounts, Microsoft personal accounts (MSA), and other Identity providers through the [AAD B2C](https://docs.microsoft.com/azure/active-directory-b2c/overview) service. +The Azure Identity library provides [Azure Active Directory (AAD)](https://docs.microsoft.com/azure/active-directory/fundamentals/active-directory-whatis) token authentication support across the Azure SDK. It provides a set of TokenCredential implementations which can be used to construct Azure SDK clients which support AAD token authentication. [Source code][source] | [API reference documentation][javadoc] | [Azure Active Directory documentation][aad_doc] @@ -61,10 +61,9 @@ Maven dependency for Azure Secret Client library. Add it to your project's pom f When debugging and executing code locally it is typical for a developer to use their own account for authenticating calls to Azure services. There are several developer tools which can be used to perform this authentication in your development environment: -- [Azure Toolkit for IntelliJ](https://github.com/Azure/azure-sdk-for-java/wiki/Set-up-Your-Environment-for-Authentication#sign-in-azure-toolkit-for-intellij-for-intellijcredential) -- [Visual Studio Code Azure Account Extension](https://github.com/Azure/azure-sdk-for-java/wiki/Set-up-Your-Environment-for-Authentication#sign-in-visual-studio-code-azure-account-extension-for-visualstudiocodecredential) -- [Azure CLI](https://github.com/Azure/azure-sdk-for-java/wiki/Set-up-Your-Environment-for-Authentication#sign-in-azure-cli-for-azureclicredential) -- [Visual Studio 2019 (Shared token cache)](https://github.com/Azure/azure-sdk-for-java/wiki/Set-up-Your-Environment-for-Authentication#enable-applications-for-shared-token-cache-credential) +- [Azure Toolkit for IntelliJ](https://docs.microsoft.com/azure/developer/java/sdk/identity-dev-env-auth#intellij-credential) +- [Visual Studio Code Azure Account Extension](https://docs.microsoft.com/azure/developer/java/sdk/identity-dev-env-auth#visual-studio-code-credential) +- [Azure CLI](https://docs.microsoft.com/azure/developer/java/sdk/identity-dev-env-auth#azure-cli-credential) Click on each item above to learn about how to configure them for Azure Identity authentication. @@ -118,7 +117,9 @@ public void createDefaultAzureCredential() { See more how to configure the `DefaultAzureCredential` on your workstation or Azure in [Configure DefaultAzureCredential](https://github.com/Azure/azure-sdk-for-java/wiki/Set-up-Your-Environment-for-Authentication#configure-defaultazurecredential). ### Authenticating a user assigned managed identity with `DefaultAzureCredential` -This example demonstrates authenticating the `SecretClient` from the [azure-security-keyvault-secrets][secrets_client_library] client library using the `DefaultAzureCredential`, deployed to an Azure resource with a user assigned managed identity configured. +To Authenticate using User Assigned Managed Identity, please ensure that configuration instructions for your supported Azure Resource [here](#managed-identity-support) have been successfully completed. + +The below example demonstrates authenticating the `SecretClient` from the [azure-security-keyvault-secrets][secrets_client_library] client library using the `DefaultAzureCredential`, deployed to an Azure resource with a user assigned managed identity configured. See more about how to configure a user assigned managed identity for an Azure resource in [Enable managed identity for Azure resources](https://github.com/Azure/azure-sdk-for-java/wiki/Set-up-Your-Environment-for-Authentication#enable-managed-identity-for-azure-resources). @@ -143,7 +144,9 @@ public void createDefaultAzureCredentialForUserAssignedManagedIdentity() { In addition to configuring the `managedIdentityClientId` via code, it can also be set using the `AZURE_CLIENT_ID` environment variable. These two approaches are equivalent when using the `DefaultAzureCredential`. ### Authenticating a user in Azure Toolkit for IntelliJ with `DefaultAzureCredential` -This example demonstrates authenticating the `SecretClient` from the [azure-security-keyvault-secrets][secrets_client_library] client library using the `DefaultAzureCredential`, on a workstation with IntelliJ IDEA installed, and the user has signed in with an Azure account to the Azure Toolkit for IntelliJ. +To Authenticate using IntelliJ, please ensure that configuration instructions [here](https://docs.microsoft.com/azure/developer/java/sdk/identity-dev-env-auth#sign-in-azure-toolkit-for-intellij-for-intellijcredential) have been successfully completed. + +The below example demonstrates authenticating the `SecretClient` from the [azure-security-keyvault-secrets][secrets_client_library] client library using the `DefaultAzureCredential`, on a workstation with IntelliJ IDEA installed, and the user has signed in with an Azure account to the Azure Toolkit for IntelliJ. See more about how to configure your IntelliJ IDEA in [Sign in Azure Toolkit for IntelliJ for IntelliJCredential](https://github.com/Azure/azure-sdk-for-java/wiki/Set-up-Your-Environment-for-Authentication#sign-in-azure-toolkit-for-intellij-for-intellijcredential). @@ -169,6 +172,7 @@ public void createDefaultAzureCredentialForIntelliJ() { ## Managed Identity Support The [Managed identity authentication](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview) is supported via either the `DefaultAzureCredential` or the `ManagedIdentityCredential` directly for the following Azure Services: * [Azure Virtual Machines](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token) +* [Azure Virtual Machines Scale Sets](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/qs-configure-powershell-windows-vmss) * [Azure App Service](https://docs.microsoft.com/azure/app-service/overview-managed-identity?tabs=dotnet) * [Azure Kubernetes Service](https://docs.microsoft.com/azure/aks/use-managed-identity) * [Azure Cloud Shell](https://docs.microsoft.com/azure/cloud-shell/msi-authorization) @@ -214,6 +218,22 @@ public void createManagedIdentityCredential() { } ``` +## Cloud Configuration +Credentials default to authenticating to the Azure Active Directory endpoint for +Azure Public Cloud. To access resources in other clouds, such as Azure Government +or a private cloud, configure credentials with the `auhtorityHost` argument. +[AzureAuthorityHosts](https://docs.microsoft.com/java/api/com.azure.identity.azureauthorityhosts?view=azure-java-stable) +defines authorities for well-known clouds: +```java +DefaultAzureCredential defaultAzureCredential = new DefaultAzureCredentialBuilder() + .authorityHost(AzureAuthorityHosts.AZURE_GOVERNMENT) + .build(); +``` +Not all credentials require this configuration. Credentials which authenticate +through a development tool, such as `AzureCliCredential`, use that tool's +configuration. Similarly, `VisualStudioCodeCredential` accepts an `authority` +argument but defaults to the authority matching VS Code's "Azure: Cloud" setting. + ## Credential classes ### Authenticating Azure Hosted Applications @@ -455,6 +475,7 @@ describes why authentication failed. When this exception is raised by `ChainedTo When credentials cannot execute authentication due to one of the underlying resources required by the credential being unavailable on the machine, the`CredentialUnavailableException` is raised and it has a `message` attribute which describes why the credential is unavailable for authentication execution . When this exception is raised by `ChainedTokenCredential`, the message collects error messages from each credential in the chain. +See the [troubleshooting guide](https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/identity/azure-identity/TROUBLESHOOT.md) for details on how to diagnose various failure scenarios. ### Enable client logging Azure SDK for Java offers a consistent logging story to help aid in troubleshooting application errors and expedite diff --git a/sdk/identity/azure-identity/TROUBLESHOOT.md b/sdk/identity/azure-identity/TROUBLESHOOT.md new file mode 100644 index 0000000000000..ba4da5affd217 --- /dev/null +++ b/sdk/identity/azure-identity/TROUBLESHOOT.md @@ -0,0 +1,223 @@ +## Troubleshooting Azure Identity Authentication Issues +The Azure Identity SDK offers various `TokenCredential` implementations. These implementations tend to throw `CredentialUnavailable` and `ClientAuthentication` exceptions. +The `CredentialUnavailableException` indicates that the credential cannot execute in the current environment setup due to lack of required configuration. +The `ClientAuthenticationException` indicates that the credential was able to run/execute but ran into an authentication issue from the server's end. This can happen due to invalid configuration/details passed in to the credential at construction time. +This troubleshooting guide covers mitigation steps to resolve these exceptions thrown by various `TokenCredential` implementations in the Azure Identity Java client library. + +## Table of contents + - [Troubleshooting Default Azure Credential Authentication Issues](#troubleshooting-default-azure-credential-authentication-issues) + - [Troubleshooting Environment Credential Authentication Issues](#troubleshooting-environment-credential-authentication-issues) + - [Troubleshooting Service Principal Authentication Issues](#troubleshooting-service-principal-authentication-issues) + - [Troubleshooting Username Password Authentication Issues](#troubleshooting-username-password-authentication-issues) + - [Troubleshooting Managed Identity Authentication Issues](#troubleshooting-managed-identity-authentication-issues) + - [Troubleshooting Visual Studio Code Authentication Issues](#troubleshooting-visual-studio-code-authentication-issues) + - [Troubleshooting Azure CLI Authentication Issues](#troubleshooting-azure-cli-authentication-issues) + - [Troubleshooting Azure Powershell Authentication Issues](#troubleshooting-azure-powershell-authentication-issues) + + + +## Troubleshooting Default Azure Credential Authentication Issues. + +### Credential Unavailable Exception +The `DefaultAzureCredential` attempts to retrieve an access token by sequentially invoking a chain of credentials. The `CredentialUnavailableException` in this scenario signifies that all the credentials in the chain failed to retrieve the token in the current environment setup/configuration. You need to follow the configuration instructions for the respective credential you're looking to use via `DefaultAzureCredential` chain, so that the credential can work in your environment. + +Please follow the configuration instructions in the `Credential Unvavailable` section of hte troubleshooting guidelines below for the respective credential/authentication type you're looking to use via `DefaultAzureCredential`: + +| Credential Type | Troubleshoot Guide | +| --- | --- | +| Environment Credential | [Environment Credential Troubleshooting Guide](#troubleshooting-environment-credential-authentication-issues) | +| Managed Identity Credential | [Managed Identity Troubleshooting Guide](#troubleshooting-managed-identity-authentication-issues) | +| Visual Studio Code Credential | [Visual Studio Code Troubleshooting Guide](#troubleshooting-visual-studio-code-authentication-issues) | +| Azure CLI Credential | [Azure CLI Troubleshooting Guide](#troubleshooting-azure-cli-authentication-issues) | +| Azure Powershell Credential | [Azure Powershell Troubleshooting Guide](#troubleshooting-azure-powershell-authentication-issues) | + + + + +## Troubleshooting Environment Credential Authentication Issues. + +### Credential Unavailable Exception + +#### Environment variables not configured +The `EnvironmentCredential` supports Service Principal authentication and Username + Password Authentication. To utilize the desired way of authentication via `EnvironmentCredential`, you need to ensure the environment variables below are configured properly and the application is able to read them. + + +##### Service principal with secret +| Variable Name | Value | +| --- | --- | +AZURE_CLIENT_ID | ID of an Azure Active Directory application. | +AZURE_TENANT_ID |ID of the application's Azure Active Directory tenant. | +AZURE_CLIENT_SECRET | One of the application's client secrets. | + +##### Service principal with certificate +| Variable name | Value | +| --- | --- | +AZURE_CLIENT_ID |ID of an Azure Active Directory application. | +AZURE_TENANT_ID | ID of the application's Azure Active Directory tenant. | +AZURE_CLIENT_CERTIFICATE_PATH | Path to a PEM-encoded certificate file including private key (without password protection). | + +##### Username and password +| Variable name | Value | +| --- | --- | +AZURE_CLIENT_ID | ID of an Azure Active Directory application. | +AZURE_USERNAME | A username (usually an email address). | +AZURE_PASSWORD | The associated password for the given username. | + +### Client Authentication Exception +The `EnvironmentCredential` supports Service Principal authentication and Username + Password Authentication. +Please follow the troubleshooting guidelines below for the respective authentication which you tried and failed. + +| Authentication Type | Troubleshoot Guide | +| --- | --- | +| Service Principal | [Service Principal Auth Troubleshooting Guide](#troubleshooting-username-password-authentication-issues) | +| Username Password | [Username Password Auth Troubleshooting Guide](#troubleshooting-username-password-authentication-issues) | + + + + +## Troubleshooting Username Password Authentication Issues. + +### Two-Factor Authentication Required Error. +The `UsernamePassword` credential works only for users whose two-factor authentication has been disabled in Azure Active Directory. You can change the Multi-Factor Authentication in Azure Portal by following the steps [here](https://docs.microsoft.com/azure/active-directory/authentication/howto-mfa-userstates#change-the-status-for-a-user). + + + + +## Troubleshooting Service Principal Authentication Issues. + +### Illegal/Invalid Argument Issues + +#### Client Id + +The Client ID is the application ID of the registered application / service principal in Azure Active Directory. +It is a required parameter for `ClientSecretCredential` and `ClientCertificateCredential`. If you have already created your service principal +then you can retrieve the client/app id by following the instructions [here](https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal#get-tenant-and-app-id-values-for-signing-in). + +#### Tenant Id +The tenant id is te Global Unique Identifier (GUID) that identifies your organization. It is a required parameter for +`ClientSecretCredential` and `ClientCertificateCredential`. If you have already created your service principal +then you can retrieve the client/app id by following the instructions [here](https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal#get-tenant-and-app-id-values-for-signing-in). + +### Client Secret Credential Issues + +#### Client Secret Argument +The client secret is the secret string that the application uses to prove its identity when requesting a token, this can also can be referred to as an application password. +If you have already created a service principal you can follow the instructions [here](https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal#option-2-create-a-new-application-secret) to get the client secret for your application. + +### Client Certificate Credential Issues + +#### Client Certificate Argument +The `Client Certificate Credential` accepts `pfx` and `pem` certificates. The certificate needs to be associated with your registered application/service principal. To create and associate a certificate with your registered app. please follow the instructions [here](https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal#option-1-upload-a-certificate). + +### Create a new service principal +If you're looking to create a new service principal and would like to use that, then follow tne instructions [here](https://docs.microsoft.com/azure/developer/java/sdk/identity-service-principal-auth#create-a-service-principal-with-the-azure-cli) to create a new service principal. + + + + +## Troubleshooting Managed Identity Authentication Issues + +### Credential Unavailable + +#### Connection Timed Out / Connection could not be established / Target Environment could not be determined. +The Managed Identity credential runs only on Azure Hosted machines/servers. So ensure that you are running your application on an +Azure Hosted resource. Currently, Azure Identity SDK supports Managed Identity Authentication +for the below listed Azure Services, so ensure you're running your application on one of these resources and have enabled the Managed Identity on +them by following the instructions at their configuration links below. + +Azure Service | Managed Identity Configuration +--- | --- | +[Azure Virtual Machines](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token) | [Configuration Instructions](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/qs-configure-portal-windows-vm) +[Azure App Service](https://docs.microsoft.com/azure/app-service/overview-managed-identity?tabs=java) | [Configuration Instructions](https://docs.microsoft.com/azure/app-service/overview-managed-identity?tabs=java) +[Azure Kubernetes Service](https://docs.microsoft.com/azure/aks/use-managed-identity) | [Configuration Instructions](https://docs.microsoft.com/azure/aks/use-managed-identity) +[Azure Cloud Shell](https://docs.microsoft.com/azure/cloud-shell/msi-authorization) | | +[Azure Arc](https://docs.microsoft.com/azure/azure-arc/servers/managed-identity-authentication) | [Configuration Instructions](https://docs.microsoft.com/azure/azure-arc/servers/security-overview#using-a-managed-identity-with-arc-enabled-servers) +[Azure Service Fabric](https://docs.microsoft.com/azure/service-fabric/concepts-managed-identity) | [Configuration Instructions](https://docs.microsoft.com/azure/service-fabric/configure-existing-cluster-enable-managed-identity-token-service) + + + + +## Troubleshooting Visual Studio Code Authentication Issues + +### Credential Unavailable + +#### Failed To Read VS Code Credentials / Authenticate via Azure Tools plugin in VS Code. +THe `VS Code Credential` failed to read the credential details from the cache. + +The Visual Studio Code authentication is handled by an integration with the Azure Account extension. +To use this form of authentication, ensure that you have installed the Azure Account extension, +then use View > Command Palette to execute the Azure: Sign In command. This command opens a browser window and displays a page that allows you +to sign in to Azure. After you've completed the login process, you can close the browser as directed. Running your application +(either in the debugger or anywhere on the development machine) will use the credential from your sign-in. + +If you already had the Azure Account extension installed and had logged in to your account. Then try logging out and logging in again, as +that will re-populate the cache on the disk and potentially mitigate the error you're getting. + +#### Msal Interaction Required Error +THe `VS Code Credential` was able to read the cached credentials from the cache but the cached token is likely expired. +Log into the Azure Account extension by via View > Command Palette to execute the Azure: Sign In command in the VS Code IDE. + +#### ADFS Tenant Not Supported +The ADFS Tenants are not supported via the Azure Account extension in VS Code currently. +The supported clouds are: + +Azure Cloud | Cloud Authority Host +--- | --- | +AZURE PUBLIC CLOUD | https://login.microsoftonline.com/ +AZURE GERMANY | https://login.microsoftonline.de/ +AZURE CHINA | https://login.chinacloudapi.cn/ +AZURE GOVERNMENT | https://login.microsoftonline.us/ + + + + +## Troubleshooting Azure CLI Authentication Issues + +### Credential Unavailable + +#### Azure CLI Not Installed. +THe `Azure CLI Credential` failed to execute as Azure CLI command line tool is not installed. +To use Azure CLI credential, the Azure CLI needs to be installed, please follow the instructions [here](https://docs.microsoft.com/cli/azure/install-azure-cli) +to install it for your platform and then try running the credential again. + +#### Azure account not logged in. +The `Azure CLI Credential` utilizes the current logged in Azure user in Azure CLI to fetch an access token. +You need to log in to your account in Azure CLI via `az login` command. You can further read instructions to [Sign in with Azure CLI](https://docs.microsoft.com/cli/azure/authenticate-azure-cli). +Once logged in try running the credential again. + +### Illegal State +#### Safe Working Directory Not Located. +The `Azure CLI Credential` was not able to locate a value for System Environment property `SystemRoot` to execute in. +Please ensure the `SystemRoot` environment variable is configured to a safe working directory and then try running the credential again. + + + + +## Troubleshooting Azure Powershell Authentication Issues + +### Credential Unavailable + +#### Powershell not installed. + +The `Azure Powershell Credential` utilizes the locally installed `Powershell` command line tool to fetch an access token. Please ensure it is installed on your platform by following the instructions [here](https://docs.microsoft.com/powershell/scripting/install/installing-powershell?view=powershell-7.1) and then run the credential again. + +#### Azure Az Module Not Installed. +The `Azure Powershell Credential` failed to execute as Azure az module is not installed. +To use Azure Powershell credential, the Azure az module needs to be installed, please follow the instructions [here](https://docs.microsoft.com/powershell/azure/install-az-ps?view=azps-6.3.0) +to install it for your platform and then try running the credential again. + +#### Azure account not logged in. +The `Azure Powershell Credential` utilizes the current logged in Azure user in Azure Powershell to fetch an access token. +You need to log in to your account in Azure Powershell via `Connect-AzAccount` command. You can further read instructions to [Sign in with Azure Powershell](https://docs.microsoft.com/powershell/azure/authenticate-azureps?view=azps-6.3.0). +Once logged in try running the credential again. + + +#### Deserialization error. +The `Azure Powershell Credential` was able to retrieve a response from the Azure Powershell when attempting to get an access token but failed +to parse that response. +In your local powershell window, run the following command to ensure that Azure Powershell is returning an access token in correct format. + +```pwsh +Get-AzAccessToken -ResourceUrl "" +``` +In the event above command is not working properly, follow the instructions to resolve the Azure Powershell issue being faced and then try running the credential again. diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzureApplicationCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzureApplicationCredential.java index 87b8db33e205e..785fd885ab058 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzureApplicationCredential.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzureApplicationCredential.java @@ -19,7 +19,7 @@ * */ @Immutable -public final class AzureApplicationCredential extends ChainedTokenCredential { +final class AzureApplicationCredential extends ChainedTokenCredential { /** * Creates default AzureApplicationCredential instance to use. This will use environment variables to create * {@link EnvironmentCredential} diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzureApplicationCredentialBuilder.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzureApplicationCredentialBuilder.java index e6b250882725a..a4e796c98fbfb 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzureApplicationCredentialBuilder.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzureApplicationCredentialBuilder.java @@ -15,13 +15,13 @@ * * @see AzureApplicationCredential */ -public class AzureApplicationCredentialBuilder extends CredentialBuilderBase { +class AzureApplicationCredentialBuilder extends CredentialBuilderBase { private String managedIdentityClientId; /** * Creates an instance of a AzureApplicationCredentialBuilder. */ - public AzureApplicationCredentialBuilder() { + AzureApplicationCredentialBuilder() { Configuration configuration = Configuration.getGlobalConfiguration().clone(); managedIdentityClientId = configuration.get(Configuration.PROPERTY_AZURE_CLIENT_ID); } diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzureCliCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzureCliCredential.java index 7f62a1eb8cd98..b2d1447a292b0 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzureCliCredential.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzureCliCredential.java @@ -25,10 +25,13 @@ public class AzureCliCredential implements TokenCredential { /** * Creates an AzureCliSecretCredential with default identity client options. + * @param tenantId the tenant id of the application * @param identityClientOptions the options to configure the identity client */ - AzureCliCredential(IdentityClientOptions identityClientOptions) { - identityClient = new IdentityClientBuilder().identityClientOptions(identityClientOptions).build(); + AzureCliCredential(String tenantId, IdentityClientOptions identityClientOptions) { + identityClient = new IdentityClientBuilder() + .tenantId(tenantId) + .identityClientOptions(identityClientOptions).build(); } @Override diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzureCliCredentialBuilder.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzureCliCredentialBuilder.java index 1c754534bfcc1..318489d3f0c16 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzureCliCredentialBuilder.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzureCliCredentialBuilder.java @@ -3,18 +3,34 @@ package com.azure.identity; +import com.azure.identity.implementation.util.ValidationUtil; + /** * Fluent credential builder for instantiating a {@link AzureCliCredential}. * * @see AzureCliCredential */ public class AzureCliCredentialBuilder extends CredentialBuilderBase { + private String tenantId; + + /** + * 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(getClass().getSimpleName(), tenantId); + this.tenantId = tenantId; + 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(identityClientOptions); + return new AzureCliCredential(tenantId, identityClientOptions); } } diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzurePowerShellCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzurePowerShellCredential.java index 3ee02f127e0a1..55917355fcbc4 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzurePowerShellCredential.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzurePowerShellCredential.java @@ -22,11 +22,11 @@ public class AzurePowerShellCredential implements TokenCredential { private final IdentityClient identityClient; private final ClientLogger logger = new ClientLogger(AzurePowerShellCredential.class); - AzurePowerShellCredential(IdentityClientOptions options) { + AzurePowerShellCredential(String tenantId, IdentityClientOptions options) { identityClient = new IdentityClientBuilder() .identityClientOptions(options) + .tenantId(tenantId) .build(); - } @Override diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzurePowerShellCredentialBuilder.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzurePowerShellCredentialBuilder.java index df063861e59cd..f9d0cf3ac344d 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzurePowerShellCredentialBuilder.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/AzurePowerShellCredentialBuilder.java @@ -3,12 +3,27 @@ package com.azure.identity; +import com.azure.identity.implementation.util.ValidationUtil; + /** * Fluent credential builder for instantiating a {@link AzurePowerShellCredential}. * * @see AzurePowerShellCredential */ public class AzurePowerShellCredentialBuilder extends CredentialBuilderBase { + private String tenantId; + + /** + * 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(getClass().getSimpleName(), tenantId); + this.tenantId = tenantId; + return this; + } /** * Creates a new {@link AzurePowerShellCredential} with the current configurations. @@ -16,6 +31,6 @@ public class AzurePowerShellCredentialBuilder extends CredentialBuilderBase getToken(TokenRequestContext request) { 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(), - last.getCause()); + 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" + : "")); } return Mono.error(last); })); diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/ClientCertificateCredentialBuilder.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/ClientCertificateCredentialBuilder.java index c264067dc2dc5..2416918e477f3 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/ClientCertificateCredentialBuilder.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/ClientCertificateCredentialBuilder.java @@ -4,6 +4,7 @@ package com.azure.identity; import com.azure.core.util.logging.ClientLogger; +import com.azure.identity.implementation.RegionalAuthority; import com.azure.identity.implementation.util.ValidationUtil; import java.io.InputStream; @@ -128,7 +129,7 @@ public ClientCertificateCredentialBuilder sendCertificateChain(boolean sendCerti * @param regionalAuthority the regional authority * @return An updated instance of this builder with the regional authority configured. */ - public ClientCertificateCredentialBuilder regionalAuthority(RegionalAuthority regionalAuthority) { + ClientCertificateCredentialBuilder regionalAuthority(RegionalAuthority regionalAuthority) { this.identityClientOptions.setRegionalAuthority(regionalAuthority); return this; } diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/ClientSecretCredentialBuilder.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/ClientSecretCredentialBuilder.java index 21cfab1a59c25..f255eeed2d0c0 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/ClientSecretCredentialBuilder.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/ClientSecretCredentialBuilder.java @@ -3,6 +3,7 @@ package com.azure.identity; +import com.azure.identity.implementation.RegionalAuthority; import com.azure.identity.implementation.util.ValidationUtil; import java.util.HashMap; @@ -70,7 +71,7 @@ public ClientSecretCredentialBuilder tokenCachePersistenceOptions(TokenCachePers * @param regionalAuthority the regional authority * @return An updated instance of this builder with the regional authority configured. */ - public ClientSecretCredentialBuilder regionalAuthority(RegionalAuthority regionalAuthority) { + ClientSecretCredentialBuilder regionalAuthority(RegionalAuthority regionalAuthority) { this.identityClientOptions.setRegionalAuthority(regionalAuthority); return this; } diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/CredentialBuilderBase.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/CredentialBuilderBase.java index 8297cd5f71c8e..ed0626880188d 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/CredentialBuilderBase.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/CredentialBuilderBase.java @@ -6,6 +6,7 @@ import com.azure.core.http.HttpClient; import com.azure.core.http.HttpPipeline; import com.azure.core.http.ProxyOptions; +import com.azure.core.util.Configuration; import com.azure.identity.implementation.IdentityClientOptions; import java.time.Duration; @@ -91,14 +92,18 @@ public T httpClient(HttpClient client) { } /** - * Allows to override the tenant being used in the authentication request - * via {@link com.azure.core.credential.TokenRequestContext#setTenantId(String)}. + * Sets the configuration store that is used during construction of the credential. * - * @return An updated instance of this builder. + * The default configuration store is a clone of the {@link Configuration#getGlobalConfiguration() global + * configuration store}. + * + * @param configuration The configuration store used to load Env variables and/or properties from. + * + * @return An updated instance of this builder with the configuration store set as specified. */ @SuppressWarnings("unchecked") - public T allowMultiTenantAuthentication() { - this.identityClientOptions.setAllowMultiTenantAuthentication(true); + public T configuration(Configuration configuration) { + identityClientOptions.setConfiguration(configuration); return (T) this; } } diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/DefaultAzureCredentialBuilder.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/DefaultAzureCredentialBuilder.java index 0a99a1c4b4685..29c43de1de864 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/DefaultAzureCredentialBuilder.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/DefaultAzureCredentialBuilder.java @@ -131,8 +131,8 @@ private ArrayList getCredentialsChain() { tenantId, identityClientOptions)); output.add(new IntelliJCredential(tenantId, identityClientOptions)); output.add(new VisualStudioCodeCredential(tenantId, identityClientOptions)); - output.add(new AzureCliCredential(identityClientOptions)); - output.add(new AzurePowerShellCredential(identityClientOptions)); + output.add(new AzureCliCredential(tenantId, identityClientOptions)); + output.add(new AzurePowerShellCredential(tenantId, identityClientOptions)); return output; } } diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EnvironmentCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EnvironmentCredential.java index ddedd404519d7..eda54d16a29d1 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EnvironmentCredential.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EnvironmentCredential.java @@ -123,7 +123,9 @@ public Mono getToken(TokenRequestContext request) { if (tokenCredential == null) { return Mono.error(logger.logExceptionAsError(new CredentialUnavailableException( "EnvironmentCredential authentication unavailable." - + " Environment variables are not fully configured."))); + + " Environment variables are not fully configured." + + "To mitigate this issue, please refer to the troubleshooting guidelines here at" + + " https://aka.ms/azsdk/net/identity/environmentcredential/troubleshoot"))); } else { return tokenCredential.getToken(request); } diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EnvironmentCredentialBuilder.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EnvironmentCredentialBuilder.java index ae41633ad224e..b38056a3c015b 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EnvironmentCredentialBuilder.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EnvironmentCredentialBuilder.java @@ -3,7 +3,6 @@ package com.azure.identity; -import com.azure.core.util.Configuration; import com.azure.core.util.CoreUtils; import com.azure.identity.implementation.util.ValidationUtil; @@ -50,21 +49,6 @@ public EnvironmentCredentialBuilder executorService(ExecutorService executorServ return this; } - /** - * Sets the configuration store that is used during construction of the credential. - * - * The default configuration store is a clone of the {@link Configuration#getGlobalConfiguration() global - * configuration store}. - * - * @param configuration The configuration store used to load Env variables and/or properties from. - * - * @return An updated instance of this builder with the configuration store set as specified. - */ - public EnvironmentCredentialBuilder configuration(Configuration configuration) { - identityClientOptions.setConfiguration(configuration); - return this; - } - /** * Creates a new {@link EnvironmentCredential} with the current configurations. * diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/ManagedIdentityCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/ManagedIdentityCredential.java index 334bd4cf3d88b..4b1f1b85f4ded 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/ManagedIdentityCredential.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/ManagedIdentityCredential.java @@ -39,7 +39,8 @@ public final class ManagedIdentityCredential implements TokenCredential { .clientId(clientId) .identityClientOptions(identityClientOptions); - Configuration configuration = Configuration.getGlobalConfiguration().clone(); + Configuration configuration = identityClientOptions.getConfiguration() == null + ? Configuration.getGlobalConfiguration().clone() : identityClientOptions.getConfiguration(); if (configuration.contains(Configuration.PROPERTY_MSI_ENDPOINT)) { managedIdentityServiceCredential = new AppServiceMsiCredential(clientId, clientBuilder.build()); @@ -83,7 +84,9 @@ public Mono getToken(TokenRequestContext request) { if (managedIdentityServiceCredential == null) { return Mono.error(logger.logExceptionAsError( new CredentialUnavailableException("ManagedIdentityCredential authentication unavailable. " - + "The Target Azure platform could not be determined from environment variables."))); + + "The Target Azure platform could not be determined from environment variables." + + "To mitigate this issue, please refer to the troubleshooting guidelines here at" + + " https://aka.ms/azsdk/net/identity/managedidentitycredential/troubleshoot"))); } return managedIdentityServiceCredential.authenticate(request) .doOnSuccess(t -> logger.info("Azure Identity => Managed Identity environment: {}", diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/OnBehalfOfCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/OnBehalfOfCredential.java index eacf48b73050a..02781d6ed3301 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/OnBehalfOfCredential.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/OnBehalfOfCredential.java @@ -32,7 +32,7 @@ public class OnBehalfOfCredential implements TokenCredential { * @param certificatePassword the password protecting the PFX file * @param identityClientOptions the options for configuring the identity client */ - public OnBehalfOfCredential(String clientId, String tenantId, String clientSecret, String certificatePath, + OnBehalfOfCredential(String clientId, String tenantId, String clientSecret, String certificatePath, String certificatePassword, IdentityClientOptions identityClientOptions) { this.identityClient = new IdentityClientBuilder() .tenantId(tenantId) diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/OnBehalfOfCredentialBuilder.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/OnBehalfOfCredentialBuilder.java index c988f1b886281..a452a73569566 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/OnBehalfOfCredentialBuilder.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/OnBehalfOfCredentialBuilder.java @@ -4,6 +4,7 @@ package com.azure.identity; import com.azure.core.util.logging.ClientLogger; +import com.azure.identity.implementation.RegionalAuthority; import com.azure.identity.implementation.util.ValidationUtil; import java.util.HashMap; @@ -43,16 +44,35 @@ public OnBehalfOfCredentialBuilder tokenCachePersistenceOptions(TokenCachePersis return this; } + /** + * Sets the path of the PEM certificate for authenticating to AAD. + * + * @param pemCertificatePath the PEM file containing the certificate + * @return An updated instance of this builder. + */ + public OnBehalfOfCredentialBuilder pemCertificate(String pemCertificatePath) { + this.clientCertificatePath = pemCertificatePath; + return this; + } + /** * Sets the path and password of the PFX certificate for authenticating to AAD. * - * @param certificatePath the password protected PFX file containing the certificate - * @param clientCertificatePassword the password protecting the PFX file + * @param pfxCertificatePath the password protected PFX file containing the certificate + * @return An updated instance of this builder. + */ + public OnBehalfOfCredentialBuilder pfxCertificate(String pfxCertificatePath) { + this.clientCertificatePath = pfxCertificatePath; + return this; + } + + /** + * Sets the password of the client certificate for authenticating to AAD. + * + * @param clientCertificatePassword the password protecting the certificate * @return An updated instance of this builder. */ - public OnBehalfOfCredentialBuilder pfxCertificate(String certificatePath, - String clientCertificatePassword) { - this.clientCertificatePath = certificatePath; + public OnBehalfOfCredentialBuilder clientCertificatePassword(String clientCertificatePassword) { this.clientCertificatePassword = clientCertificatePassword; return this; } @@ -78,7 +98,7 @@ public OnBehalfOfCredentialBuilder sendCertificateChain(boolean sendCertificateC * @param regionalAuthority the regional authority * @return An updated instance of this builder with the regional authority configured. */ - public OnBehalfOfCredentialBuilder regionalAuthority(RegionalAuthority regionalAuthority) { + OnBehalfOfCredentialBuilder regionalAuthority(RegionalAuthority regionalAuthority) { this.identityClientOptions.setRegionalAuthority(regionalAuthority); return this; } diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/SharedTokenCacheCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/SharedTokenCacheCredential.java index 479b309798fca..fe9ff7c136a72 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/SharedTokenCacheCredential.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/SharedTokenCacheCredential.java @@ -43,7 +43,8 @@ public class SharedTokenCacheCredential implements TokenCredential { */ SharedTokenCacheCredential(String username, String clientId, String tenantId, IdentityClientOptions identityClientOptions) { - Configuration configuration = Configuration.getGlobalConfiguration().clone(); + Configuration configuration = identityClientOptions.getConfiguration() == null + ? Configuration.getGlobalConfiguration().clone() : identityClientOptions.getConfiguration(); if (username == null) { this.username = configuration.get(Configuration.PROPERTY_AZURE_USERNAME); diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClient.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClient.java index 87b3326091dde..665db5fe24c00 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClient.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClient.java @@ -23,7 +23,6 @@ import com.azure.core.util.serializer.SerializerEncoding; import com.azure.identity.CredentialUnavailableException; import com.azure.identity.DeviceCodeInfo; -import com.azure.identity.RegionalAuthority; import com.azure.identity.TokenCachePersistenceOptions; import com.azure.identity.implementation.util.CertificateUtil; import com.azure.identity.implementation.util.IdentityConstants; @@ -219,7 +218,9 @@ private Mono getConfidentialClientApplication() { } } else { return Mono.error(logger.logExceptionAsError( - new IllegalArgumentException("Must provide client secret or client certificate path"))); + new IllegalArgumentException("Must provide client secret or client certificate path." + + " To mitigate this issue, please refer to the troubleshooting guidelines here at " + + "https://aka.ms/azsdk/java/identity/serviceprincipalauthentication/troubleshoot"))); } ConfidentialClientApplication.Builder applicationBuilder = @@ -368,8 +369,11 @@ public Mono authenticateWithIntelliJ(TokenRequestContext request) { return Mono.error(e); } } else if (authType.equalsIgnoreCase("DC")) { - + logger.verbose("IntelliJ Authentication => Device Code Authentication scheme detected in Azure Tools" + + " for IntelliJ Plugin."); if (isADFSTenant()) { + logger.verbose("IntelliJ Authentication => The input tenant is detected to be ADFS and" + + " the ADFS tenants are not supported via IntelliJ Authentication currently."); return Mono.error(new CredentialUnavailableException("IntelliJCredential " + "authentication unavailable. ADFS tenant/authorities are not supported.")); } @@ -389,6 +393,9 @@ public Mono authenticateWithIntelliJ(TokenRequestContext request) { .map(MsalToken::new)); } else { + logger.verbose("IntelliJ Authentication = > Only Service Principal and Device Code Authentication" + + " schemes are currently supported via IntelliJ Credential currently. Please ensure you used one" + + " of those schemes from Azure Tools for IntelliJ plugin."); throw logger.logExceptionAsError(new CredentialUnavailableException( "IntelliJ Authentication not available." + " Please login with Azure Tools for IntelliJ plugin in the IDE.")); @@ -405,10 +412,8 @@ public Mono authenticateWithIntelliJ(TokenRequestContext request) { * @return a Publisher that emits an AccessToken */ public Mono authenticateWithAzureCli(TokenRequestContext request) { - String azCommand = "az account get-access-token --output json --resource "; - StringBuilder command = new StringBuilder(); - command.append(azCommand); + StringBuilder azCommand = new StringBuilder("az account get-access-token --output json --resource "); String scopes = ScopeUtil.scopesToResource(request.getScopes()); @@ -418,7 +423,12 @@ public Mono authenticateWithAzureCli(TokenRequestContext request) { return Mono.error(logger.logExceptionAsError(ex)); } - command.append(scopes); + azCommand.append(scopes); + + String tenant = IdentityUtil.resolveTenantId(null, request, options); + if (!CoreUtils.isNullOrEmpty(tenant)) { + azCommand.append("--tenant " + tenant); + } AccessToken token = null; BufferedReader reader = null; @@ -433,13 +443,15 @@ public Mono authenticateWithAzureCli(TokenRequestContext request) { switcher = LINUX_MAC_SWITCHER; } - ProcessBuilder builder = new ProcessBuilder(starter, switcher, command.toString()); + ProcessBuilder builder = new ProcessBuilder(starter, switcher, azCommand.toString()); + String workingDirectory = getSafeWorkingDirectory(); if (workingDirectory != null) { builder.directory(new File(workingDirectory)); } else { throw logger.logExceptionAsError(new IllegalStateException("A Safe Working directory could not be" - + " found to execute CLI command from.")); + + " found to execute CLI command from. To mitigate this issue, please refer to the troubleshooting " + + " guidelines here at https://aka.ms/azsdk/java/identity/azclicredential/troubleshoot")); } builder.redirectErrorStream(true); Process process = builder.start(); @@ -454,8 +466,10 @@ public Mono authenticateWithAzureCli(TokenRequestContext request) { } if (line.startsWith(WINDOWS_PROCESS_ERROR_MESSAGE) || line.matches(LINUX_MAC_PROCESS_ERROR_MESSAGE)) { throw logger.logExceptionAsError( - new CredentialUnavailableException( - "AzureCliCredential authentication unavailable. Azure CLI not installed")); + new CredentialUnavailableException( + "AzureCliCredential authentication unavailable. Azure CLI not installed." + + "To mitigate this issue, please refer to the troubleshooting guidelines here at " + + "https://aka.ms/azsdk/java/identity/azclicredential/troubleshoot")); } output.append(line); } @@ -468,9 +482,11 @@ public Mono authenticateWithAzureCli(TokenRequestContext request) { String redactedOutput = redactInfo("\"accessToken\": \"(.*?)(\"|$)", processOutput); if (redactedOutput.contains("az login") || redactedOutput.contains("az account set")) { throw logger.logExceptionAsError( - new CredentialUnavailableException( - "AzureCliCredential authentication unavailable." - + " Please run 'az login' to set up account")); + new CredentialUnavailableException( + "AzureCliCredential authentication unavailable." + + " Please run 'az login' to set up account. To further mitigate this" + + " issue, please refer to the troubleshooting guidelines here at " + + "https://aka.ms/azsdk/java/identity/azclicredential/troubleshoot")); } throw logger.logExceptionAsError(new ClientAuthenticationException(redactedOutput, null)); } else { @@ -478,6 +494,9 @@ public Mono authenticateWithAzureCli(TokenRequestContext request) { new ClientAuthenticationException("Failed to invoke Azure CLI ", null)); } } + + logger.verbose("Azure CLI Authentication => A token response was received from Azure CLI, deserializing the" + + " response into an Access Token."); Map objectMap = SERIALIZER_ADAPTER.deserialize(processOutput, Map.class, SerializerEncoding.JSON); String accessToken = objectMap.get("accessToken"); @@ -531,7 +550,9 @@ public Mono authenticateWithAzurePowerShell(TokenRequestContext req .onErrorResume(t -> { if (!t.getClass().getSimpleName().equals("CredentialUnavailableException")) { return Mono.error(new ClientAuthenticationException( - "Azure Powershell authentication failed. Error Details: " + t.getMessage(), + "Azure Powershell authentication failed. Error Details: " + t.getMessage() + + ". To mitigate this issue, please refer to the troubleshooting guidelines here at " + + "https://aka.ms/azsdk/java/identity/powershellcredential/troubleshoot", null, t)); } exceptions.add((CredentialUnavailableException) t); @@ -573,38 +594,49 @@ public Mono authenticateWithOBO(TokenRequestContext request) { private Mono getAccessTokenFromPowerShell(TokenRequestContext request, PowershellManager powershellManager) { return powershellManager.initSession() - .flatMap(manager -> manager.runCommand("Import-Module Az.Accounts -MinimumVersion 2.2.0 -PassThru") - .flatMap(output -> { - if (output.contains("The specified module 'Az.Accounts' with version '2.2.0' was not loaded " - + "because no valid module file")) { - return Mono.error(new CredentialUnavailableException( - "Az.Account module with version >= 2.2.0 is not installed. It needs to be installed to use" - + "Azure PowerShell Credential.")); - } - StringBuilder accessTokenCommand = new StringBuilder("Get-AzAccessToken -ResourceUrl "); - accessTokenCommand.append(ScopeUtil.scopesToResource(request.getScopes())); - accessTokenCommand.append(" | ConvertTo-Json"); - return manager.runCommand(accessTokenCommand.toString()) - .flatMap(out -> { - if (out.contains("Run Connect-AzAccount to login")) { - return Mono.error(new CredentialUnavailableException( - "Run Connect-AzAccount to login to Azure account in PowerShell.")); - } - try { - Map objectMap = SERIALIZER_ADAPTER.deserialize(out, Map.class, - SerializerEncoding.JSON); - String accessToken = objectMap.get("Token"); - String time = objectMap.get("ExpiresOn"); - OffsetDateTime expiresOn = OffsetDateTime.parse(time) - .withOffsetSameInstant(ZoneOffset.UTC); - return Mono.just(new AccessToken(accessToken, expiresOn)); - } catch (IOException e) { - return Mono.error(logger - .logExceptionAsError(new CredentialUnavailableException( - "Encountered error when deserializing response from Azure Power Shell.", e))); - } - }); - })).doFinally(ignored -> powershellManager.close()); + .flatMap(manager -> { + String azAccountsCommand = "Import-Module Az.Accounts -MinimumVersion 2.2.0 -PassThru"; + return manager.runCommand(azAccountsCommand) + .flatMap(output -> { + if (output.contains("The specified module 'Az.Accounts' with version '2.2.0' was not loaded " + + "because no valid module file")) { + return Mono.error(new CredentialUnavailableException( + "Az.Account module with version >= 2.2.0 is not installed. It needs to be installed to" + + " use Azure PowerShell Credential.")); + } + logger.verbose("Az.accounts module was found installed."); + StringBuilder accessTokenCommand = new StringBuilder("Get-AzAccessToken -ResourceUrl "); + accessTokenCommand.append(ScopeUtil.scopesToResource(request.getScopes())); + accessTokenCommand.append(" | ConvertTo-Json"); + String command = accessTokenCommand.toString(); + logger.verbose("Azure Powershell Authentication => Executing the command `%s` in Azure " + + "Powershell to retrieve the Access Token.", + accessTokenCommand); + return manager.runCommand(accessTokenCommand.toString()) + .flatMap(out -> { + if (out.contains("Run Connect-AzAccount to login")) { + return Mono.error(new CredentialUnavailableException( + "Run Connect-AzAccount to login to Azure account in PowerShell.")); + } + try { + logger.verbose("Azure Powershell Authentication => Attempting to deserialize the " + + "received response from Azure Powershell."); + Map objectMap = SERIALIZER_ADAPTER.deserialize(out, Map.class, + SerializerEncoding.JSON); + String accessToken = objectMap.get("Token"); + String time = objectMap.get("ExpiresOn"); + OffsetDateTime expiresOn = OffsetDateTime.parse(time) + .withOffsetSameInstant(ZoneOffset.UTC); + return Mono.just(new AccessToken(accessToken, expiresOn)); + } catch (IOException e) { + return Mono.error(logger + .logExceptionAsError(new CredentialUnavailableException( + "Encountered error when deserializing response from Azure Power Shell.", + e))); + } + }); + }); + }).doFinally(ignored -> powershellManager.close()); } /** @@ -662,7 +694,9 @@ public Mono authenticateWithUsernamePassword(TokenRequestContext requ return pc.acquireToken(userNamePasswordParametersBuilder.build()); } )).onErrorMap(t -> new ClientAuthenticationException("Failed to acquire token with username and " - + "password", null, t)).map(MsalToken::new); + + "password. To mitigate this issue, please refer to the troubleshooting guidelines " + + "here at https://aka.ms/azsdk/net/identity/usernamepasswordcredential/troubleshoot", + null, t)).map(MsalToken::new); } /** @@ -781,7 +815,9 @@ public Mono authenticateWithVsCodeCredential(TokenRequestContext requ if (isADFSTenant()) { return Mono.error(new CredentialUnavailableException("VsCodeCredential " - + "authentication unavailable. ADFS tenant/authorities are not supported.")); + + "authentication unavailable. ADFS tenant/authorities are not supported. " + + "To mitigate this issue, please refer to the troubleshooting guidelines here at " + + "https://aka.ms/azsdk/net/identity/vscodecredential/troubleshoot")); } VisualStudioCacheAccessor accessor = new VisualStudioCacheAccessor(); @@ -800,7 +836,9 @@ public Mono authenticateWithVsCodeCredential(TokenRequestContext requ .onErrorResume(t -> { if (t instanceof MsalInteractionRequiredException) { return Mono.error(new CredentialUnavailableException("Failed to acquire token with" - + " VS code credential", t)); + + " VS code credential." + + " To mitigate this issue, please refer to the troubleshooting guidelines here at " + + "https://aka.ms/azsdk/net/identity/vscodecredential/troubleshoot", t)); } return Mono.error(new ClientAuthenticationException("Failed to acquire token with" + " VS code credential", null, t)); diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClientOptions.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClientOptions.java index 235b5c2da6cd6..94645d459df04 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClientOptions.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClientOptions.java @@ -9,7 +9,6 @@ import com.azure.core.util.Configuration; import com.azure.identity.AzureAuthorityHosts; import com.azure.identity.AuthenticationRecord; -import com.azure.identity.RegionalAuthority; import com.azure.identity.TokenCachePersistenceOptions; import com.azure.identity.implementation.util.ValidationUtil; import com.microsoft.aad.msal4j.UserAssertion; @@ -24,7 +23,7 @@ */ public final class IdentityClientOptions { private static final int MAX_RETRY_DEFAULT_LIMIT = 3; - public static final String AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION = "AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION"; + public static final String AZURE_IDENTITY_DISABLE_MULTI_TENANT_AUTH = "AZURE_IDENTITY_DISABLE_MULTITENANTAUTH"; private String authorityHost; private int maxRetry; @@ -34,7 +33,6 @@ public final class IdentityClientOptions { private ExecutorService executorService; private HttpClient httpClient; private boolean allowUnencryptedCache; - private boolean allowMultiTenantAuthentication; private boolean sharedTokenCacheEnabled; private String keePassDatabasePath; private boolean includeX5c; @@ -43,21 +41,17 @@ public final class IdentityClientOptions { private boolean cp1Disabled; private RegionalAuthority regionalAuthority; private UserAssertion userAssertion; - private boolean identityLegacyTenantSelection; + private boolean multiTenantAuthDisabled; private Configuration configuration; /** * Creates an instance of IdentityClientOptions with default settings. */ public IdentityClientOptions() { - Configuration configuration = Configuration.getGlobalConfiguration(); + Configuration configuration = Configuration.getGlobalConfiguration().clone(); loadFromConfiugration(configuration); maxRetry = MAX_RETRY_DEFAULT_LIMIT; retryTimeout = i -> Duration.ofSeconds((long) Math.pow(2, i.getSeconds() - 1)); - regionalAuthority = RegionalAuthority.fromString( - configuration.get(Configuration.PROPERTY_AZURE_REGIONAL_AUTHORITY_NAME)); - identityLegacyTenantSelection = configuration - .get(AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION, false); } /** @@ -202,32 +196,10 @@ public IdentityClientOptions setAllowUnencryptedCache(boolean allowUnencryptedCa return this; } - /** - * Allows to override the tenant being used in the authentication request - * via {@link com.azure.core.experimental.credential.TokenRequestContextExperimental#setTenantId(String)}. - * - * @param allowMultiTenantAuthentication the flag to indicate if multi tenant authentication is enabled or not. - * @return The updated identity client options. - */ - public IdentityClientOptions setAllowMultiTenantAuthentication(boolean allowMultiTenantAuthentication) { - this.allowMultiTenantAuthentication = allowMultiTenantAuthentication; - return this; - } - - public boolean getAllowUnencryptedCache() { return this.allowUnencryptedCache; } - /** - * Get the flag indicating if multi tenant authentication is enabled or not. - * - * @return the boolean status indicating if multi tenant authentication is enabled or not. - */ - public boolean isMultiTenantAuthenticationAllowed() { - return this.allowMultiTenantAuthentication; - } - /** * Specifies the database to extract IntelliJ cached credentials from. * @param keePassDatabasePath the database to extract intellij credentials from. @@ -378,11 +350,20 @@ public UserAssertion getUserAssertion() { } /** - * Gets the regional authority, or null if regional authority should not be used. - * @return the regional authority value if specified + * Gets the status whether multi tenant auth is disabled or not. + * @return the flag indicating if multi tenant is disabled or not. */ - public boolean isLegacyTenantSelectionEnabled() { - return identityLegacyTenantSelection; + public boolean isMultiTenantAuthenticationDisabled() { + return multiTenantAuthDisabled; + } + + /** + * Disable the multi tenant authentication. + * @return the updated identity client options + */ + public IdentityClientOptions disableMultiTenantAuthentication() { + this.multiTenantAuthDisabled = true; + return this; } /** @@ -409,15 +390,15 @@ public Configuration getConfiguration() { /** * Loads the details from the specified Configuration Store. * - * @return the regional authority value if specified + * @return the updated identity client options */ private IdentityClientOptions loadFromConfiugration(Configuration configuration) { authorityHost = configuration.get(Configuration.PROPERTY_AZURE_AUTHORITY_HOST, AzureAuthorityHosts.AZURE_PUBLIC_CLOUD); ValidationUtil.validateAuthHost(getClass().getSimpleName(), authorityHost); cp1Disabled = configuration.get(Configuration.PROPERTY_AZURE_IDENTITY_DISABLE_CP1, false); - regionalAuthority = RegionalAuthority.fromString( - configuration.get(Configuration.PROPERTY_AZURE_REGIONAL_AUTHORITY_NAME)); + multiTenantAuthDisabled = configuration + .get(AZURE_IDENTITY_DISABLE_MULTI_TENANT_AUTH, false); return this; } } diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/RegionalAuthority.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/RegionalAuthority.java similarity index 99% rename from sdk/identity/azure-identity/src/main/java/com/azure/identity/RegionalAuthority.java rename to sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/RegionalAuthority.java index 46feb07dd0850..819c7c359000f 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/RegionalAuthority.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/RegionalAuthority.java @@ -1,13 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.identity; +package com.azure.identity.implementation; import com.azure.core.util.ExpandableStringEnum; /** * Defines currently available regional authorities, or "AutoDiscoverRegion" to auto-detect the region. */ +//TODO: Move to public package when ready for GA. public final class RegionalAuthority extends ExpandableStringEnum { /** * In cases where the region is not known ahead of time, attempts to automatically discover the appropriate diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/util/IdentityUtil.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/util/IdentityUtil.java index 0b7ee13a42aa3..5594c2b678dfb 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/util/IdentityUtil.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/util/IdentityUtil.java @@ -25,17 +25,23 @@ public static String resolveTenantId(String currentTenantId, TokenRequestContext String contextTenantId = requestContext.getTenantId(); - if (!options.isMultiTenantAuthenticationAllowed()) { - if (contextTenantId != null && !currentTenantId.equals(contextTenantId) - && !options.isLegacyTenantSelectionEnabled()) { - throw LOGGER.logExceptionAsError(new ClientAuthenticationException("The TenantId received from a" - + " challenge did not match the configured TenantId and AllowMultiTenantAuthentication is false.", + if (contextTenantId != null && currentTenantId != null && !currentTenantId.equals(contextTenantId)) { + if (options.isMultiTenantAuthenticationDisabled()) { + throw LOGGER.logExceptionAsError(new ClientAuthenticationException("The Multi Tenant Authentication " + + "is disabled. An updated Tenant Id provided via TokenRequestContext cannot be used in this " + + "scenario. To resolve this issue, set the env var AZURE_IDENTITY_DISABLE_MULTITENANTAUTH" + + " to false ", null)); + } else if (currentTenantId.equals("adfs")) { + throw LOGGER.logExceptionAsError(new ClientAuthenticationException("The credential is configured with" + + "`adfs` tenant id and it cannot be replaced with a tenant id challenge provided via " + + "TokenRequestContext class. ", null)); } - return CoreUtils.isNullOrEmpty(currentTenantId) ? contextTenantId : currentTenantId; + return CoreUtils.isNullOrEmpty(contextTenantId) ? currentTenantId + : contextTenantId; } - return CoreUtils.isNullOrEmpty(contextTenantId) ? currentTenantId - : contextTenantId; + return currentTenantId; + } } diff --git a/sdk/identity/azure-identity/src/test/java/com/azure/identity/DefaultAzureCredentialTest.java b/sdk/identity/azure-identity/src/test/java/com/azure/identity/DefaultAzureCredentialTest.java index edea9c5cfcd7f..b5e0c2db20bea 100644 --- a/sdk/identity/azure-identity/src/test/java/com/azure/identity/DefaultAzureCredentialTest.java +++ b/sdk/identity/azure-identity/src/test/java/com/azure/identity/DefaultAzureCredentialTest.java @@ -211,6 +211,12 @@ public void testCredentialUnavailable() throws Exception { PowerMockito.whenNew(AzurePowerShellCredential.class).withAnyArguments() .thenReturn(powerShellCredential); + AzureCliCredential azureCliCredential = PowerMockito.mock(AzureCliCredential.class); + when(azureCliCredential.getToken(request)) + .thenReturn(Mono.error(new CredentialUnavailableException("Cannot get token from Cli credential"))); + PowerMockito.whenNew(AzureCliCredential.class).withAnyArguments() + .thenReturn(azureCliCredential); + // test DefaultAzureCredential credential = new DefaultAzureCredentialBuilder() .build(); diff --git a/sdk/identity/azure-identity/src/test/java/com/azure/identity/DeviceCodeCredentialTest.java b/sdk/identity/azure-identity/src/test/java/com/azure/identity/DeviceCodeCredentialTest.java index e7ba381b32e4b..8ae10d5f6e46d 100644 --- a/sdk/identity/azure-identity/src/test/java/com/azure/identity/DeviceCodeCredentialTest.java +++ b/sdk/identity/azure-identity/src/test/java/com/azure/identity/DeviceCodeCredentialTest.java @@ -114,7 +114,6 @@ public void testMultiTenantAuthenticationEnabled() throws Exception { // test DeviceCodeCredential credential = new DeviceCodeCredentialBuilder().challengeConsumer(consumer) - .allowMultiTenantAuthentication() .clientId(clientId) .disableAutomaticAuthentication().build(); StepVerifier.create(credential.authenticate(request1)) diff --git a/sdk/identity/azure-identity/src/test/java/com/azure/identity/util/IdentityUtilTests.java b/sdk/identity/azure-identity/src/test/java/com/azure/identity/util/IdentityUtilTests.java index cc6a126df821c..dc45f52be89f7 100644 --- a/sdk/identity/azure-identity/src/test/java/com/azure/identity/util/IdentityUtilTests.java +++ b/sdk/identity/azure-identity/src/test/java/com/azure/identity/util/IdentityUtilTests.java @@ -24,7 +24,6 @@ public void testMultiTenantAuthenticationEnabled() throws Exception { .setScopes(Arrays.asList("http://vault.azure.net/.default")) .setTenantId(newTenant); IdentityClientOptions options = new IdentityClientOptions(); - options.setAllowMultiTenantAuthentication(true); Assert.assertEquals(newTenant, IdentityUtil.resolveTenantId(currentTenant, trc, options)); } @@ -37,7 +36,7 @@ public void testMultiTenantAuthenticationDisabled() throws Exception { .setScopes(Arrays.asList("http://vault.azure.net/.default")) .setTenantId("newTenant"); IdentityClientOptions options = new IdentityClientOptions(); - options.setAllowMultiTenantAuthentication(false); + options.disableMultiTenantAuthentication(); IdentityUtil.resolveTenantId(currentTenant, trc, options); } diff --git a/sdk/synapse/azure-analytics-synapse-spark/CHANGELOG.md b/sdk/synapse/azure-analytics-synapse-spark/CHANGELOG.md index efeced85f6fb5..f4a0e2366e78b 100644 --- a/sdk/synapse/azure-analytics-synapse-spark/CHANGELOG.md +++ b/sdk/synapse/azure-analytics-synapse-spark/CHANGELOG.md @@ -1,6 +1,6 @@ # Release History -## 1.0.0-beta.5 (2020-11-10) +## 1.0.0-beta.5 (2020-10-13) ### New Features - Added `ClientOptions` to `SparkClientBuilder`