Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
using System.Net;
using Microsoft.Identity.Client.ApiConfig.Parameters;
using System.Text;
#if SUPPORTS_SYSTEM_TEXT_JSON
using System.Text.Json;
#else
using Microsoft.Identity.Json;
#endif

namespace Microsoft.Identity.Client.ManagedIdentity
{
Expand All @@ -29,7 +34,7 @@ protected AbstractManagedIdentity(RequestContext requestContext, ManagedIdentity
}

public virtual async Task<ManagedIdentityResponse> AuthenticateAsync(
AcquireTokenForManagedIdentityParameters parameters,
AcquireTokenForManagedIdentityParameters parameters,
CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
Expand Down Expand Up @@ -107,7 +112,7 @@ protected virtual Task<ManagedIdentityResponse> HandleResponseAsync(
}

string message = GetMessageFromErrorResponse(response);

_requestContext.Logger.Error($"[Managed Identity] request failed, HttpStatusCode: {response.StatusCode} Error message: {message}");

MsalException exception = MsalServiceExceptionFactory.CreateManagedIdentityException(
Expand All @@ -124,20 +129,39 @@ protected virtual Task<ManagedIdentityResponse> HandleResponseAsync(

protected ManagedIdentityResponse GetSuccessfulResponse(HttpResponse response)
{
ManagedIdentityResponse managedIdentityResponse = JsonHelper.DeserializeFromJson<ManagedIdentityResponse>(response.Body);
ManagedIdentityResponse managedIdentityResponse;
try
{
managedIdentityResponse = JsonHelper.DeserializeFromJson<ManagedIdentityResponse>(response.Body);
}
catch (JsonException ex)
{
_requestContext.Logger.Error("[Managed Identity] MSI json response failed to parse. " + ex);

if (managedIdentityResponse == null || managedIdentityResponse.AccessToken.IsNullOrEmpty() || managedIdentityResponse.ExpiresOn.IsNullOrEmpty())
var exception = MsalServiceExceptionFactory.CreateManagedIdentityException(
MsalError.ManagedIdentityResponseParseFailure,
MsalErrorMessage.ManagedIdentityJsonParseFailure,
ex,
_sourceType,
(int)HttpStatusCode.OK);

throw exception;
}

if (managedIdentityResponse == null ||
managedIdentityResponse.AccessToken.IsNullOrEmpty() ||
managedIdentityResponse.ExpiresOn.IsNullOrEmpty())
{
_requestContext.Logger.Error("[Managed Identity] Response is either null or insufficient for authentication.");

var exception = MsalServiceExceptionFactory.CreateManagedIdentityException(
MsalError.ManagedIdentityRequestFailed,
MsalErrorMessage.ManagedIdentityInvalidResponse,
null,
_sourceType,
null);
null,
_sourceType,
(int)HttpStatusCode.OK);

throw exception;
throw exception;
}

return managedIdentityResponse;
Expand All @@ -158,7 +182,7 @@ internal string GetMessageFromErrorResponse(HttpResponse response)
catch
{
return TryGetMessageFromNestedErrorResponse(response.Body);
}
}
}

private string ExtractErrorMessageFromManagedIdentityErrorResponse(ManagedIdentityErrorResponse managedIdentityErrorResponse)
Expand Down Expand Up @@ -218,7 +242,8 @@ private string TryGetMessageFromNestedErrorResponse(string response)
{
return errorMessage.ToString();
}
} catch
}
catch
{
// Ignore any exceptions that occur during parsing and send the error message.
}
Expand All @@ -227,8 +252,8 @@ private string TryGetMessageFromNestedErrorResponse(string response)
return $"{MsalErrorMessage.ManagedIdentityUnexpectedErrorResponse}. Error response received from the server: {response}.";
}

private void HandleException(Exception ex,
ManagedIdentitySource managedIdentitySource = ManagedIdentitySource.None,
private void HandleException(Exception ex,
ManagedIdentitySource managedIdentitySource = ManagedIdentitySource.None,
string additionalInfo = null)
{
ManagedIdentitySource source = managedIdentitySource != ManagedIdentitySource.None ? managedIdentitySource : _sourceType;
Expand All @@ -254,9 +279,9 @@ private void HandleException(Exception ex,
}
}

private static void CreateAndThrowException(string errorCode,
string errorMessage,
Exception innerException,
private static void CreateAndThrowException(string errorCode,
string errorMessage,
Exception innerException,
ManagedIdentitySource source)
{
MsalException exception = MsalServiceExceptionFactory.CreateManagedIdentityException(
Expand Down
5 changes: 5 additions & 0 deletions src/client/Microsoft.Identity.Client/MsalError.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1110,6 +1110,11 @@ public static class MsalError
/// </summary>
public const string ManagedIdentityRequestFailed = "managed_identity_request_failed";

/// <summary>
/// Managed Identity error response was received.
/// </summary>
public const string ManagedIdentityResponseParseFailure = "managed_identity_response_parse_failure";

/// <summary>
/// Managed Identity endpoint is not reachable.
/// </summary>
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 @@ -414,6 +414,7 @@ public static string InvalidTokenProviderResponseValue(string invalidValueName)

public const string ManagedIdentityNoResponseReceived = "[Managed Identity] Authentication unavailable. No response received from the managed identity endpoint.";
public const string ManagedIdentityInvalidResponse = "[Managed Identity] Invalid response, the authentication response received did not contain the expected fields.";
public const string ManagedIdentityJsonParseFailure = "[Managed Identity] MSI returned 200 OK, but the response could not be parsed.";
public const string ManagedIdentityUnexpectedResponse = "[Managed Identity] Unexpected exception occurred when parsing the response. See the inner exception for details.";
public const string ManagedIdentityExactlyOneScopeExpected = "[Managed Identity] To acquire token for managed identity, exactly one scope must be passed.";
public const string ManagedIdentityUnexpectedErrorResponse = "[Managed Identity] The error response was either empty or could not be parsed.";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const Microsoft.Identity.Client.MsalError.ManagedIdentityResponseParseFailure = "managed_identity_response_parse_failure" -> string
Microsoft.Identity.Client.AbstractConfidentialClientAcquireTokenParameterBuilder<T>.WithSignedHttpRequestProofOfPossession(Microsoft.Identity.Client.AppConfig.PoPAuthenticationConfiguration popAuthenticationConfiguration) -> T
Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder.WithMtlsProofOfPossession() -> Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder
const Microsoft.Identity.Client.MsalError.MissingTenantedAuthority = "missing_tenanted_authority" -> string
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const Microsoft.Identity.Client.MsalError.ManagedIdentityResponseParseFailure = "managed_identity_response_parse_failure" -> string
Microsoft.Identity.Client.AbstractConfidentialClientAcquireTokenParameterBuilder<T>.WithSignedHttpRequestProofOfPossession(Microsoft.Identity.Client.AppConfig.PoPAuthenticationConfiguration popAuthenticationConfiguration) -> T
Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder.WithMtlsProofOfPossession() -> Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder
const Microsoft.Identity.Client.MsalError.MissingTenantedAuthority = "missing_tenanted_authority" -> string
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const Microsoft.Identity.Client.MsalError.ManagedIdentityResponseParseFailure = "managed_identity_response_parse_failure" -> string
Microsoft.Identity.Client.AbstractConfidentialClientAcquireTokenParameterBuilder<T>.WithSignedHttpRequestProofOfPossession(Microsoft.Identity.Client.AppConfig.PoPAuthenticationConfiguration popAuthenticationConfiguration) -> T
Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder.WithMtlsProofOfPossession() -> Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder
const Microsoft.Identity.Client.MsalError.MissingTenantedAuthority = "missing_tenanted_authority" -> string
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const Microsoft.Identity.Client.MsalError.ManagedIdentityResponseParseFailure = "managed_identity_response_parse_failure" -> string
Microsoft.Identity.Client.AbstractConfidentialClientAcquireTokenParameterBuilder<T>.WithSignedHttpRequestProofOfPossession(Microsoft.Identity.Client.AppConfig.PoPAuthenticationConfiguration popAuthenticationConfiguration) -> T
Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder.WithMtlsProofOfPossession() -> Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder
const Microsoft.Identity.Client.MsalError.MissingTenantedAuthority = "missing_tenanted_authority" -> string
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const Microsoft.Identity.Client.MsalError.ManagedIdentityResponseParseFailure = "managed_identity_response_parse_failure" -> string
Microsoft.Identity.Client.AbstractConfidentialClientAcquireTokenParameterBuilder<T>.WithSignedHttpRequestProofOfPossession(Microsoft.Identity.Client.AppConfig.PoPAuthenticationConfiguration popAuthenticationConfiguration) -> T
Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder.WithMtlsProofOfPossession() -> Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder
const Microsoft.Identity.Client.MsalError.MissingTenantedAuthority = "missing_tenanted_authority" -> string
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const Microsoft.Identity.Client.MsalError.ManagedIdentityResponseParseFailure = "managed_identity_response_parse_failure" -> string
Microsoft.Identity.Client.AbstractConfidentialClientAcquireTokenParameterBuilder<T>.WithSignedHttpRequestProofOfPossession(Microsoft.Identity.Client.AppConfig.PoPAuthenticationConfiguration popAuthenticationConfiguration) -> T
Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder.WithMtlsProofOfPossession() -> Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder
const Microsoft.Identity.Client.MsalError.MissingTenantedAuthority = "missing_tenanted_authority" -> string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,10 @@ public static string GetMsiSuccessfulResponse(int expiresInHours = 1, bool useIs
"\"Bearer\",\"client_id\":\"client_id\"}";
}

public static string GetMsiImdsSuccessfulResponse()
public static string GetMsiErrorBadJson()
{
string expiresOn = DateTimeHelpers.DateTimeToUnixTimestamp(DateTime.UtcNow.AddHours(1));
return
"{\"access_token\":\"" + TestConstants.ATSecret + "\",\"client_id\":\"client-id\"," +
"\"expires_in\":\"12345\",\"expires_on\":\"" + expiresOn + "\",\"resource\":\"https://management.azure.com/\"," +
"\"ext_expires_in\":\"12345\",\"token_type\":\"Bearer\"}";
string successResponse = GetMsiSuccessfulResponse();
return successResponse.Replace("{", "|");
}

public static string GetMsiErrorResponse(ManagedIdentitySource source)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1073,5 +1073,43 @@ await AssertException.TaskThrowsAsync<TaskCanceledException>(
.WithForceRefresh(true)
.ExecuteAsync(tokenSource.Token)).ConfigureAwait(false);
}

[DataTestMethod]
[DataRow(ManagedIdentitySource.Imds, ImdsEndpoint)]
[DataRow(ManagedIdentitySource.AppService, AppServiceEndpoint)]
[DataRow(ManagedIdentitySource.AzureArc, AzureArcEndpoint)]
[DataRow(ManagedIdentitySource.CloudShell, CloudShellEndpoint)]
[DataRow(ManagedIdentitySource.ServiceFabric, ServiceFabricEndpoint)]
public async Task InvalidJsonResponseHandling(ManagedIdentitySource managedIdentitySource, string endpoint)
{
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager(isManagedIdentity: true))
{
SetEnvironmentVariables(managedIdentitySource, endpoint);

var miBuilder = ManagedIdentityApplicationBuilder
.Create(ManagedIdentityId.SystemAssigned)
.WithHttpManager(httpManager);

// Disabling shared cache options to avoid cross test pollution.
miBuilder.Config.AccessorOptions = null;

var mi = miBuilder.Build();

httpManager.AddManagedIdentityMockHandler(
endpoint,
"scope",
MockHelpers.GetMsiErrorBadJson(),
managedIdentitySource);

MsalServiceException ex = await Assert.ThrowsExceptionAsync<MsalServiceException>(async () =>
await mi.AcquireTokenForManagedIdentity("scope")
.ExecuteAsync().ConfigureAwait(false)).ConfigureAwait(false);

Assert.AreEqual(managedIdentitySource.ToString(), ex.AdditionalExceptionData[MsalException.ManagedIdentitySource]);
Assert.AreEqual(MsalError.ManagedIdentityResponseParseFailure, ex.ErrorCode);
Assert.AreEqual(MsalErrorMessage.ManagedIdentityJsonParseFailure, ex.Message);
}
}
}
}