Skip to content

Commit 92c33ec

Browse files
CopilotbgavrilMS
andcommitted
Fix KeyNotFoundException when headers don't contain correlation ID
Co-authored-by: bgavrilMS <[email protected]>
1 parent a066158 commit 92c33ec

File tree

2 files changed

+53
-5
lines changed

2 files changed

+53
-5
lines changed

src/client/Microsoft.Identity.Client/Http/HttpManager.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,17 +140,24 @@ public async Task<HttpResponse> SendRequestAsync(
140140

141141
if (headers != null && headers.Count > 0)
142142
{
143-
var correlationId = headers[OAuth2Header.CorrelationId];
144-
string correlationIdMsg = headers.ContainsKey(OAuth2Header.CorrelationId) ?
145-
$" CorrelationId: {correlationId}" :
146-
string.Empty;
143+
string correlationIdMsg = string.Empty;
144+
string correlationId = null;
145+
146+
if (headers.ContainsKey(OAuth2Header.CorrelationId))
147+
{
148+
correlationId = headers[OAuth2Header.CorrelationId];
149+
correlationIdMsg = $" CorrelationId: {correlationId}";
150+
}
147151

148152
var ex = new MsalServiceException(
149153
MsalError.RequestTimeout,
150154
msg + correlationIdMsg,
151155
timeoutException);
152156

153-
ex.CorrelationId = correlationId;
157+
if (correlationId != null)
158+
{
159+
ex.CorrelationId = correlationId;
160+
}
154161

155162
throw ex;
156163
}

tests/Microsoft.Identity.Test.Unit/CoreTests/HttpTests/HttpManagerTests.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,47 @@ public async Task TestCorrelationIdWithRetryOnTimeoutFailureAsync(bool addCorrel
581581
}
582582
}
583583

584+
// Regression test for https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/XXXX
585+
// Verifies that timeout with headers but without correlation ID does not throw KeyNotFoundException
586+
[TestMethod]
587+
public async Task TestRetryOnTimeoutWithHeadersButNoCorrelationIdAsync()
588+
{
589+
using (var httpManager = new MockHttpManager())
590+
{
591+
// Simulate permanent errors (to trigger the maximum number of retries)
592+
const int Num500Errors = 1 + TestDefaultRetryPolicy.DefaultStsMaxRetries; // initial request + maximum number of retries
593+
for (int i = 0; i < Num500Errors; i++)
594+
{
595+
httpManager.AddRequestTimeoutResponseMessageMockHandler(HttpMethod.Post);
596+
}
597+
598+
// Create headers without correlation ID - this was causing KeyNotFoundException
599+
var headers = new Dictionary<string, string>
600+
{
601+
["some-other-header"] = "some-value"
602+
};
603+
604+
var exc = await AssertException.TaskThrowsAsync<MsalServiceException>(() =>
605+
httpManager.SendRequestAsync(
606+
new Uri(TestConstants.AuthorityHomeTenant + "oauth2/token"),
607+
headers: headers,
608+
body: new FormUrlEncodedContent(new Dictionary<string, string>()),
609+
method: HttpMethod.Post,
610+
logger: Substitute.For<ILoggerAdapter>(),
611+
doNotThrow: false,
612+
mtlsCertificate: null,
613+
validateServerCert: null,
614+
cancellationToken: default,
615+
retryPolicy: _stsRetryPolicy))
616+
.ConfigureAwait(false);
617+
618+
// Should get timeout error without KeyNotFoundException
619+
Assert.AreEqual(MsalError.RequestTimeout, exc.ErrorCode);
620+
Assert.AreEqual("Request to the endpoint timed out.", exc.Message);
621+
Assert.IsNull(exc.CorrelationId);
622+
}
623+
}
624+
584625
[TestMethod]
585626
public async Task TestWithCorrelationId_RetryOnTimeoutFailureAsync()
586627
{

0 commit comments

Comments
 (0)