Skip to content

Commit 371fec2

Browse files
Resolve the identity the backend authenticates as
1 parent 613a793 commit 371fec2

2 files changed

Lines changed: 55 additions & 11 deletions

File tree

backend/api/Configurations/CustomServiceConfigurations.cs

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -229,15 +229,57 @@ TokenCredential runtimeCredential
229229
);
230230
}
231231

232-
public static TokenCredential CreateCredential(IConfiguration config)
232+
/// <summary>
233+
/// Resolves the identity the backend authenticates as. Used by both the Key Vault and the
234+
/// runtime credential so they cannot disagree. Appsettings wins; AZURE_* is only a fallback.
235+
/// In AKS, AZURE_CLIENT_ID is injected from the <c>azure.workload.identity/client-id</c>
236+
/// annotation in robotics-infrastructure, which must match <c>AzureAd:ClientId</c>.
237+
/// </summary>
238+
private static (string? TenantId, string? ClientId) ResolveIdentity(IConfiguration config)
233239
{
234-
string? tenantId = config["AzureAd:TenantId"];
235-
string? clientId = config["AzureAd:ClientId"];
236-
string? clientSecret = config["AzureAd:ClientSecret"];
240+
return (
241+
config["AzureAd:TenantId"] ?? config["AZURE_TENANT_ID"],
242+
config["AzureAd:ClientId"] ?? config["AZURE_CLIENT_ID"]
243+
);
244+
}
245+
246+
/// <summary>
247+
/// Options for the credential that reads Key Vault. It needs its own credential because
248+
/// Key Vault is read before <see cref="CreateCredential"/> can run — the client secret
249+
/// that method may use is itself stored there. DefaultAzureCredential keeps local
250+
/// development working via <c>az login</c>, pinned to <see cref="ResolveIdentity"/>.
251+
/// </summary>
252+
public static DefaultAzureCredentialOptions CreateKeyVaultCredentialOptions(
253+
IConfiguration config
254+
)
255+
{
256+
var (tenantId, clientId) = ResolveIdentity(config);
257+
258+
var options = new DefaultAzureCredentialOptions();
259+
260+
if (!string.IsNullOrWhiteSpace(tenantId))
261+
options.TenantId = tenantId;
237262

238-
tenantId ??= config["AZURE_TENANT_ID"];
239-
clientId ??= config["AZURE_CLIENT_ID"];
240-
clientSecret ??= config["AZURE_CLIENT_SECRET"];
263+
if (!string.IsNullOrWhiteSpace(clientId))
264+
options.WorkloadIdentityClientId = clientId;
265+
266+
return options;
267+
}
268+
269+
public static TokenCredential CreateCredential(IConfiguration config)
270+
{
271+
var (tenantId, clientId) = ResolveIdentity(config);
272+
string? clientSecret = config["AzureAd:ClientSecret"] ?? config["AZURE_CLIENT_SECRET"];
273+
274+
Console.WriteLine(
275+
$"Backend identity: clientId={clientId} (source: "
276+
+ (
277+
config["AzureAd:ClientId"] is not null
278+
? "appsettings"
279+
: "AZURE_CLIENT_ID environment variable"
280+
)
281+
+ ")"
282+
);
241283

242284
var workloadOptions = new WorkloadIdentityCredentialOptions();
243285
if (!string.IsNullOrWhiteSpace(clientId))
@@ -247,7 +289,7 @@ public static TokenCredential CreateCredential(IConfiguration config)
247289

248290
var workloadIdentity = new WorkloadIdentityCredential(workloadOptions);
249291

250-
bool allowUsingClientSecret = config.GetValue<bool>("AllowUsingClientSecret");
292+
bool allowUsingClientSecret = config.GetValue<bool>("AzureAd:AllowUsingClientSecret");
251293

252294
if (
253295
allowUsingClientSecret

backend/api/Program.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,16 @@
3030
builder.Configuration.AddEnvironmentVariables();
3131
if (builder.Configuration.GetSection("KeyVault").GetValue<bool>("UseKeyVault"))
3232
{
33-
// The ExcludeSharedTokenCacheCredential option is a recommended workaround by Azure for dockerization
34-
// See https://github.com/Azure/azure-sdk-for-net/issues/17052
33+
// Key Vault is read before the runtime credential exists, so it uses its own credential
34+
// pinned to the same identity. See CustomServiceConfigurations.ResolveIdentity.
3535
string? vaultUri = builder.Configuration.GetSection("KeyVault")["VaultUri"];
3636
if (!string.IsNullOrEmpty(vaultUri))
3737
{
3838
builder.Configuration.AddAzureKeyVault(
3939
new Uri(vaultUri),
40-
new DefaultAzureCredential(new DefaultAzureCredentialOptions())
40+
new DefaultAzureCredential(
41+
CustomServiceConfigurations.CreateKeyVaultCredentialOptions(builder.Configuration)
42+
)
4143
);
4244
}
4345
else

0 commit comments

Comments
 (0)