0
votes

I am trying to send data from React App to .NET Core Web App using SignalR. The .NET Core Web App has Windows Authentication enabled. With Windows Authentication enabled, I am getting the CORS error when my React App tries to send message to .NET Core App via SignalR. It works fine if I disable windows authentication and enable anonymous authentication.

Can you please help me with your valuable inputs to make the connection work?

React JS app code looks like below:

const hubConnection = new HubConnectionBuilder()
      .withUrl(window.config.APP_URL, options)
      .withAutomaticReconnect()
      .build();

    this.setState({ hubConnection }, () => {
      this.state.hubConnection
        .start()
        .then(() => console.log("SignalR Connection started!"))
        .catch((err) =>
          console.log("SignalR Error while establishing connection :(", err)
        );
    });
  }

  sendMessage = () => {
    console.log("sendMessage() Properties: ", this.props);

    const signalRMessage = {
      UserName: this.props.userName,
    };
    this.state.hubConnection
      .invoke("SendMessage", signalRMessage)
      .catch((err) => console.error(err));
  };

I tried to explicitly add the ‘Access-Control-Allow-Origin’ header as shown below. But still I see the CORS error.

componentDidMount() {

    let options = {
      httpClient: {
        post: (url, httpOptions) => {
    
          // httpOptions.headers = {
          //   ...httpOptions.headers,
          //   "Access-Control-Allow-Origin": window.config.CORS_ALLOW_ORIGIN_URL,
          //   //"Access-Control-Allow-Methods": "POST, GET, HEAD",
          // };
          // httpOptions.method = "POST";
          // httpOptions.url = url;
    
          // return httpOptions;

          const headers = {
            ...httpOptions.headers,
            "Access-Control-Allow-Origin": window.config.CORS_ALLOW_ORIGIN_URL,
            "Access-Control-Allow-Methods": "POST, GET, HEAD",
          };
          let newResponse = {};
    
          return axios.post(url, {}, { headers }).then(response => {
            return (newResponse = {
              statusCode: response.status,
              statusText: response.statusText,
              content: JSON.stringify(response.data)
            });
          });
    
        }  
      } 
    };

Below is the CORS error that I see in console logs at React App side: enter image description here

public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(IISDefaults.AuthenticationScheme);

            services.AddRazorPages();

            services.AddCors(options =>
            {
                options.AddPolicy("ClientPermissionPolicy", policy =>
                {
                    
                    policy.WithOrigins("https://xxxx.com")
                        .AllowAnyHeader()
                        .AllowAnyMethod().AllowCredentials();
                });
            });            

            ...
        }

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseCors("ClientPermissionPolicy");       

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapRazorPages();
                endpoints.MapHub<TapHub>("/hubs/tap");
            });

                     
        }

Went through many posts here in stackoverflow and tried below but in vain.

  • In Configure():

    app.UseCors(builder => builder.WithOrigins("https://xxxx.com").AllowAnyMethod().AllowAnyHeader().AllowCredentials());

  • In ConfigureServices():

    services.AddCors(options => { options.AddPolicy("ClientPermissionPolicy", policy => { policy.WithOrigins("https://xxxxx.com") .AllowAnyHeader() .AllowAnyMethod().AllowCredentials() .SetIsOriginAllowed((host) => true); }); });

  • Tried AllowAnyOrigin()

  • Tried removing AllowCredentials()

Like I said above, it works fine if I disable windows authentication and enable anonymous authentication. The React App successfully connects to the hub endpoint in case of anonymous authentication. The CORS error comes into picture only when I enable windows authentication. I need Windows Authentication enabled for my requirement. Requesting you to help fix the issue.

Thanks!

2

2 Answers

0
votes

You have to place UseCors between UseRouting and UseAutorization

app.UseRouting();
app.UseCors("ClientPermissionPolicy");      
app.UseAuthorization();

and maybe you can try to move AddCors to the top of ConfigureServices method

Just the test pourposes I would use this

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(o => o.AddPolicy("ClientPermissionPolicy", builder =>
            {
                builder.AllowAnyOrigin()
                       .AllowAnyMethod()
                       .AllowAnyHeader();
            }));
.....
}

only if it works, I would try to use specific origins.

0
votes

For those using .NET Core 3.1, here is a COMPLETE solution (front-end to back-end):

My problem: When I enabled the windows authentication on my web API, I could not do fetch calls from my react app to my .NET Core 3.1 web API, CORS was freaking out. With Anonymous authentication it worked, but not when windows authentication is enabled.

1.launchSettings.json

this will be used only for your dev environnment, make sure windows auth is also enabled in IIS on your prod server.

{
  "iisSettings": {
    "windowsAuthentication": true,
    "anonymousAuthentication": false,
    "iisExpress": {
      "applicationUrl": "http://localhost:58747",
      "sslPort": 0
    }
  },
 {... more settings if any}
}

2.Startup.cs:

CORS policy is enabled here. The order of methods is important here. Also, you don't need to set those in a web.config

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy("CorsPolicy", //give it the name you want
                       builder =>
                       {
                            builder.WithOrigins( "http://localhost:3000", //dev site
                                                "production web site"
                                               .AllowAnyHeader()
                                               .AllowAnyMethod()
                                               .AllowCredentials();
                       });
    });

    //database services here

    services.AddControllers();
}

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

    app.UseRouting();

    // global policy same name as in the ConfigureServices()
    app.UseCors("CorsPolicy");

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

3.Controller(s):

using Microsoft.AspNetCore.Cors;
... your other usings

namespace ProjectTest.Controllers
{
    [ApiController]
    [EnableCors("CorsPolicy")] //THIS HERE needs to be the same name as set in your startup.cs
    [Route("[controller]")]
    public class FooController:Controller
    {
        [HttpGet("getTest")]
        public JsonResult GetTest()
        {
            return Json("bar");
        }
    }
}

4.React Component fetch call example:

The "credential: 'include'" is the secret

await fetch('http://localhost:3000/Foo/getTest', {
    method: 'GET',
    credentials: 'include'
}).then(resp => resp.json());