6

I just finished creating an ASP.NET Core app with Angular as described in this tutorial. When creating the ASP.NET Core project, I checked the option to enable Windows Authentication. As long as I add [AllowAnonymous] to my controllers in the ASP.NET Core project, everything already works just fine, but as soon as I replace that with [Authorize], the requests from my Angular app no longer reach my code in the backend. Instead, the webserver returns a 400 Bad Request error. Here is a request including the response headers where this problem occurs: Example 400 Bad Request

After doing some research on my own, I found out that I probably need to enable CORS in the backend, since I am using a proxy via proxy.conf.json in the Angular app during development. Here is the content of the proxy.conf.json:

{
  "/api/*": {
    "target": "https://localhost:7139",
    "secure": false,
    "changeOrigin": true
  }
}

Therefore, I tried to add UseCors() in my Program.cs of the ASP.NET Core project. Here is how the Program.cs currently looks like:

using Microsoft.AspNetCore.Authentication.Negotiate;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddCors(setup =>
{
    setup.AddDefaultPolicy(builder =>
    {
        builder.WithOrigins("http://localhost:4200")
          .AllowAnyHeader()
          .AllowAnyMethod()
          .AllowCredentials();
    });
});

builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
   .AddNegotiate();

builder.Services.AddAuthorization(options =>
{
    // By default, all incoming requests will be authorized according to the default policy.
    options.FallbackPolicy = options.DefaultPolicy;
});

builder.Services.AddControllers();

var app = builder.Build();

// Configure the HTTP request pipeline.

app.UseHttpsRedirection();

app.UseCors();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

Unfortunately, this does not seem to make any difference. This is how my http GET call is implemented in the frontend:

this.http.get<string>(`api/test/1`, { responseType: 'text', withCredentials: true } as Record<string, unknown>).subscribe(value => {
      alert(value);
    }, error => console.error(error));

I feel like I am missing something important which I need to make Windows Authentication work here, but I don't know what exactly I am doing wrong. I do not even get any additional information from the webserver about why it even returns 400 Bad Request here, so I don't know where to start debugging this issue.

UPDATE: I figured out how to enable more Kestrel-Logging by adding the following configuration-entries in the Logging section of my appsettings.Development.json:

"Default": "Debug",
"Microsoft.AspNetCore": "Debug",
"Microsoft.AspNetCore.Server.Kestrel": "Debug",
"Microsoft.AspNetCore.Server.Kestrel.BadRequests": "Debug",
"Microsoft.AspNetCore.Server.Kestrel.Connections": "Debug",
"Microsoft.AspNetCore.Server.Kestrel.Http2": "Debug",
"Microsoft.AspNetCore.Server.Kestrel.Http3": "Debug"

Here is what Kestrel is logging while handling the request which ends with a 400 Bad Request error:

dbug: Microsoft.AspNetCore.Server.Kestrel.Connections[39]
      Connection id "0HMGSHHNJQ68N" accepted.
dbug: Microsoft.AspNetCore.Server.Kestrel.Connections[1]
      Connection id "0HMGSHHNJQ68N" started.
dbug: Microsoft.AspNetCore.Server.Kestrel.Https.Internal.HttpsConnectionMiddleware[3]
      Connection 0HMGSHHNJQ68N established using the following protocol: Tls12
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET https://localhost:7139/api/test/1 - -
dbug: Microsoft.AspNetCore.HostFiltering.HostFilteringMiddleware[0]
      Wildcard detected, all requests with hosts will be allowed.
dbug: Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1001]
      1 candidate(s) found for the request path '/api/test/1'
dbug: Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1005]
      Endpoint 'WebApp.Controllers.TestController.Get (WebApp)' with route pattern 'api/Test/{id}' is valid for the request path '/api/test/1'
dbug: Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[1]
      Request matched endpoint 'WebApp.Controllers.TestController.Get (WebApp)'
dbug: Microsoft.AspNetCore.Authentication.Negotiate.NegotiateHandler[9]
      AuthenticationScheme: Negotiate was not authenticated.
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
      Authorization failed. These requirements were not met:
      DenyAnonymousAuthorizationRequirement: Requires an authenticated user.
dbug: Microsoft.AspNetCore.Authentication.Negotiate.NegotiateHandler[6]
      Challenged 401 Negotiate.
info: Microsoft.AspNetCore.Authentication.Negotiate.NegotiateHandler[12]
      AuthenticationScheme: Negotiate was challenged.
dbug: Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[6]
      Connection id "0HMGSHHNJQ68N" received FIN.
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished HTTP/1.1 GET https://localhost:7139/api/test/1 - - - 401 0 - 189.6680ms
dbug: Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[7]
      Connection id "0HMGSHHNJQ68N" sending FIN because: "The client closed the connection."
dbug: Microsoft.AspNetCore.Server.Kestrel.Connections[10]
      Connection id "0HMGSHHNJQ68N" disconnecting.
dbug: Microsoft.AspNetCore.Server.Kestrel.Connections[2]
      Connection id "0HMGSHHNJQ68N" stopped.
dbug: Microsoft.AspNetCore.Server.Kestrel.Connections[39]
      Connection id "0HMGSHHNJQ68O" accepted.
dbug: Microsoft.AspNetCore.Server.Kestrel.Connections[1]
      Connection id "0HMGSHHNJQ68O" started.
dbug: Microsoft.AspNetCore.Server.Kestrel.Https.Internal.HttpsConnectionMiddleware[3]
      Connection 0HMGSHHNJQ68O established using the following protocol: Tls12
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET https://localhost:7139/api/test/1 - -
dbug: Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1001]
      1 candidate(s) found for the request path '/api/test/1'
dbug: Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1005]
      Endpoint 'WebApp.Controllers.TestController.Get (WebApp)' with route pattern 'api/Test/{id}' is valid for the request path '/api/test/1'
dbug: Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[1]
      Request matched endpoint 'WebApp.Controllers.TestController.Get (WebApp)'
info: Microsoft.AspNetCore.Authentication.Negotiate.NegotiateHandler[1]
      Incomplete Negotiate handshake, sending an additional 401 Negotiate challenge.
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished HTTP/1.1 GET https://localhost:7139/api/test/1 - - - 401 0 - 68.9309ms
dbug: Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[6]
      Connection id "0HMGSHHNJQ68O" received FIN.
dbug: Microsoft.AspNetCore.Server.Kestrel.Connections[10]
      Connection id "0HMGSHHNJQ68O" disconnecting.
dbug: Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[7]
      Connection id "0HMGSHHNJQ68O" sending FIN because: "The client closed the connection."
dbug: Microsoft.AspNetCore.Server.Kestrel.Connections[39]
      Connection id "0HMGSHHNJQ68P" accepted.
dbug: Microsoft.AspNetCore.Server.Kestrel.Connections[1]
      Connection id "0HMGSHHNJQ68P" started.
dbug: Microsoft.AspNetCore.Server.Kestrel.Connections[2]
      Connection id "0HMGSHHNJQ68O" stopped.
dbug: Microsoft.AspNetCore.Server.Kestrel.Https.Internal.HttpsConnectionMiddleware[3]
      Connection 0HMGSHHNJQ68P established using the following protocol: Tls12
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET https://localhost:7139/api/test/1 - -
dbug: Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1001]
      1 candidate(s) found for the request path '/api/test/1'
dbug: Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1005]
      Endpoint 'WebApp.Controllers.TestController.Get (WebApp)' with route pattern 'api/Test/{id}' is valid for the request path '/api/test/1'
dbug: Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[1]
      Request matched endpoint 'WebApp.Controllers.TestController.Get (WebApp)'
dbug: Microsoft.AspNetCore.Authentication.Negotiate.NegotiateHandler[11]
      Negotiate error code: ClientError.
dbug: Microsoft.AspNetCore.Authentication.Negotiate.NegotiateHandler[10]
      The users authentication request was invalid.
      System.ComponentModel.Win32Exception (0x80090308): Das Token, das der Funktion übergeben wurde, ist ungültig.
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished HTTP/1.1 GET https://localhost:7139/api/test/1 - - - 400 0 - 40.9717ms
dbug: Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[6]
      Connection id "0HMGSHHNJQ68P" received FIN.
dbug: Microsoft.AspNetCore.Server.Kestrel.Connections[10]
      Connection id "0HMGSHHNJQ68P" disconnecting.
dbug: Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[7]
      Connection id "0HMGSHHNJQ68P" sending FIN because: "The client closed the connection."
dbug: Microsoft.AspNetCore.Server.Kestrel.Connections[2]
      Connection id "0HMGSHHNJQ68P" stopped.

The first connection (0HMGSHHNJQ68N ) seems to be where I initially get the login-dialog in my browser where I enter my AD credentials. The other connections are then logged when I hit OK after entering my credentials. Sadly, I can't really figure out how to solve the problem based on those logs, but I am quite sure that the credentials are not provided correctly from the Angular app.

What am I doing wrong and/or how can I figure out the root cause of the problem?

5
  • 1
    Instead of using the webpack proxy, can you try to access the API directly? Can you show your proxy.conf.js? Also, this answer might help: stackoverflow.com/q/64862939/642579 Commented Apr 12, 2022 at 13:58
  • @Markus Yes, trying to access the API directly works fine. For example, I can just access it directly in my browser via the URL https://localhost:7139/api/test/1, enter my AD credentials and it works just fine. Therefore, I think that the Angular app does not yet know how to deal with the credentials, but I have no clue how to configure it to do that. I also figured out how to get more useful logging out of Kestrel. I added the logs and the content of my proxy.conf.json to my question above. Do you have an idea how I can make this work? Commented Apr 12, 2022 at 14:06
  • In our case, we didn't use the proxy - that worked just fine. What happens if you change the request in Angular to this.http.get<string>('https://localhost:7139/api/test/1', ...? Commented Apr 12, 2022 at 14:10
  • @Markus When I try that I get the following error: Access to XMLHttpRequest at 'https://localhost:7139/api/test/1' from origin 'https://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Commented Apr 12, 2022 at 14:15
  • 1
    Hi @Chris, it should be builder.WithOrigins("https://localhost:4200") instead of builder.WithOrigins("http://localhost:4200") Commented Apr 13, 2022 at 5:25

2 Answers 2

4

In your CORS configuration, you try to open the API in a very broad way. Unfortunately, when dealing with requests containing an AUTHORIZATION header, you cannot use "*" to allow all hosts, but you need to specify the hosts. Also, you need to call AllowCredentials to accept requests with credentials:

services.AddCors(setup =>
{
  setup.AddDefaultPolicy(builder =>
  {
    builder.WithOrigins("http://localhost:4200")
      .AllowAnyHeader()
      .AllowAnyMethod()
      .AllowCredentials();
  });
});

Usually, the API is hosted under the same origin as the SPA in production, so these settings can be omitted in environments other than dev. Also, you might want to add a configuration setting instead of hard-coding http://localhost:4200.

Sign up to request clarification or add additional context in comments.

9 Comments

Do I still need the UseCors() call here, or is that unnecessary when using your code?
Yes, you still need UseCors.
Without a policy name (so just app.UseCors())?
Yes, just app.UseCors();. The sample sets the default policy, so I don't think you have to specify a policy. At least it works like this in our app.
Thanks! I just tried that but unfortunately I am still getting a 400 Bad Request error with my example GET call. If I would get more details about the error I would at least have a starting point for debugging the problem, but right now I am just clueless.
|
3

I had the same problem. The problem is with the SPA proxy not passing www-authenticate header.

I solved with this solution: https://stackoverflow.com/a/64863390/12094844

1 Comment

This is a key part of making windows auth work with Angular. Yet I have not found it anywhere except here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.