I'm using Basic Authentication to retrieve xml order information from a website. The website endpoint url receives the authentication and then redirects to an url with the order information. I can successfully authenticate but not retrieve the order information (I get a blank page, with status 200. NOTE: This is the same result I get in a browser if I navigate directly to the redirected url without first go to the url which handles authentication). I believe this is because I need to handle the authentication cookie. However, as soon as I add a cookie container to my request, request.GetResponse() bombs with an internal server error 500.
I have tested the url endpoint using Fiddler, and I get the same result as when I don't add a cookie container to my request. Authentication is successful, and then the redirected page is blank with status code 200. (P.S. I can tell from Fiddler that the cookie is HttpOnly)
However, if I use a web browser and enter credentials at the initial url, it redirects to the order page with order content displayed.
Question 1: How can I determine (or further troubleshoot) if this is a problem with my code or with the order endpoint?
Question 2: If it's a problem with my code, how should I handle the cookie?
My code:
public void Main()
{
Uri endpointUri = new Uri(Dts.Variables["User::endpointUrl"].Value.ToString());
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(endpointUri);
request.Method = "GET"; //I have also tried POST
request.KeepAlive = true;
request.AllowAutoRedirect = true;
request.MaximumAutomaticRedirections = 1;
SetBasicAuthHeader(request, Dts.Variables["User::endpointUsername"].Value.ToString(), Dts.Variables["User::endpointPassword"].Value.ToString());
request.CookieContainer = new CookieContainer(); //If I comment out this line, the error does not occur, but I also don't see any order info.
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
Stream dataStream = response.GetResponseStream();
Debug.WriteLine((new System.IO.StreamReader(dataStream)).ReadToEnd());
return;
}
}
public void SetBasicAuthHeader(WebRequest req, String userName, String userPassword)
{
string authInfo = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(userName + ":" + userPassword));
req.Headers["Authorization"] = "Basic " + authInfo;
}
The error:
The remote server returned an error: (500) Internal Server Error at
System.Net.HttpWebRequest.GetResponse()
Related
I am working on a simple Windows Forms program that take a username and password from a "Textbox" then it show my linked-in name in a "Messagebox".
I want to accomplish the code with the using of "HttpWebRequest" or using any method to send my POST request to Linked-in then i can get the response and find my name to shown it in a "Messagebox".
I am familiar with creating a "GET" Request and also i made some "POST" Requests but in this case i didn't know how can i send my "txt_UserName.Text" and "txt_Password" with the POST Request and how can i receives the Response.
I tried to using Fiddler to capture POST request (=POST) from linkedin when i try to login but it captures more than 4 requests when i see the header of them it seem like a GET Request this is an example of one:
GET /nhome/?trk= HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
and all of them have a multiple cookies values.
This is my POST request code:
public void SubmitData()
{
try
{
string postData = "This is a test that posts this string to a Web server.";
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
// Create a request using a URL that can receive a post.
WebRequest request = WebRequest.Create("http://www.linkedin.com");
// Set the Method property of the request to POST.
request.Method = "POST";
// Set the ContentLength property of the WebRequest.
request.ContentLength = byteArray.Length;
//Content Length
request.ContentLength = byteArray.Length;
// Get the request stream.
Stream dataStream = request.GetRequestStream();
// Write the data to the request stream.
dataStream.Write(byteArray, 0, byteArray.Length);
// Close the Stream object.
dataStream.Close();
// Get the response.
WebResponse response = request.GetResponse();
dataStream = response.GetResponseStream();
StreamReader sr = new StreamReader(dataStream);
MessageBox.Show(sr.ReadToEnd());
sr.Close();
dataStream.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message);
}
}
Now the only thing i wish to know, how can i send my username and password as a values to login to linked-in?
Edit:
Below is my second try, it's ok, i can now send the User and Password in postData and i can store the Cookies and retrive it. but there are two issues:
1- how can i make sure that the login is accomplished and not failed
2- if the login is accomplished i want to know what is the second step to get my name from profile, is it made another request or what ?
private void button1_Click(object sender, EventArgs e)
{
PostMessage();
}
private void PostMessage()
{
try {
// POST Data and the POST uri
string postData = "isJsEnabled=true&source_app=&session_key=" + textBox1.Text + "&session_password=" + textBox2.Text + "&signin=Sign+In&session_redirect=";
string uri = "https://www.linkedin.com/uas/login-submit";
// Encoding the POST Data
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
// Create the POST Request
HttpWebRequest WebReq = (HttpWebRequest)WebRequest.Create(uri);
//POST Parameters (Method and etc.)
WebReq.Method = "POST";
WebReq.ContentType = "application/x-www-form-urlencoded";
WebReq.ContentLength = byteArray.Length;
// Set the POST Request Cookies
var cookieContainer = new CookieContainer();
WebReq.CookieContainer = cookieContainer;
// Get the request stream.
Stream dataStream = WebReq.GetRequestStream();
// Write the data to the request stream.
dataStream.Write(byteArray, 0, byteArray.Length);
// Close the Stream object.
dataStream.Close();
// Get the response.
HttpWebResponse response = (HttpWebResponse)WebReq.GetResponse();
dataStream = response.GetResponseStream();
StreamReader sr = new StreamReader(dataStream);
//MessageBox.Show(sr.ReadToEnd());
sr.Close();
dataStream.Close();
if (response.StatusCode != HttpStatusCode.OK)
{
MessageBox.Show(" Error: " + response.StatusDescription);
response.Close();
}
foreach (Cookie cook in response.Cookies)
{
MessageBox.Show(cook.Name + " " + cook.Value);
}
}
catch (Exception ex)
{
MessageBox.Show("POST Message Error: " + ex.Message);
}
}
I used Fiddler while I was logging in and found a request to https://www.linkedin.com/uas/login-submit containing the username and password. Found it? Now, if you want to look at it completely from an HTTP Request perspective, you will have to figure out how to generate the other data in the post data/cookie header using the other requests and responses that your browser sent and received to and from the site before this particular request (the information should be there). I think this will lead you to what you need to do, but there's some work to be done!
You're going to need more than you think to log in there is good documentation on how to do this just here, you are going to need an auth token etc, This is because like other services, for example google, they are using oauth2 to secure applications etc.
oauth works by issuing tokens and refreshing tokens and there's a bit of a learning curve but its not especially difficult.
Essentially the following happens
You register your application with linked in and they give you a
client secret.
You pass this code to linked in in your application
and they will generate an auth screen saying that the application is
requesting permission.
you then approve this and it will give you an access token
You then log in with the access token (access tokens on linkedin are valid for 60 days, you must refresh them by this time).
On the plus side the linked in api is pretty straight forward and once authorised you will be able to get stuff pretty easily. All of this is detailed in the link provided in nice step by step stages.
By the way there is also a nuget package that gives you access to profile information.
Try Install-Package LinkedIn
I should point out that the nuget package above gives you a login provider to help authenticate if you don't want to roll your own.
Added after your comments below.
If all you want to do is know how to send a post request here's a generic bit of code that does just that:
string url = "www.foo.bar.com";
using (var webClient= new WebClient())
{
var data = new NameValueCollection();
data["username"] = "<yourusername>";
data["password"] = "<yourPassword>";
var response = webClient.UploadValues(url, "POST", data);
}
note: because its a web uri you should use POST in the method argument here.
The problem you up against is that Linkedin uses a redirect within the login session and is differcult to catch.
So somehow within the login session you need to code that it redirects to the https://www.linkedin.com/nhome/?trk= this provides the user page access.
I am also testing with this, however did not manage to figure this part out, normally
httpWebRequest.AllowAutoRedirect = true;
should do the trick but not in this case it does not work.
So if you find the solution let me know, if I find will post it also.
I am building a WCF that encapsulates Google OAuth 2.0.
The plan: Client calls WCF endpoint which then redirects to the Google Sign In page for user authentication. On successful authentication, Google kicks back to the redirect URI and returns the Access Token in the response.
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class AdWordsOAuthService : IOAuthService
{
public string AuthenticateAndGetRefreshToken()
{
string refreshToken = string.Empty;
try
{
AuthenticateUser();
}
catch (Exception ex)
{
throw;
}
return refreshToken;
}
private void AuthenticateUser()
{
// build the SOAP header here...
string authUrl = "https://accounts.google.com/o/oauth2/auth"; //redirect to this url
string postData = "response_type=code"
+ "&client_id=" + HttpUtility.UrlEncode("9999999999999.apps.googleusercontent.com")
//+ "&client_secret=" + HttpUtility.UrlEncode("dsflkdfsljkdfskjldskjlfds")
+ "&redirect_uri=" + HttpUtility.UrlEncode("http://localhost:50306/NextOAuthService/AdWordsOAuthService/AuthenticateAndGetRefreshToken")
+ "&scope=" + HttpUtility.UrlEncode("https://adwords.google.com/api/adwords")
+ "&access_type=offline";
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
//handle webrequest stuff...
var request = (HttpWebRequest)WebRequest.Create(authUrl);// + postData);
if (request != null)
{
request.Method = "POST";
request.Timeout = 20000;
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byteArray.Length; // byteArray.Length;
Stream dataStream = request.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
//attempt to redirect to the Adwords OAuth URL: https://adwords.google.com/api/adwords&?response.....blah blah
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.RedirectKeepVerb;
WebOperationContext.Current.OutgoingResponse.Location = response.ResponseUri.AbsoluteUri;
return;
}
}
}
What's Actually Happening: When the code attempts the redirect, I get following exception:
The content type text/html; charset=UTF-8 of the response message does not match the content type of the binding (application/soap+xml;> charset=utf-8)
When I examine the response (using fiddler), the response contains the actual HTML code for the Google OAuth Sign In page. So, instead of redirecting the WCF to the url, I am instead receiving an html response containing the page source.
How do I get around this and force the redirect?
I think you are miss understanding the calls a bit. The First URL that you are building there is the one that you should be displaying to the user asking if they want to let you access there data.
You need to wait for them to except. You need to deal with the Authentication Code. Google 3 Legged OAuth2 Flow
You should consider using Googles dot net client lib. It will handle all this for you.
i'm currently trying to develop an api for google reader and when i'm trying to get the token, the following error is being generated:
System.Net.WebException: The remote server returned an error: (401) Unauthorized.
at System.Net.HttpWebRequest.GetResponse()
first i'm getting the session... and this works perfectly. then the following method i being called to get the token:
public String setToken()
{
HttpWebResponse response;
HttpWebRequest request;
cookie = new Cookie("SID", this.sessionID, "/", ".google.com");
String url = "http://www.google.com/reader/api/0/token";
request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
request.CookieContainer = new CookieContainer();
request.CookieContainer.Add(this.cookie);
response= (HttpWebResponse)request.GetResponse();
using (var stream = response.GetResponseStream())
{
StreamReader r = new StreamReader(stream);
this.token = r.ReadToEnd();
}
return this.token;
}
the exception is being generated in this line:
response= (HttpWebResponse)request.GetResponse();
does anyone know what might be causing this error please?
PS. I read the question : Why am I getting a 401 (Unauthorized) error when POSTing to Google Reader API? however he was getting this error when he tried to post.
Google has changed, according to Eric Mann:
"As it turns out, Google has changed the authentication portion of the Reader API. Now, instead of passing the SID in a cookie when you make a request, you set an authentication header with the “Auth” key originally passed with the SID."
Source
I need to autheticate on a site using forms authentication and then redirect the user to that site along with the session cookie. I have not figured out how to successfully do this. Here's my code so far.. I still get redirected to that apps login page. Any help is much appreciated!
protected void Button1_Click(object sender, EventArgs e)
{
string data = "nickname=&login={0}&password={1}&action_login.x=70&action_login.y=14action_login=Login";
string postdata = String.Format(data, "test", "test");
string page = #"http://1.1.1.1/home.asp";
string loginPage = #"http://1.1.1.1/auth.asp";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(loginPage);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.AllowAutoRedirect = false;
ASCIIEncoding encoding = new ASCIIEncoding(); //encoder
byte[] requestData = encoding.GetBytes(postdata); //encode post data
request.ContentLength = requestData.Length;
//write the post data to the request
Stream requestStream = request.GetRequestStream();
// Send the data.
requestStream.Write(requestData, 0, requestData.Length);
requestStream.Close();
try
{
HttpWebResponse response = (HttpWebResponse) request.GetResponse();
string cookieHeader = response.GetResponseHeader("Set-Cookie");
string cookieValue = cookieHeader.Replace("pp_session_id=", "");
HttpCookie cookie = new HttpCookie("pp_session_id",cookieValue);
cookie.Domain = "1.1.1.1";
cookie.Path = "/";
Response.Clear();
Response.StatusCode = 302;
//Response.AddHeader("Set-Cookie", cookieHeader);
Response.AddHeader("Location",page);
Response.RedirectLocation = page;
Response.Cookies.Add(cookie);
Response.Flush();
}
catch (WebException ex)
{
Response.Write(ex.Message);
}
}
Use Firebug on Mozilla Firefox to see what exactly the browser does when logging into the webapp. Then simulate the same sequence through code.
Or, you can use wireshark to sniff the requests sent by the browser.
One thing I can see from your code, is that you are adding the cookie explicitly. You shouldnt be doing this. You should set a CookieContainer on the request, so that the cookies get sent with all the requests to that site.
hope that helps.
What's wrong with using the FormsAuthentication class? In particular, have you tried the following sequence (or a variation of it):
FormsAuthentication.Authenticate();
FormsAuthentication.SetAuthCookie();
FormsAuthentication.RedirectFromLoginPage();
i believe you have to do a request to an authenticated page on the remote web app.
you'll have to grab the cookie it gives you so you have a valid session. aspnet session id is passed in the cookie. Then you will need to pass the username and password required for that app along with the cookie you received so you will have a valid authenticated session.
I have an application that reads parts of the source code on a website. That all works; but the problem is that the page in question requires the user to be logged in to access this source code. What my program needs a way to initially log the user into the website- after that is done, I'll be able to access and read the source code.
The website that needs to be logged into is:
mmoinn.com/index.do?PageModule=UsersLogin
You can continue using WebClient to POST (instead of GET, which is the HTTP verb you're currently using with DownloadString), but I think you'll find it easier to work with the (slightly) lower-level classes WebRequest and WebResponse.
There are two parts to this - the first is to post the login form, the second is recovering the "Set-cookie" header and sending that back to the server as "Cookie" along with your GET request. The server will use this cookie to identify you from now on (assuming it's using cookie-based authentication which I'm fairly confident it is as that page returns a Set-cookie header which includes "PHPSESSID").
POSTing to the login form
Form posts are easy to simulate, it's just a case of formatting your post data as follows:
field1=value1&field2=value2
Using WebRequest and code I adapted from Scott Hanselman, here's how you'd POST form data to your login form:
string formUrl = "http://www.mmoinn.com/index.do?PageModule=UsersAction&Action=UsersLogin"; // NOTE: This is the URL the form POSTs to, not the URL of the form (you can find this in the "action" attribute of the HTML's form tag
string formParams = string.Format("email_address={0}&password={1}", "your email", "your password");
string cookieHeader;
WebRequest req = WebRequest.Create(formUrl);
req.ContentType = "application/x-www-form-urlencoded";
req.Method = "POST";
byte[] bytes = Encoding.ASCII.GetBytes(formParams);
req.ContentLength = bytes.Length;
using (Stream os = req.GetRequestStream())
{
os.Write(bytes, 0, bytes.Length);
}
WebResponse resp = req.GetResponse();
cookieHeader = resp.Headers["Set-cookie"];
Here's an example of what you should see in the Set-cookie header for your login form:
PHPSESSID=c4812cffcf2c45e0357a5a93c137642e; path=/; domain=.mmoinn.com,wowmine_referer=directenter; path=/; domain=.mmoinn.com,lang=en; path=/;domain=.mmoinn.com,adt_usertype=other,adt_host=-
GETting the page behind the login form
Now you can perform your GET request to a page that you need to be logged in for.
string pageSource;
string getUrl = "the url of the page behind the login";
WebRequest getRequest = WebRequest.Create(getUrl);
getRequest.Headers.Add("Cookie", cookieHeader);
WebResponse getResponse = getRequest.GetResponse();
using (StreamReader sr = new StreamReader(getResponse.GetResponseStream()))
{
pageSource = sr.ReadToEnd();
}
EDIT:
If you need to view the results of the first POST, you can recover the HTML it returned with:
using (StreamReader sr = new StreamReader(resp.GetResponseStream()))
{
pageSource = sr.ReadToEnd();
}
Place this directly below cookieHeader = resp.Headers["Set-cookie"]; and then inspect the string held in pageSource.
You can simplify things quite a bit by creating a class that derives from WebClient, overriding its GetWebRequest method and setting a CookieContainer object on it. If you always set the same CookieContainer instance, then cookie management will be handled automatically for you.
But the only way to get at the HttpWebRequest before it is sent is to inherit from WebClient and override that method.
public class CookieAwareWebClient : WebClient
{
private CookieContainer cookie = new CookieContainer();
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);
if (request is HttpWebRequest)
{
(request as HttpWebRequest).CookieContainer = cookie;
}
return request;
}
}
var client = new CookieAwareWebClient();
client.BaseAddress = #"https://www.site.com/any/base/url/";
var loginData = new NameValueCollection();
loginData.Add("login", "YourLogin");
loginData.Add("password", "YourPassword");
client.UploadValues("login.php", "POST", loginData);
//Now you are logged in and can request pages
string htmlSource = client.DownloadString("index.php");
Matthew Brindley, your code worked very good for some website I needed (with login), but I needed to change to HttpWebRequest and HttpWebResponse otherwise I get a 404 Bad Request from the remote server. Also I would like to share my workaround using your code, and is that I tried it to login to a website based on moodle, but it didn't work at your step "GETting the page behind the login form" because when successfully POSTing the login, the Header 'Set-Cookie' didn't return anything despite other websites does.
So I think this where we need to store cookies for next Requests, so I added this.
To the "POSTing to the login form" code block :
var cookies = new CookieContainer();
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(formUrl);
req.CookieContainer = cookies;
And To the "GETting the page behind the login form" :
HttpWebRequest getRequest = (HttpWebRequest)WebRequest.Create(getUrl);
getRequest.CookieContainer = new CookieContainer();
getRequest.CookieContainer.Add(resp.Cookies);
getRequest.Headers.Add("Cookie", cookieHeader);
Doing this, lets me Log me in and get the source code of the "page behind login" (website based moodle) I know this is a vague use of the CookieContainer and HTTPCookies because we may ask first is there a previously set of cookies saved before sending the request to the server. This works without problem anyway, but here's a good info to read about WebRequest and WebResponse with sample projects and tutorial:
Retrieving HTTP content in .NET
How to use HttpWebRequest and HttpWebResponse in .NET
Sometimes, it may help switching off AllowAutoRedirect and setting both login POST and page GET requests the same user agent.
request.UserAgent = userAgent;
request.AllowAutoRedirect = false;