Getting Cookie with Flurl 3.0 - c#

I need to call an endpoint to perform and retrieve 3 cookies to call another application's endpoint, and I'm using flurl 3.0 to perform the call.
The login is successful, but the cookie session is always empty. If I perform the call with Postman, I see the cookies and call the other endpoint.
This is the code I'm using:
var loginData = new
{
lt = GetLoginTicket(),
username = _enConfig.Username,
password = _enConfig.Password
};
using var session = new CookieSession(loginUrl);
{
var login = await session.Request(loginUrl).AllowAnyHttpStatus().PostUrlEncodedAsync(loginData);
_jar = login.ResponseMessage.IsSuccessStatusCode ? session.Cookies : null;
}
The documentation of the application says this:
The response code can be:
302, a redirection The authentication is successful. An SSO cookie
(also named ######) is sent in the response header.
I get the 302 Status Code and in the body, I get the correct HTML page, but the cookies are always empty.
Am I missing something?
EDIT:
I've modified the code as follows to have the Cookies. I almost solved my problem. Still, 1 cookie is missing after the post with the Url encoded body.
The code is now:
using var session = new CookieSession(_enConfig.PassportBaseEndpoint);
{
_logger.LogInformation("Getting login ticket");
var ltData = new LoginTicket();
var ltResponse = await session.Request("/login?action=get_auth_params").GetAsync();
if (ltResponse.ResponseMessage.IsSuccessStatusCode)
{
var ltString = await ltResponse.ResponseMessage.Content.ReadAsStringAsync();
ltData = JsonConvert.DeserializeObject<LoginTicket>(ltString);
if (ltData == null || string.IsNullOrEmpty(ltData.Lt))
throw new Exception("Could not deserialize login token");
}
else
throw new Exception("Could not get Login Ticket from enovia");
var loginData = new
{
lt = ltData.Lt,
username = _enConfig.Username,
password = _enConfig.Password
};
var login = await session.Request("/login/").AllowAnyHttpStatus().PostUrlEncodedAsync(loginData);
_jar = login.ResponseMessage.IsSuccessStatusCode ? session.Cookies : null;
}
After the first GET call to retrieve the login Ticket, I also get 2 cookies with a SessionID. When I perform the POST, I must also pass these cookies to have a successful login. In the response, I should get 1 more cookie with a token.
The problem now is that the login is successful, but I don't get the third cookie in the session or the login response.
EDIT 2:
Without Flurl I'm able to get the cookies with this code
CookieContainer cookies = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookies;
handler.ServerCertificateCustomValidationCallback = (a, b, c, d) => true;
var client = new HttpClient(handler);
var response = client.GetAsync($"{_enConfig.PassportBaseEndpoint}/login?action=get_auth_params").Result;
var loginTicket = JsonConvert.DeserializeObject<LoginTicket>(await response.Content.ReadAsStringAsync());
var loginBody = new Dictionary<string, string>();
loginBody.Add("lt", loginTicket.Lt);
loginBody.Add("username", _enConfig.Username);
loginBody.Add("password", _enConfig.Password);
var encodedContent = new FormUrlEncodedContent(loginBody);
response = await client.PostAsync($"{_enConfig.PassportBaseEndpoint}/login", encodedContent);
_logger.LogInformation(await response.Content.ReadAsStringAsync());
Uri uri = new Uri($"{_enConfig.PassportBaseEndpoint}/login");
IEnumerable<Cookie> responseCookies = cookies.GetCookies(uri).Cast<Cookie>();
foreach (Cookie cookie in responseCookies)
Console.WriteLine(cookie.Name + ": " + cookie.Value);

Related

Get Gmail Inbox feed Google.Apis C# .NET

I need to read the gmail inbox feed using Oauth2.0. Simulating in the postman,
Auth URL : https://accounts.google.com/o/oauth2/auth
Access Token URL : https://accounts.google.com/o/oauth2/token
Client ID : XXXXX.apps.googleusercontent.com
Client Secret : XXXXX
Scope : https://mail.google.com/mail/feed/atom
GrantType: Authorization Code
I requested the token and used it on the header
Authorization - Bearer XXXXXXXXXX.
And I made the request via GET right in my scope and got my email feeds. Works!!!
The postman generates a code in C #, but the token expires.
var client = new RestClient("https://mail.google.com/mail/feed/atom/");
var request = new RestRequest(Method.GET);
request.AddHeader("postman-token", "d48cac24-bd3e-07b5-c616-XXXXXXXX");
request.AddHeader("cache-control", "no-cache");
request.AddHeader("authorization", "Bearer ya29.a0AfH6SMDZlUmw0xLHAoYIJuIfTkXXXXXXXXQSPP17GmXT26fJEfWB9w8UiwQ2YF32-nOp6zY9H_lwJEEXXXXXXXXXXXYK4e0tcZkieGbBl5Eow2M-7Gxp20kfDtXXXXXVjiXymLXyMkYEI");
IRestResponse response = client.Execute(request);
I'm trying to do it via Google.Api, using GoogleAuthorizationCodeFlow and already using token refresh.
With the code below, I got authorization from the application, but I can't read the xml atom feed
GoogleAuthorizationCodeFlow flow;
var assembly = Assembly.GetExecutingAssembly();
var clientfile = #"client_secrets.json";
using (var stream = new FileStream(clientfile, FileMode.Open, FileAccess.Read))
{
flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
DataStore = new FileDataStore("StoreTest"),
ClientSecretsStream = stream,
Scopes = new[] { "https://mail.google.com/mail/feed/atom/" }
});
}
var uri = Request.Url.ToString();
var code = Request["code"];
if (code != null)
{
var token = flow.ExchangeCodeForTokenAsync(UserId, code,
uri.Substring(0, uri.IndexOf("?")), CancellationToken.None).Result;
// Extract the right state.
var oauthState = AuthWebUtility.ExtracRedirectFromState(
flow.DataStore, UserId, Request["state"]).Result;
Response.Redirect(oauthState);
}
else
{
var result = new AuthorizationCodeWebApp(flow, uri, uri).AuthorizeAsync(UserId,
CancellationToken.None).Result;
if (result.RedirectUri != null)
{
// Redirect the user to the authorization server.
Response.Redirect(result.RedirectUri);
}
else
{
// The data store contains the user credential, so the user has been already authenticated.
var gmailfeed = new GmailService(new BaseClientService.Initializer
{
HttpClientInitializer = result.Credential,
ApplicationName = "GetFeed",
});
var inboxlistRequest = gmailfeed.Users.Messages.List("me");
inboxlistRequest.LabelIds = "Label_19780355190759038";
inboxlistRequest.IncludeSpamTrash = false;
var emailListResponse = inboxlistRequest.Execute();
foreach (var mail in emailListResponse.Messages)
{
var mailId = mail.Id;
var threadId = mail.ThreadId;
Message message = gmailfeed.Users.Messages.Get("me", mailId).Execute();
Console.WriteLine((message.Snippet));
}
}
}
I got to read the email, but I need the xml atom feed.
Could someone help me how I make this call to get the atom feed, using the granted token. If there is an easier way to do it too, it would be cool to share.
Thank you
Resolved using respsharp, restclient!!
tks

Request an MVC page, from a console app, passing in a context containing FormsAuthentication cookie

I have a console app (that will ultimately run as a worker in Azure) that requests a page from my main ASP.Net MVC site, and converts it to a PDF. That site is secured by forms authentication.
In the code that requests the page, I pass in an HttpContext that contains a valid forms authentication cookie:
byte[] pdfBytes = PdfHelper.UrlToPdfBytes(url, new HttpContextWrapper(GetHttpContext(url)));
And the GetHttpContext method:
private static HttpContext GetHttpContext(string url)
{
var stringWriter = new StringWriter();
var httpRequest = new HttpRequest("", url, "");
var httpResponse = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponse);
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
2,
"remoteuser",
DateTime.Now,
DateTime.Now.AddMinutes(5),
false,
String.Empty,
FormsAuthentication.FormsCookiePath);
// Encrypt the ticket.
string encryptedTicket = FormsAuthentication.Encrypt(ticket);
// Create the cookie.
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket)
{
HttpOnly = true//,
//Domain = new Uri(url).Host
};
httpRequest.Cookies.Add(cookie);
return httpContext;
}
No matter what I try, though, the resulting PDF always contains the login form - the authentication cookie is seemingly ignored.
The cookie names are the same, and the values inside match those of the cookie that exists in the browser, yet I can't get it to work.
The attributes on my controller actions don't even get hit (I have one that ensures the logged in user can access the data being requested), which suggests that it's IIS that is throwing the request to the login form (via the settings in web.config).
I got the request to work by adding the AllowAnonymous attribute on the controller, so I know the request itself works as expected, but I can not get the FormsAuthentication aspect working. I'm only working locally in VS2017 at the moment, using IIS (not IIS Express).
I've set breakpoints in the PDFHelper.UrlToPdfBytes method, and the cookie is there (and the ticket decrypts correctly).
This has been frustrating me for days now, so any help is appreciated.
You can get the cookies from creating a POST request to the login URL, then use those cookies when getting your PDF. Here is a method that returns the HttpCookies for a given request Url. I called it GetAuthCookies.
private static IEnumerable<HttpCookie> GetAuthCookies(string loginUrl, string requestUrl, string username, string password)
{
var formContent = new Dictionary<string, string>();
/* set these keys to the login form input's name values for username/password
I.E. If you have a login form like this
<form>
<input name="userName" id="userName_21342353465" type="text" />
<input name="password" id="password_21342353465" type="password" />
</form>
then you would use "userName" and "password" for your keys below.
*/
formContent.Add("userName", username);
formContent.Add("password", password);
// add any other required (or optional) fields in the form...
var cookieContainer = new CookieContainer();
var content = new FormUrlEncodedContent(formContent);
var handler = new HttpClientHandler { CookieContainer = cookieContainer };
var cookieCollection = new CookieCollection();
using (var client = new HttpClient(handler))
{
using (var response = client.PostAsync(loginUrl, content).Result)
{
// Below is some getting the resposne string, you can use this to determine login status, may help with finding missing values in request
//var responseString = response.Content.ReadAsStringAsync().Result;
}
foreach (var cookie in cookieContainer.GetCookies(new Uri(requestUrl)).Cast<Cookie>())
{
cookieCollection.Add(cookie);
}
foreach (var cookie in cookieCollection.Cast<Cookie>())
{
yield return new HttpCookie(cookie.Name, cookie.Value);
}
}
}
You can hard code the values, use config for them or have the user enter them from the console... here I hard coded the LoginUrl and have get the username and password from the console, here is the code changes GetHttpContext along with the hard coded LoginUrl and two methods to retrieve the UserName/Password from the console.
private static HttpContext GetHttpContext(string url)
{
var stringWriter = new StringWriter();
var httpRequest = new HttpRequest("", url, "");
var httpResponse = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponse);
var username = GetUserName();
var password = GetPassword();
var cookies = GetAuthCookies(LoginUrl, url, username, password);
foreach (var cookie in cookies)
{
httpContext.Request.Cookies.Add(cookie);
}
return httpContext;
}
private const string LoginUrl = #"{{ Login's Post Back URL }}";
private static string GetPassword()
{
var password = new StringBuilder();
while (password.Length == 0)
{
Console.Write("Enter password: ");
ConsoleKeyInfo key;
while ((key = Console.ReadKey(true)).Key != ConsoleKey.Enter)
{
switch (key.Key)
{
case ConsoleKey.Backspace:
if (password.Length > 0)
{
password.Remove(password.Length - 1, 1);
Console.Write(key.KeyChar);
Console.Write(' ');
Console.Write(key.KeyChar);
}
break;
default:
password.Append(key.KeyChar);
Console.Write('*');
break;
}
}
Console.WriteLine();
}
return password.ToString();
}
private static string GetUserName()
{
var username = string.Empty;
while (string.IsNullOrWhiteSpace(username))
{
Console.Write("Enter username: ");
username = Console.ReadLine();
}
return username;
}
FormsAuthentication.Encrypt uses the keys from machine.config on which you execute this console app.
Instead you could try 'logging in' using HttpRequest/Response and use the cookies you received in response for your 'pdf' request.

C# - How to redirect with HttpRequestMessage

I have problem to redirect to other page, for example, while i am keeping Credential Cache. I'm using:
cCache.Add(new Uri("http://mypage.com"), "Basic", new NetworkCredential("admin", "admin"));
and with:
using (var client = new HttpClient(new HttpClientHandler { Credentials = cCache }))
{
var request = new System.Net.Http.HttpRequestMessage()
{
RequestUri = new Uri("http://mypage.com"),
Method = HttpMethod.Get,
};
var result = new HttpResponseMessage();
var requestTask = client.SendAsync(request).ContinueWith((argRequestTask) =>
{
result = argRequestTask.Result;
});
requestTask.Wait();
}
status code that i get is 200 OK, and that is good. Result that i get is content of that page and that is good to, but im still on same page. If i redirect with:
Response.Redirect("http://mypage.com");
i lose my credentials. How i can solve this? tnx

How to "refresh" cookie returned with HttpClient

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.

c# windows phone and store app post data to php with cookie

I'm working with one of user controlled application. This app is connecting with PHP files to login and do any other processes that requested.
in PHP I'm using session for detect if user already login. When they login successfully php sets and send login token id successfully.
I already working with IOS for same project and in IOS login process is working fine. IOS uses cookies automatically but I couldn't use cookies for windows app. Session has named cookie token id and php file checks token id with posted. I stuck in windows but I have no problem with IOS.
In windows store and phone app I can post perfectly and I can get respond message too. I can login perfectly as well. But when I navigate to second page of app it checks we are login or not again. When it check I'm facing with not logged in message. In my IOS app run perfectly. But in windows session token id check fails..
this is my php file that checks session token id in second page:
$tokencheck = $_POST['Tokenid'];
if($_SESSION["token"] == $tokencheck){
first page is login page and when login successfull windows phone gets token id and saves it successfully. After login, in a second page saved token id posting to php that I shared. I checked token id and thats true.
I do some research and I found problem is cookies. I can't use cookies right now. I did some more codes but still I stuck and couldn't solved this problem.
Codes sends post perfectly and also gets respond messages perfectly but I couldn't check session token id, this is because login check fails.
First page, first attempt with httpClient
var values = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("Email", txtEmail.Text),
new KeyValuePair<string, string>("Password", txtPassword.Password)
};
string url = ".../login.php";
CookieContainer Cookiejar = new CookieContainer();
var handler = new HttpClientHandler
{
CookieContainer = Cookiejar,
UseCookies = true//,
// UseDefaultCredentials = false
};
var httpClient = new HttpClient(handler);
httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
HttpResponseMessage response = await httpClient.PostAsync(url, new FormUrlEncodedContent(values));
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
var wwwUri = new Uri(url);
Cookiejar.SetCookies(wwwUri, Cookiejar.GetCookieHeader(wwwUri));
First page second attempt with HttpWebRequest
CookieContainer cookies = new CookieContainer();
HttpWebRequest getRequest = (HttpWebRequest)WebRequest.Create(url);
getRequest.CookieContainer = cookies;
getRequest.Method = "POST";
HttpWebResponse response = (HttpWebResponse)await getRequest.GetResponseAsync();
var sr = new StreamReader(response.GetResponseStream() );
string responseString = sr.ReadToEnd();
I also tried some other codes that I found in internet but can't solved yet.
Second page is also :
object lgntoken = Windows.Storage.ApplicationData.Current.LocalSettings.Values["logintokenid"];
var values = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("Tokenid", Convert.ToString(lgntoken))
};
string url = ".../get_projects_list.php";
CookieContainer Cookiejar = new CookieContainer();
var wwwUri = new Uri(url);
// Cookiejar.SetCookies(wwwUri, Cookiejar.GetCookieHeader(wwwUri));
Cookiejar.GetCookieHeader(wwwUri);
var handler = new HttpClientHandler
{
CookieContainer = Cookiejar,
UseCookies = true
};
System.Diagnostics.Debug.WriteLine("Login Token: " + Convert.ToString(lgntoken) + "..");
var httpClient = new HttpClient(handler);
HttpResponseMessage response = await httpClient.PostAsync(url, new FormUrlEncodedContent(values));
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
System.Diagnostics.Debug.WriteLine("Project List Data : " + responseString + " ++ Login Token: " + Convert.ToString(lgntoken) + "..");
I have some experiance in IOS but I'm newbie in windows store apps. I stuck about this issue and already not understood what should I do, how can I set or get cookies in a first and second page. Waiting your helps, thank you.
SOLVED !
problem is cookies resets in second page. And also need define cookies..
using cookies with httpclient :
public static CookieContainer cookies = new CookieContainer();
var handler = new HttpClientHandler
{
CookieContainer = cookies,
UseCookies = true,
UseDefaultCredentials = false
};
HttpClient htclient = new HttpClient(handler);
Full code example
First page login screen :
public static CookieContainer cookies = new CookieContainer();
private async void btnLogin_Click(object sender, RoutedEventArgs e)
{
...
var values = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("Email", txtEmail.Text),
new KeyValuePair<string, string>("Password", txtPassword.Password)
};
string url = ".../login.php";
var handler = new HttpClientHandler
{
CookieContainer = cookies,
UseCookies = true,
UseDefaultCredentials = false
};
HttpClient htclient = new HttpClient(handler);
htclient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
HttpResponseMessage response = await htclient.PostAsync(url, new FormUrlEncodedContent(values));
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
Second page should use first page cookies.. After login :
var values = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("Tokenid", Convert.ToString(lgntoken))
};
string url = ".../get_projects_list.php";
var handler = new HttpClientHandler
{
CookieContainer = MainPage.cookies,
UseDefaultCredentials = true,
UseCookies = true
};
System.Diagnostics.Debug.WriteLine("Login Token: " + Convert.ToString(lgntoken) + "..");
// var httpClient = new HttpClient(handler);
HttpClient htclient = new HttpClient(handler);
htclient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
HttpResponseMessage response = await htclient.PostAsync(url, new FormUrlEncodedContent(values));
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
System.Diagnostics.Debug.WriteLine("Project List Data : " + responseString +
" ++ Login Token: " + Convert.ToString(lgntoken) + "..");
...
}

Categories