Skip to content

Commit edfcf84

Browse files
authored
Fix instance metadata validation error (#32520)
* Fix instance metadata validation error
1 parent fbe6633 commit edfcf84

File tree

4 files changed

+86
-8
lines changed

4 files changed

+86
-8
lines changed

sdk/identity/Azure.Identity/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
### Bugs Fixed
1010
- Fixed error message parsing in `AzureCliCredential` which would misinterpret AAD errors with the need to login with `az login`.
1111
- `ManagedIdentityCredential` will no longer fail when a response received from the endpoint is invalid JSON. It now treats this scenario as if the credential is unavailable.
12+
- Fixed an issue when using `ManagedIdentityCredential` in combination with authorities other than Azure public cloud that resulted in a bogus instance metadata validation error. [#32498](https://github.com/Azure/azure-sdk-for-net/issues/32498)
1213

1314
### Other Changes
1415

sdk/identity/Azure.Identity/src/MsalConfidentialClient.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the MIT License.
33

44
using System;
5-
using System.Runtime.CompilerServices;
65
using System.Security.Cryptography.X509Certificates;
76
using System.Threading;
87
using System.Threading.Tasks;
@@ -20,6 +19,7 @@ internal class MsalConfidentialClient : MsalClientBase<IConfidentialClientApplic
2019
private readonly Func<string> _assertionCallback;
2120
private readonly Func<CancellationToken, Task<string>> _asyncAssertionCallback;
2221
private readonly Func<AppTokenProviderParameters, Task<AppTokenProviderResult>> _appTokenProviderCallback;
22+
private readonly Uri _authority;
2323

2424
internal string RedirectUrl { get; }
2525

@@ -59,6 +59,7 @@ public MsalConfidentialClient(CredentialPipeline pipeline, string tenantId, stri
5959
: base(pipeline, tenantId, clientId, options)
6060
{
6161
_appTokenProviderCallback = appTokenProviderCallback;
62+
_authority = options?.AuthorityHost ?? AzureAuthorityHosts.AzurePublicCloud;
6263
}
6364

6465
internal string RegionalAuthority { get; } = EnvironmentVariables.AzureRegionalAuthorityName;
@@ -69,11 +70,12 @@ protected override async ValueTask<IConfidentialClientApplication> CreateClientA
6970
.WithHttpClientFactory(new HttpPipelineClientFactory(Pipeline.HttpPipeline))
7071
.WithLogging(LogMsal, enablePiiLogging: IsPiiLoggingEnabled);
7172

72-
//special case for using appTokenProviderCallback, authority validation and instance metadata discovery should be disabled since we're not calling the STS
73+
// Special case for using appTokenProviderCallback, authority validation and instance metadata discovery should be disabled since we're not calling the STS
74+
// The authority matches the one configured in the CredentialOptions.
7375
if (_appTokenProviderCallback != null)
7476
{
7577
confClientBuilder.WithAppTokenProvider(_appTokenProviderCallback)
76-
.WithAuthority(Pipeline.AuthorityHost.AbsoluteUri, TenantId, false)
78+
.WithAuthority(_authority.AbsoluteUri, TenantId, false)
7779
.WithInstanceDiscoveryMetadata(s_instanceMetadata);
7880
}
7981
else
@@ -102,7 +104,7 @@ protected override async ValueTask<IConfidentialClientApplication> CreateClientA
102104
confClientBuilder.WithCertificate(clientCertificate);
103105
}
104106

105-
if (!string.IsNullOrEmpty(RegionalAuthority))
107+
if (_appTokenProviderCallback == null && !string.IsNullOrEmpty(RegionalAuthority))
106108
{
107109
confClientBuilder.WithAzureRegion(RegionalAuthority);
108110
}

sdk/identity/Azure.Identity/tests/ManagedIdentityCredentialImdsLiveTests.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6-
using System.Reflection;
7-
using System.Text;
86
using System.Threading.Tasks;
9-
using Azure.Core.TestFramework;
10-
using Azure.Identity.Tests.Mock;
117
using Azure.Security.KeyVault.Secrets;
128
using NUnit.Framework;
139

sdk/identity/Azure.Identity/tests/ManagedIdentityCredentialTests.cs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System.Web;
1212
using Azure.Core;
1313
using Azure.Core.Diagnostics;
14+
using Azure.Core.Pipeline;
1415
using Azure.Core.TestFramework;
1516
using Azure.Identity.Tests.Mock;
1617
using Microsoft.AspNetCore.Http;
@@ -100,6 +101,73 @@ public async Task VerifyImdsRequestWithClientIdMockAsync()
100101
Assert.AreEqual("true", metadataValue);
101102
}
102103

104+
[NonParallelizable]
105+
[Test]
106+
[TestCase(null)]
107+
[TestCase("Auto-Detect")]
108+
[TestCase("eastus")]
109+
[TestCase("westus")]
110+
public async Task VerifyImdsRequestWithClientIdAndRegionalAuthorityNameMockAsync(string regionName)
111+
{
112+
using var environment = new TestEnvVar(new() { {"AZURE_REGIONAL_AUTHORITY_NAME", regionName}, {"MSI_ENDPOINT", null }, { "MSI_SECRET", null }, { "IDENTITY_ENDPOINT", null }, { "IDENTITY_HEADER", null }, { "AZURE_POD_IDENTITY_AUTHORITY_HOST", null } });
113+
114+
var response = CreateMockResponse(200, ExpectedToken);
115+
var mockTransport = new MockTransport(response);
116+
var options = new TokenCredentialOptions() { Transport = mockTransport };
117+
var pipeline = CredentialPipeline.GetInstance(options);
118+
119+
ManagedIdentityCredential credential = InstrumentClient(new ManagedIdentityCredential("mock-client-id", pipeline));
120+
121+
AccessToken actualToken = await credential.GetTokenAsync(new TokenRequestContext(MockScopes.Default));
122+
123+
Assert.AreEqual(ExpectedToken, actualToken.Token);
124+
125+
MockRequest request = mockTransport.Requests[0];
126+
127+
string query = request.Uri.Query;
128+
129+
Assert.AreEqual(request.Uri.Host, "169.254.169.254");
130+
Assert.AreEqual(request.Uri.Path, "/metadata/identity/oauth2/token");
131+
Assert.IsTrue(query.Contains("api-version=2018-02-01"));
132+
Assert.IsTrue(query.Contains($"resource={Uri.EscapeDataString(ScopeUtilities.ScopesToResource(MockScopes.Default))}"));
133+
Assert.IsTrue(request.Headers.TryGetValue("Metadata", out string metadataValue));
134+
Assert.IsTrue(query.Contains($"{Constants.ManagedIdentityClientId}=mock-client-id"));
135+
Assert.AreEqual("true", metadataValue);
136+
}
137+
138+
[NonParallelizable]
139+
[Test]
140+
[TestCaseSource(nameof(AuthorityHostValues))]
141+
public async Task VerifyImdsRequestWithClientIdAndNonPubCloudMockAsync(Uri authority)
142+
{
143+
using var environment = new TestEnvVar(new() { { "MSI_ENDPOINT", null }, { "MSI_SECRET", null }, { "IDENTITY_ENDPOINT", null }, { "IDENTITY_HEADER", null }, { "AZURE_POD_IDENTITY_AUTHORITY_HOST", null } });
144+
145+
var response = CreateMockResponse(200, ExpectedToken);
146+
var mockTransport = new MockTransport(response);
147+
var options = new TokenCredentialOptions() { Transport = mockTransport, AuthorityHost = authority };
148+
//var pipeline = CredentialPipeline.GetInstance(options);
149+
var _pipeline = new HttpPipeline(mockTransport);
150+
var pipeline = new CredentialPipeline(authority, _pipeline, new ClientDiagnostics(options));
151+
152+
ManagedIdentityCredential credential = InstrumentClient(new ManagedIdentityCredential(new ManagedIdentityClient( pipeline, "mock-client-id")));
153+
154+
AccessToken actualToken = await credential.GetTokenAsync(new TokenRequestContext(MockScopes.Default));
155+
156+
Assert.AreEqual(ExpectedToken, actualToken.Token);
157+
158+
MockRequest request = mockTransport.Requests[0];
159+
160+
string query = request.Uri.Query;
161+
162+
Assert.AreEqual(request.Uri.Host, "169.254.169.254");
163+
Assert.AreEqual(request.Uri.Path, "/metadata/identity/oauth2/token");
164+
Assert.IsTrue(query.Contains("api-version=2018-02-01"));
165+
Assert.IsTrue(query.Contains($"resource={Uri.EscapeDataString(ScopeUtilities.ScopesToResource(MockScopes.Default))}"));
166+
Assert.IsTrue(request.Headers.TryGetValue("Metadata", out string metadataValue));
167+
Assert.IsTrue(query.Contains($"{Constants.ManagedIdentityClientId}=mock-client-id"));
168+
Assert.AreEqual("true", metadataValue);
169+
}
170+
103171
[NonParallelizable]
104172
[Test]
105173
public async Task VerifyImdsRequestWithResourceIdMockAsync()
@@ -781,6 +849,17 @@ private static IEnumerable<TestCaseData> ExceptionalEnvironmentConfigs()
781849
yield return new TestCaseData(new Dictionary<string, string>() { { "MSI_ENDPOINT", null }, { "MSI_SECRET", null }, { "IDENTITY_ENDPOINT", null }, { "IDENTITY_HEADER", null }, { "IDENTITY_SERVER_THUMBPRINT", "null" }, { "AZURE_POD_IDENTITY_AUTHORITY_HOST", "http::@/bogusuri" } });
782850
}
783851

852+
public static IEnumerable<object[]> AuthorityHostValues()
853+
{
854+
// params
855+
// az thrown Exception message, expected message, expected exception
856+
yield return new object[] { AzureAuthorityHosts.AzureChina };
857+
yield return new object[] { AzureAuthorityHosts.AzureGermany };
858+
yield return new object[] { AzureAuthorityHosts.AzureGovernment };
859+
yield return new object[] { AzureAuthorityHosts.AzurePublicCloud };
860+
yield return new object[] { new Uri("https://foo.bar") };
861+
}
862+
784863
private MockResponse CreateMockResponse(int responseCode, string token)
785864
{
786865
var response = new MockResponse(responseCode);

0 commit comments

Comments
 (0)