Why CORS error "Response to preflight request doesn't pass access control check"?

10,685

Solution 1

In case anyone wondered, this is how I solved this problem:

It was not about not setting the cors policy in asp.net web api (as I have mentioned that I did what Microsoft docs website was suggesting.)

The problem was that the IIS was not configured to handle OPTION verb methods! and because the pre-flight request was using OPTION method, It was always getting 404 not found (rejection from the web server), hence the error.

I should mention that to this day I still do not know why "/token" worked before the configuration of the web server and why controllers did not have the same reaction!

but anyhow, the problem just solved like that. Hope that helps!

Solution 2

Maybe a bit old but worth to mention that you need to add the cors headers in global.asax

protected void Application_BeginRequest(object sender, EventArgs e)
{
    var context = System.Web.HttpContext.Current;
    var origins = System.Configuration.ConfigurationManager.AppSettings["AllowedCorsDomain"]?.TrimEnd('/');
    context.Response.AddHeader("Access-Control-Allow-Origin", origins);

    if (context.Request.HttpMethod == "OPTIONS")
    {
        //These headers are handling the "pre-flight" OPTIONS call sent by the browser
        context.Response.AddHeader("Access-Control-Allow-credentials", "true");
        context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, POST, DELETE, OPTIONS");
        context.Response.AddHeader("Access-Control-Allow-Headers", "x-requested-with, Content-Type, origin, authorization, Accepts, accept, client-security-token, access-control-allow-headers");
        context.Response.AddHeader("Access-Control-Max-Age", "86400");
        context.Response.End();
    }
}
Share:
10,685
aRasH
Author by

aRasH

I am an experienced software developer. I’ve been developing software for almost 10 years. I have experience in managing software projects from the ground up and I’m really hoping to expand my network and be a part of awesome development teams.

Updated on June 04, 2022

Comments

  • aRasH
    aRasH almost 2 years

    I'm using ASP.NET Web API with OAuth authorization in my project.

    I have tried to decouple every tier in the solution using best practices. I have a web project which contains AngularJS files and other resources which is uploaded on www.example.com and I have another project which is protected backend web api controllers and server side stuff which is uploaded on api.example.com

    It all works fine in localhost. when I publish this to production server request for "/token" is successful but requesting any action in any controller in the back-end api returns this error: "Access to XMLHttpRequest at 'http://api.example.com/someRoute' from origin 'http://www.example.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.".

    I searched almost any active link for similar error on the internet and no answers yet for me!

    I paste some of my code here from the back-end API so you can understand my approach better.

    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
    
            HttpConfiguration httpConfig = new HttpConfiguration();
    
            UnityConfig.Register(httpConfig);
    
            ConfigureAuth(app);
    
            WebApiConfig.Register(httpConfig);
    
            app.UseWebApi(httpConfig);
    
            #region AutoMapper Init
            DtoMapping.Map();
            #endregion
        }
    }
    
    
    public void ConfigureAuth(IAppBuilder app)
        {
            // Configure the auth context, user manager and signin manager to use a single instance per request
            app.CreatePerOwinContext(AuthContext.Create);
            app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
            app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
    
            OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
            {
                //remove this line on production
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
                Provider = new SimpleAuthorizationServerProvider()
            };
    
            // Token Generation
            app.UseOAuthBearerTokens(OAuthServerOptions);
            app.UseOAuthAuthorizationServer(OAuthServerOptions);
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
        }
    
    public class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            EnableCorsAttribute cors = new EnableCorsAttribute("*", "*", "*");
            config.EnableCors(cors);
    
            config.MapHttpAttributeRoutes();
    
            //...
        }
    }
    
    [EnableCors("*", "*", "*")]
    public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
    {
        public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            context.Validated();
            return Task.FromResult<object>(null);
        }
    
        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            var identity = new ClaimsIdentity(context.Options.AuthenticationType);
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
    
            IList<string> roleNames;
            using (var _repo = new IdentityRepository())
            {
                var user = await _repo.FindUserAsync(context.UserName, context.Password);
                if (user == null)
                {
                    context.SetError("invalid_grant", "username or password is invalid.");
                    context.Rejected();
                    return;
                }
    
                roleNames = await _repo.GetRolesForUserAsync(user.Id);
            }
    
            identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
            foreach (var roleName in roleNames)
            {
                identity.AddClaim(new Claim(ClaimTypes.Role, roleName));
            }
    
            var props = new AuthenticationProperties(new Dictionary<string, string>
                {
                    {
                        "userName", context.UserName
                    }
                });
    
            var ticket = new AuthenticationTicket(identity, props);
            context.Validated(ticket);
    
        }
    }
    

    So, Can anyone please help me?

    Thank you for your time.

    • SNO
      SNO over 5 years
      I think it doesn't work for you because you set "Cors" in WebApiConfig globally and additional as attribute in SimpleAuthorizationServerProvider. Try to remove the attribute in SimpleAuthorizationServerProvider and try again. As far as I can imagine I had the same issue and this worked for me.
    • SNO
      SNO over 5 years
      Additionaly I recommend to set CORS only for DEBUG (/Development) and not for release. It's a security mechanism that shouldn't be always disabled. Using the subdomain you should set cors at least for the subdomain and not as wildcard.
    • aRasH
      aRasH over 5 years
      Thanks for your answer, but removing the attribute does not solve the problem. I think the only reason that "/token" works for generating access token is this attribute available on the authorization provider class. If I remove it, POST request to "/token" returns the cors error same as requesting any other route so the login process won't work. I even tried to put this attribute on web api controllers to see if it works, despite using the global cors setting, but the result was still the same.
    • aRasH
      aRasH over 5 years
      For security reason that you have mentioned , I will definitely remove the star and restrict cors to some domains in production but now I just need to see if it works for any setting.
  • Bel
    Bel over 2 years
    Thank you. CORS has always been fiddly as hell. Since I only need it for DEV, I wrapped this in a #if DEBUG and got on with my work. One change I had to make for Windows Authentication was to move Access-Control-Allow-credentials outside the conditional.