I swear it is so many times to me that I really hate CORS. I just split my application into two in order to process only part of the API, and the other - the client part. I did this before, so I knew that I needed to make sure CORS is enabled and enabled, so I installed it in WebApiConfig.cs
public static void Register(HttpConfiguration config) { // Enable CORS config.EnableCors(new EnableCorsAttribute("*", "*", "*")); // Web API configuration and services var formatters = config.Formatters; var jsonFormatter = formatters.JsonFormatter; var serializerSettings = jsonFormatter.SerializerSettings; // Remove XML formatting formatters.Remove(config.Formatters.XmlFormatter); jsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json")); // Configure our JSON output serializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); serializerSettings.Formatting = Formatting.Indented; serializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; serializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.None; // Configure the API route config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); }
As you can see, my first line includes CORS, so it should work. If I open my client application and request the API, it really works (without EnableCors I get the expected CORS error. The problem is that my / token still gets the CORS error. Now I know that the endpoint / token is not part of the WebAPI, so I created my own OAuthProvider (which, as I should note, used elsewhere just fine), and looks like this:
public class OAuthProvider<TUser> : OAuthAuthorizationServerProvider where TUser : class, IUser { private readonly string publicClientId; private readonly UserService<TUser> userService; public OAuthProvider(string publicClientId, UserService<TUser> userService) { if (publicClientId == null) throw new ArgumentNullException("publicClientId"); if (userService == null) throw new ArgumentNullException("userService"); this.publicClientId = publicClientId; this.userService = userService; } public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); var user = await this.userService.FindByUserNameAsync(context.UserName, context.Password); if (user == null) { context.SetError("invalid_grant", "The user name or password is incorrect."); return; } var oAuthIdentity = this.userService.CreateIdentity(user, context.Options.AuthenticationType); var cookiesIdentity = this.userService.CreateIdentity(user, CookieAuthenticationDefaults.AuthenticationType); var properties = CreateProperties(user.UserName); var ticket = new AuthenticationTicket(oAuthIdentity, properties); context.Validated(ticket); context.Request.Context.Authentication.SignIn(cookiesIdentity); } public override Task TokenEndpoint(OAuthTokenEndpointContext context) { foreach (KeyValuePair<string, string> property in context.Properties.Dictionary) context.AdditionalResponseParameters.Add(property.Key, property.Value); return Task.FromResult<object>(null); } public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) {
As you can see, in the GrantResourceOwnerCredentials method , I again allow access to all CORS. This should work for all requests / tokens, but it is not. When I try to log in from my client application, I get a CORS error. Chrome shows this:
XMLHttpRequest cannot load http: // localhost: 62605 / token . The response to the request before the flight does not pass the access control check. There is no "Access-Control-Allow-Origin" header on the requested resource. Origin ' http: // localhost: 50098 ' is therefore not allowed. The response was an HTTP 400 status code.
and Firefox shows this:
The cross-request request is blocked: a policy of the same origin prohibits reading the remote resource at http: // localhost: 62605 / token . (Reason: CORS header "Access-Control-Allow-Origin" is missing). The cross-request request is blocked: a policy of the same origin prohibits reading the remote resource at http: // localhost: 62605 / token . (Reason: CORS request failed).
For testing purposes, I decided to use a violinist to see if I could see anything else that could make me understand what was going on. When I try to log in, FIddler shows the response code as 400, and if I look at the original answer, I can see the error:
{"error":"unsupported_grant_type"}
which is strange, because the data that I send did not change and worked perfectly before separation. I decided to use Composer on the violin and reproduce what I expect the POST request should look like. When I execute it, it works fine and I get a 200 response code.
Can anyone understand why this could happen?
Update 1
Just for reference, a request from my client application is as follows:
OPTIONS http://localhost:62605/token HTTP/1.1 Host: localhost:62605 Connection: keep-alive Pragma: no-cache Cache-Control: no-cache Access-Control-Request-Method: POST Origin: http://localhost:50098 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.71 Safari/537.36 Access-Control-Request-Headers: accept, authorization, content-type Accept: */* Referer: http://localhost:50098/account/signin Accept-Encoding: gzip, deflate, sdch Accept-Language: en-US,en;q=0.8
from the composer, it looks like this:
POST http://localhost:62605/token HTTP/1.1 User-Agent: Fiddler Content-Type: 'application/x-www-form-urlencoded' Host: localhost:62605 Content-Length: 67 grant_type=password&userName=foo&password=bar