I'm using a test automation platform called Ranorex. The code is C#. I would like to set a cookie to the server using HttpWebRequest before I open a browser to begin the test.
Below is the code. Everything executes with no problem. When I view the cookies using the browser - mine is not there (there are 54 other cookies) - when I iterate the response as shown below - I only have three (3) cookies.
Your help is appreciated.
This method will execute the test
void ITestModule.Run()
{
SummaryHelper.KillAllInternetExplorerProcesses();
uri = this.createURI();
// Using HttpWebRequest to set a cookie to the session
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.CookieContainer = new CookieContainer();
Cookie myCookie = new Cookie("mockFlagForTesting", "true", "/", "safeqa.thomson.com");
request.CookieContainer.Add(myCookie);
// Create the processStartInfo obejct to open the IE Browser
// I expect the cookie to be loaded into the session
ProcessStartInfo processStartInfo = new ProcessStartInfo(
#"C:\Program Files\Internet Explorer\iexplore.exe");
processStartInfo.WindowStyle = ProcessWindowStyle.Maximized;
processStartInfo.Arguments = uri;
SummaryBase.process = Process.Start(processStartInfo);
// Create and set a session cookie.
setHTTPCookie();
}
private void setHTTPCookie()
{
// We will attempt to set the cookie here
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.CookieContainer = new CookieContainer();
Cookie myCookie = new Cookie("mockFlagForTesting", "true", "/", "safeqa.thomson.com");
// Add the cookie
request.CookieContainer.Add(myCookie);
// Do we need to use POST here to write to the server ??
// Set the request.Method using WebRequestMethods.Http.Get
request.Method = WebRequestMethods.Http.Get;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
// Iterate the cookies
// We only display three (3) ??
foreach (Cookie cook in response.Cookies)
{
Report.Info("-------------------------------------------------------------");
Report.Info("cook.Name", cook.Name);
Report.Info("cook.Value", cook.Value);
Report.Info("Domain: ", cook.Domain);
Report.Info("Path: ", cook.Path);
}
response.Close();
}
Thanks
Chris
You need to set cookie in browser, not on some random web request.
You can either push cookies via script running on the page OR inject cookies to request if you can intercept requests (i.e. using Fiddler/ Fiddler Core).
Related
I have been trying to log in to a server to grab the authentication cookie (a session cookie), which I can then use for further calls to the server. Every example I have read follows the same pattern:
HttpWebRequest request = WebRequest.Create(loginURL) as HttpWebRequest;
var response = request.GetResponse() as HttpWebResponse;
var cookies = response.Cookies;
This didn't work for me, as the cookies variable ended up empty, and a debug analysis showed response.Cookies was empty. The server is mine, and I can see, through debugging, the cookie is being set. I can also see the cookie in Firefox if I log in to my site with it. So I know the cookie is being set.
After some messing around, I discovered the cookie was being set in the request, not the response. So the code below worked. My question is: Why? Why is the request being populated, but not the response? Is it something to do with being a post, not a get? I am totally baffled.
private void Login()
{
string userName = UserNameText.Text;
string password = PasswordText.Password;
string baseURL = URLText.Text;
string loginURL = baseURL + "/Authentication/LoginAction";
HttpWebRequest request = WebRequest.Create(loginURL) as HttpWebRequest;
request.Method = "POST";
string formContent =
"UserName=" + userName +
"&Password=" + password;
var byteArray = Encoding.UTF8.GetBytes(formContent);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byteArray.Length;
request.CookieContainer = new CookieContainer();
try
{
using (var dataStream = request.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
using (var response = request.GetResponse() as HttpWebResponse)
{
var cookies = request.CookieContainer;
if (cookies.Count != 0)
{
cookies_ = cookies;
}
}
}
}
catch(Exception ex)
{
// don't bother too much
Debug.WriteLine(ex.Message);
}
}
The CookieContainer should be considered similar to a browser's cookie cache for a particular site. The idea is that you supply the container as part of the request, and then it's populated by the cookies you receive and you can reuse that container for subsequent requests. When you make a request, the cookies in the container are sent with the request (just like the browser would with stored cookies).
So, for example, if you have a page that uses cookies to store an authentication token, you can pass the cookie container with the login request, and then pass it with subsequent requests which require an authenticated cookie.
As to why you can't simply extract it from the request, I guess Microsoft just didn't want to duplicate things when you can pass in a reference to a mutable cookie container in the request.
I'm trying to write a utility which will attempt to login to the Microsoft Online Admin website and report back as to whether it is reachable.
Using code mainly from this article, http://odetocode.com/articles/162.aspx and some screen scraping I have pieced together the following. Unfortunately it doesn't work, the final response shows that I am still looking at the login page rather than the target page.
Any help would be terrific. Thanks in advance.
private void LoginToSite()
{
const string LOGIN_URL = "https://admin.microsoftonline.com/Login.aspx";
const string USERNAME = "<username>";
const string PASSWORD = "<password>";
const string TARGET_PAGE_URL = "https://admin.noam.microsoftonline.com/Home/Home.aspx";
// first, request the login form to get the viewstate value
HttpWebRequest webRequest = WebRequest.Create(LOGIN_URL) as HttpWebRequest;
StreamReader responseReader = new StreamReader(
webRequest.GetResponse().GetResponseStream()
);
string responseData = responseReader.ReadToEnd();
responseReader.Close();
// extract the viewstate value and build out POST data
string viewState = ExtractViewState(responseData);
string postData =
String.Format(
"__VIEWSTATE={0}&AdminCenterLoginControl$UserNameTextBox={1}&AdminCenterLoginControl$PasswordTextbox={2}&__EVENTTARGET=AdminCenterLoginControl_ActionButton",
viewState, USERNAME, PASSWORD
);
// have a cookie container ready to receive the forms auth cookie
CookieContainer cookies = new CookieContainer();
// now post to the login form
webRequest = WebRequest.Create(LOGIN_URL) as HttpWebRequest;
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.CookieContainer = cookies;
// write the form values into the request message
StreamWriter requestWriter = new StreamWriter(webRequest.GetRequestStream());
requestWriter.Write(postData);
requestWriter.Close();
// we don't need the contents of the response, just the cookie it issues
webRequest.GetResponse().Close();
// now we can send out cookie along with a request for the protected page
webRequest = WebRequest.Create(TARGET_PAGE_URL) as HttpWebRequest;
webRequest.CookieContainer = cookies;
responseReader = new StreamReader(webRequest.GetResponse().GetResponseStream());
// and read the response
responseData = responseReader.ReadToEnd();
responseReader.Close();
MessageBox.Show(responseData);
}
private string ExtractViewState(string s)
{
string viewStateNameDelimiter = "__VIEWSTATE";
string valueDelimiter = "value=\"";
int viewStateNamePosition = s.IndexOf(viewStateNameDelimiter);
int viewStateValuePosition = s.IndexOf(
valueDelimiter, viewStateNamePosition
);
int viewStateStartPosition = viewStateValuePosition +
valueDelimiter.Length;
int viewStateEndPosition = s.IndexOf("\"", viewStateStartPosition);
return HttpUtility.UrlEncodeUnicode(
s.Substring(
viewStateStartPosition,
viewStateEndPosition - viewStateStartPosition
)
);
}
edit
private void LoginToSite()
{
const string LOGIN_URL = "https://admin.microsoftonline.com/login.aspx?ReturnUrl=%2fDefault.aspx";
const string USERNAME = "<username>";
const string PASSWORD = "<password>";
// Request the login form to get the viewstate value
HttpWebRequest webRequest = WebRequest.Create(LOGIN_URL) as HttpWebRequest;
string response1 = new StreamReader(webRequest.GetResponse().GetResponseStream()).ReadToEnd();
// Extract the viewstate value and build our POST data
string viewState = ExtractViewState(response1);
string postData = String.Format(
"__VIEWSTATE={0}&AdminCenterLoginControl$UserNameTextBox={1}&AdminCenterLoginControl$PasswordTextbox={2}&__EVENTTARGET=AdminCenterLoginControl_ActionButton",
viewState, USERNAME, PASSWORD);
// Set up the Request properties
webRequest = WebRequest.Create(LOGIN_URL) as HttpWebRequest;
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
CookieContainer cookies = new CookieContainer();
webRequest.CookieContainer = cookies;
// Post back to the form
using (StreamWriter requestWriter = new StreamWriter(webRequest.GetRequestStream()))
{
requestWriter.Write(postData);
}
// Read response
string response2 = new StreamReader(webRequest.GetResponse().GetResponseStream()).ReadToEnd();
MessageBox.Show(response2);
}
It would appear that MicrosoftOnline.com does not use Windows Live IDs (aka Passport) for login. This is a shame, since there are libraries available that make logging into LiveID pretty simple for client apps.
Your code hits the login page first, scraps cookies from the response, then attempts to navigate to the target page. This doesn't match the normal flow of user behavior. Normally, the user clicks on a link to go to a target page and the web site redirects the request to the login page if the user is not logged in. After logging in, the login page redirects back to the originally requested target page.
You can see this by looking at the login URL when you visit admin.microsoftonline.com in the browser. You are immediately redirected to the login page, but the full URL on the login page is: https://admin.microsoftonline.com/login.aspx?ReturnUrl=%2fDefault.aspx
Note the ReturnUrl query param at the end. This tells the login page what page to redirect back to when the login is completed.
I don't know if redirect is required by the login page, but since this is the primary path for actual end user interaction (that works) and not the path your code is taking, it's something to consider. Among other things, the redirect to login/ redirect back to target technique will take care of setting the browser cookies for the target domain automatically.
p.s. I notice also that the email administration portion of Microsoft Online services uses a different login URL. From this page (http://www.microsoft.com/online/signin.aspx) clicking on the Exchange Hosted Services Administrative Center link takes you to http:admin.messaging.microsoft.com, which immediately redirects to a login url of https://sts.messaging.microsoft.com/login.aspx?ReturnUrl=%2fDefault.aspx%3fwa%3dwsignin1.0%26wtrealm%3dhttps%253a%252f%252fadmin.messaging.microsoft.com%26wctx%3drm%253d0%2526id%253dpassive%2526ru%253d%25252f%26wct%3d2010-10-27T17%253a11%253a50Z&wa=wsignin1.0&wtrealm=https%3a%2f%2fadmin.messaging.microsoft.com&wctx=rm%3d0%26id%3dpassive%26ru%3d%252f&wct=2010-10-27T17%3a11%3a50Z
The domain name sts.messaging.microsoft.com suggests that the Exchange Hosted Services portion of Microsoft Online Services is using a Security Token Service, which suggests that this login system is capable of federated single sign on between different services. You might be able to connect to this using something like the Windows Identity Foundation (WIF) client components. Will that work with the rest of Microsoft Online Services? I don't know.
> // now we can send out cookie along with a request for the protected page
> webRequest = WebRequest.Create(TARGET_PAGE_URL) as
> HttpWebRequest;
> webRequest.CookieContainer = cookies;
> responseReader = new StreamReader(webRequest.GetResponse().GetResponseStream());
Aren't you setting WebRequest.CookieContainer equal to the blank cookie container that you generated earlier?
Shouldn't you be doing something like:
// we don't need the contents of the response, just the cookie it issues
WebResponse response = webRequest.GetResponse();
cookies = response.cookies;
response.Close();
Everything I find is either about a POST request or does not assume cookies.
I have an URL like this:
http://page.com/find/1,1,1,find.html?advanced=1¶m1=val1¶m2[]=val2
When put into a browser, this will direct me to a search results page. Now I would like to replicate it in a C# program. I have this so far:
WebRequest req = WebRequest.Create(url);
((HttpWebRequest)req).UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.2) Gecko/20100101 Firefox/10.0.2";
req.Method = "GET";
WebResponse response = req.GetResponse();
When I run it, it returns a "please login" page, as expected. But there is a problem with one of the parameters. This is the response URL:
http://page.com/login.html?ref=find/1,1,1,find.html?advanced=1¶m1=val1¶m2=Array
So, two questions: what might have happened to param2? And how do I add cookies to this?
EDIT: Managed to set the cookies by casting to HttpWebRequest.
As devio said you should use HttpWebRequest. I did dirty test to check it.
Prepare cookies to send. I made available for whole localhost:
HttpWebRequest rq = (HttpWebRequest)WebRequest.Create("http://localhost/test.php");
rq.CookieContainer = new CookieContainer();
rq.CookieContainer.Add(new Cookie("test", "xxxx", "/", "localhost"));
Your script should set cookies to make them available in response. And you could use them.
HttpWebResponse resp = (HttpWebResponse)rq.GetResponse();
foreach(var c in resp.Cookies)
{
Debug("{0}: {1}", c.Name, c.Value);
}
You can use this code piece:
Cookie SessionCookie = new Cookie("{CookieName}", {Cookievalue})
{
Domain = "{domain you want to hit}", Path = "/", Expired = false, HttpOnly = true
};
CookieContainer SessionCookieHolder = new CookieContainer();
SessionCookie.Add(SessionCookie);
try
{
HttpWebRequest WebReq = (HttpWebRequest)WebRequest.Create("{URL}");
WebReq.CookieContainer = SessionCookie;
WebReq.Method = "GET/POST/HEAD"; //depending on the request type//
WebReq.KeepAlive = true;
HttpWebResponse Resp = (HttpWebResponse)WebReq.GetResponse();
}
catch(Exception ex)
{
string ExceptionReader = ex.Message;
}
Cookies are stored in the HttpWebRequest.CookieContainer and HttpWebResponse.Cookies properties.
For subsequent requests, you need to add the response's cookies to the request's cookie container.
I am running in a stupid problem. I have an method which returns initialized request resposible for loggin in on some external web site.
protected HttpWebRequest GetLoginRequest()
{
const string url = "https://someurl.com/login";
var queryParams = new ArrayList
{
String.Format("{0}={1}", "email", Email),
String.Format("{0}={1}", "password", DecryptedPassword)
};
var parameters = String.Join("&", (String[])queryParams.ToArray(typeof(String)));
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = parameters.Length;
request.Timeout = 10000;
var streamWriter = new StreamWriter(request.GetRequestStream());
streamWriter.Write(parameters);
streamWriter.Close();
return request;
}
I'm calling this method from two places in my code. First call looks like that:
var request = GetLoginRequest();
var response = (HttpWebResponse)request.GetResponse();
And the second one has CookieContainer assigned to request:
var cookieContainer = new CookieContainer();
var request = GetLoginRequest();
request.CookieContainer = cookieContainer;
var response = (HttpWebResponse)request.GetResponse();
because I need to store CookieContainer.
The thing is that the logon is performed only in second case. In the first case i'm getting response from the login page. I've checked all the cases and both resulting requests seem identic. I would suggest that it is target site secific, but still I don't see any reason for that.
Can you please explain what is the reason, because this behavior seems pretty unobvious to me.
When you set the CookieContainer property on your request, the response is populating that CookieContainer instance with the cookies received from the executed request.
Most login mechanisms use a cookie to store the state related to the established login. I.e. in the case of Forms authentication the cookie is the container for the forms authentication ticket. The ticket is passed as the value of the forms authentication cookie with each request and is used by forms authentication, on the server, to identify an authenticated user.
In short you need a CookieContainer for each request after you login, and it needs to contain the forms authentication cookie that you received when you logged in.
Edit to clarify comment - from MSDN:
CookieContainer is null by default.
You must assign a CookieContainer
object to the property to have cookies
returned in the Cookies property of
the HttpWebResponse returned by the
GetResponse method.
For security reasons, cookies are disabled by default. If you want to
use cookies, use the CookieContainer
property to enable cookies.
I'm trying to write a utility which will attempt to login to the Microsoft Online Admin website and report back as to whether it is reachable.
Using code mainly from this article, http://odetocode.com/articles/162.aspx and some screen scraping I have pieced together the following. Unfortunately it doesn't work, the final response shows that I am still looking at the login page rather than the target page.
Any help would be terrific. Thanks in advance.
private void LoginToSite()
{
const string LOGIN_URL = "https://admin.microsoftonline.com/Login.aspx";
const string USERNAME = "<username>";
const string PASSWORD = "<password>";
const string TARGET_PAGE_URL = "https://admin.noam.microsoftonline.com/Home/Home.aspx";
// first, request the login form to get the viewstate value
HttpWebRequest webRequest = WebRequest.Create(LOGIN_URL) as HttpWebRequest;
StreamReader responseReader = new StreamReader(
webRequest.GetResponse().GetResponseStream()
);
string responseData = responseReader.ReadToEnd();
responseReader.Close();
// extract the viewstate value and build out POST data
string viewState = ExtractViewState(responseData);
string postData =
String.Format(
"__VIEWSTATE={0}&AdminCenterLoginControl$UserNameTextBox={1}&AdminCenterLoginControl$PasswordTextbox={2}&__EVENTTARGET=AdminCenterLoginControl_ActionButton",
viewState, USERNAME, PASSWORD
);
// have a cookie container ready to receive the forms auth cookie
CookieContainer cookies = new CookieContainer();
// now post to the login form
webRequest = WebRequest.Create(LOGIN_URL) as HttpWebRequest;
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.CookieContainer = cookies;
// write the form values into the request message
StreamWriter requestWriter = new StreamWriter(webRequest.GetRequestStream());
requestWriter.Write(postData);
requestWriter.Close();
// we don't need the contents of the response, just the cookie it issues
webRequest.GetResponse().Close();
// now we can send out cookie along with a request for the protected page
webRequest = WebRequest.Create(TARGET_PAGE_URL) as HttpWebRequest;
webRequest.CookieContainer = cookies;
responseReader = new StreamReader(webRequest.GetResponse().GetResponseStream());
// and read the response
responseData = responseReader.ReadToEnd();
responseReader.Close();
MessageBox.Show(responseData);
}
private string ExtractViewState(string s)
{
string viewStateNameDelimiter = "__VIEWSTATE";
string valueDelimiter = "value=\"";
int viewStateNamePosition = s.IndexOf(viewStateNameDelimiter);
int viewStateValuePosition = s.IndexOf(
valueDelimiter, viewStateNamePosition
);
int viewStateStartPosition = viewStateValuePosition +
valueDelimiter.Length;
int viewStateEndPosition = s.IndexOf("\"", viewStateStartPosition);
return HttpUtility.UrlEncodeUnicode(
s.Substring(
viewStateStartPosition,
viewStateEndPosition - viewStateStartPosition
)
);
}
edit
private void LoginToSite()
{
const string LOGIN_URL = "https://admin.microsoftonline.com/login.aspx?ReturnUrl=%2fDefault.aspx";
const string USERNAME = "<username>";
const string PASSWORD = "<password>";
// Request the login form to get the viewstate value
HttpWebRequest webRequest = WebRequest.Create(LOGIN_URL) as HttpWebRequest;
string response1 = new StreamReader(webRequest.GetResponse().GetResponseStream()).ReadToEnd();
// Extract the viewstate value and build our POST data
string viewState = ExtractViewState(response1);
string postData = String.Format(
"__VIEWSTATE={0}&AdminCenterLoginControl$UserNameTextBox={1}&AdminCenterLoginControl$PasswordTextbox={2}&__EVENTTARGET=AdminCenterLoginControl_ActionButton",
viewState, USERNAME, PASSWORD);
// Set up the Request properties
webRequest = WebRequest.Create(LOGIN_URL) as HttpWebRequest;
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
CookieContainer cookies = new CookieContainer();
webRequest.CookieContainer = cookies;
// Post back to the form
using (StreamWriter requestWriter = new StreamWriter(webRequest.GetRequestStream()))
{
requestWriter.Write(postData);
}
// Read response
string response2 = new StreamReader(webRequest.GetResponse().GetResponseStream()).ReadToEnd();
MessageBox.Show(response2);
}
It would appear that MicrosoftOnline.com does not use Windows Live IDs (aka Passport) for login. This is a shame, since there are libraries available that make logging into LiveID pretty simple for client apps.
Your code hits the login page first, scraps cookies from the response, then attempts to navigate to the target page. This doesn't match the normal flow of user behavior. Normally, the user clicks on a link to go to a target page and the web site redirects the request to the login page if the user is not logged in. After logging in, the login page redirects back to the originally requested target page.
You can see this by looking at the login URL when you visit admin.microsoftonline.com in the browser. You are immediately redirected to the login page, but the full URL on the login page is: https://admin.microsoftonline.com/login.aspx?ReturnUrl=%2fDefault.aspx
Note the ReturnUrl query param at the end. This tells the login page what page to redirect back to when the login is completed.
I don't know if redirect is required by the login page, but since this is the primary path for actual end user interaction (that works) and not the path your code is taking, it's something to consider. Among other things, the redirect to login/ redirect back to target technique will take care of setting the browser cookies for the target domain automatically.
p.s. I notice also that the email administration portion of Microsoft Online services uses a different login URL. From this page (http://www.microsoft.com/online/signin.aspx) clicking on the Exchange Hosted Services Administrative Center link takes you to http:admin.messaging.microsoft.com, which immediately redirects to a login url of https://sts.messaging.microsoft.com/login.aspx?ReturnUrl=%2fDefault.aspx%3fwa%3dwsignin1.0%26wtrealm%3dhttps%253a%252f%252fadmin.messaging.microsoft.com%26wctx%3drm%253d0%2526id%253dpassive%2526ru%253d%25252f%26wct%3d2010-10-27T17%253a11%253a50Z&wa=wsignin1.0&wtrealm=https%3a%2f%2fadmin.messaging.microsoft.com&wctx=rm%3d0%26id%3dpassive%26ru%3d%252f&wct=2010-10-27T17%3a11%3a50Z
The domain name sts.messaging.microsoft.com suggests that the Exchange Hosted Services portion of Microsoft Online Services is using a Security Token Service, which suggests that this login system is capable of federated single sign on between different services. You might be able to connect to this using something like the Windows Identity Foundation (WIF) client components. Will that work with the rest of Microsoft Online Services? I don't know.
> // now we can send out cookie along with a request for the protected page
> webRequest = WebRequest.Create(TARGET_PAGE_URL) as
> HttpWebRequest;
> webRequest.CookieContainer = cookies;
> responseReader = new StreamReader(webRequest.GetResponse().GetResponseStream());
Aren't you setting WebRequest.CookieContainer equal to the blank cookie container that you generated earlier?
Shouldn't you be doing something like:
// we don't need the contents of the response, just the cookie it issues
WebResponse response = webRequest.GetResponse();
cookies = response.cookies;
response.Close();