The Azure Identity library provides Microsoft Entra ID token authentication support across the Azure SDK with a comprehensive set of TokenCredential implementations.
—
Credential chaining allows you to combine multiple TokenCredential implementations, attempting each in sequence until one successfully authenticates. This pattern enables robust authentication strategies that work across different environments and scenarios.
import com.azure.identity.ChainedTokenCredential;
import com.azure.identity.ChainedTokenCredentialBuilder;
// Create a custom credential chain
TokenCredential chainedCredential = new ChainedTokenCredentialBuilder()
.addLast(new ManagedIdentityCredentialBuilder().build())
.addLast(new AzureCliCredentialBuilder().build())
.addLast(new EnvironmentCredentialBuilder().build())
.build();
// Use with Azure SDK client
BlobServiceClient client = new BlobServiceClientBuilder()
.endpoint("https://mystorageaccount.blob.core.windows.net/")
.credential(chainedCredential)
.buildClient();// Development-focused chain
TokenCredential devChain = new ChainedTokenCredentialBuilder()
.addLast(new AzureCliCredentialBuilder().build())
.addLast(new AzurePowerShellCredentialBuilder().build())
.addLast(new IntelliJCredentialBuilder().build())
.addLast(new VisualStudioCodeCredentialBuilder().build())
.build();
// Production-focused chain
TokenCredential prodChain = new ChainedTokenCredentialBuilder()
.addLast(new ManagedIdentityCredentialBuilder().build())
.addLast(new EnvironmentCredentialBuilder().build())
.addLast(new ClientSecretCredentialBuilder()
.tenantId(System.getenv("AZURE_TENANT_ID"))
.clientId(System.getenv("AZURE_CLIENT_ID"))
.clientSecret(System.getenv("AZURE_CLIENT_SECRET"))
.build())
.build();// Conditional chain based on environment
public static TokenCredential createCredential() {
String environment = System.getenv("ENVIRONMENT");
if ("development".equals(environment)) {
return new ChainedTokenCredentialBuilder()
.addLast(new AzureCliCredentialBuilder().build())
.addLast(new IntelliJCredentialBuilder().build())
.build();
} else if ("production".equals(environment)) {
return new ChainedTokenCredentialBuilder()
.addLast(new ManagedIdentityCredentialBuilder().build())
.addLast(new EnvironmentCredentialBuilder().build())
.build();
} else {
// Default chain for unknown environments
return new DefaultAzureCredentialBuilder().build();
}
}// Primary credential with multiple fallbacks
TokenCredential robustCredential = new ChainedTokenCredentialBuilder()
// Primary: Managed Identity (for Azure-hosted apps)
.addLast(new ManagedIdentityCredentialBuilder().build())
// Fallback 1: Environment variables (for containerized apps)
.addLast(new EnvironmentCredentialBuilder().build())
// Fallback 2: Service principal with certificate
.addLast(new ClientCertificateCredentialBuilder()
.tenantId("tenant-id")
.clientId("client-id")
.pfxCertificate("path/to/cert.pfx", "password")
.build())
// Fallback 3: Interactive browser (for development)
.addLast(new InteractiveBrowserCredentialBuilder()
.redirectUrl("http://localhost:8765")
.build())
.build();// Recommended ordering: Most secure/reliable first, interactive last
TokenCredential wellOrderedChain = new ChainedTokenCredentialBuilder()
// 1. Managed Identity - Most secure for Azure environments
.addLast(new ManagedIdentityCredentialBuilder().build())
// 2. Environment variables - Good for containerized environments
.addLast(new EnvironmentCredentialBuilder().build())
// 3. Service principal with certificate - More secure than secret
.addLast(new ClientCertificateCredentialBuilder()
.tenantId("tenant-id")
.clientId("client-id")
.pfxCertificate("cert.pfx", "password")
.build())
// 4. Developer tools - Good for local development
.addLast(new AzureCliCredentialBuilder().build())
.addLast(new AzurePowerShellCredentialBuilder().build())
// 5. Interactive credentials - Last resort requiring user interaction
.addLast(new DeviceCodeCredentialBuilder().build())
.build();// Custom credential that implements specific logic
public class CustomCredential implements TokenCredential {
@Override
public Mono<AccessToken> getToken(TokenRequestContext request) {
// Custom authentication logic
if (someCondition()) {
return authenticateWithCustomMethod(request);
} else {
return Mono.error(new CredentialUnavailableException("Custom credential not available"));
}
}
private Mono<AccessToken> authenticateWithCustomMethod(TokenRequestContext request) {
// Implementation details
return Mono.just(new AccessToken("token", OffsetDateTime.now().plusHours(1)));
}
}
// Use custom credential in chain
TokenCredential chainWithCustom = new ChainedTokenCredentialBuilder()
.addLast(new ManagedIdentityCredentialBuilder().build())
.addLast(new CustomCredential())
.addLast(new EnvironmentCredentialBuilder().build())
.build();try {
ChainedTokenCredential credential = new ChainedTokenCredentialBuilder()
.addLast(new ManagedIdentityCredentialBuilder().build())
.addLast(new AzureCliCredentialBuilder().build())
.build();
AccessToken token = credential.getTokenSync(
new TokenRequestContext().addScopes("https://management.azure.com/.default")
);
System.out.println("Authentication successful with chained credential");
} catch (CredentialUnavailableException e) {
System.err.println("All credentials in chain failed: " + e.getMessage());
// This means none of the credentials in the chain were able to authenticate
} catch (ClientAuthenticationException e) {
System.err.println("Authentication error: " + e.getMessage());
// One of the credentials attempted authentication but failed
}// Enable logging to see which credentials are being tried
TokenCredential debugChain = new ChainedTokenCredentialBuilder()
.addLast(new ManagedIdentityCredentialBuilder()
.enableAccountIdentifierLogging() // Enable account logging
.build())
.addLast(new AzureCliCredentialBuilder()
.enableAccountIdentifierLogging()
.build())
.build();
// Configure logging in your application to see credential attempts
// Add to logging.properties or logback.xml:
// com.azure.identity=DEBUG// Optimize chain order for performance
TokenCredential optimizedChain = new ChainedTokenCredentialBuilder()
// Fast credentials first
.addLast(new ManagedIdentityCredentialBuilder().build()) // Very fast in Azure
.addLast(new EnvironmentCredentialBuilder().build()) // Fast environment lookup
// Slower credentials later
.addLast(new AzureCliCredentialBuilder().build()) // Subprocess call
.addLast(new InteractiveBrowserCredentialBuilder() // User interaction required
.redirectUrl("http://localhost:8765")
.build())
.build();class ChainedTokenCredential implements TokenCredential {
Mono<AccessToken> getToken(TokenRequestContext request);
AccessToken getTokenSync(TokenRequestContext request);
}
class ChainedTokenCredentialBuilder {
ChainedTokenCredentialBuilder addLast(TokenCredential credential);
ChainedTokenCredential build();
}// Conditional chaining based on environment variables
public class EnvironmentAwareCredentialChain {
public static TokenCredential create() {
ChainedTokenCredentialBuilder builder = new ChainedTokenCredentialBuilder();
// Always try managed identity first in Azure environments
if (isRunningInAzure()) {
builder.addLast(new ManagedIdentityCredentialBuilder().build());
}
// Add environment credential if variables are present
if (hasEnvironmentCredentials()) {
builder.addLast(new EnvironmentCredentialBuilder().build());
}
// Add developer tools in development mode
if (isDevelopmentMode()) {
builder.addLast(new AzureCliCredentialBuilder().build())
.addLast(new IntelliJCredentialBuilder().build());
}
// Fallback to interactive if nothing else works
if (isInteractiveMode()) {
builder.addLast(new InteractiveBrowserCredentialBuilder()
.redirectUrl("http://localhost:8765")
.build());
}
return builder.build();
}
private static boolean isRunningInAzure() {
return System.getenv("MSI_ENDPOINT") != null ||
System.getenv("IDENTITY_ENDPOINT") != null;
}
private static boolean hasEnvironmentCredentials() {
return System.getenv("AZURE_CLIENT_SECRET") != null ||
System.getenv("AZURE_CLIENT_CERTIFICATE_PATH") != null;
}
private static boolean isDevelopmentMode() {
return "development".equals(System.getenv("ENVIRONMENT"));
}
private static boolean isInteractiveMode() {
return System.console() != null;
}
}Common issues with credential chains:
Install with Tessl CLI
npx tessl i tessl/maven-com-azure--azure-identitydocs