docs
0
# Credential Chaining
1
2
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.
3
4
## Basic Chaining
5
6
```java
7
import com.azure.identity.ChainedTokenCredential;
8
import com.azure.identity.ChainedTokenCredentialBuilder;
9
10
// Create a custom credential chain
11
TokenCredential chainedCredential = new ChainedTokenCredentialBuilder()
12
.addLast(new ManagedIdentityCredentialBuilder().build())
13
.addLast(new AzureCliCredentialBuilder().build())
14
.addLast(new EnvironmentCredentialBuilder().build())
15
.build();
16
17
// Use with Azure SDK client
18
BlobServiceClient client = new BlobServiceClientBuilder()
19
.endpoint("https://mystorageaccount.blob.core.windows.net/")
20
.credential(chainedCredential)
21
.buildClient();
22
```
23
24
## Development vs Production Chain
25
26
```java
27
// Development-focused chain
28
TokenCredential devChain = new ChainedTokenCredentialBuilder()
29
.addLast(new AzureCliCredentialBuilder().build())
30
.addLast(new AzurePowerShellCredentialBuilder().build())
31
.addLast(new IntelliJCredentialBuilder().build())
32
.addLast(new VisualStudioCodeCredentialBuilder().build())
33
.build();
34
35
// Production-focused chain
36
TokenCredential prodChain = new ChainedTokenCredentialBuilder()
37
.addLast(new ManagedIdentityCredentialBuilder().build())
38
.addLast(new EnvironmentCredentialBuilder().build())
39
.addLast(new ClientSecretCredentialBuilder()
40
.tenantId(System.getenv("AZURE_TENANT_ID"))
41
.clientId(System.getenv("AZURE_CLIENT_ID"))
42
.clientSecret(System.getenv("AZURE_CLIENT_SECRET"))
43
.build())
44
.build();
45
```
46
47
## Environment-Aware Chaining
48
49
```java
50
// Conditional chain based on environment
51
public static TokenCredential createCredential() {
52
String environment = System.getenv("ENVIRONMENT");
53
54
if ("development".equals(environment)) {
55
return new ChainedTokenCredentialBuilder()
56
.addLast(new AzureCliCredentialBuilder().build())
57
.addLast(new IntelliJCredentialBuilder().build())
58
.build();
59
} else if ("production".equals(environment)) {
60
return new ChainedTokenCredentialBuilder()
61
.addLast(new ManagedIdentityCredentialBuilder().build())
62
.addLast(new EnvironmentCredentialBuilder().build())
63
.build();
64
} else {
65
// Default chain for unknown environments
66
return new DefaultAzureCredentialBuilder().build();
67
}
68
}
69
```
70
71
## Fallback Strategies
72
73
```java
74
// Primary credential with multiple fallbacks
75
TokenCredential robustCredential = new ChainedTokenCredentialBuilder()
76
// Primary: Managed Identity (for Azure-hosted apps)
77
.addLast(new ManagedIdentityCredentialBuilder().build())
78
79
// Fallback 1: Environment variables (for containerized apps)
80
.addLast(new EnvironmentCredentialBuilder().build())
81
82
// Fallback 2: Service principal with certificate
83
.addLast(new ClientCertificateCredentialBuilder()
84
.tenantId("tenant-id")
85
.clientId("client-id")
86
.pfxCertificate("path/to/cert.pfx", "password")
87
.build())
88
89
// Fallback 3: Interactive browser (for development)
90
.addLast(new InteractiveBrowserCredentialBuilder()
91
.redirectUrl("http://localhost:8765")
92
.build())
93
94
.build();
95
```
96
97
## Credential Ordering Best Practices
98
99
```java
100
// Recommended ordering: Most secure/reliable first, interactive last
101
TokenCredential wellOrderedChain = new ChainedTokenCredentialBuilder()
102
// 1. Managed Identity - Most secure for Azure environments
103
.addLast(new ManagedIdentityCredentialBuilder().build())
104
105
// 2. Environment variables - Good for containerized environments
106
.addLast(new EnvironmentCredentialBuilder().build())
107
108
// 3. Service principal with certificate - More secure than secret
109
.addLast(new ClientCertificateCredentialBuilder()
110
.tenantId("tenant-id")
111
.clientId("client-id")
112
.pfxCertificate("cert.pfx", "password")
113
.build())
114
115
// 4. Developer tools - Good for local development
116
.addLast(new AzureCliCredentialBuilder().build())
117
.addLast(new AzurePowerShellCredentialBuilder().build())
118
119
// 5. Interactive credentials - Last resort requiring user interaction
120
.addLast(new DeviceCodeCredentialBuilder().build())
121
122
.build();
123
```
124
125
## Custom Credential Implementation
126
127
```java
128
// Custom credential that implements specific logic
129
public class CustomCredential implements TokenCredential {
130
@Override
131
public Mono<AccessToken> getToken(TokenRequestContext request) {
132
// Custom authentication logic
133
if (someCondition()) {
134
return authenticateWithCustomMethod(request);
135
} else {
136
return Mono.error(new CredentialUnavailableException("Custom credential not available"));
137
}
138
}
139
140
private Mono<AccessToken> authenticateWithCustomMethod(TokenRequestContext request) {
141
// Implementation details
142
return Mono.just(new AccessToken("token", OffsetDateTime.now().plusHours(1)));
143
}
144
}
145
146
// Use custom credential in chain
147
TokenCredential chainWithCustom = new ChainedTokenCredentialBuilder()
148
.addLast(new ManagedIdentityCredentialBuilder().build())
149
.addLast(new CustomCredential())
150
.addLast(new EnvironmentCredentialBuilder().build())
151
.build();
152
```
153
154
## Error Handling in Chains
155
156
```java
157
try {
158
ChainedTokenCredential credential = new ChainedTokenCredentialBuilder()
159
.addLast(new ManagedIdentityCredentialBuilder().build())
160
.addLast(new AzureCliCredentialBuilder().build())
161
.build();
162
163
AccessToken token = credential.getTokenSync(
164
new TokenRequestContext().addScopes("https://management.azure.com/.default")
165
);
166
167
System.out.println("Authentication successful with chained credential");
168
169
} catch (CredentialUnavailableException e) {
170
System.err.println("All credentials in chain failed: " + e.getMessage());
171
// This means none of the credentials in the chain were able to authenticate
172
} catch (ClientAuthenticationException e) {
173
System.err.println("Authentication error: " + e.getMessage());
174
// One of the credentials attempted authentication but failed
175
}
176
```
177
178
## Debugging Credential Chains
179
180
```java
181
// Enable logging to see which credentials are being tried
182
TokenCredential debugChain = new ChainedTokenCredentialBuilder()
183
.addLast(new ManagedIdentityCredentialBuilder()
184
.enableAccountIdentifierLogging() // Enable account logging
185
.build())
186
.addLast(new AzureCliCredentialBuilder()
187
.enableAccountIdentifierLogging()
188
.build())
189
.build();
190
191
// Configure logging in your application to see credential attempts
192
// Add to logging.properties or logback.xml:
193
// com.azure.identity=DEBUG
194
```
195
196
## Performance Considerations
197
198
```java
199
// Optimize chain order for performance
200
TokenCredential optimizedChain = new ChainedTokenCredentialBuilder()
201
// Fast credentials first
202
.addLast(new ManagedIdentityCredentialBuilder().build()) // Very fast in Azure
203
.addLast(new EnvironmentCredentialBuilder().build()) // Fast environment lookup
204
205
// Slower credentials later
206
.addLast(new AzureCliCredentialBuilder().build()) // Subprocess call
207
.addLast(new InteractiveBrowserCredentialBuilder() // User interaction required
208
.redirectUrl("http://localhost:8765")
209
.build())
210
211
.build();
212
```
213
214
## API Reference
215
216
```java { .api }
217
class ChainedTokenCredential implements TokenCredential {
218
Mono<AccessToken> getToken(TokenRequestContext request);
219
AccessToken getTokenSync(TokenRequestContext request);
220
}
221
222
class ChainedTokenCredentialBuilder {
223
ChainedTokenCredentialBuilder addLast(TokenCredential credential);
224
ChainedTokenCredential build();
225
}
226
```
227
228
## Advanced Chaining Patterns
229
230
```java
231
// Conditional chaining based on environment variables
232
public class EnvironmentAwareCredentialChain {
233
public static TokenCredential create() {
234
ChainedTokenCredentialBuilder builder = new ChainedTokenCredentialBuilder();
235
236
// Always try managed identity first in Azure environments
237
if (isRunningInAzure()) {
238
builder.addLast(new ManagedIdentityCredentialBuilder().build());
239
}
240
241
// Add environment credential if variables are present
242
if (hasEnvironmentCredentials()) {
243
builder.addLast(new EnvironmentCredentialBuilder().build());
244
}
245
246
// Add developer tools in development mode
247
if (isDevelopmentMode()) {
248
builder.addLast(new AzureCliCredentialBuilder().build())
249
.addLast(new IntelliJCredentialBuilder().build());
250
}
251
252
// Fallback to interactive if nothing else works
253
if (isInteractiveMode()) {
254
builder.addLast(new InteractiveBrowserCredentialBuilder()
255
.redirectUrl("http://localhost:8765")
256
.build());
257
}
258
259
return builder.build();
260
}
261
262
private static boolean isRunningInAzure() {
263
return System.getenv("MSI_ENDPOINT") != null ||
264
System.getenv("IDENTITY_ENDPOINT") != null;
265
}
266
267
private static boolean hasEnvironmentCredentials() {
268
return System.getenv("AZURE_CLIENT_SECRET") != null ||
269
System.getenv("AZURE_CLIENT_CERTIFICATE_PATH") != null;
270
}
271
272
private static boolean isDevelopmentMode() {
273
return "development".equals(System.getenv("ENVIRONMENT"));
274
}
275
276
private static boolean isInteractiveMode() {
277
return System.console() != null;
278
}
279
}
280
```
281
282
## Best Practices
283
284
1. **Order by Reliability**: Place most reliable credentials first in the chain
285
2. **Environment-Specific Chains**: Create different chains for different deployment environments
286
3. **Fast Credentials First**: Order credentials by authentication speed for better performance
287
4. **Avoid Interactive in Production**: Don't include interactive credentials in production chains
288
5. **Handle All Failures**: Implement proper error handling for when all credentials fail
289
6. **Enable Logging**: Use logging to debug which credentials are being attempted
290
7. **Security First**: Prioritize more secure authentication methods (certificates over secrets)
291
8. **Minimize Chain Length**: Keep chains as short as possible while maintaining reliability
292
293
## Troubleshooting
294
295
Common issues with credential chains:
296
297
- **All Credentials Fail**: Ensure at least one credential type is properly configured
298
- **Slow Authentication**: Reorder chain to put faster credentials first
299
- **Interactive Prompts**: Remove interactive credentials for automated scenarios
300
- **Permission Errors**: Ensure all credentials have appropriate permissions
301
- **Environment Issues**: Verify environment variables and tool installations for each credential type