Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/client/Microsoft.Identity.Client/Internal/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/client/Microsoft.Identity.Client/MsalErrorMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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?";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ internal static MsalServiceException FromHttpResponse(
}
else
{
errorMessageToUse = errorMessage;
errorMessageToUse = errorMessage;
}

if (oAuth2Response.Claims == null)
Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
7 changes: 4 additions & 3 deletions src/client/Microsoft.Identity.Client/OAuth2/OAuth2Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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\"," +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<MsalServiceException>(() =>
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<MsalServiceException>(() =>
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()
{
Expand Down
Loading