How to resolve CORS error in REACT with ASP.NET Core
Asked Answered
B

10

13

I have an ASP.NET Core Web API and a separate React app. The Web API uses Windows Authentication. When deployed to the server, I don't have any issues, but when I try to run the app locally, I get CORS errors and only on POST actions. Here's what I have in my Startup.cs:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Server.DAL;
using Server.Data;

namespace Server
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<AppDbContext>(options => options
               .UseSqlServer(Configuration.GetConnectionString("DatabaseConnection"))
               .UseLazyLoadingProxies());

            services.AddScoped<IUnitOfWork, UnitOfWork>();

            services.AddControllers();

            services.AddCors(options => options.AddPolicy("CorsPolicy",
                builder =>
                {
                    builder
                    .WithOrigins("http://localhost:3000")                        
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials();
                }));    
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseSwagger();

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseCors("CorsPolicy");

            app.UseAuthorization();

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

And, here's my axios configuration in the React app:

import axios, { AxiosInstance } from "axios";

let axiosInstance: AxiosInstance;

const axiosBaseConfig = {
  baseURL: process.env.REACT_APP_BASE_URL,
  timeout: 1000 * 20,
  withCredentials: true,
  headers: {
    Accept: "applicaiton/json",
    "Content-Type": "application/json",
  },
};

export const getAxios = () => {
  if (axiosInstance) {
    return axiosInstance;
  }

  axiosInstance = axios.create(axiosBaseConfig);

  return axiosInstance;
};

Is there anything I'm missing or doing wrong here?

UPDATE:

Here's the CORS error I'm gettings:

Access to XMLHttpRequest at 'https://localhost:44376/api/reservation' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

Bewitch answered 15/9, 2021 at 16:59 Comment(9)
Try to move services.AddCors to the top and remove .AllowCredentials();Klarrisa
@Klarrisa I need AllowCredentials, because it's Windows Authentication.Bewitch
Just try without this for testingKlarrisa
Could you add the "CORS errors" to your question?Lanny
@Klarrisa I did that, and it didn't work.Bewitch
@Lanny I added the error to the question.Bewitch
Could you try this answer? https://mcmap.net/q/904448/-39-access-control-allow-credentials-39-header-in-the-response-is-39-39-when-trying-to-send-a-post-request-to-an-api-using-axios or maybe: .WithHeaders(HeaderNames.AccessControlAllowCredentials, "true") instead of .AllowCredentials()?Lanny
@Lanny That didn't work.Bewitch
For local development, just launch your browser with CORS turned off! alfilatov.com/posts/run-chrome-without-corsRegenerator
B
2

Fei Han and Jonathan Alfaro put me in the right direction and I finally found the solution. As Fei Han rightly points out in his answer, the CORS preflight requests are always anonymous and they use an HTTP OPTIONS method. So, here's what I did to resolve the issue.

  1. Enable anonymous authentication in launchSettings.json:
  "iisSettings": {
    "windowsAuthentication": true,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:62682",
      "sslPort": 44376
    }
  },
  1. Create an Authenitcation Middleware like below, that returns 401 if the http request has any method other than OPTIONS and the user is not authenticated.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace Server
{
    public class AuthenticationMiddleware
    {
        private readonly RequestDelegate _next;

        public AuthenticationMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            BeginInvoke(context);
            await _next.Invoke(context);
        }

        private async void BeginInvoke(HttpContext context)
        {
            if (context.Request.Method != "OPTIONS" && !context.User.Identity.IsAuthenticated)
            {
                context.Response.StatusCode = 401;
                await context.Response.WriteAsync("Not Authenticated");
            }
        }
    }
}
  1. Use the middleware in Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
 
    app.UseSwagger();

    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseMiddleware<AuthenticationMiddleware>();

    app.UseCors("CorsPolicy");

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}
Bewitch answered 26/10, 2021 at 16:43 Comment(0)
J
14

Try this:-

In your ConfigureServices

services.AddCors();

And your configure method app.UseCors(x => x.AllowAnyHeader().AllowAnyMethod().WithOrigins("http://localhost:3000"));

Hope it will resolve your issue.

Jazzman answered 16/9, 2021 at 9:47 Comment(6)
That did not work.Bewitch
@Bewitch It's will help you:- #57009871Jazzman
It worked for me when I moved AddCors() and app.UseCors(x => x.AllowAnyHeader().AllowAnyMethod().WithOrigins("localhost:3000")); to the top of their respective methods and made sure there was no trailing slash on localhost:3000.Howler
that did the trick for meJarmon
this worked for me! I added builder.Services.AddCors() in my program.cs file and then the rest.Cheesewood
This helped me with my issue. I am using asp.net core 7 with reactjs. I had to add this in my program.cs file and change the withorigins port # to that of what reactjs was running on (node). The error was i getting was with my Fetch. ThanksAnimation
B
9

if you have a create-react-app project the way for solving this is by adding a property proxy to your package.json file with the url of the backend

{
...
   "proxy": "https://localhost:44376"
...
}

now yow fetches url must be relative

fetch("/api/users")

instead of

fetch("https://localhost:44376/api/users")
Bodice answered 24/9, 2021 at 15:0 Comment(0)
B
2

Access to XMLHttpRequest at 'https://localhost:44376/api/reservation' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

Please note that a CORS preflight request (using the HTTP OPTIONS method) is used to check whether the CORS protocol is understood and a server is aware using specific methods and headers.

And the HTTP OPTIONS requests are always anonymous, if you enabled Windows Authentication and disabled anonymous access on local project, which would cause web server not correctly respond to the preflight request.

For local testing purpose, you can do windows authentication test, and then separately do CORS test by allowing anonymous access.

The Web API uses Windows Authentication. When deployed to the server, I don't have any issues, but when I try to run the app locally, I get CORS errors

If the IIS CORS module has been installed and CORS policy is configured for the app/site on your server, or both Windows Authentication and Anonymous Authentication are enabled for your site, which might be the cause of the app can work well on server but not on local IIS express.

Babbette answered 1/10, 2021 at 4:41 Comment(1)
Thank you! This helped me find the answer.Bewitch
B
2

Fei Han and Jonathan Alfaro put me in the right direction and I finally found the solution. As Fei Han rightly points out in his answer, the CORS preflight requests are always anonymous and they use an HTTP OPTIONS method. So, here's what I did to resolve the issue.

  1. Enable anonymous authentication in launchSettings.json:
  "iisSettings": {
    "windowsAuthentication": true,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:62682",
      "sslPort": 44376
    }
  },
  1. Create an Authenitcation Middleware like below, that returns 401 if the http request has any method other than OPTIONS and the user is not authenticated.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace Server
{
    public class AuthenticationMiddleware
    {
        private readonly RequestDelegate _next;

        public AuthenticationMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            BeginInvoke(context);
            await _next.Invoke(context);
        }

        private async void BeginInvoke(HttpContext context)
        {
            if (context.Request.Method != "OPTIONS" && !context.User.Identity.IsAuthenticated)
            {
                context.Response.StatusCode = 401;
                await context.Response.WriteAsync("Not Authenticated");
            }
        }
    }
}
  1. Use the middleware in Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
 
    app.UseSwagger();

    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseMiddleware<AuthenticationMiddleware>();

    app.UseCors("CorsPolicy");

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}
Bewitch answered 26/10, 2021 at 16:43 Comment(0)
G
1

I would make two changes just for development and running on the local machine:

  1. move the cors call before adding the controllers

  2. use any origin like this

            public void ConfigureServices(IServiceCollection services)
            {
                services.AddDbContext<AppDbContext>(options => options
                   .UseSqlServer(Configuration.GetConnectionString("DatabaseConnection"))
                   .UseLazyLoadingProxies());
    
                services.AddScoped<IUnitOfWork, UnitOfWork>();
    
                services.AddCors(options => options.AddPolicy("CorsPolicy",
                    builder =>
                    {
                        builder
                        .AllowAnyOrigin()                        
                        .AllowAnyMethod()
                        .AllowAnyHeader()
                        .AllowCredentials();
                    })); 
    
                services.AddControllers();                       
            }
    

Also if you are using IIS you might want to take a look at this post since it could be a preflight issue:

Asp.net core web api using windows authentication - Cors request unauthorised

You might need to add this to your launch settings allowing both window and anonymous for preflights:

{
  "iisSettings": {
    "windowsAuthentication": true,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:3000",
      "sslPort": 0
    }
  },
 {... more settings if any}
}
Goya answered 15/9, 2021 at 17:21 Comment(4)
Thank you! This helped me find the answer.Bewitch
@Bewitch if you feel this helped you can you please mark as the answer. Also let me know if I need to add anything to this answer to make it more complete.Goya
Please look at my answer.Bewitch
@Bewitch got it.Goya
M
1

You can try to add like the below:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
 ...
 app.UseCors(cors => cors.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
 app.UseAuthorization();
 ...
}

public void ConfigureServices(IServiceCollection services) {
 ...
 services.AddCors(c => { c.AddPolicy("AllowOrigin", options => options.AllowAnyOrigin()); });
 services.AddControllers();
 ...
}
Mcburney answered 18/9, 2021 at 17:14 Comment(3)
Isn’t this what I’m doing already?Bewitch
@Bewitch This is the code on real production. If it's still not working, you should re-clone the project and rebuild it in another directory. And you can check step by step: - Check backend: call local API via Postman. - Check frontend: change API URL on frontend to production API and run it. Hope it helps you.Mcburney
As I've mentioned in my post, it works in production. It doesn't work in development.Bewitch
C
0

Your code looks good at first sight, similar to some code of mine, but I don't like this setup, because these are unrelated domains and this is asking for browser problems:

  • Web URL: http://localhost:3000
  • API URL: https://localhost:44376

VERIFY API HEADERS

First verify that the API is behaving correctly with a simple curl request:

curl -i -X OPTIONS "https://localhost:44376/api/reservation" \
-H "origin: http://localhost:3000"

For browser testing I tend to use an incognito window to prevent issues where old CORS headers have been cached.

MOVE TO A SAME SITE SETUP

I would think that CORS will work, but I know you will get dropped cookies later on POST requests, since the API cookie will be considered third party. Try changing to this setup instead, where both browser and API are in the same site of mycompany.com. Note that you should either use http or https for both, though different ports is fine:

The domains can easily be registered in your hosts file by adding entries like this, which points them to localhost:

127.0.0.1 localhost www.mycompany.com api.mycompany.com
:1        localhost

You will then get a better developer experience in the browser with cookies. And you can see if it makes a difference to CORS behaviour.

curl -i -X OPTIONS "http://api.mycompany.com:44376/api/reservation" \
-H "origin: http://www.mycompany.com:3000"

SUMMARY

I am not entirely sure if this will resolve your CORS problem, but hopefully it gives you some pointers that will help later. Out of interest this is also the type of setup we recommend at Curity, so that cookies work deterministically - as for this CORS related solution of ours:

Cambist answered 23/9, 2021 at 22:11 Comment(2)
The setup is out of my control. This is an internal app that will be deployed to a server inside the companies internal network.Bewitch
Sure - but you are highly likely to need a domain relationship between the React app and an API it is sending cookies to. Maybe spend a brief time investigating the edits to your local PC's hosts file and see if it has any impact on your problem. This is a very quick local change so will cost you very little time. See also this similar answer of mine to someone struggling with cross domain browser problems.Cambist
W
0

Within .net Program.cs

First Declare a "Deactivate CORS" policy

// Add CORS services
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowAll", b =>
    {
        b.AllowAnyOrigin()
            .AllowAnyMethod()
            .AllowAnyHeader();
    });
});

// ...

var app = builder.Build();

// Then consume from App
app.UseCors("AllowAll");

This mitigates any CORS related errors and allows for a .net SPA to run (where there is more than one effective port)

Whoso answered 24/7, 2024 at 20:6 Comment(0)
S
-1

Try adding [EnableCors("NAME_OF_YOUR_POLICY")] on every endpoints that require CORS.

Example:

[EnableCors("CorsPolicy")]
[Route("api/v1/test)]
public class TestApiController 
{
  ...
}

Source:

Stonefly answered 19/9, 2021 at 10:42 Comment(1)
That doesn't work.Bewitch
Q
-2

It's very easy, this thing worked for me. Just install allow CORS browser extension for your respective browser. And turn it on while making API requests. You will not get any errors, also you don't need to change anything in your code!

Quid answered 18/9, 2021 at 17:45 Comment(1)
The extensions often don't work.Regenerator

© 2022 - 2025 — McMap. All rights reserved.