CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-azure--azure-identity

The Azure Identity library provides Microsoft Entra ID token authentication support across the Azure SDK with a comprehensive set of TokenCredential implementations.

Pending
Overview
Eval results
Files

credential-chaining.mddocs/

Credential Chaining

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.

Basic Chaining

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 vs Production Chain

// 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();

Environment-Aware Chaining

// 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();
    }
}

Fallback Strategies

// 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();

Credential Ordering Best Practices

// 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 Implementation

// 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();

Error Handling in Chains

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
}

Debugging Credential Chains

// 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

Performance Considerations

// 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();

API Reference

class ChainedTokenCredential implements TokenCredential {
    Mono<AccessToken> getToken(TokenRequestContext request);
    AccessToken getTokenSync(TokenRequestContext request);
}

class ChainedTokenCredentialBuilder {
    ChainedTokenCredentialBuilder addLast(TokenCredential credential);
    ChainedTokenCredential build();
}

Advanced Chaining Patterns

// 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;
    }
}

Best Practices

  1. Order by Reliability: Place most reliable credentials first in the chain
  2. Environment-Specific Chains: Create different chains for different deployment environments
  3. Fast Credentials First: Order credentials by authentication speed for better performance
  4. Avoid Interactive in Production: Don't include interactive credentials in production chains
  5. Handle All Failures: Implement proper error handling for when all credentials fail
  6. Enable Logging: Use logging to debug which credentials are being attempted
  7. Security First: Prioritize more secure authentication methods (certificates over secrets)
  8. Minimize Chain Length: Keep chains as short as possible while maintaining reliability

Troubleshooting

Common issues with credential chains:

  • All Credentials Fail: Ensure at least one credential type is properly configured
  • Slow Authentication: Reorder chain to put faster credentials first
  • Interactive Prompts: Remove interactive credentials for automated scenarios
  • Permission Errors: Ensure all credentials have appropriate permissions
  • Environment Issues: Verify environment variables and tool installations for each credential type

Install with Tessl CLI

npx tessl i tessl/maven-com-azure--azure-identity

docs

advanced-authentication-flows.md

authorization-code-authentication.md

azure-developer-cli-authentication.md

azure-pipelines-authentication.md

client-assertion-authentication.md

configuration-and-utilities.md

credential-chaining.md

default-azure-credential.md

developer-tool-credentials.md

environment-credential.md

index.md

interactive-user-authentication.md

managed-identity-credential.md

service-principal-authentication.md

shared-token-cache-authentication.md

username-password-authentication.md

visual-studio-code-authentication.md

tile.json