I'm opening a NTLM authenticated site via a MVC app.
public ActionResult Login(Credentials data)
{
Uri queryUrl = new Uri("http://someurl.com");
string domain = "DMZ";
HttpWebRequest req = WebRequest.Create(queryUrl) as HttpWebRequest;
NetworkCredential cred = new NetworkCredential(data.UName, data.Pass, domain);
var cache = new CredentialCache { { queryUrl, "Ntlm", cred } };
req.Credentials = cache;
HttpWebResponse response = req.GetResponse() as HttpWebResponse;
return Redirect(response.ResponseUri.OriginalString);
//return View();
}
if i put a break point on return, i do get the response.statuscode as "OK", the authentication is working fine. but i don't know how to open the url with the authentication. With the redirect I'm using its asking for credentials again.
Related
There are a lot of questions here that seem to be from the web page building side, but it is a lot harder from the client prospective. I am attempting to fill out (what I hope) is a simple login form and then use the cookie it returns to make web api calls because this web api requires a form login before a user can make calls. Below is my code it is currently returning the error that the required anti-forgery cookie is not present.
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;
}
}
static void Main(string[] args)
{
try
{
var client = new CookieAwareWebClient();
client.BaseAddress = #"https://telogapi.placeholder.org/Account/Login?ReturnUrl=%2F";
var loginData = new NameValueCollection();
loginData.Add("login", "user");
loginData.Add("password", "password");
client.UploadValues("/Account/Login?ReturnUrl=%2F", "POST", loginData);
//Now you are logged in and can request pages
string htmlSource = client.DownloadString("index.php");
}
catch (WebException e)
{
var pageContent = new StreamReader(e.Response.GetResponseStream())
.ReadToEnd();
}
}
I'm trying to log in to a server (REST API) which uses HTTP Basic Authentication. The request looks like this:
public JObject PerformLogin(string username, string password)
{
string html = string.Empty;
this.username = username;
this.password = password;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(auth_url_internal);
request.AllowAutoRedirect = true;
request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
request.Method = "GET";
request.CookieContainer = cookies;
request.KeepAlive = true;
//request.ServicePoint.Expect100Continue = false;
request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8";
request.Headers.Add("Accept-Language", "de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4");
request.PreAuthenticate = true;
request.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequested;
string authInfo = username + ":" + password;
authInfo = Convert.ToBase64String(System.Text.Encoding.Default.GetBytes(authInfo));
request.Headers.Add("Authorization", "Basic " + authInfo);
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(stream))
{
html = reader.ReadToEnd();
}
JObject jresponse = JObject.Parse(html);
sess_url_internal = jresponse["internalUrl"].ToString();
sess_url_public = jresponse["publicUrl"].ToString();
return jresponse;
}
which basically works, however the credentials are being sent too early.
First I used curl to see what the traffic looks like in detail and found a "Location:"-Header, which means that there is a redirect happening. In detail, the server redirects me from /api/rest/authenticate?version=1.0, which is the authentication URL (lets call it URL1), to /authenticationbasic/login?AlcApplicationUrl=/api/rest/authenticate%3fversion=1.0 (URL2).
However, Chrome sends the Credentials to URL2, where my program sends them to URL1 which is too early, because the server expects them at URL2, where my application doesn't send any and therefore gets a false return.
How can i change that behaviour?
So with the kind help of x... I figured out how to do it:
After the HttpWebResponse response = (HttpWebResponse)request.GetResponse(); simply add
if ((int)response.StatusCode == 302) // redirect
{
/*
Call the function recursively with the new URL, found in
response.Headers["Location"], in my case this would be:
*/
auth_url_internal = response.Headers["Location"];
return PerformLogin(username, password);
}
I work on a Xamarin.Forms project where I call some WebServices that using cookies, as they was intially made for a website. There are some webservices that only return cookies, but ohers need to receive cookies for working well.
For example, the Login webservice gets a JSON and returns 2 cookies, while the Logout webservice gets an "empty" JSON, the 2 previous cookies and must return an updated value for one of these cookies.
I based on the following link to manage cookies with HttpClient:
Struggling trying to get cookie out of response with HttpClient in .net 4.5
My problem is that I can send a cookie to a webservice, I can receive the returned cookie by a webservice, but I can't receive any "updated" cookie if I've sent it before the call...
The code of the call to the Login webservice looks like this:
public async Task Login()
{
Uri uri = new Uri("http://www.website.com/Login");
CookieContainer cookies = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookies;
var httpClient = new HttpClient(handler);
var jsonParam = "{\"data\":{\"device\":\"xxx\",\"login\":\"my#email.com\",\"password\":\"password\"}}";
HttpContent httpContent = new StringContent(jsonParam);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
httpClient.DefaultRequestHeaders.Add("X-HTTP-Method-Override", "PUT");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
try
{
HttpResponseMessage httpResponse = httpClient.PostAsync(uri), httpContent).Result;
//Treatment of the recovered cookies
IEnumerable<Cookie> responseCookies = cookies.GetCookies(uri).Cast<Cookie>();
foreach (Cookie cookie in responseCookies)
{
Debug.WriteLine(cookie.Name + " : " + cookie.Value);
wsCookies.Add(new KeyValuePair<string, string>(cookie.Name, cookie.Value));
}
if (httpResponse.IsSuccessStatusCode)
{
var responseText = await httpResponse.Content.ReadAsStringAsync();
}
}
catch (Exception e)
{
}
}
=> it works well: I get the expected cookies: SESSIONID=xxx and USERID=xxx
The code of my Logout method looks like this:
public async Task Logout(String test)
{
Uri uri = new Uri("http://www.website.com/Logout");
CookieContainer cookies = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookies;
var httpClient = new HttpClient(handler);
var jsonParam = "{\"data\":{}}";
HttpContent httpContent = new StringContent(jsonParam);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
httpClient.DefaultRequestHeaders.Add("X-HTTP-Method-Override", "GET");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//Retrieving cookies to send
foreach (KeyValuePair<string, string> kvpCookie in wsCookies)
{
cookies.Add(uri, new Cookie(kvpCookie.Key, kvpCookie.Value));
}
try
{
HttpResponseMessage httpResponse = httpClient.PostAsync(uri, httpContent).Result;
//Treatment of the recovered cookies
IEnumerable<Cookie> responseCookies = cookies.GetCookies(uri).Cast<Cookie>();
foreach (Cookie cookie in responseCookies)
{
Debug.WriteLine(cookie.Name + " : " + cookie.Value);
wsCookies.Add(new KeyValuePair<string, string>(cookie.Name, cookie.Value));
}
if (httpResponse.IsSuccessStatusCode)
{
var responseText = await httpResponse.Content.ReadAsStringAsync();
}
}
catch (Exception e)
{
}
}
=> this method works but I don't get the expected cookies. I send the cookies (names and values) that I've received earlier (SESSIONID=xxx and USERID=xxx), but I don't get a new value for the cookies, whereas I wait USERID=deleted as I can see when I sniff it in Fiddler: I only find the 2 cookies I've sent in "responseCookies"...
=> Do you have an explanation? Is there something that I'm doing wrong? Is there another way to manage webservices and cookies?
Not sure if that is the issue, but why create the cookie container every time? You can pass the cookies along every time.
So initialize once, and then assign the existing cookie container to the requests in Login and Logout.
I was trying to log in to my WordPress admin panel using C# (WebClient/HttpWebRequest).
I send a POST request to /wp-login.php.
It responds with cookies like this:
Set-Cookie: wordpress_26...cf9e; path=/wordpress/wp-content/plugins; httponly
Set-Cookie: wordpress_26...cf9e; path=/wordpress/wp-admin; httponly
Set-Cookie: wordpress_logged_in_26...43b3; path=/wordpress/; httponly
And it also redirects to /wp-admin/: Location: http://109.120.169.99/wordpress/wp-admin/
The problem is that the second cookie (with path=/wp-admin) is not added to CookieContainer, so login fails.
I looked into HttpWebRequest source code and http://referencesource.microsoft.com/#System/net/System/Net/_CookieModule.cs (OnReceiveHeaders) and found out that it uses ResponseUri for all cookies.
I tried to add the cookie manually using response uri and got an exception that path is invalid. After that I found this answer CookieException with CookieContainer: The 'Path' part of the cookie is invalid : it says that the Uri has to match the path from the cookie when adding to CookieContainer.
I ended up disabling AllowAutoRedict (handling it manually) and manually adding these cookies.
Here is my code:
public static void Auth(string url, string username, string password)
{
var webClient = new CookieAwareWebClient();
// load page to get some cookies
string loginPageHtml = webClient.DownloadString(url);
webClient.Headers.Add(HttpRequestHeader.ContentType, "application/x-www-form-urlencoded");
webClient.Headers.Add(HttpRequestHeader.Referer, url);
var postValues = new NameValueCollection()
{
{"log", username},
{"pwd", password},
{"wp-sumbit", "Log In"},
{"redirect_to", url.Replace("wp-login.php", "wp-admin/") },
{"testcookie", "1"}
};
webClient.AllowRedirects = false; // to handle cookies and redirect manually
byte[] response = webClient.UploadValues(url, postValues);
string html = Encoding.UTF8.GetString(response);
// <FIX>, handle cookies and redirect manually
string cookies = webClient.ResponseHeaders[HttpResponseHeader.SetCookie];
var cookiesArr = cookies.Split(',');
foreach (var cookieStr in cookiesArr)
{
var parts = cookieStr.Split(';').Select(s => s.Trim());
foreach (var part in parts)
{
if (part.Contains("path="))
{
string path = part.Replace("path=", "");
var uri = new Uri(url);
if (path != "/" && !uri.LocalPath.Contains(path))
{
var uriBuilder = new UriBuilder(uri.Scheme, uri.Host, 80,
path);
webClient.CookieContainer.SetCookies(uriBuilder.Uri, cookieStr);
}
}
}
}
// </FIX>
while (webClient.StatusCode() == HttpStatusCode.Redirect)
{
string redirectUrl = webClient.ResponseHeaders[HttpResponseHeader.Location];
html = webClient.DownloadString(redirectUrl);
}
if (html.Contains("?action=logout"))
Console.WriteLine("Login success");
else
Console.WriteLine("Login fail");
}
WebClient:
// WebClient with CookieContainer
public class CookieAwareWebClient : WebClient
{
private WebRequest _request = null;
private WebResponse _response = null;
public CookieContainer CookieContainer { get; private set; }
public string UserAgent { get; set; }
public bool AllowRedirects { get; set; }
public CookieAwareWebClient()
{
UserAgent = "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36";
CookieContainer = new CookieContainer();
AllowRedirects = true;
}
protected override WebRequest GetWebRequest(Uri address)
{
_request = base.GetWebRequest(address);
if (_request is HttpWebRequest)
{
var httpRequest = (HttpWebRequest)_request;
httpRequest.CookieContainer = CookieContainer;
httpRequest.UserAgent = UserAgent;
httpRequest.AllowAutoRedirect = AllowRedirects;
httpRequest.ProtocolVersion = HttpVersion.Version11;
httpRequest.Timeout = 50000;
}
return _request;
}
protected override WebResponse GetWebResponse(WebRequest request)
{
_response = base.GetWebResponse(request);
return _response;
}
// Returns status code of the last response
public HttpStatusCode StatusCode()
{
var httpResponse = _response as HttpWebResponse;
if (httpResponse == null)
throw new InvalidOperationException("Unable to retrieve the status code, maybe you have not made a request yet.");
var result = httpResponse.StatusCode;
return result;
}
}
I also tried using .NET 4.5 HttpClient but the same result (I think it uses HttpWebRequest underneath too):
public static void Auth2(string url, string username, string password)
{
using (var httpClient = new HttpClient())
{
var loginPageHtml = httpClient.GetStringAsync(url).Result;
var postContent = new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("log", username),
new KeyValuePair<string, string>("pwd", password),
new KeyValuePair<string, string>("wp-submit", "Log In"),
new KeyValuePair<string, string>("redirect_to", url.Replace("wp-login.php", "wp-admin/")),
new KeyValuePair<string, string>("testcookie", "1")
};
var response = httpClient.PostAsync(url, new FormUrlEncodedContent(postContent)).Result;
string html = response.Content.ReadAsStringAsync().Result;
if (html.Contains("?action=logout"))
Console.WriteLine("Login success");
else
Console.WriteLine("Login fail");
}
}
Am I doing something wrong or is it a bug in HttpWebRequest/CookieContainer?
Here is the full source code for convenience if someone wants to test it: https://gist.github.com/AlexP11223/5c176972426605ee2112 (my test website and login/password also should work)
And Fiddler logs:
http://1drv.ms/1qyAgGi — webbrowser (Chrome), it adds this cookie
http://1drv.ms/1kqsLDI — WebClient
http://1drv.ms/1kqsC2Y —
HttpClient
This is what I use to authenticate myself and make an action:
var uri = new Uri(#"http://localhost:5898/forums/AddPost/1");
var request = WebRequest.Create(uri);
request.Credentials = new CredentialCache { { new Uri(#"http://localhost:5898/Account/Login"), "Basic", new NetworkCredential("asdf", "ghijkl") } };
request.Method = "POST";
const string strPost = "Content=TestAplikacji&AddedValue=0";
StreamWriter myWriter = null;
request.ContentLength = strPost.Length;
request.ContentType = "application/x-www-form-urlencoded";
string response;
try
{
myWriter = new StreamWriter(request.GetRequestStream());
myWriter.Write(strPost);
}
catch
{
}
finally
{
if (myWriter != null) myWriter.Close();
}
var objResponse = (HttpWebResponse)request.GetResponse();
using (var sr = new StreamReader(objResponse.GetResponseStream()))
{
response = sr.ReadToEnd();
sr.Close();
}
When the code executes, in the response I get validation errors for login and password. But my provided login and password ("asdf", "ghijkl") are satisfying those requirements, and that means, the http://localhost:5898/Account/Login is not receiving credentials that I have provided. What am I doing wrong?
I believe, you should use Windows authentication / Basic Authentication, if you want to use the Network credential.
For forms authentication you need to post the username and password to the page just like what a browser will do and read the authentication cookie from the response. You should be using this cookie for communicating further with authenticated pages