AzureAD / microsoft-identity-web

Helps creating protected web apps and web APIs with Microsoft identity platform and Azure AD B2C
MIT License
681 stars 211 forks source link

Calling downstream Graph fails due to incorrect reply address (AADSTS500112) when behind a TLS termination proxy #3114

Open oddeirik opened 3 days ago

oddeirik commented 3 days ago

Microsoft.Identity.Web Library

Microsoft.Identity.Web

Microsoft.Identity.Web version

3.2.2

Web app

Sign-in users and call web APIs

Web API

Protected web APIs call downstream web APIs

Token cache serialization

In-memory caches

Description

I've added the Microsoft.Identity.Web.GraphServiceClient package to an ASP Net Core 8 web application because we want to do some graph calls on behalf of the user.

Prior to this we've had AAD authentication enabled and working for this application, it's just the graph part that's new.

This works fine locally, but when deployed to Azure (linux container app service) it fails with an HTTP 500. Checking the logs for the application, it looks like the downstream call to acquire a token fails because it doesn't pick up the correct scheme for the redirect URL/reply address.

We've already enabled forwarding of headers to ensure this worked for AAD authentication, and this does pick up the correct scheme and works fine.

I was able to reproduce this locally by adding Caddy in front of the application.

Here are some snippets from Program.cs for the sections adding forwarded headers and the authentication parts, based on the documentation at https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-8.0:

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
                               ForwardedHeaders.XForwardedProto;

    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();
});

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(configurationSection: builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi(["user.read"])
    .AddMicrosoftGraph()
    .AddInMemoryTokenCaches();

// [...]
var app = builder.Build();
app.UseForwardedHeaders();
app.UseHsts();

The error message being logged:

Microsoft.Identity.Client.MsalServiceException: A configuration issue is preventing authentication - check the error message from the server for details. You can modify the configuration in the application registration portal. See https://aka.ms/msal-net-invalid-client for details.  Original exception: AADSTS500112: The reply address 'http://localhost:4443/signin-oidc' does not match the reply address 'https://localhost:4443/signin-oidc' provided when requesting Authorization code.

The behavior is exactly the same as when running this in an Azure Linux container, it's just a different redirect URL, of course. And AAD authentication without the downstream API call works just fine in both cases, e.g. if I remove the graph-related extension method calls.

Are there additional configuration options that need to be set for the GraphServiceClient to pick up the correct redirect URL (well, the scheme, at least), or is this actually a bug?

Reproduction steps

  1. Add the graph client Nuget package, Microsoft.Identity.Web.GraphServiceClient.
  2. Add downstream graph calls to the application:
    // Existing AAD auth, works fine
    builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
      .AddMicrosoftIdentityWebApp(configurationSection: builder.Configuration.GetSection("AzureAd"))
      // These are new
      .EnableTokenAcquisitionToCallDownstreamApi(["user.read"])
      .AddMicrosoftGraph()
      .AddInMemoryTokenCaches();
  3. Run the application as HTTP behind a TLS-terminating proxy like Azure Linux Container.

Error message

[09:00:03 DBG] False MSAL 4.65.0.0 MSAL.NetCore .NET 8.0.10 Linux [2024-10-28 09:00:03Z - d5457b33-e74f-46bc-a4b7-f80a119426fe] [HttpManager] Received response. Status code: BadRequest. [09:00:03 DBG] False MSAL 4.65.0.0 MSAL.NetCore .NET 8.0.10 Linux [2024-10-28 09:00:03Z - d5457b33-e74f-46bc-a4b7-f80a119426fe] Finished [HttpManager] ExecuteAsync in 159 ms [09:00:03 INF] False MSAL 4.65.0.0 MSAL.NetCore .NET 8.0.10 Linux [2024-10-28 09:00:03Z - d5457b33-e74f-46bc-a4b7-f80a119426fe] Response status code does not indicate success: 400 (BadRequest). [09:00:03 WRN] False MSAL 4.65.0.0 MSAL.NetCore .NET 8.0.10 Linux [2024-10-28 09:00:03Z - d5457b33-e74f-46bc-a4b7-f80a119426fe] Request retry failed. [09:00:03 DBG] False MSAL 4.65.0.0 MSAL.NetCore .NET 8.0.10 Linux [2024-10-28 09:00:03Z - d5457b33-e74f-46bc-a4b7-f80a119426fe] Finished [Oauth2Client] Sending POST request in 162 ms [09:00:03 DBG] False MSAL 4.65.0.0 MSAL.NetCore .NET 8.0.10 Linux [2024-10-28 09:00:03Z - d5457b33-e74f-46bc-a4b7-f80a119426fe] [Oauth2Client] Processing error response [09:00:03 INF] False MSAL 4.65.0.0 MSAL.NetCore .NET 8.0.10 Linux [2024-10-28 09:00:03Z - d5457b33-e74f-46bc-a4b7-f80a119426fe] HttpStatusCode: 400: BadRequest [09:00:03 ERR] False MSAL 4.65.0.0 MSAL.NetCore .NET 8.0.10 Linux [2024-10-28 09:00:03Z - d5457b33-e74f-46bc-a4b7-f80a119426fe] === Token Acquisition (1000) failed. Host: login.microsoftonline.com. [09:00:03 ERR] False MSAL 4.65.0.0 MSAL.NetCore .NET 8.0.10 Linux [2024-10-28 09:00:03Z - d5457b33-e74f-46bc-a4b7-f80a119426fe] Exception type: Microsoft.Identity.Client.MsalServiceException , ErrorCode: invalid_client HTTP StatusCode 400 CorrelationId d5457b33-e74f-46bc-a4b7-f80a119426fe Microsoft Entra ID Error Code AADSTS500112 To see full exception details, enable PII Logging. See https://aka.ms/msal-net-logging

[09:00:03 DBG] False MSAL 4.65.0.0 MSAL.NetCore .NET 8.0.10 Linux [2024-10-28 09:00:03Z - d5457b33-e74f-46bc-a4b7-f80a119426fe] Finished TokenClient:SendTokenRequestAsync in 196 ms [09:00:03 ERR] False MSAL 4.65.0.0 MSAL.NetCore .NET 8.0.10 Linux [2024-10-28 09:00:03Z - d5457b33-e74f-46bc-a4b7-f80a119426fe] Exception type: Microsoft.Identity.Client.MsalServiceException , ErrorCode: invalid_client HTTP StatusCode 400 CorrelationId d5457b33-e74f-46bc-a4b7-f80a119426fe Microsoft Entra ID Error Code AADSTS500112 To see full exception details, enable PII Logging. See https://aka.ms/msal-net-logging at Microsoft.Identity.Client.OAuth2.OAuth2Client.ThrowServerException(HttpResponse response, RequestContext requestContext) at Microsoft.Identity.Client.OAuth2.OAuth2Client.CreateResponse[T](HttpResponse response, RequestContext requestContext) at Microsoft.Identity.Client.OAuth2.OAuth2Client.ExecuteRequestAsync[T](Uri endPoint, HttpMethod method, RequestContext requestContext, Boolean expectErrorsOn200OK, Boolean addCommonHeaders, Func2 onBeforePostRequestData) at Microsoft.Identity.Client.OAuth2.TokenClient.SendHttpAndClearTelemetryAsync(String tokenEndpoint, ILoggerAdapter logger) at Microsoft.Identity.Client.OAuth2.TokenClient.SendHttpAndClearTelemetryAsync(String tokenEndpoint, ILoggerAdapter logger) at Microsoft.Identity.Client.OAuth2.TokenClient.SendTokenRequestAsync(IDictionary2 additionalBodyParameters, String scopeOverride, String tokenEndpointOverride, CancellationToken cancellationToken) at Microsoft.Identity.Client.Internal.Requests.RequestBase.SendTokenRequestAsync(IDictionary2 additionalBodyParameters, CancellationToken cancellationToken) at Microsoft.Identity.Client.Internal.Requests.ConfidentialAuthCodeRequest.ExecuteAsync(CancellationToken cancellationToken) at Microsoft.Identity.Client.Internal.Requests.RequestBase.<>c__DisplayClass11_1.<<RunAsync>b__1>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.Identity.Client.Utils.StopwatchService.MeasureCodeBlockAsync(Func1 codeBlock) at Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(CancellationToken cancellationToken)

[09:00:03 INF] [MsIdWeb] An error occured during token acquisition: Exception occurred while adding an account to the cache from the auth code. MSAL.NetCore.4.65.0.0.MsalServiceException: ErrorCode: invalid_client Microsoft.Identity.Client.MsalServiceException: A configuration issue is preventing authentication - check the error message from the server for details. You can modify the configuration in the application registration portal. See https://aka.ms/msal-net-invalid-client for details. Original exception: AADSTS500112: The reply address 'http://localhost:4443/signin-oidc' does not match the reply address 'https://localhost:4443/signin-oidc' provided when requesting Authorization code. Trace ID: 6f361eeb-cad3-4383-9635-847ff5da5700 Correlation ID: d5457b33-e74f-46bc-a4b7-f80a119426fe Timestamp: 2024-10-28 09:00:03Z at Microsoft.Identity.Client.OAuth2.OAuth2Client.ThrowServerException(HttpResponse response, RequestContext requestContext) at Microsoft.Identity.Client.OAuth2.OAuth2Client.CreateResponse[T](HttpResponse response, RequestContext requestContext) at Microsoft.Identity.Client.OAuth2.OAuth2Client.ExecuteRequestAsync[T](Uri endPoint, HttpMethod method, RequestContext requestContext, Boolean expectErrorsOn200OK, Boolean addCommonHeaders, Func2 onBeforePostRequestData) at Microsoft.Identity.Client.OAuth2.TokenClient.SendHttpAndClearTelemetryAsync(String tokenEndpoint, ILoggerAdapter logger) at Microsoft.Identity.Client.OAuth2.TokenClient.SendHttpAndClearTelemetryAsync(String tokenEndpoint, ILoggerAdapter logger) at Microsoft.Identity.Client.OAuth2.TokenClient.SendTokenRequestAsync(IDictionary2 additionalBodyParameters, String scopeOverride, String tokenEndpointOverride, CancellationToken cancellationToken) at Microsoft.Identity.Client.Internal.Requests.RequestBase.SendTokenRequestAsync(IDictionary2 additionalBodyParameters, CancellationToken cancellationToken) at Microsoft.Identity.Client.Internal.Requests.ConfidentialAuthCodeRequest.ExecuteAsync(CancellationToken cancellationToken) at Microsoft.Identity.Client.Internal.Requests.RequestBase.<>c__DisplayClass11_1.<<RunAsync>b__1>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.Identity.Client.Utils.StopwatchService.MeasureCodeBlockAsync(Func1 codeBlock) at Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(CancellationToken cancellationToken) at Microsoft.Identity.Client.ApiConfig.Executors.ConfidentialClientExecutor.ExecuteAsync(AcquireTokenCommonParameters commonParameters, AcquireTokenByAuthorizationCodeParameters authorizationCodeParameters, CancellationToken cancellationToken) at Microsoft.Identity.Web.TokenAcquisition.AddAccountToCacheFromAuthorizationCodeAsync(AuthCodeRedemptionParameters authCodeRedemptionParameters) StatusCode: 400 ResponseBody: {"error":"invalid_client","error_description":"AADSTS500112: The reply address 'http://localhost:4443/signin-oidc' does not match the reply address 'https://localhost:4443/signin-oidc' provided when requesting Authorization code. Trace ID: 6f361eeb-cad3-4383-9635-847ff5da5700 Correlation ID: d5457b33-e74f-46bc-a4b7-f80a119426fe Timestamp: 2024-10-28 09:00:03Z","error_codes":[500112],"timestamp":"2024-10-28 09:00:03Z","trace_id":"6f361eeb-cad3-4383-9635-847ff5da5700","correlation_id":"d5457b33-e74f-46bc-a4b7-f80a119426fe"} Headers: Cache-Control: no-store, no-cache Pragma: no-cache Strict-Transport-Security: max-age=31536000; includeSubDomains X-Content-Type-Options: nosniff P3P: CP="DSP CUR OTPi IND OTRi ONL FIN" client-request-id: d5457b33-e74f-46bc-a4b7-f80a119426fe x-ms-request-id: 6f361eeb-cad3-4383-9635-847ff5da5700 x-ms-ests-server: 2.1.19267.5 - WEULR1 ProdSlices x-ms-clitelem: 1,500112,0,, x-ms-srs: 1.P X-XSS-Protection: 0 Set-Cookie: fpc=AuSJN61grv9AnMYA8fc7wHCYKtoUAQAAAJJJsd4OAAAA; expires=Wed, 27-Nov-2024 09:00:03 GMT; path=/; secure; HttpOnly; SameSite=None, x-ms-gateway-slice=estsfd; path=/; secure; httponly Date: Mon, 28 Oct 2024 09:00:03 GMT

Id Web logs

No response

Relevant code snippets

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
                               ForwardedHeaders.XForwardedProto;

    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();
});

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(configurationSection: builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi(["user.read"])
    .AddMicrosoftGraph()
    .AddInMemoryTokenCaches();

// [...]
var app = builder.Build();
app.UseForwardedHeaders();
app.UseHsts();

Regression

No response

Expected behavior

The GraphServiceClient should pick up the correct redirect URL, same as the AAD authentication does.

jmprieur commented 2 days ago

@oddeirik did you see this article? https://github.com/AzureAD/microsoft-identity-web/wiki/Deploying-Web-apps-to-App-services-as-Linux-containers ?

oddeirik commented 2 days ago

Yes, I have tried setting the "ASPNETCORE_FORWARDEDHEADERS_ENABLED"=true environment variable as well.

But according to that Wiki article it also sounds like the "proper" fix (?) in those cases is to configure the forwarded headers middleware, which is what we already have in place.

And this works fine for AAD authentication, it detects the correct scheme and the redirect works. But not for the downstream token acquisition for the graph client.