1

The Client application makes double queries to a single resource on the server. The first frame has no authorization header and the second frame does. Unfortunately, after reading the first frame, the server does not get the second frame. How to handle it on the ASP.NET CORE 5 server?

Endpoint for testing. value always = {} when i call from client, from postman everything is working

        [ApiExplorerSettings(IgnoreApi = true)]
        [HttpPost("Service")]
        public IActionResult GetHeader()
        {
            var value = HttpContext.Request.Headers["Authorization"];
            return Ok();
        }
        app.UseMiddleware<SerilogMiddleware>();
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapHub<NotificationHub>("/api/socket");
            endpoints.UseSoapEndpoint<SVPService.SVPServiceSoap>((options) =>
            {
                options.Path = "/Service.asmx";
                options.Binding = new BasicHttpBinding()
                {
                    TextEncoding = new UTF8Encoding(false),
                    Security = new BasicHttpSecurity()
                    {
                        Mode = BasicHttpSecurityMode.TransportCredentialOnly,
                        Transport = new HttpTransportSecurity() { ClientCredentialType = HttpClientCredentialType.Basic }
                    }
                };
                options.SoapSerializer = SoapSerializer.XmlSerializer;
            }).RequireAuthorization();
        });
        app.UseMvc();

Logged Request from client on node.js server to get headers.

First Request Headers
{
  'user-agent': 'Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 4.0.30319.42000)',
  'content-type': 'text/xml; charset=utf-8',
  'content-length': '806',
  expect: '100-continue',
  connection: 'Keep-Alive'
}
Second Request Headers
{
  'user-agent': 'Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 4.0.30319.42000)',
  'content-type': 'text/xml; charset=utf-8',
  authorization: 'Basic dGVzdG93ZV91c2VybmFtZTp0ZXN0b3dlX3Bhc3N3b3Jk',
  'content-length': '806',
  expect: '100-continue'
}

Its a my startup.cs file

public void ConfigureServices(IServiceCollection services)
        {
            services.AddCors(options => options.AddPolicy("CorsPolicy", builder =>
            {
                builder
                    //.AllowAnyOrigin()
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials().SetIsOriginAllowed(hostName => true);
                
            }));
            
            services.AddQuartz();

            services.Configure<JwtAuthentication>(Configuration.GetSection("JwtAuthentication"));
            services.AddAuthentication("BasicAuthentication")
                .AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);


            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo
                {
                    Version = "xxx",
                    Title = "xxx",
                    Description = "xxx",
                    Contact = new OpenApiContact
                    {
                        Name = "xxx",
                        Email = "xxx",
                        Url = new Uri("xxx"),
                    },
                });

                // Set the comments path for the Swagger JSON and UI.
                string xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                string xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                c.IncludeXmlComments(xmlPath);
            });

            MapperConfiguration mappingConfig = new MapperConfiguration(mc =>
            {
                mc.AddProfile(new MappingProfile());
            });

            IMapper mapper = mappingConfig.CreateMapper();
            services.AddSingleton(mapper);

            services.AddSignalR().AddNewtonsoftJsonProtocol();
            services.AddSingleton<ITokenService, TokenService>();
            services.AddSingleton<IPasswordService, PasswordService>();
            services.AddSingleton<IUserProfile, UserProfile>();
            services.AddSingleton<IReceiptService, ReceiptService>();
            services.AddSingleton<ISend, Send>();
            services.AddSingleton<IEncryption, Encryption>();
            services.AddSingleton<ParkingTicketManagementServiceV3, TicketManagement>();
            services.AddScoped<SVPService.SVPServiceSoap, SVPServiceSoap>();
            services.AddScoped<IManageSVP, ManageSVP>();
            services.AddScoped<IStripeMethods, StripeMethods>();
            services.AddScoped<IManageSchedullerRecurringPayment, ManageSchedullerRecurringPayment>();
            services.AddRepository();
            services.AddSingleton<IAuthorizationHandler, DenyAnonymousAuthorizationRequirement>();

            services.AddMvc(options =>
            {
                options.InputFormatters.Insert(0, new RawJsonBodyInputFormatter());
                options.EnableEndpointRouting = false;

            })
            .SetCompatibilityVersion(CompatibilityVersion.Latest)
            .AddNewtonsoftJson(opt =>
            {
                opt.SerializerSettings.ContractResolver = new DefaultContractResolver() { NamingStrategy = new LowerCaseNamingStrategy() };
                opt.SerializerSettings.StringEscapeHandling = Newtonsoft.Json.StringEscapeHandling.Default;
                opt.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
                opt.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
                opt.SerializerSettings.MaxDepth = null;
                opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
            });
            services.AddSwaggerGenNewtonsoftSupport();

            services.AddControllers();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseFileServer(new FileServerOptions
            {
                FileProvider = new PhysicalFileProvider(
                    Path.Combine(Directory.GetCurrentDirectory(), "StaticFile")),
                RequestPath = "/staticfile"
            });

            app.UseCors("CorsPolicy");
            app.UseHttpsRedirection();
            app.UseSwagger();

            app.UseReDoc(c =>
            {
                c.SpecUrl = "xxx";
                c.DocumentTitle = "xxx";
            });

            app.UseMiddleware<SerilogMiddleware>();
            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                endpoints.MapHub<NotificationHub>("/api/socket");
                endpoints.UseSoapEndpoint<SVPService.SVPServiceSoap>((options) =>
                {
                    options.Path = "/Service.asmx";
                    options.Binding = new BasicHttpBinding()
                    {
                        TextEncoding = new UTF8Encoding(false),
                        Security = new BasicHttpSecurity()
                        {
                            Mode = BasicHttpSecurityMode.TransportCredentialOnly,
                            Transport = new HttpTransportSecurity() { ClientCredentialType = HttpClientCredentialType.Basic }
                        }
                    };
                    options.SoapSerializer = SoapSerializer.XmlSerializer;
                }).RequireAuthorization();
            });
            app.UseMvc();
        }
    }
  • Maybe I am wrong, but I would assume that we need to see some code to figure out why two requests are made. Maybe some frontend client code, endpoint code or similar? If you have an idea of what unit of code could cause it – thesystem Oct 28 '21 at 07:06
  • @thesystem I do not have a preview of the code source in the client application. So I can't send anything like that. I assume that the situation is similar to that described in this article "https://stackoverflow.com/questions/6338942/why-my-http-client-making-2-requests-when-i-specify-credentials", but no change code in the client application I am looking for a solution that I can use on the server side. – Franczyk Rafał Oct 28 '21 at 07:36
  • Ah okay, now I understand better. I thought you also had access to frontend/client code. I am not sure I can help right away (have never been in that situation), but now the question/problem is more clear. Interesting problem nonetheless, hope someone will come by and help – thesystem Oct 28 '21 at 07:52
  • 1
    Can you [edit] your question to include the code as text instead of image ? StackOverflow use `markdown` for beautiful code area :) – Elikill58 Oct 28 '21 at 09:10
  • I am slightly baffled, why the second request doesn't hit the endpoint. No matter how many requests a client sends, it should hit the endpoint. That could hint at a backend problem. Can you show a snippet of, how the endpoint, that the client tries to connect with, looks? And is the first request (no authorization) processed successfully? That's probably the first problem to solve - next is to find out, how to handle that the client sends two requests and how to ignore one of them, I guess. – thesystem Oct 28 '21 at 09:32
  • @thesystem It looks like this, because the created server in node.js registers both requests. But I wonder what can be a problem on the site created in ASP.NET Core. I added a simple middleware to register incoming requests so it should catch everything but unfortunately it only logs the first request. – Franczyk Rafał Oct 28 '21 at 09:40
  • Looks like first request is failing authentication so client sends a second request with the authorization header. Does the URL the client is attempting to hit require authorization? – phuzi Oct 28 '21 at 09:42
  • You should try to put logging in the endpoint as well, so that you can see, if the reuqests are processed. Would be cool to see the endpoint also. – thesystem Oct 28 '21 at 09:45
  • @phuzi In the current configuration, as you can see above, I added a code snippet where in app.UseEndpoints I added for UseSoapEndpoint requests ".RequireAuthorization ();" to run middleware to check if there is an authorization header in the request. – Franczyk Rafał Oct 28 '21 at 09:54
  • 1
    @thesystem i pasted endpoint code – Franczyk Rafał Oct 28 '21 at 09:58
  • @FranczykRafał Can it be a problem, that the first request contains `connection; keep-alive`? I am not really familiar with that header, but from Wikipedia, among others: `"If the client does not close the connection when all of the data it needs has been received, the resources needed to keep the connection open on the server will be unavailable for other clients. How much this affects the server's availability and how long the resources are unavailable depend on the server's architecture/config."` But if its from same client, then thats probably not issue. Can also cause race condition – thesystem Oct 28 '21 at 10:51
  • In your `.Configure()` you enabled authentication and authorization. Your first request contains no authorization header. leading to be answered with a 401 from the authentication middleware before your action comes into play. – Oliver Oct 28 '21 at 11:59
  • @Oliver if i remove the authentication middleware i still have the same problem. Only the first request reaches the server without an authorization header. – Franczyk Rafał Oct 28 '21 at 13:04
  • You are not logging in the endpoint. How can you be sure, that second request does not hit endpoint? – thesystem Oct 28 '21 at 15:41

2 Answers2

1

Just check if the response has the correct headers

JHBonarius
  • 10,824
  • 3
  • 22
  • 41
0

Yes, To answer my question the header was actually missing the WWW-Authenticate: Basic realm = header.