Skip to content

Commit 8ee59a6

Browse files
rauchyclaude
andcommitted
Switch to fallback approach for federated token authentication
Instead of detecting federated tokens via GUID patterns, use a more robust fallback mechanism: try with tenant ID first (preserving existing behavior), then retry without tenant ID if it fails for service principals. This addresses the concern that GUID detection cannot distinguish between regular service principals and federated token service principals, as both use client IDs as names. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 761e0a1 commit 8ee59a6

2 files changed

Lines changed: 17 additions & 25 deletions

File tree

config/auth_azure_cli.go

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -177,17 +177,17 @@ func (ts *azureCliTokenSource) getTokenBytes() ([]byte, error) {
177177
return nil, fmt.Errorf("cannot unmarshal account info: %w", err)
178178
}
179179

180-
// Check for authentication types that should not use tenant ID:
181-
// 1. Traditional MSI (system/user assigned identities)
182180
isMsi := account.User.Type == "servicePrincipal" && (account.User.Name == "systemAssignedIdentity" || account.User.Name == "userAssignedIdentity")
183-
184-
// 2. Service principal with client ID as name (likely federated token)
185-
// When user.name is a GUID and user.type is servicePrincipal, it's often federated token auth
186-
// where specifying tenant ID can cause failures
187-
isFederatedToken := account.User.Type == "servicePrincipal" && ts.isGuidLike(account.User.Name)
188-
189-
if !isMsi && !isFederatedToken {
190-
return ts.getTokenBytesWithTenantId(ts.azureTenantId)
181+
if !isMsi {
182+
// For regular service principals, try with tenant ID first
183+
result, err := ts.getTokenBytesWithTenantId(ts.azureTenantId)
184+
if err != nil && account.User.Type == "servicePrincipal" {
185+
// If it fails for service principals, it might be a federated token scenario
186+
// where tenant ID should not be specified. Fall back to no tenant ID.
187+
logger.Infof(ts.ctx, "Failed to get token with tenant ID for service principal, trying without tenant ID: %v", err)
188+
return ts.getTokenBytesWithTenantId("")
189+
}
190+
return result, err
191191
}
192192
return ts.getTokenBytesWithTenantId("")
193193
}
@@ -219,11 +219,3 @@ func (ts *azureCliTokenSource) getTokenBytesWithTenantId(tenantId string) ([]byt
219219
}
220220
return result, nil
221221
}
222-
223-
// isGuidLike checks if the string looks like a GUID (client ID)
224-
func (ts *azureCliTokenSource) isGuidLike(s string) bool {
225-
// Simple check for GUID format: 8-4-4-4-12 characters separated by hyphens
226-
parts := strings.Split(s, "-")
227-
return len(parts) == 5 && len(parts[0]) == 8 && len(parts[1]) == 4 &&
228-
len(parts[2]) == 4 && len(parts[3]) == 4 && len(parts[4]) == 12
229-
}

config/auth_azure_cli_federated_token_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import (
1010

1111
func TestAzureCliCredentials_FederatedTokenServicePrincipal(t *testing.T) {
1212
// This test verifies the fix where service principals authenticated via
13-
// federated token (like in AKS with workload identity) should be detected via GUID pattern
14-
// and skip tenant ID usage entirely (no fallback needed with the new approach).
13+
// federated token (like in AKS with workload identity) use a fallback mechanism:
14+
// try with tenant ID first, then retry without tenant ID if it fails.
1515

1616
env.CleanupEnvironment(t)
1717
t.Setenv("PATH", testdataPath())
@@ -21,8 +21,8 @@ func TestAzureCliCredentials_FederatedTokenServicePrincipal(t *testing.T) {
2121
t.Setenv("AZ_USER_NAME", "5817e630-86b3-4f67-a38e-a63e6a1a401c")
2222
t.Setenv("AZ_USER_TYPE", "servicePrincipal")
2323

24-
// This would make the mock az command fail if --tenant were passed, but with the GUID
25-
// detection fix, --tenant should never be passed for this GUID-like service principal name
24+
// This makes the mock az command fail when --tenant is passed, simulating the federated
25+
// token scenario where tenant ID causes authentication failure
2626
t.Setenv("FAIL_IF_TENANT_ID_SET", "true")
2727

2828
aa := AzureCliCredentials{}
@@ -31,10 +31,10 @@ func TestAzureCliCredentials_FederatedTokenServicePrincipal(t *testing.T) {
3131
AzureTenantID: "e6a2f6d5-ece9-4c0d-9464-9c493497cb8f",
3232
}
3333

34-
// With the GUID detection fix, this should work because the service principal name
35-
// matches the GUID pattern and is treated like MSI (no --tenant parameter used)
34+
// With the fallback fix, this should work: first attempt with --tenant fails,
35+
// then fallback without --tenant succeeds
3636
visitor, err := aa.Configure(context.Background(), cfg)
3737

38-
assert.NoError(t, err, "Authentication should work with federated token service principals via GUID detection")
38+
assert.NoError(t, err, "Authentication should work with federated token service principals via fallback mechanism")
3939
assert.NotNil(t, visitor, "Should return a valid credentials provider")
4040
}

0 commit comments

Comments
 (0)