32

I have an ASP.NET MVC 5 webproject (localhost:81) that calls functions from my WebApi 2 project (localhost:82) using Knockoutjs, to make the communication between the two projects I enable CORS. Everything works so far until I tried to implement OWIN token authentication to the WebApi.

To use the /token endpoint on the WebApi, I also need to enable CORS on the endpoint but after hours of trying and searching for solutions it is still now working and the api/token still results in:

XMLHttpRequest cannot load http://localhost:82/token. No 'Access-Control-Allow-Origin' header is present on the requested resource. 
public void Configuration(IAppBuilder app)
{
    app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
    TokenConfig.ConfigureOAuth(app);
    ...
}

TokenConfig

public static void ConfigureOAuth(IAppBuilder app)
{
    app.CreatePerOwinContext(ApplicationDbContext.Create);
    app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create);

    OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
    {
        AllowInsecureHttp = true,
        TokenEndpointPath = new PathString("/token"),
        AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
        Provider = new SimpleAuthorizationServerProvider()
    };

    app.UseOAuthAuthorizationServer(OAuthServerOptions);
    app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}

AuthorizationProvider

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
    context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

    var appUserManager = context.OwinContext.GetUserManager<AppUserManager>();
    IdentityUser user = await appUserManager.FindAsync(context.UserName, context.Password);

    if (user == null)
    {
        context.SetError("invalid_grant", "The user name or password is incorrect.");
        return;
    }
    ... claims
}

IdentityConfig

public static AppUserManager Create(IdentityFactoryOptions<AppUserManager> options, IOwinContext context)
{
    // Tried to enable it again without success. 
    //context.Response.Headers.Add("Access-Control-Allow-Origin", new[] {"*"});
    
    var manager = new AppUserManager(new UserStore<AppUser>(context.Get<ApplicationDbContect>()));
        
    ...

    var dataProtectionProvider = options.DataProtectionProvider;
    if (dataProtectionProvider != null)
    {
        manager.UserTokenProvider =
                new DataProtectorTokenProvider<AppUser>(dataProtectionProvider.Create("ASP.NET Identity"));
    }
    return manager;
}

EDIT:

1. Important note is that opening the endpoint directly (localhost:82/token) works.

2. Calling the Api (localhost:82/api/..) from the webproject also works, so the CORS is enabled for WebApi.

Zarepheth
  • 2,225
  • 2
  • 22
  • 47
Sam
  • 1,153
  • 3
  • 16
  • 38
  • have a look in to this link http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api – rashfmnb Mar 29 '16 at 13:10
  • 1
    I read that article a couple of times and it helped me to enable the CORS for Web Api, the problem is to also enable it for the token endpoint. – Sam Mar 29 '16 at 13:11
  • i was facing the same issue with the Chrome and Firefox and then i read the whole link and do the changes as directed in the document have you tried this context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); in ApplicationOAuthProvider.cs file – rashfmnb Mar 29 '16 at 13:19
  • Thanks but yes it is already in there, if you look at my sample code the AuthorizationProvider.cs (ApplicationOAuthProvider in tutorial) already contains that line of code. – Sam Mar 29 '16 at 13:24
  • and these line in WebApiConfig.cs var cors = new EnableCorsAttribute("*", "*", "*"); config.EnableCors(cors); in Register method – rashfmnb Mar 29 '16 at 13:29
  • 1
    Thanks but I read in an other answer that when using OWIN the app.UseCors(CorsOptions.AllowAll) replaces the EnableCors(): http://stackoverflow.com/a/32294445/4836952 – Sam Mar 29 '16 at 13:36
  • 4
    Could you try and place app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); after your TokenConfig.ConfigureOAuth(app); The TokenConfig does a lot of nasty things to the context – Marcus Höglund Mar 29 '16 at 14:06
  • I can't believe it.. that worked and I recieved my token. I read a couple of answers/tutorials that suggested to place the app.UseCors() at the top.. wierd. Feel free to make that an answer and you will get my vote! – Sam Mar 29 '16 at 14:23
  • In other words: Install the package Microsoft.Owin.Cors, and then put the code "app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);" in the class Startup.cs, as the first line of the method Configuration – Ricardo França Jun 22 '17 at 22:49

3 Answers3

49

I know your issue was solved inside comments, but I believe is important to understand what was causing it and how to resolve this entire class of problems.

Looking at your code I can see you are setting the Access-Control-Allow-Origin header more than once for the Token endpoint:

app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

And inside GrantResourceOwnerCredentials method:

context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); 

This, looking at the CORS specifications, is itself an issue because:

If the response includes zero or more than one Access-Control-Allow-Origin header values, return fail and terminate this algorithm.

In your scenario, the framework is setting this header two times, and understanding how CORS must be implemented, this will result in the header removed in certain circumstances (possibly client-related).

This is also confirmed by the following question answer: Duplicate Access-Control-Allow-Origin: * causing COR error?

For this reason moving the call to app.UseCors after the call to ConfigureOAuth allows your CORS header to be set only once (because the owin pipeline is interrupted at the OAuth middleware, and never reaches the Microsoft CORS middleware for the Token endpoint) and makes your Ajax call working.

For a better and global solution you may try to put again app.UseCors before the OAuth middleware call, and remove the second Access-Control-Allow-Origin insertion inside GrantResourceOwnerCredentials.

Community
  • 1
  • 1
Federico Dipuma
  • 15,660
  • 3
  • 39
  • 52
  • I tried and can confirm that your global solution worked (removing the Access-Control-Allow-Origin inside of the GrantResourceOwnerCredentials). Thanks for this well explained answer. – Sam May 03 '16 at 10:31
11

Follow below steps and you will have your API working:

  1. Remove any code like config.EnableCors(), [EnableCors(header:"*"....)] from your API.
  2. Go to startup.cs and add below line

    app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
    

before

    ConfigureAuth(app);

Uou will also need to install Microsoft.owin.cors package to use this functionality

Stephen Rauch
  • 40,722
  • 30
  • 82
  • 105
santosh kumar
  • 113
  • 1
  • 4
  • If anyone else is perplexed by "before ConfigureAuth(app)" because you're setting Cors inside ConfigureAuth (wrong as that may be), the real point is it needs to be set before you set up your token endpoint (app.UseOAuthAuthorizationServer and/or app.UseOAuthBearerTokens etc) – bech Aug 07 '19 at 08:44
2

Solving the problem without using app.UseCors()

I had the same problem. I used a Vue.Js client with axois to access my REST-API with cross-corps. On my Owin-Api-Server I was not able to add Microsoft.Owin.Cors nuget due to version conflicts with other 3rd party components. So I couldn't use app.UseCors() method but I solved it by using the middleware pipeline.

private IDisposable _webServer = null;

public void Start(ClientCredentials credentials)
{
    ...
    _webServer = WebApp.Start(BaseAddress, (x) => Configuration(x));
    ...
}

public void Configuration(IAppBuilder app)
{
    ...
    // added middleware insted of app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
    app.Use<MyOwinMiddleware>();
    app.UseWebApi(config);
    ...
}

public class MyOwinMiddleware : OwinMiddleware
{
    public MyOwinMiddleware(OwinMiddleware next) :
        base(next)
    { }

    public override async Task Invoke(IOwinContext context)
    {
        var request = context.Request;
        var response = context.Response;

        response.OnSendingHeaders(state =>
        {
            var resp = (IOwinResponse)state;

            // without this headers -> client apps will be blocked to consume data from this api
            if (!resp.Headers.ContainsKey("Access-Control-Allow-Origin"))
                resp.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
            if (!resp.Headers.ContainsKey("Access-Control-Allow-Headers"))
                resp.Headers.Add("Access-Control-Allow-Headers", new[] { "*" });
            if (!resp.Headers.ContainsKey("Access-Control-Allow-Methods"))
                resp.Headers.Add("Access-Control-Allow-Methods", new[] { "*" });

            // by default owin is blocking options not from same origin with MethodNotAllowed
            if (resp.StatusCode == (int)HttpStatusCode.MethodNotAllowed &&
                HttpMethod.Options == new HttpMethod(request.Method))
            {
                resp.StatusCode = (int)HttpStatusCode.OK;
                resp.ReasonPhrase = HttpStatusCode.OK.ToString();
            }

        }, response);

        await Next.Invoke(context);
    }
}

So I created my own middleware and manipulated the response. GET calls only needed the Access-Control-Allow headers whereas for OPTIONS calls I also needed to manipulate the StatusCode because axois.post() is calling first with OPTIONS-method before sending the POST. If OPTIONS return StatusCode 405, the POST will never be sent.

This solved my problem. Maybe this can help somebody too.

jaz
  • 149
  • 8