I am trying to incorporate CORS into an existing asp.net website that hosts several WCF services. Unfortunately, it’s hard for me to work. At first glance, they redirect me to the login page (which does not exist), so there is probably something wrong with the authentication cookie created by the user authentication service.
Before continuing, here is a simple description of my current scenario:
- I have a site A, which provides several web services (everything except access to the authentication service can only be accessed by authorized users), and a site B, which has a page that tries to interact with the service’s website
- to reproduce this scenario, I put the services + html page in one place and I created an alias (hosts file) to simulate a cors call (let's say I'm called outro.pt)
- An authentication cookie is created using FormsAuthentication.GetAuthCookie and then added to the response by calling the Response.AppendCookie Authentication method
works as expected: first you need to call OPTIONS, then a POST call; when the second web service is called (POST again), there is no preliminary check (because the first call ends up generating an Access-Control-Max-Age header to cache the pre-flight checks for 5 minutes), but the website (outro.pt) ends up returning 302 which redirects the browser to the login page.
Before continuing, here is the demo code used to handle preflight and resolve cors calls (I placed it in the application's beginrequest event):
private void Application_BeginRequest(object sender, EventArgs e) { var ctx = HttpContext.Current; //allow credentials: always ctx.Response.AddHeader("Access-Control-Allow-Credentials", "true"); //add allow origin header if (ctx.Request.Headers["Origin"] != null) { ctx.Response.AddHeader("Access-Control-Allow-Origin", ctx.Request.Headers["Origin"]); } if (ctx.Request.Headers["Access-Control-Request-Headers"] != null) { ctx.Response.AddHeader("Access-Control-Allow-Headers", ctx.Request.Headers["Access-Control-Request-Headers"]); } if (ctx.Request.HttpMethod.ToUpper() == "OPTIONS" ) { ctx.Response.AddHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS"); //keep preflight info for 5 mins ctx.Response.AddHeader("Access-Control-Max-Age", (5 * 60).ToString()); ctx.Response.End(); return; } }
Now, here is the demo code for creating cookie authentication (since this is a demo code, I decided to use a simple common handler for it:
public void ProcessRequest(HttpContext context) { context.Response.ContentType = "application/json"; var name = ""; using (var reader = new StreamReader(context.Request.InputStream)) { var std = (Student)JsonConvert.DeserializeObject(reader.ReadToEnd(),typeof(Student)); name = std == null ? "" : std.name; } if (!String.IsNullOrEmpty(name)) { var cookie = FormsAuthentication.GetAuthCookie(name, false); cookie.Domain = "outro.pt"; context.Response.AppendCookie(cookie); context.Response.Write(JsonConvert.SerializeObject(new {valid = true})); } else { context.Response.Write(JsonConvert.SerializeObject(new { valid = false })); } }
Now, after using the violinist, I see that the cookie is returned when the validation handler is called. for example, here is an example of a single cookie created by calling the previous authentication handler:
Set-Cookie: .ASPXAUTH = 1CB7EF39DBA39BDA2FD2AA0A0AAFDAB67821A286D04F1678C89CFD6036094A3F9C36D2C9E48FFE35B95C8A742F553138A8899E2CE63259F6DDC7A4D59868AF8A3F50EC037F1DAB73827B5D906A115C28FC8E4B744D661AF77592955F525E236D; domain = outro.pt; Path = /; HttpOnly
At first glance, I don’t see anything wrong with this authentication cookie (I think it is connected to the correct domain and it should be sent whenever I delete any page used inside outro.pt, right? - even this placed inside another asp.net application, e.g. outro.pt/demo/webservice.ashx?)
Since I was redirected to the login page when I tried to access the second web service, I, although the problem was that the authentication cookie was not sent. And this is correct: according to the violinist, in the request headers for this second call cookies are not sent. I tried a few things, but I can't get it to work.
After reading the documents, I believe that setting the withCredentials field to true is the only thing I need to do to send cookies, right? Btw, here is the second web service call that is executed using jquery2:
var complete = url + "studentservice.ashx"; payload = { url: complete, type: 'POST', xhrFields: { withCredentials: true }, contentType: 'application/json', crossDomain: true, dataType: 'json' }; $.ajax(payload) .done(function (data, status, xhr) { alert("success"); }) .fail(function (xhr, status, error) { alert("error" + error); });
So, did someone manage to use cors when web services that need access require asp.net authentication? What am I missing?
thanks.