1

I've found many threads about this issue, but not matter what I change it just doesn't work. I am desperate as I don't understand what is wrong.

My application works fine if run on IIS Express.

As soon as I deploy it to the IIS, it starts to complain about the "query string is too long"

What I don't understand is why it is calling Account\Login - my application has no controllers or any views. It is ASP.NET Core React template where we don't use controllers but our communication is fully through SignalR. We don't have even API controllers.

I tried to set web.config as is suggested on many places (for example here but it's suggested in many other threads) - it doesn't help.

I have identified that the issue in the code is setup of authorization in Program.cs/Startup .cs file. However, it is done according to Microsoft guide.

In my Program.cs I have this to configure authentication:

builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
builder.Services.AddAuthorization(options => options.FallbackPolicy = options.DefaultPolicy);

If I run application via the IIS Express, it loads fine, authenticates user and everything seems to be working just fine. As soon as I deploy it to the IIS server, I receive this error: enter image description here

Note: I have noticed that even if run on IIS Express, it still sends requests to /Account/Login but it doesn't crash the app for some reason.

What I really don't understand is, why is it calling Account/Login path, it just doesn't exist and I don't have it set anywhere in the code. And as mentioned, my application have no controllers or views. It's react application communicating with server via SignalR (but it doesn't get that far, so SignalR is not involved yet).

If I change the code above to:

builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
builder.Services.AddAuthorization(options => options.FallbackPolicy = null);

then it won't crash, but it won't log in windows user either.

I tried many things in last two days but can't figure out, what is wrong.

I appreciate any ideas of what could be wrong.

Edit 1 - web.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
    <security>
        <requestFiltering removeServerHeader="true">
            <requestLimits maxUrl="4096" maxQueryString="5000"></requestLimits>
        </requestFiltering>
    </security>
<httpProtocol>
    <customHeaders>
        <remove name="X-Powered-By" />
        <add name="X-Frame-Options" value="DENY" />
    </customHeaders>
</httpProtocol>
<handlers>
    <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="bin\x86\Debug\net6.0-windows\PPSim.Web.exe" arguments="" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" startupTimeLimit="3600" requestTimeout="23:00:00" hostingModel="inprocess">
  <environmentVariables>
    <environmentVariable name="ASPNETCORE_HTTPS_PORT" value="443" />
    <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" />
    </environmentVariables>
</aspNetCore>
</system.webServer>
<system.web>
    <httpRuntime enableVersionHeader="false" maxQueryStringLength="32768" maxUrlLength="65536" />
</system.web>
</configuration>

Edit 2 - Program.cs:

var builder = WebApplication.CreateBuilder(args);

#region Services

builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
builder.Services.AddAuthorization(options => options.FallbackPolicy = options.DefaultPolicy);

builder.Services.AddAntiforgery(options =>
{
    options.HeaderName = CsrfConstants.CsrfHeaderName;
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    options.Cookie.SameSite = SameSiteMode.Strict;
});

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

var settings = new JsonSerializerSettings { ContractResolver = new SignalRContractResolver() };
var serializer = JsonSerializer.Create(settings);
builder.Services.AddSingleton(serializer);

builder.Services.AddHsts(options =>
{
    options.Preload = true;
    options.IncludeSubDomains = true;
    options.MaxAge = TimeSpan.FromDays(60);
});

builder.Services.AddSingleton<IUserSettingsWrapper, UserSettingsWrapper>();
builder.Services.AddSingleton<ISignalREventsPusher, SignalRDataPusher>();


builder.Services.AddSignalR().AddNewtonsoftJsonProtocol(options =>
{
    options.PayloadSerializerSettings.ContractResolver = new CustomCamelCasePropertyNamesContractResolver();
    options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
    options.PayloadSerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
});

builder.Services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
builder.Services.AddLogging(loggingBuilder =>
{            loggingBuilder.AddConfiguration(builder.Configuration.GetSection("Logging"));
    loggingBuilder.AddConsole();
    loggingBuilder.AddDebug();
});

builder.Services.AddCors(options =>
    {
        options.AddPolicy("CORSPermission", policy =>
        {
            policy.AllowAnyHeader()
                .AllowAnyMethod()
                .SetIsOriginAllowed(host => true)
                .AllowCredentials();
        });
    });

#endregion

var app = builder.Build();

#region Configure

var antiforgery = app.Services.GetRequiredService<IAntiforgery>();

app.Use((context, next) =>
{
    var requestPath = context.Request.Path.Value;
    
    if (string.Equals(requestPath, "/", StringComparison.OrdinalIgnoreCase)
        || string.Equals(requestPath, "/index.html", StringComparison.OrdinalIgnoreCase))
    {
        var tokenSet = antiforgery.GetAndStoreTokens(context);
        context.Response.Cookies.Append(CsrfConstants.CsrfCookiesName, tokenSet.RequestToken!,
            new CookieOptions
            {
                HttpOnly = false,
                Secure = true, // 15018: Cookie generated by application does not contain the “Secure flag” attribute
                SameSite = SameSiteMode.Strict
            });
    }

    return next(context);
});

if (!app.Environment.IsDevelopment())
{
}
else
{
    app.UseHsts();
    //app.UseExceptionHandler("/Home/Error");
}

app.UseStaticFiles();

app.UseHttpsRedirection();
app.UseRouting();
app.UseCors("CORSPermission")

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

#endregion

app.MapFallbackToFile("index.html");

app.UseEndpoints(endpoints =>
{
    endpoints.MapHub<SimulatorHub>("/SimulatorHub",
        options => options.Transports = GetTransportToUse(app.Environment)).RequireCors("CORSPermission");
});

app.Run();
Ademar
  • 1,418
  • 1
  • 17
  • 27
  • "I tried to set web.config as is suggested" needs to be proved and FRT can give you more hints, https://learn.microsoft.com/en-us/iis/troubleshoot/using-failed-request-tracing/troubleshoot-with-failed-request-tracing – Lex Li Mar 03 '23 at 22:53
  • This error means the request filtering module is configured to deny a request where the query string is too long, you can try to enable the requestFiltering feature of iis and restart iis server. – samwu Mar 06 '23 at 03:30
  • @LexLi Thanks for the hint. I have added web.config example with settings I expected to work. I also enabled tracing but it didn't give me any new info. I just see there that request failed because 404.15 and the url. – Ademar Mar 06 '23 at 06:15
  • @samwu Added web.config example. I thought request filtering is enabled there with these settings (and I see Request filtering option in the site menu). You made me think, is it still disabled? However, it more looks like that query is composited from infinite loop. – Ademar Mar 06 '23 at 06:16
  • @Ademar You indeed seem to have a loop. The requested url gets redirected to the login page, which seems not to allow an anonymous request/user and thereby gets redirected again the login page ...You have set up url/page authorization whereas you need it only for the signalR part. Maybe [this](https://stackoverflow.com/questions/43047265/how-to-use-windows-authentication-on-the-signalr-javascript-client) helps. – pfx Mar 06 '23 at 20:47
  • @pfx Thank you for the link. I'll go through but so far it doesn't look like it can help. Setup in the NET6 is different already. Anyway, point with url/page for authorization is that I don't have any. I really have just Hub (I even tested to not mark it with Authorize attribute). Only thing related to authentication/authorization I currently have are two lines from question (and useAuthentication(), useAuthorization()). That redirect has to be done by it. Concretely DefaultFallbackPolicy - without it, it doesn't do it (do not work, but do not redirect either) – Ademar Mar 07 '23 at 06:56
  • Thing is that those don't introduce an `Account/Login` url. That could for example be from [ASP.NET Identity](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity). Can you share your full `program.cs/startup.cs` with these `Add...` and `Use...` statements? – pfx Mar 07 '23 at 07:03
  • @pfx Program.cs added. – Ademar Mar 07 '23 at 08:49
  • @Ademar It is the `AddIdentity`. That one provides out-of-the-box account related pages, like login, ect. See that link from my previous comment. – pfx Mar 07 '23 at 08:53
  • 1
    @pfx You're amazing! That was it. Reason of this error was just simply that before I upgraded to NET6, we supported also LDAP authentication so we had controller and view with Account/Login which was returning the view but when Windows authentication was used, it just redirected out of it. Now we decided to support Windows authentication only so I removed controller/view. And since I didn't know AddIdentity does this magic automatically, problem was here - in my case it was just to remove that part of the code out and all works fine. Please, post the answer about it. You deserve the bounty. – Ademar Mar 07 '23 at 09:32

1 Answers1

1

As a follow-up on our insights from the comments section in your question.

There's indeed an infinite redirect going on to the account/login url.

Since you don't have such a controller and/or page, that one is provided out-of-the-box by ASP.NET Core Identity.

From that page.

ASP.NET Core Identity adds user interface (UI) login functionality to ASP.NET Core web apps.

The generated project provides ASP.NET Core Identity as a Razor Class Library.
The Identity Razor Class Library exposes endpoints with the Identity area. For example:

  • /Identity/Account/Login

That does get activated by below call you have in program.cs.

builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

To resolve the issue, that line is to be removed since you'll be using Windows authentication.

pfx
  • 20,323
  • 43
  • 37
  • 57