I just spent a couple days on this and, in the end, I didn’t really figure it out. Someone else at my work (the brilliant Rylan Barnes) showed me the workaround for it.
So… you want to add a cookie to your Windows Phone 7 Silverlight HttpWebRequest. Guess what? It sucks to be you! Or, at least, it sucked to be me because I couldn’t find anything on how to do this that worked.
I was working on a project that requires the user to authenticate and, on authentication, sends back a token (unique to that user) to be attached as a cookie for further authentication. However, when I attached the cookie and made my next request, it made that request without the authorization header. Our server said “Hey, looks like you’re not authenticated, I will now forget about that last cookie because you obviously didn’t get it. Have a new one!”
First I’m going to show you things that don’t work.
Didn’t Work: Adding An Empty Cookie Container to the Request
public void DoSomeCRUD(string userName, string pass) { HttpWebRequest request = System.Net.HttpWebRequest.Create("https://myurl.com") as HttpWebRequest; if (HasSavedCookieContainer) request.CookieContainer = SavedCookieContainer(); else request.CookieContainer = new CookieContainer(); request.Credentials = new NetworkCredential(userName, pass); request.BeginGetResponse(new AsyncCallback(GetMyResponse), request); } private void GetMyResponse(IAsyncResult MyResponseAsync) { HttpWebRequest request = (HttpWebRequest)MyResponseAsync.AsyncState; HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(MyResponseAsync); // this method save the cookie container to IsolatedStorage // so I can attach it to the request the next time I open the app. SaveCookieContainer(request.CookieContainer); }
The saving worked, the cookie container came back just as I saved it. And then… it didn’t work. It was as if the cookie didn’t exist.
Didn’t Work: Ripping The Token Out Of the “Set-Cookie” Header
public void DoSomeCRUD(string userName, string pass) { HttpWebRequest request = System.Net.HttpWebRequest.Create("https://myurl.com") as HttpWebRequest; if (IsolatedStorageSettings.ApplicationSettings.Contains["MyToken"]) { string myToken = (string)IsolatedStorageSettings.ApplicationSettings["MyToken"]; Cookie c = new Cookie("MyCookieName", myToken, "[valid path]", "[valid domain]"); request.CookieContainer = new CookieContainer(); request.CookieContainer.Add(new Uri("http://myurl.com"), c); } else request.CookieContainer = new CookieContainer(); request.Credentials = new NetworkCredential(userName, pass); request.BeginGetResponse(new AsyncCallback(GetMyResponse), request); } private void GetMyResponse(IAsyncResult MyResponseAsync) { HttpWebRequest request = (HttpWebRequest)MyResponseAsync.AsyncState; HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(MyResponseAsync); // this method looked for "Set-Cookie" information in the header // and the pulls out the token data by brute force and save the // token as a string if (ResponseHasSetCookieInHeader(response)) { string rawToken = GetTokenFromResponse(response); IsolatedStorageSettings.ApplicationSettings["MyToken"] = rawToken; IsolatedStorageSettings.ApplicationSettings.Save(); } }
The save worked here too, but the same result… it was like the cookie didn’t exist.
This Worked: Adding the Cookie as a Header
Finally we added the token and the credential information as header items. This finally worked.
public void DoSomeCRUD(string userName, string pass) { HttpWebRequest request = System.Net.HttpWebRequest.Create("https://myurl.com") as HttpWebRequest; if (IsolatedStorageSettings.ApplicationSettings.Contains["MyToken"]) request.Headers["Cookie"] = "MyTokenName=" + (string)IsolatedStorageSettings.ApplicationSettings["MyToken"]; request.Headers["Authentication"] = "Basic " + Convert.ToBase64String(StringToAscii(userName, pass)); request.BeginGetResponse(new AsyncCallback(GetMyResponse), request); } private void GetMyResponse(IAsyncResult MyResponseAsync) { HttpWebRequest request = (HttpWebRequest)MyResponseAsync.AsyncState; HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(MyResponseAsync); // this method looks for "Set-Cookie" information in the header // and the pulls out the token data, saving it to Isolated Storage if (ResponseHasSetCookieInHeader(response)) { string rawToken = GetTokenFromResponse(response); IsolatedStorageSettings.ApplicationSettings["MyToken"] = rawToken; IsolatedStorageSettings.ApplicationSettings.Save(); } }
For the sake of giving credit, here is the StringToAscii method for the Authentication, tweaked from this StackOverflow answer from Hans Passant
public static byte[] StringToAscii(string userName, string pass) { string s = userName + ":" + pass; byte[] retval = new byte[s.Length]; for (int ix = 0; ix < s.Length; ++ix) { char ch = s[ix]; if (ch <= 0x7f) retval[ix] = (byte)ch; else retval[ix] = (byte)'?'; } return retval; }