Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions sdk/identity/identity/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
# Release History

## 4.11.0-beta.1 (Unreleased)
## 4.11.0-beta.1 (2025-07-17)

### Features Added

- `VisualStudioCodeCredential` has been restored and now supports **broker authentication** using the Azure account signed in via Visual Studio Code. The credential has been added to `DefaultAzureCredential` [#35150](https://github.com/Azure/azure-sdk-for-js/pull/35150)
- `DefaultAzureCredential` now supports authentication with the currently signed-in Windows account when the `@azure/identity-broker` package is installed and configured with `useIdentityPlugin`. This auth mechanism is added at the end of the `DefaultAzureCredential` credential chain. [#35213](https://github.com/Azure/azure-sdk-for-js/pull/35213)

### Breaking Changes

### Bugs Fixed
- Added support for more `AZURE_TOKEN_CREDENTIALS` environment variable values to specify a single credential type to use in `DefaultAzureCredential`. In addition to `dev` and `prod`, possible values now include `VisualStudioCodeCredential`, `EnvironmentCredential`, `WorkloadIdentityCredential`, `ManagedIdentityCredential`, `AzureDeveloperCliCredential`, `AzurePowershellCredential` and `AzureCliCredential` - each for the corresponding credential type. [#34966](https://github.com/Azure/azure-sdk-for-js/pull/34966)

### Other Changes

Expand Down Expand Up @@ -40,7 +37,7 @@

### Other Changes

- Added deprecation warnings for username password usage in `EnvironmentCredential` constructor to warn the users. `UsernamePassword` authentication doesn't support Multi-Factor Authentication (MFA), and MFA will enabled soon on all tenants. For more details, see [Planning for mandatory MFA](https://aka.ms/mfaforazure). [#34054](https://github.com/Azure/azure-sdk-for-js/pull/34054)
- Added deprecation warnings for username password usage in `EnvironmentCredential` constructor to warn the users. `UsernamePassword` authentication doesn't support Multi-Factor Authentication (MFA), and MFA will enabled soon on all tenants. For more details, see [Planning for mandatory MFA](https://aka.ms/mfaforazure). [#34054](https://github.com/Azure/azure-sdk-for-js/pull/34054)

## 4.9.1 (2025-04-17)

Expand Down
1 change: 1 addition & 0 deletions sdk/identity/identity/TROUBLESHOOTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ The `DefaultAzureCredential` attempts to retrieve an access token by sequentiall
| ----------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CredentialUnavailableError` thrown with message `DefaultAzureCredential failed to retrieve a token from the included credentials.` | All credentials in the `DefaultAzureCredential` chain failed to retrieve a token, each throwing a `CredentialUnavailableError` themselves. | <ul><li>[Enable logging](#enable-and-configure-logging) to verify the credentials being tried, and get further diagnostic information.</li><li>Consult the troubleshooting guide for underlying credential types for more information.</li><ul><li>[EnvironmentCredential](#troubleshoot-environment-credential-authentication-issues)</li><li>[ManagedIdentityCredential](#troubleshoot-managed-identity-authentication-issues)</li><li>[VisualStudioCodeCredential](#troubleshoot-visual-studio-code-authentication-issues)</li><li>[AzureCliCredential](#troubleshoot-azure-cli-authentication-issues)</li><li>[AzurePowerShellCredential](#troubleshoot-azure-powershell-authentication-issues)</li></ul> |
| `RestError` raised from the client with a status code of 401 or 403. | Authentication succeeded but the authorizing Azure service responded with a 401 (Authenticate), or 403 (Forbidden) status code. This can often be caused by the `DefaultAzureCredential` authenticating an account other than the intended or that the required role assignment is not configured. | <ul><li>[Enable logging](#enable-and-configure-logging) to determine which credential in the chain returned the authenticating token.</li><li>In the case a credential other than the expected is returning a token, you may bypass this by signing out of the corresponding development tool.</li><li>Confirm that the correct RBAC role is assigned to the identity being used to authenticate. For example, the resource-specific role, rather than just the inherited "Owner" role.</li></ul> |
|Invalid value for AZURE_TOKEN_CREDENTIALS = "...". |AZURE_TOKEN_CREDENTIALS has an unexpected value| Specify a valid value as described in DefaultAzureCredential documentation. Valid values are 'prod' or 'dev' or any of these credentials - "EnvironmentCredential" or "ManagedIdentityCredential or "WorkloadIdentityCredential" or "VisualStudioCodeCredential" or "AzureCliCredential" or "AzureDeveloperCliCredential" or "AzurePowershellCredential".|

> 📢 The Azure Identity library for JavaScript does _not_ support the `ExcludeXXXCredential` properties that exist for languages like .NET and Python. We recommend creating a custom [ChainedTokenCredential](https://github.com/Azure/azure-sdk-for-js/blob/f0ac28977d26172f79e5c5100148e7f767f4dbf9/sdk/identity/identity/README.md#define-a-custom-authentication-flow-with-the-chainedtokencredential) if you require a different set or ordering of credentials than those offered by `DefaultAzureCredential`.

Expand Down
241 changes: 51 additions & 190 deletions sdk/identity/identity/src/credentials/defaultAzureCredential.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,201 +6,30 @@ import type {
DefaultAzureCredentialOptions,
DefaultAzureCredentialResourceIdOptions,
} from "./defaultAzureCredentialOptions.js";
import type {
ManagedIdentityCredentialClientIdOptions,
ManagedIdentityCredentialResourceIdOptions,
} from "./managedIdentityCredential/options.js";
import { ManagedIdentityCredential } from "./managedIdentityCredential/index.js";

import { ManagedIdentityCredential } from "./managedIdentityCredential/index.js";
import { VisualStudioCodeCredential } from "./visualStudioCodeCredential.js";
import { AzureCliCredential } from "./azureCliCredential.js";
import { AzureDeveloperCliCredential } from "./azureDeveloperCliCredential.js";
import { AzurePowerShellCredential } from "./azurePowerShellCredential.js";
import { ChainedTokenCredential } from "./chainedTokenCredential.js";
import { EnvironmentCredential } from "./environmentCredential.js";
import type { TokenCredential } from "@azure/core-auth";
import { WorkloadIdentityCredential } from "./workloadIdentityCredential.js";
import type { WorkloadIdentityCredentialOptions } from "./workloadIdentityCredentialOptions.js";
import { credentialLogger } from "../util/logging.js";
import { VisualStudioCodeCredential } from "./visualStudioCodeCredential.js";
import { BrokerCredential } from "./brokerCredential.js";
import {
createDefaultAzureCliCredential,
createDefaultAzureDeveloperCliCredential,
createDefaultAzurePowershellCredential,
createDefaultBrokerCredential,
createDefaultManagedIdentityCredential,
createDefaultVisualStudioCodeCredential,
createDefaultWorkloadIdentityCredential,
createDefaultEnvironmentCredential,
} from "./defaultAzureCredentialFunctions.js";

const logger = credentialLogger("DefaultAzureCredential");

/**
* Creates a {@link ManagedIdentityCredential} from the provided options.
* @param options - Options to configure the credential.
*
* @internal
*/
export function createDefaultManagedIdentityCredential(
options:
| DefaultAzureCredentialOptions
| DefaultAzureCredentialResourceIdOptions
| DefaultAzureCredentialClientIdOptions = {},
): TokenCredential {
options.retryOptions ??= {
maxRetries: 5,
retryDelayInMs: 800,
};
const managedIdentityClientId =
(options as DefaultAzureCredentialClientIdOptions)?.managedIdentityClientId ??
process.env.AZURE_CLIENT_ID;
const workloadIdentityClientId =
(options as DefaultAzureCredentialClientIdOptions)?.workloadIdentityClientId ??
managedIdentityClientId;
const managedResourceId = (options as DefaultAzureCredentialResourceIdOptions)
?.managedIdentityResourceId;
const workloadFile = process.env.AZURE_FEDERATED_TOKEN_FILE;
const tenantId = options?.tenantId ?? process.env.AZURE_TENANT_ID;
if (managedResourceId) {
const managedIdentityResourceIdOptions: ManagedIdentityCredentialResourceIdOptions = {
...options,
resourceId: managedResourceId,
};
return new ManagedIdentityCredential(managedIdentityResourceIdOptions);
}

if (workloadFile && workloadIdentityClientId) {
const workloadIdentityCredentialOptions: DefaultAzureCredentialOptions = {
...options,
tenantId: tenantId,
};

return new ManagedIdentityCredential(
workloadIdentityClientId,
workloadIdentityCredentialOptions,
);
}

if (managedIdentityClientId) {
const managedIdentityClientOptions: ManagedIdentityCredentialClientIdOptions = {
...options,
clientId: managedIdentityClientId,
};

return new ManagedIdentityCredential(managedIdentityClientOptions);
}

// We may be able to return a UnavailableCredential here, but that may be a breaking change
return new ManagedIdentityCredential(options);
}

/**
* Creates a {@link WorkloadIdentityCredential} from the provided options.
* @param options - Options to configure the credential.
*
* @internal
*/
function createDefaultWorkloadIdentityCredential(
options?: DefaultAzureCredentialOptions | DefaultAzureCredentialClientIdOptions,
): TokenCredential {
const managedIdentityClientId =
(options as DefaultAzureCredentialClientIdOptions)?.managedIdentityClientId ??
process.env.AZURE_CLIENT_ID;
const workloadIdentityClientId =
(options as DefaultAzureCredentialClientIdOptions)?.workloadIdentityClientId ??
managedIdentityClientId;
const workloadFile = process.env.AZURE_FEDERATED_TOKEN_FILE;
const tenantId = options?.tenantId ?? process.env.AZURE_TENANT_ID;
if (workloadFile && workloadIdentityClientId) {
const workloadIdentityCredentialOptions: WorkloadIdentityCredentialOptions = {
...options,
tenantId,
clientId: workloadIdentityClientId,
tokenFilePath: workloadFile,
};
return new WorkloadIdentityCredential(workloadIdentityCredentialOptions);
}
if (tenantId) {
const workloadIdentityClientTenantOptions: WorkloadIdentityCredentialOptions = {
...options,
tenantId,
};
return new WorkloadIdentityCredential(workloadIdentityClientTenantOptions);
}

// We may be able to return a UnavailableCredential here, but that may be a breaking change
return new WorkloadIdentityCredential(options);
}

/**
* Creates a {@link AzureDeveloperCliCredential} from the provided options.
* @param options - Options to configure the credential.
*
* @internal
*/
function createDefaultAzureDeveloperCliCredential(
options: DefaultAzureCredentialOptions = {},
): TokenCredential {
const processTimeoutInMs = options.processTimeoutInMs;
return new AzureDeveloperCliCredential({ processTimeoutInMs, ...options });
}

/**
* Creates a {@link AzureCliCredential} from the provided options.
* @param options - Options to configure the credential.
*
* @internal
*/
function createDefaultAzureCliCredential(
options: DefaultAzureCredentialOptions = {},
): TokenCredential {
const processTimeoutInMs = options.processTimeoutInMs;
return new AzureCliCredential({ processTimeoutInMs, ...options });
}

/**
* Creates a {@link AzurePowerShellCredential} from the provided options.
* @param options - Options to configure the credential.
*
* @internal
*/
function createDefaultAzurePowershellCredential(
options: DefaultAzureCredentialOptions = {},
): TokenCredential {
const processTimeoutInMs = options.processTimeoutInMs;
return new AzurePowerShellCredential({ processTimeoutInMs, ...options });
}

/**
* Creates a BrokerCredential instance with the provided options.
* This credential uses the Windows Authentication Manager (WAM) broker for authentication.
* It will only attempt to authenticate silently using the default broker account
*
* @param options - Options for configuring the credential.
*
* @internal
*/
export function createDefaultBrokerCredential(
options: DefaultAzureCredentialOptions = {},
): TokenCredential {
return new BrokerCredential(options);
}

/**
* Creates an {@link EnvironmentCredential} from the provided options.
* @param options - Options to configure the credential.
*
* @internal
*/
export function createEnvironmentCredential(
options: DefaultAzureCredentialOptions = {},
): TokenCredential {
return new EnvironmentCredential(options);
}

/**
* Creates a {@link VisualStudioCodeCredential} from the provided options.
* @param options - Options to configure the credential.
*
* @internal
*/
export function createDefaultVisualStudioCodeCredential(
options: DefaultAzureCredentialOptions = {},
): TokenCredential {
return new VisualStudioCodeCredential(options);
}

/**
* A no-op credential that logs the reason it was skipped if getToken is called.
* @internal
Expand Down Expand Up @@ -232,13 +61,24 @@ export class UnavailableDefaultCredential implements TokenCredential {
* - {@link EnvironmentCredential}
* - {@link WorkloadIdentityCredential}
* - {@link ManagedIdentityCredential}
* - {@link VisualStudioCodeCredential}
* - {@link AzureCliCredential}
* - {@link AzurePowerShellCredential}
* - {@link AzureDeveloperCliCredential}
* - {@link VisualStudioCodeCredential}
*
* Consult the documentation of these credential types for more information
* on how they attempt authentication.
*
* Selecting credentials
*
* Set environment variable AZURE_TOKEN_CREDENTIALS to select a subset of the credential chain.
* DefaultAzureCredential will try only the specified credential(s), but its other behavior remains the same.
* Valid values for AZURE_TOKEN_CREDENTIALS are the name of any single type in the above chain, for example
* "EnvironmentCredential" or "AzureCliCredential", and these special values:
*
* - "dev": try [VisualStudioCodeCredential], [AzureCliCredential], [AzurePowerShellCredential] and [AzureDeveloperCliCredential], in that order
* - "prod": try [EnvironmentCredential], [WorkloadIdentityCredential], and [ManagedIdentityCredential], in that order
*
*/
export class DefaultAzureCredential extends ChainedTokenCredential {
/**
Expand Down Expand Up @@ -275,27 +115,48 @@ export class DefaultAzureCredential extends ChainedTokenCredential {
createDefaultBrokerCredential,
];
const prodCredentialFunctions = [
createEnvironmentCredential,
createDefaultEnvironmentCredential,
createDefaultWorkloadIdentityCredential,
createDefaultManagedIdentityCredential,
];
let credentialFunctions = [];
const validCredentialNames =
"EnvironmentCredential, WorkloadIdentityCredential, ManagedIdentityCredential, VisualStudioCodeCredential, AzureCliCredential, AzurePowerShellCredential, AzureDeveloperCliCredential";
// If AZURE_TOKEN_CREDENTIALS is set, use it to determine which credentials to use.
// The value of AZURE_TOKEN_CREDENTIALS should be either "dev" or "prod".
// The value of AZURE_TOKEN_CREDENTIALS should be either "dev" or "prod" or any one of these credentials - {validCredentialNames}.
if (azureTokenCredentials) {
switch (azureTokenCredentials) {
case "dev":
// If AZURE_TOKEN_CREDENTIALS is set to "dev", use the developer tool-based credential chain.
credentialFunctions = devCredentialFunctions;
break;
case "prod":
// If AZURE_TOKEN_CREDENTIALS is set to "prod", use the production credential chain.
credentialFunctions = prodCredentialFunctions;
break;
case "environmentcredential":
credentialFunctions = [createDefaultEnvironmentCredential];
break;
case "workloadidentitycredential":
credentialFunctions = [createDefaultWorkloadIdentityCredential];
break;
case "managedidentitycredential":
credentialFunctions = [createDefaultManagedIdentityCredential];
break;
case "visualstudiocodecredential":
credentialFunctions = [createDefaultVisualStudioCodeCredential];
break;
case "azureclicredential":
credentialFunctions = [createDefaultAzureCliCredential];
break;
case "azurepowershellcredential":
credentialFunctions = [createDefaultAzurePowershellCredential];
break;
case "azuredeveloperclicredential":
credentialFunctions = [createDefaultAzureDeveloperCliCredential];
break;
default: {
// If AZURE_TOKEN_CREDENTIALS is set to an unsupported value, throw an error.
// We will throw an error here to prevent the creation of the DefaultAzureCredential.
const errorMessage = `Invalid value for AZURE_TOKEN_CREDENTIALS = ${process.env.AZURE_TOKEN_CREDENTIALS}. Valid values are 'prod' or 'dev'.`;
// This will prevent the creation of the DefaultAzureCredential.
const errorMessage = `Invalid value for AZURE_TOKEN_CREDENTIALS = ${process.env.AZURE_TOKEN_CREDENTIALS}. Valid values are 'prod' or 'dev' or any of these credentials - ${validCredentialNames}.`;
logger.warning(errorMessage);
throw new Error(errorMessage);
}
Expand Down
Loading