From ce27d37ba0c2b3fbc8a673317506956fc58aa11e Mon Sep 17 00:00:00 2001 From: trwalke Date: Mon, 11 Aug 2025 01:06:53 -0700 Subject: [PATCH 1/4] Added better error message for OIDC error --- .../Internal/Constants.cs | 3 ++ .../MsalErrorMessage.cs | 1 + .../MsalServiceExceptionFactory.cs | 19 +++++++- .../OAuth2/OAuth2Client.cs | 7 +-- .../Core/Mocks/MockHelpers.cs | 5 +- .../Core/Mocks/MockHttpManagerExtensions.cs | 11 +++-- .../InstanceTests/GenericAuthorityTests.cs | 47 +++++++++++++++++++ 7 files changed, 83 insertions(+), 10 deletions(-) diff --git a/src/client/Microsoft.Identity.Client/Internal/Constants.cs b/src/client/Microsoft.Identity.Client/Internal/Constants.cs index d5cefd6f78..a7f2a9719a 100644 --- a/src/client/Microsoft.Identity.Client/Internal/Constants.cs +++ b/src/client/Microsoft.Identity.Client/Internal/Constants.cs @@ -28,6 +28,9 @@ internal static class Constants public const string CcsRoutingHintHeader = "x-anchormailbox"; public const string AadThrottledErrorCode = "AADSTS50196"; + public const string AadAccountTypeAndResourceIncompatibleErrorCode = "AADSTS500207"; + public const string AadMissingScopeErrorCode = "AADSTS900144"; + //Represents 5 minutes in Unit time stamp public const int DefaultJitterRangeInSeconds = 300; public static readonly TimeSpan AccessTokenExpirationBuffer = TimeSpan.FromMinutes(5); diff --git a/src/client/Microsoft.Identity.Client/MsalErrorMessage.cs b/src/client/Microsoft.Identity.Client/MsalErrorMessage.cs index d5b61c67e8..69629c0868 100644 --- a/src/client/Microsoft.Identity.Client/MsalErrorMessage.cs +++ b/src/client/Microsoft.Identity.Client/MsalErrorMessage.cs @@ -442,5 +442,6 @@ public static string InvalidTokenProviderResponseValue(string invalidValueName) public const string RegionRequiredForMtlsPopMessage = "Regional auto-detect failed. mTLS Proof-of-Possession requires a region to be specified, as there is no global endpoint for mTLS. See https://aka.ms/msal-net-pop for details."; public const string ForceRefreshAndTokenHasNotCompatible = "Cannot specify ForceRefresh and AccessTokenSha256ToRefresh in the same request."; public const string RequestTimeOut = "Request to the endpoint timed out."; + public const string MalformedOidcAuthority = "Did you forget to append \"/v2.0\" to your OIDC authority?"; } } diff --git a/src/client/Microsoft.Identity.Client/MsalServiceExceptionFactory.cs b/src/client/Microsoft.Identity.Client/MsalServiceExceptionFactory.cs index 2919312d0b..d15b44cecc 100644 --- a/src/client/Microsoft.Identity.Client/MsalServiceExceptionFactory.cs +++ b/src/client/Microsoft.Identity.Client/MsalServiceExceptionFactory.cs @@ -41,7 +41,7 @@ internal static MsalServiceException FromHttpResponse( } else { - errorMessageToUse = errorMessage; + errorMessageToUse = errorMessage; } if (oAuth2Response.Claims == null) @@ -64,6 +64,11 @@ internal static MsalServiceException FromHttpResponse( innerException); } + if (IsOidcAuthorityError(context, oAuth2Response)) + { + errorMessage += $" {MsalErrorMessage.MalformedOidcAuthority}"; + } + ex ??= new MsalServiceException(errorCode, GetErrorMessage(errorMessage, httpResponse, context), innerException); SetHttpExceptionData(ex, httpResponse); @@ -108,6 +113,18 @@ private static bool IsThrottled(OAuth2ResponseBase oAuth2Response) oAuth2Response.ErrorDescription.StartsWith(Constants.AadThrottledErrorCode); } + private static bool IsOidcAuthorityError(RequestContext context, OAuth2ResponseBase oAuth2Response) + { + var authortyInfo = context.ServiceBundle.Config.Authority.AuthorityInfo; + + return context is not null && + authortyInfo.AuthorityType == AuthorityType.Generic && // Generic Oidc authority + !authortyInfo.CanonicalAuthority.AbsoluteUri.EndsWith("/v2.0") && // Does not end with /v2.0 + oAuth2Response.ErrorDescription != null && + (oAuth2Response.ErrorDescription.StartsWith(Constants.AadAccountTypeAndResourceIncompatibleErrorCode) || // Certain error codes are returned + oAuth2Response.ErrorDescription.StartsWith(Constants.AadMissingScopeErrorCode)); + } + internal static MsalServiceException FromBrokerResponse( MsalTokenResponse msalTokenResponse, string errorMessage) diff --git a/src/client/Microsoft.Identity.Client/OAuth2/OAuth2Client.cs b/src/client/Microsoft.Identity.Client/OAuth2/OAuth2Client.cs index ae6384fe5f..e91c614f50 100644 --- a/src/client/Microsoft.Identity.Client/OAuth2/OAuth2Client.cs +++ b/src/client/Microsoft.Identity.Client/OAuth2/OAuth2Client.cs @@ -254,7 +254,7 @@ private static void ThrowServerException(HttpResponse response, RequestContext r MsalServiceException exceptionToThrow; try { - exceptionToThrow = ExtractErrorsFromTheResponse(response, ref shouldLogAsError); + exceptionToThrow = ExtractErrorsFromTheResponse(response, ref shouldLogAsError, requestContext); } catch (JsonException) // in the rare case we get an error response we cannot deserialize { @@ -304,7 +304,7 @@ private static void ThrowServerException(HttpResponse response, RequestContext r throw exceptionToThrow; } - private static MsalServiceException ExtractErrorsFromTheResponse(HttpResponse response, ref bool shouldLogAsError) + private static MsalServiceException ExtractErrorsFromTheResponse(HttpResponse response, ref bool shouldLogAsError, RequestContext context = null) { // In cases where the end-point is not found (404) response.body will be empty. if (string.IsNullOrWhiteSpace(response.Body)) @@ -347,7 +347,8 @@ private static MsalServiceException ExtractErrorsFromTheResponse(HttpResponse re return MsalServiceExceptionFactory.FromHttpResponse( msalTokenResponse.Error, msalTokenResponse.ErrorDescription, - response); + response, + context: context); } private Uri AddExtraQueryParams(Uri endPoint) diff --git a/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHelpers.cs b/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHelpers.cs index 91e5c3d268..4cac3b7f17 100644 --- a/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHelpers.cs +++ b/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHelpers.cs @@ -260,9 +260,10 @@ public static HttpResponseMessage CreateFailureTokenResponseMessage( string error, string subError = null, string correlationId = null, - HttpStatusCode? customStatusCode = null) + HttpStatusCode? customStatusCode = null, + string errorCode = "AADSTS00000") { - string message = "{\"error\":\"" + error + "\",\"error_description\":\"AADSTS00000: Error for test." + + string message = "{\"error\":\"" + error + "\",\"error_description\":\"" + errorCode + ": Error for test." + "Trace ID: f7ec686c-9196-4220-a754-cd9197de44e9Correlation ID: " + "04bb0cae-580b-49ac-9a10-b6c3316b1eaaTimestamp: 2015-09-16 07:24:55Z\"," + "\"error_codes\":[70002,70008],\"timestamp\":\"2015-09-16 07:24:55Z\"," + diff --git a/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpManagerExtensions.cs b/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpManagerExtensions.cs index 38bd9239e3..27eb8fc4ab 100644 --- a/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpManagerExtensions.cs +++ b/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpManagerExtensions.cs @@ -86,15 +86,18 @@ public static MockHttpMessageHandler AddFailureTokenEndpointResponse( this MockHttpManager httpManager, string error, string authority = TestConstants.AuthorityCommonTenant, - string correlationId = null) + string correlationId = null, + string AadErrorCode = "AADSTS00000", + string expectedUrl = null) { var handler = new MockHttpMessageHandler() { - ExpectedUrl = authority + "oauth2/v2.0/token", + ExpectedUrl = expectedUrl != null? expectedUrl : authority + "oauth2/v2.0/token", ExpectedMethod = HttpMethod.Post, ResponseMessage = MockHelpers.CreateFailureTokenResponseMessage( - error, - correlationId: correlationId) + error, + correlationId: correlationId, + errorCode: AadErrorCode) }; httpManager.AddMockHandler(handler); return handler; diff --git a/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/GenericAuthorityTests.cs b/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/GenericAuthorityTests.cs index 9f138860d4..5ca5be4788 100644 --- a/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/GenericAuthorityTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/GenericAuthorityTests.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Globalization; using System.Linq; +using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -333,6 +334,52 @@ public async Task BadOidcResponse_ThrowsException_Async(string badOidcResponseTy } } + [TestMethod] + public async Task Oidc_Malformed_Failure_Async() + { + using (var httpManager = new MockHttpManager()) + { + string authority = "https://demo.duendesoftware.com"; + IConfidentialClientApplication app = ConfidentialClientApplicationBuilder + .Create(TestConstants.ClientId) + .WithHttpManager(httpManager) + .WithOidcAuthority(authority) + .WithClientSecret(TestConstants.ClientSecret) + .Build(); + + httpManager.AddMockHandler( + CreateOidcHttpHandler(authority + @"/" + Constants.WellKnownOpenIdConfigurationPath)); + + httpManager.AddFailureTokenEndpointResponse( + error: "error", + AadErrorCode: "AADSTS500207", + expectedUrl: "https://demo.duendesoftware.com/connect/token"); + + Assert.AreEqual(authority + "/", app.Authority); + var confidentailClientApp = (ConfidentialClientApplication)app; + Assert.AreEqual(AuthorityType.Generic, confidentailClientApp.AuthorityInfo.AuthorityType); + + var ex = await AssertException.TaskThrowsAsync(() => + app.AcquireTokenForClient(new[] { "api" }) + .ExecuteAsync()) + .ConfigureAwait(false); + + Assert.IsTrue(ex.Message.Contains("Did you forget to append \"/v2.0\" to your OIDC authority?")); + + httpManager.AddFailureTokenEndpointResponse( + error: "error", + AadErrorCode: "AADSTS900144", + expectedUrl: "https://demo.duendesoftware.com/connect/token"); + + ex = await AssertException.TaskThrowsAsync(() => + app.AcquireTokenForClient(new[] { "api" }) + .ExecuteAsync()) + .ConfigureAwait(false); + + Assert.IsTrue(ex.Message.Contains("Did you forget to append \"/v2.0\" to your OIDC authority?")); + } + } + [TestMethod] public async Task OidcIssuerValidation_ThrowsForNonMatchingIssuer_Async() { From eafc8766a92c8a4c1c5a76465e23b555ee7f9bb5 Mon Sep 17 00:00:00 2001 From: trwalke Date: Wed, 13 Aug 2025 03:45:46 -0700 Subject: [PATCH 2/4] Refactoring --- .../MsalErrorMessage.cs | 2 +- .../MsalServiceExceptionFactory.cs | 21 ++++++++------- .../Core/Mocks/MockHttpManagerExtensions.cs | 2 +- .../TestConstants.cs | 5 ++++ .../InstanceTests/GenericAuthorityTests.cs | 26 ++++++++++++------- 5 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/client/Microsoft.Identity.Client/MsalErrorMessage.cs b/src/client/Microsoft.Identity.Client/MsalErrorMessage.cs index 69629c0868..35a63934cd 100644 --- a/src/client/Microsoft.Identity.Client/MsalErrorMessage.cs +++ b/src/client/Microsoft.Identity.Client/MsalErrorMessage.cs @@ -442,6 +442,6 @@ public static string InvalidTokenProviderResponseValue(string invalidValueName) public const string RegionRequiredForMtlsPopMessage = "Regional auto-detect failed. mTLS Proof-of-Possession requires a region to be specified, as there is no global endpoint for mTLS. See https://aka.ms/msal-net-pop for details."; public const string ForceRefreshAndTokenHasNotCompatible = "Cannot specify ForceRefresh and AccessTokenSha256ToRefresh in the same request."; public const string RequestTimeOut = "Request to the endpoint timed out."; - public const string MalformedOidcAuthority = "Did you forget to append \"/v2.0\" to your OIDC authority?"; + public const string MalformedOidcAuthorityFormat = "Possible cause: When using Entra External ID, you didn't append /v2.0, for example {0}/v2.0\""; } } diff --git a/src/client/Microsoft.Identity.Client/MsalServiceExceptionFactory.cs b/src/client/Microsoft.Identity.Client/MsalServiceExceptionFactory.cs index d15b44cecc..bfbff7181c 100644 --- a/src/client/Microsoft.Identity.Client/MsalServiceExceptionFactory.cs +++ b/src/client/Microsoft.Identity.Client/MsalServiceExceptionFactory.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Net; using System.Text; using Microsoft.Identity.Client.Http; @@ -64,9 +65,11 @@ internal static MsalServiceException FromHttpResponse( innerException); } - if (IsOidcAuthorityError(context, oAuth2Response)) + var authorityInfo = context.ServiceBundle.Config.Authority.AuthorityInfo; + + if (IsOidcAuthorityError(authorityInfo, oAuth2Response.ErrorDescription)) { - errorMessage += $" {MsalErrorMessage.MalformedOidcAuthority}"; + errorMessage += string.Format(CultureInfo.InvariantCulture, MsalErrorMessage.MalformedOidcAuthorityFormat, $" {authorityInfo.CanonicalAuthority}"); } ex ??= new MsalServiceException(errorCode, GetErrorMessage(errorMessage, httpResponse, context), innerException); @@ -113,16 +116,14 @@ private static bool IsThrottled(OAuth2ResponseBase oAuth2Response) oAuth2Response.ErrorDescription.StartsWith(Constants.AadThrottledErrorCode); } - private static bool IsOidcAuthorityError(RequestContext context, OAuth2ResponseBase oAuth2Response) + private static bool IsOidcAuthorityError(AuthorityInfo authortyInfo, string ErrorDescription) { - var authortyInfo = context.ServiceBundle.Config.Authority.AuthorityInfo; - - return context is not null && + return authortyInfo is not null && authortyInfo.AuthorityType == AuthorityType.Generic && // Generic Oidc authority - !authortyInfo.CanonicalAuthority.AbsoluteUri.EndsWith("/v2.0") && // Does not end with /v2.0 - oAuth2Response.ErrorDescription != null && - (oAuth2Response.ErrorDescription.StartsWith(Constants.AadAccountTypeAndResourceIncompatibleErrorCode) || // Certain error codes are returned - oAuth2Response.ErrorDescription.StartsWith(Constants.AadMissingScopeErrorCode)); + !authortyInfo.CanonicalAuthority!.AbsoluteUri.EndsWith("/v2.0") && // Does not end with /v2.0 + ErrorDescription != null && + (ErrorDescription.StartsWith(Constants.AadAccountTypeAndResourceIncompatibleErrorCode) || // Certain error codes are returned + ErrorDescription.StartsWith(Constants.AadMissingScopeErrorCode)); } internal static MsalServiceException FromBrokerResponse( diff --git a/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpManagerExtensions.cs b/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpManagerExtensions.cs index 27eb8fc4ab..da247fa418 100644 --- a/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpManagerExtensions.cs +++ b/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpManagerExtensions.cs @@ -92,7 +92,7 @@ public static MockHttpMessageHandler AddFailureTokenEndpointResponse( { var handler = new MockHttpMessageHandler() { - ExpectedUrl = expectedUrl != null? expectedUrl : authority + "oauth2/v2.0/token", + ExpectedUrl = expectedUrl != null? expectedUrl : $"{authority}oauth2/v2.0/token", ExpectedMethod = HttpMethod.Post, ResponseMessage = MockHelpers.CreateFailureTokenResponseMessage( error, diff --git a/tests/Microsoft.Identity.Test.Common/TestConstants.cs b/tests/Microsoft.Identity.Test.Common/TestConstants.cs index 3d89cc1bbe..4884764a65 100644 --- a/tests/Microsoft.Identity.Test.Common/TestConstants.cs +++ b/tests/Microsoft.Identity.Test.Common/TestConstants.cs @@ -122,6 +122,8 @@ public static HashSet s_scope public const string CiamAuthorityMainFormat = "https://tenant.ciamlogin.com/"; public const string CiamAuthorityWithFriendlyName = "https://tenant.ciamlogin.com/tenant.onmicrosoft.com"; public const string CiamAuthorityWithGuid = "https://tenant.ciamlogin.com/aaaaaaab-aaaa-aaaa-cccc-aaaaaaaaaaaa"; + public const string CiamCUDAuthority = "https://login.msidlabsciam.com/aaaaaaab-aaaa-aaaa-cccc-aaaaaaaaaaaa/v2.0"; + public const string CiamCUDAuthorityMalformed = "https://login.msidlabsciam.com/aaaaaaab-aaaa-aaaa-cccc-aaaaaaaaaaaa"; public const string B2CLoginGlobal = ".b2clogin.com"; public const string B2CLoginUSGov = ".b2clogin.us"; @@ -229,6 +231,9 @@ public static HashSet s_scope public const string Pop = "PoP"; public const string FmiNodeClientId = "urn:microsoft:identity:fmi"; + public const string AadAccountTypeAndResourceIncompatibleErrorCode = "AADSTS500207"; + public const string AadMissingScopeErrorCode = "AADSTS900144"; + public static IDictionary ExtraQueryParameters { get diff --git a/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/GenericAuthorityTests.cs b/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/GenericAuthorityTests.cs index 5ca5be4788..25ec352dbd 100644 --- a/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/GenericAuthorityTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/GenericAuthorityTests.cs @@ -339,7 +339,7 @@ public async Task Oidc_Malformed_Failure_Async() { using (var httpManager = new MockHttpManager()) { - string authority = "https://demo.duendesoftware.com"; + string authority = TestConstants.CiamCUDAuthorityMalformed; IConfidentialClientApplication app = ConfidentialClientApplicationBuilder .Create(TestConstants.ClientId) .WithHttpManager(httpManager) @@ -348,14 +348,14 @@ public async Task Oidc_Malformed_Failure_Async() .Build(); httpManager.AddMockHandler( - CreateOidcHttpHandler(authority + @"/" + Constants.WellKnownOpenIdConfigurationPath)); + CreateOidcHttpHandler(authority + "/" + Constants.WellKnownOpenIdConfigurationPath)); httpManager.AddFailureTokenEndpointResponse( error: "error", - AadErrorCode: "AADSTS500207", - expectedUrl: "https://demo.duendesoftware.com/connect/token"); + AadErrorCode: TestConstants.AadAccountTypeAndResourceIncompatibleErrorCode, + expectedUrl: $"{TestConstants.CiamCUDAuthorityMalformed}/connect/token"); - Assert.AreEqual(authority + "/", app.Authority); + Assert.AreEqual(authority, app.Authority); var confidentailClientApp = (ConfidentialClientApplication)app; Assert.AreEqual(AuthorityType.Generic, confidentailClientApp.AuthorityInfo.AuthorityType); @@ -364,19 +364,27 @@ public async Task Oidc_Malformed_Failure_Async() .ExecuteAsync()) .ConfigureAwait(false); - Assert.IsTrue(ex.Message.Contains("Did you forget to append \"/v2.0\" to your OIDC authority?")); + Assert.IsTrue(ex.Message.Contains( + string.Format( + CultureInfo.InvariantCulture, + MsalErrorMessage.MalformedOidcAuthorityFormat, + TestConstants.CiamCUDAuthorityMalformed))); httpManager.AddFailureTokenEndpointResponse( error: "error", - AadErrorCode: "AADSTS900144", - expectedUrl: "https://demo.duendesoftware.com/connect/token"); + AadErrorCode: TestConstants.AadMissingScopeErrorCode, + expectedUrl: $"{TestConstants.CiamCUDAuthorityMalformed}/connect/token"); ex = await AssertException.TaskThrowsAsync(() => app.AcquireTokenForClient(new[] { "api" }) .ExecuteAsync()) .ConfigureAwait(false); - Assert.IsTrue(ex.Message.Contains("Did you forget to append \"/v2.0\" to your OIDC authority?")); + Assert.IsTrue(ex.Message.Contains( + string.Format( + CultureInfo.InvariantCulture, + MsalErrorMessage.MalformedOidcAuthorityFormat, + TestConstants.CiamCUDAuthorityMalformed))); } } From edc831ab90c2fd0051ff361f8d8a753e8f2e0e2b Mon Sep 17 00:00:00 2001 From: trwalke Date: Mon, 15 Sep 2025 00:09:52 -0700 Subject: [PATCH 3/4] Addressing feedback --- tests/Microsoft.Identity.Test.Common/TestConstants.cs | 3 --- .../CoreTests/InstanceTests/GenericAuthorityTests.cs | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/Microsoft.Identity.Test.Common/TestConstants.cs b/tests/Microsoft.Identity.Test.Common/TestConstants.cs index 4884764a65..91e0b127e0 100644 --- a/tests/Microsoft.Identity.Test.Common/TestConstants.cs +++ b/tests/Microsoft.Identity.Test.Common/TestConstants.cs @@ -231,9 +231,6 @@ public static HashSet s_scope public const string Pop = "PoP"; public const string FmiNodeClientId = "urn:microsoft:identity:fmi"; - public const string AadAccountTypeAndResourceIncompatibleErrorCode = "AADSTS500207"; - public const string AadMissingScopeErrorCode = "AADSTS900144"; - public static IDictionary ExtraQueryParameters { get diff --git a/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/GenericAuthorityTests.cs b/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/GenericAuthorityTests.cs index 25ec352dbd..f186205d1f 100644 --- a/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/GenericAuthorityTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/GenericAuthorityTests.cs @@ -348,11 +348,11 @@ public async Task Oidc_Malformed_Failure_Async() .Build(); httpManager.AddMockHandler( - CreateOidcHttpHandler(authority + "/" + Constants.WellKnownOpenIdConfigurationPath)); + CreateOidcHttpHandler($"{authority}/{Constants.WellKnownOpenIdConfigurationPath}")); httpManager.AddFailureTokenEndpointResponse( error: "error", - AadErrorCode: TestConstants.AadAccountTypeAndResourceIncompatibleErrorCode, + AadErrorCode: Constants.AadAccountTypeAndResourceIncompatibleErrorCode, expectedUrl: $"{TestConstants.CiamCUDAuthorityMalformed}/connect/token"); Assert.AreEqual(authority, app.Authority); @@ -372,7 +372,7 @@ public async Task Oidc_Malformed_Failure_Async() httpManager.AddFailureTokenEndpointResponse( error: "error", - AadErrorCode: TestConstants.AadMissingScopeErrorCode, + AadErrorCode: Constants.AadMissingScopeErrorCode, expectedUrl: $"{TestConstants.CiamCUDAuthorityMalformed}/connect/token"); ex = await AssertException.TaskThrowsAsync(() => From d9713b1d7e57001f7e2308ba93fafc7413114686 Mon Sep 17 00:00:00 2001 From: trwalke Date: Tue, 16 Sep 2025 03:08:16 -0700 Subject: [PATCH 4/4] Resolving test issue --- .../MsalServiceExceptionFactory.cs | 6 +++--- tests/Microsoft.Identity.Test.Common/TestConstants.cs | 8 ++++++++ .../CoreTests/InstanceTests/GenericAuthorityTests.cs | 6 +++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/client/Microsoft.Identity.Client/MsalServiceExceptionFactory.cs b/src/client/Microsoft.Identity.Client/MsalServiceExceptionFactory.cs index bfbff7181c..181dd224ec 100644 --- a/src/client/Microsoft.Identity.Client/MsalServiceExceptionFactory.cs +++ b/src/client/Microsoft.Identity.Client/MsalServiceExceptionFactory.cs @@ -65,11 +65,11 @@ internal static MsalServiceException FromHttpResponse( innerException); } - var authorityInfo = context.ServiceBundle.Config.Authority.AuthorityInfo; + var authorityInfo = context?.ServiceBundle.Config.Authority.AuthorityInfo; - if (IsOidcAuthorityError(authorityInfo, oAuth2Response.ErrorDescription)) + if (IsOidcAuthorityError(authorityInfo, oAuth2Response?.ErrorDescription)) { - errorMessage += string.Format(CultureInfo.InvariantCulture, MsalErrorMessage.MalformedOidcAuthorityFormat, $" {authorityInfo.CanonicalAuthority}"); + errorMessage += " " + string.Format(CultureInfo.InvariantCulture, MsalErrorMessage.MalformedOidcAuthorityFormat, $"{authorityInfo.CanonicalAuthority}"); } ex ??= new MsalServiceException(errorCode, GetErrorMessage(errorMessage, httpResponse, context), innerException); diff --git a/tests/Microsoft.Identity.Test.Common/TestConstants.cs b/tests/Microsoft.Identity.Test.Common/TestConstants.cs index 91e0b127e0..39260c2b0e 100644 --- a/tests/Microsoft.Identity.Test.Common/TestConstants.cs +++ b/tests/Microsoft.Identity.Test.Common/TestConstants.cs @@ -542,6 +542,14 @@ public static MsalTokenResponse CreateMsalTokenResponseWithTokenSource() ], ""backchannel_user_code_parameter_supported"":true }"; + public static string GetOidcResponse(string authority = null) + { + if (string.IsNullOrEmpty(authority)) + { + return GenericOidcResponse; + } + return GenericOidcResponse.Replace("https://demo.duendesoftware.com", authority.TrimEnd('/')); + } public static MsalTokenResponse CreateAadTestTokenResponse() { diff --git a/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/GenericAuthorityTests.cs b/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/GenericAuthorityTests.cs index 260a1a811c..159a136158 100644 --- a/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/GenericAuthorityTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/CoreTests/InstanceTests/GenericAuthorityTests.cs @@ -348,7 +348,7 @@ public async Task Oidc_Malformed_Failure_Async() .Build(); httpManager.AddMockHandler( - CreateOidcHttpHandler($"{authority}/{Constants.WellKnownOpenIdConfigurationPath}")); + CreateOidcHttpHandler($"{authority}/{Constants.WellKnownOpenIdConfigurationPath}", authority)); httpManager.AddFailureTokenEndpointResponse( error: "error", @@ -534,13 +534,13 @@ private static string CreateIdToken() return string.Format(CultureInfo.InvariantCulture, "someheader.{0}.somesignature", Base64UrlHelpers.Encode(id)); } - private static MockHttpMessageHandler CreateOidcHttpHandler(string oidcEndpoint) + private static MockHttpMessageHandler CreateOidcHttpHandler(string oidcEndpoint, string authority = null) { return new MockHttpMessageHandler() { ExpectedMethod = HttpMethod.Get, ExpectedUrl = oidcEndpoint, - ResponseMessage = MockHelpers.CreateSuccessResponseMessage(TestConstants.GenericOidcResponse) + ResponseMessage = MockHelpers.CreateSuccessResponseMessage(TestConstants.GetOidcResponse(authority)) }; } }