Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 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
6 changes: 4 additions & 2 deletions sdk/identity/identity/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Release History

## 4.10.3 (Unreleased)
## 4.11.0-beta.1 (Unreleased)

### Features Added

- 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 `EnvironmentCredential`, `WorkloadIdentityCredential`, `ManagedIdentityCredential`, `AzureDeveloperCliCredential`, `AzurePowershellCredential` and `AzureCliCredential` - each for the corresponding credential type.

### Breaking Changes

### Bugs Fixed
Expand Down Expand Up @@ -35,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 @@ -196,6 +196,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>[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 "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
2 changes: 1 addition & 1 deletion sdk/identity/identity/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
]
},
"engines": {
"node": ">=20.0.0"
"node": ">=18.0.0"
},
"repository": "github:Azure/azure-sdk-for-js",
"keywords": [
Expand Down
202 changes: 47 additions & 155 deletions sdk/identity/identity/src/credentials/defaultAzureCredential.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ import type {
DefaultAzureCredentialOptions,
DefaultAzureCredentialResourceIdOptions,
} from "./defaultAzureCredentialOptions.js";
import type {
ManagedIdentityCredentialClientIdOptions,
ManagedIdentityCredentialResourceIdOptions,
} from "./managedIdentityCredential/options.js";

import { ManagedIdentityCredential } from "./managedIdentityCredential/index.js";

import { AzureCliCredential } from "./azureCliCredential.js";
Expand All @@ -19,159 +16,18 @@ 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 {
createDefaultAzureCliCredential,
createDefaultAzureDeveloperCliCredential,
createDefaultAzurePowershellCredential,
createDefaultManagedIdentityCredential,
createDefaultWorkloadIdentityCredential,
createEnvironmentCredential,
} 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 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);
}

/**
* A no-op credential that logs the reason it was skipped if getToken is called.
* @internal
Expand Down Expand Up @@ -209,6 +65,17 @@ export class UnavailableDefaultCredential implements TokenCredential {
*
* 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 [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 @@ -248,8 +115,10 @@ export class DefaultAzureCredential extends ChainedTokenCredential {
createDefaultManagedIdentityCredential,
];
let credentialFunctions = [];
const validCredentialNames =
"EnvironmentCredential, WorkloadIdentityCredential, ManagedIdentityCredential, 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":
Expand All @@ -260,10 +129,33 @@ export class DefaultAzureCredential extends ChainedTokenCredential {
// If AZURE_TOKEN_CREDENTIALS is set to "prod", use the production credential chain.
credentialFunctions = prodCredentialFunctions;
break;
case "environmentcredential":
credentialFunctions = [createEnvironmentCredential];
break;
case "workloadidentitycredential":
// If AZURE_TOKEN_CREDENTIALS is set to "workloadidentitycredential", use the WorkloadIdentityCredential.
credentialFunctions = [createDefaultWorkloadIdentityCredential];
break;
case "managedidentitycredential":
// If AZURE_TOKEN_CREDENTIALS is set to "managedidentitycredential", use the ManagedIdentityCredential.
credentialFunctions = [createDefaultManagedIdentityCredential];
break;
case "azureclicredential":
// If AZURE_TOKEN_CREDENTIALS is set to "azureclicredential", use the AzureCliCredential.
credentialFunctions = [createDefaultAzureCliCredential];
break;
case "azurepowershellcredential":
// If AZURE_TOKEN_CREDENTIALS is set to "azurepowershellcredential", use the AzurePowerShellCredential.
credentialFunctions = [createDefaultAzurePowershellCredential];
break;
case "azuredeveloperclicredential":
// If AZURE_TOKEN_CREDENTIALS is set to "azuredeveloperclicredential", use the 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'.`;
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
Loading