aRasH
aRasH

Reputation: 144

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

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.

Upvotes: 3

Views: 13520

Answers (2)

Francisco
Francisco

Reputation: 179

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();
    }
}

Upvotes: 2

aRasH
aRasH

Reputation: 144

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!

Upvotes: 1

Related Questions