Reputation: 330
I've been trying to build a local proof of concept to move over our current solution to a different URL structure. I'm using ASP.NET with 3 projects that currently has these URLs mapped to them:
mysite.com
mysite.com/api
mysite.com/app
To setup the proof of concept I've setup 3 sites locally in IIS with the following URLs:
mysite.com
api.mysite.com
app.mysite.com
And have added the following entries into the HOSTS file:
127.0.0.1 mysite.com
127.0.0.1 app.mysite.com
127.0.0.1 api.mysite.com
Currently app.mysite.com
talks to api.mysite.com
to perform a user login, which returns a cookie back in the response. The issue is that the cookie is not being stored under mysite.com
. Subsequent requests to api.mysite.com
don't have the cookie attached in the request header, and therefore fail.
I've experimented setting the cookie's domain
property with no success, as well as not including a domain property.
An example of a cookie returned in the request:
Set-Cookie: MyCookie=somestuff; domain=.mysite.com; expires=Sat, 06-Sep-2014 00:02:04 GMT; path=/; HttpOnly
Yet the cookie is never attached to any requests to api.mysite.com
nor can i see it in the cookie browser of Chrome, Firefox, IE etc...
Note that I've enabled CORS in web.config to enable cross domain requests.
EDIT: In response to Owain's answer. I'll clarify my current setup a little more.
Regarding <machineKey>
I have created a machineKey and used the same values on both applications in the web.config
file. This was already working locally and in production when using mysite.com/api
and mysite.com/app
It wasn't till moving to subdomains that i ran into this issue.
Here is my code for creating and attaching the cookie:
private void EncryptAndAttachCookieToHeaders(FormsAuthenticationTicket ticket)
{
string encryptedTicket = FormsAuthentication.Encrypt(ticket);
HttpCookie newCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
newCookie.Domain = ".mysite.com";
newCookie.Expires = DateTime.Now.AddMonths(3);
newCookie.HttpOnly = true;
newCookie.Secure = false;
System.Web.HttpContext.Current.Response.Cookies.Add(newCookie);
// For testing purposes
HttpCookie hc = new HttpCookie("cookie1", "value");
hc.Domain = ".mysite.com";
hc.Expires = DateTime.Now.AddMonths(3);
HttpContext.Current.Response.Cookies.Add(hc);
HttpCookie hd = new HttpCookie("cookie2", "value");
hd.Domain = ".api.mysite.com";
hd.Expires = DateTime.Now.AddMonths(3);
HttpContext.Current.Response.Cookies.Add(hd);
All of these cookies (real one plus the two tests) are visible when viewing the response in Fiddler. However subsequent requests to api.mysite.com
do NOT have any cookies attached in the request header. The browser doesn't seem to want to store the cookie now that I've moved to the subdomain structure.
Upvotes: 0
Views: 1900
Reputation: 330
I found out the answer. The issue actually resided in how the AJAX request was being formed. I had to add an AJAX property called withCredentials
to allow the browser to send the cross domain cookie. A drawback to this approach is that IE 9 and older don't support it....
From post Setting a cookie on a subdomain from an ajax request :
Set the allow Credentials header on api
Access-Control-Allow-Credentials: true
Use withCredentials for the request
$.ajax({
url: a_cross_domain_url,
xhrFields: {
withCredentials: true
}
});
Otherwise the XMLHttpRequest
will not send the cookies, regardless of the Access-Control-Allow-Credentials
header.
Remove the wildcard on Access-Control-Allow-Origin
Access-Control-Allow-Origin: http://www.example.com
The wildcard *
will not work. The browser will discard the response if withCredentials
was set.
Upvotes: 0
Reputation: 3349
To make cross domain login work you first need to edit the <machineKey>
element inside the web.config.
By default it is something like this
<machineKey
validationKey="AutoGenerate,IsolateApps"
decryptionKey="AutoGenerate,IsolateApps"
validation="SHA1"
decryption="Auto" />
With these settings, the .NET framework uses the automatically generated validationKey and decrytionKey
With the default settings the validation key and the decryption key will get generated by ASP.NET and will get used for authentication ticket and cookie, if you want multiple applications to share the same authentication ticket and cookie, you just need to set the validationKey and decrytionKey in all the applications to the same values
Make sure they are the same in both application something like for example
<machineKey
validationKey="FBFF3D4EFD359FD58AA480E0A63A9C817463A30EF5AFF4E212AD3321C122AECFFEC427C26D24B67296F5EBBB6A3736BF37A5027718E5426B92C9AC606F9AD66F"
decryptionKey="048F8A9F3D6A7D2E88738B36BEEB85FF0B4E23EEF11976D1C0F6F03B91CCFC37"
validation="SHA1"
decryption="AES" />
You can easily create some keys on this site http://aspnetresources.com/tools/machineKey
To make the authentication cookie visible at all other sub domains, you'll need to modify the cookie’s domain attribute.
// Call SetAuthCookie method
FormsAuthentication.SetAuthCookie("Test account", false);
//modify the Domain attribute
System.Web.HttpCookie TestCookie =
System.Web.Security.FormsAuthentication.GetAuthCookie(User.Identity.Name.ToString(), false);
TestCookie.Domain = "mysite.com"; // (Also try ".mysite.com" i'm unsure about this one) the second level domain name
Response.AppendCookie(TestCookie);
This should be everything you need to make cross domain cookies work
Upvotes: 1