Setting Cookies in a HTTPModule - asp.net

I'm having problems setting cookies in a custom http module. I'm handling the BeginRequest event and setting the cookie along these lines:
HttpCookie myCookie = new HttpCookie(config.CookieName);
SitePrefCookie["key1"] = value1;
SitePrefCookie["key2"] = value2;
SitePrefCookie.Expires = DateTime.Now.AddDays(365);
SitePrefCookie.Domain = myDomain;
SitePrefCookie.Path = "/";
HttpContext.Current.Response.Cookies.Add(SitePrefCookie);
I know there's nothing wrong with the cookie itself, because it will work if I call Response.Flush() or set the status to 302 (as it's a http module for redirecting). But I can't do both in one request, i.e. flush and then redirect because ASP.Net will complain about headers already being sent to the client.
I'm sure I can work around this weird limitation, but I just don't understand why the cookie is not even being sent back in the response in a scenario that doesn't require a redirect or flushing of the response.

This is a known issue. See http://support.microsoft.com/kb/2666571 for details and a workaround.

Related

Spring #RestController not setting cookies with response

I have the following rest endpoint that I would like to send a cookie along with my ResponseEntity. However after succesfully sending the response, the cookie is nowhere to be found.
#RequestMapping(value = "myPath", method = RequestMethod.POST)
public ResponseEntity<?> createToken(HttpServletResponse response)
final String token = "a1b2c3d4e";
Cookie cookie = new Cookie("token", token);
response.addCookie(cookie);
// Return the token
return ResponseEntity.ok(new MyCustomResponse(token));
}
MyCustomResponse
class MyCustomResponse {
private final String token;
public MyCustomResponse(String token) {
this.token = token;
}
}
I have also tried creating the ResponseEntity manually and setting the cookie in the headers with the "Set-Cookie" header but same thing, no cookie.
edit: I have confirmed that the Set-Cookie header is in fact present in the response, however it is not actually being stored in the browser. I am using a static web-app running in WebStorm to access my rest endpoint running on a different port. This web app is just using JQuery's $ajax method to call the REST endpoint, nothing fancy. When I run the web app in WebStorm I am able to see the cookie it creates in my browser, so that confirms my browser is allowing cookie storage.
I figured it out. My web application I am using to access my rest api is running on a different local port than my rest api. This causes the AJAX request to fail CORS requirements, thus the cookie doesnt actually get set.
I found the solution here What can cause a cookie not to be set on the client?
edit: I should add that it was adding the xhrFields snippet to JQuery's $ajax method that fixed it for me, the other parts weren't necessary.
(posting the answer below in case it gets deleted)
I think I found the solution. Since during development, my server is at "localhost:30002" and my web app at "localhost:8003", they are considered different hosts regarding CORS. Therefore, all my requests to the server are covered by CORS security rules, especially Requests with credentials. "Credentials" include cookies as noted on that link, so the returned cookie was not accepted because I did not pass
xhrFields: {
withCredentials: true
}
to jQuery's $.ajax function. I also have to pass that option to subsequent CORS requests in order to send the cookie.
I added the header Access-Control-Allow-Credentials: true on the server side and changed the Access-Control-Allow-Origin header from wildcard to http://localhost:8003 (port number is significant!). That solution now works for me and the cookie gets stored.

Cookie Persistence : cannot set expiry date

I am attempting to change an MVC web site that was storing some info in Session to store it in a cookie but am having some issues with the expiry of the cookie.
Scenario
While logging in to the site a cookie is created and added to the current HttpContext Response cookies collection. The code piece used to write the cookie is as follows :
HttpCookie userCredsCookie = _context.Response.Cookies.Get(Constants.Web.UserCredentialsCookieName) ?? new HttpCookie(Constants.Web.UserCredentialsCookieName);
userCredsCookie.HttpOnly = true;
userCredsCookie.Secure = true;
userCredsCookie.Value = user.ID.ToString();
userCredsCookie.Expires = DateTime.Now.AddMinutes(user.CompanySessionTimeout + user.TimezoneOffset);
_context.Response.Cookies.Add(userCredsCookie);
I have experimented with removal of the line that sets the expiry of the cookie and have found that the cookie is generated and stored at the client as expected.
When i inspect the response from the server using Fiddler i can see that the userCreds cookie is being returned :
Response sent 111 bytes of Cookie data:
Set-Cookie: UserCreds=e2ce8200-fb38-45b9-8aec-4d93e6640a84; expires=Mon, 09-Oct-2017 16:20:43 GMT; path=/; secure; HttpOnly
when the expiry is not set the response has :
Response sent 72 bytes of Cookie data:
Set-Cookie: UserCreds=e2ce8200-fb38-45b9-8aec-4d93e6640a84; path=/; secure; HttpOnly
As long as the expiry is not set, the cookie is accepted by the browser and sent on the subsequent requests made from the browser.
It feels like the problem is due to the format of the date in the response but i cannot find anything to allow me to change that format.
Any help would be greatly appreciated.
Regards
Monty
Update - Possible cause located
I have found that the problem i am having is to do with timing. It seems that my dev pc may be processing the responses from the ajax call that is setting the cookie too fast (??? is that even possible ???).
While i was debugging the front end code to ensure things look fine from that perspective i found that by placing a break point just before the line which was setting the window.location to a url that is in the result of the ajax call, the subsequent call was made with the cookie being on the request.
Perhaps, by causing the processing of the callback to pause, gave the system time to persist the cookie. I assume the process of persisting a cookie is to do a validation check then store the data. That would mean that it takes a little longer to validate the cookie when their is an expiry date set. Perhaps the reason why a cookie with no expiry set was persisting but ones with an expiry were not was cause the load of a new url interrupted the process, thus causing the cookie to not get stored.
By adding a 1 millisecond delay (yep, just 1 millisecond) in the success handler of the ajax call i was able to successfully have the cookie persisted to the browser and included in the call made to load a return url that is in the ajax response.
Does this sound even possible?
try with this code
int result = Convert.ToInt32(user.CompanySessionTimeout.ToString()) + Convert.ToInt32(user.TimezoneOffset)
and then pass
userCredsCookie.Expires = DateTime.Now.AddMinutes(result);

Error with 'Access-Control-Allow-Origin'

I have a webpage I made with angular 2 that is a form and at the end of it the form must be sent to my java server. But I'm unable to send, I get the error Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access.
I have already changed my server so it will send the Access-Control-Allow headers. The function is like this:
public static Response buildResponse(int status, Object reponseObject, MediaType mediaType) {
Response.ResponseBuilder rb = Response.status(status).entity(reponseObject);
if (mediaType != null) {
rb = rb.type(mediaType);
}
rb = rb.header("Access-Control-Allow-Origin", "*");
rb = rb.header("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE, OPTIONS");
rb = rb.header("Access-Control-Allow-Headers", "Origin, Content-Type, X-Auth-Token, accept");
rb = rb.header("Access-Control-Allow-Credentials", true);
System.out.println("teste");
return rb.build();
}
I have also tried to add the header 'Access-Control-Request-Method': 'POST' to the page request, but I still get the error. The problem is not with the software functionality itself because it works fine if I install and activate the CORS plugin for chrome, but I can't request the user to install the plugin to use my site. Does anyone knows what I'm missing? I have been sarching for sometime and the only solutions I found was to disable this browser security (I can't ask the user to do that) and to add the headers on the request, what I already have.
I was finally able to solve my problem. The problem was in the communication between my page and my server. The CORS block is imposed by the browser and the server needs to send a header authorizing the access (the headers I put in the function in the question). What I was missing was the header in the OPTION method. My JavaScript code automatically send and OPTIONS request (the preflight request) before the actual GET or POST method. What I did was to manually implement the OPTIONS method in the server that respond with the necessary headers.

Cookie handling in Google Apps Script - How to send cookies in header?

I'm trying to write a simple script that fetches text from a webpage and processes that string. But, that website requires me to be logged in. I was successful in logging in to that website. This is how I logged in:
var payload = {"name1":"val1","name2":val2"};
var opt ={"payload":payload,"method":"post"};
var respose = UrlFetchApp.fetch("http://website.com/login",opt);
After logging in, the website places me in http://website.com/home. I checked response.getContentText() and I can confirm that I have been logged in successfully as it contains the text from http://website.com/home.
Now I need to get the contents of http://website.com/page and process it.
I first assumed the script can handle cookies by itself and proceeded with
var pagedata = UrlFetchApp.fetch("http://website.com/page);//Did not work
That obviously didnt work and pagedata.getContentText() says me to login first, which indicates cookies were not successfully passed..
I then tried to extract cookies which the server responded during login and to send it along with this request.
var cookie = response.getAllHeaders()['Set-Cookie'];
// variable cookie now contains a legitimate cookie.
// It contains 'JSESSIONID=blabla;Path=/' and
// it is the ONLY cookie that server responds.
I tried to send that cookie in my page request.
var header = {'Cookie':cookie};
var opt2 = {"header":header};
var pagedata = UrlFetchApp.fetch("http://website.com/page",opt2);
I think even now cookies were not properly sent, as the content again says me to login.
Am I passing cookies correctly? I need help regarding the correct method of sending cookies in a request.
Here you can find cookies specification:
http://www.w3.org/Protocols/rfc2109/rfc2109
You have a potential issue in your code:
response.getAllHeaders()['Set-Cookie'] can return either a string or a table of string if multiple 'set-cookie' attributes are sent back from the server.
Eric is right, you cannot return the cookie without digesting it.
Second error in your code:
var opt2 = {"header":header};
should be
var opt2 = {"headers":header};
Be aware also that GAS uses Google IPs. It can happen that two consecutive fetch use different IPs.
The server your are connecting to may be session-IP dependant.
Are you sure the server only send you back one cookie after an authentification ?
It looks like you are setting the headers correctly in UrlFetchApp.fetch().
I believe that the data in the Set-Cookie header is in a different format than the data that is expected in Cookie header. For example, Set-Cookie contains information about expiration, etc.

Why do I get "Cannot redirect after HTTP headers have been sent" when I call Response.Redirect()?

When I call Response.Redirect(someUrl) I get the following HttpException:
Cannot redirect after HTTP headers have been sent.
Why do I get this? And how can I fix this issue?
According to the MSDN documentation for Response.Redirect(string url), it will throw an HttpException when "a redirection is attempted after the HTTP headers have been sent". Since Response.Redirect(string url) uses the Http "Location" response header (http://en.wikipedia.org/wiki/HTTP_headers#Responses), calling it will cause the headers to be sent to the client. This means that if you call it a second time, or if you call it after you've caused the headers to be sent in some other way, you'll get the HttpException.
One way to guard against calling Response.Redirect() multiple times is to check the Response.IsRequestBeingRedirected property (bool) before calling it.
// Causes headers to be sent to the client (Http "Location" response header)
Response.Redirect("http://www.stackoverflow.com");
if (!Response.IsRequestBeingRedirected)
// Will not be called
Response.Redirect("http://www.google.com");
Once you send any content at all to the client, the HTTP headers have already been sent. A Response.Redirect() call works by sending special information in the headers that make the browser ask for a different URL.
Since the headers were already sent, asp.net can't do what you want (modify the headers)
You can get around this by a) either doing the Redirect before you do anything else, or b) try using Response.Buffer = true before you do anything else, to make sure that no output is sent to the client until the whole page is done executing.
A Redirect can only happen if the first line in an HTTP message is "HTTP/1.x 3xx Redirect Reason".
If you already called Response.Write() or set some headers, it'll be too late for a redirect. You can try calling Response.Headers.Clear() before the Redirect to see if that helps.
Just check if you have set the buffering option to false (by default its true). For response.redirect to work,
Buffering should be true,
you should not have sent more data using response.write which exceeds the default buffer size (in which case it will flush itself causing the headers to be sent) therefore disallowing you to redirect.
Using
return RedirectPermanent(myUrl) worked for me
You can also use below mentioned code
Response.Write("<script type='text/javascript'>"); Response.Write("window.location = '" + redirect url + "'</script>");Response.Flush();
There is one simple answer for this:
You have been output something else, like text, or anything related to output from your page before you send your header. This affect why you get that error.
Just check your code for posible output or you can put the header on top of your method so it will be send first.
If you are trying to redirect after the headers have been sent (if, for instance, you are doing an error redirect from a partially-generated page), you can send some client Javascript (location.replace or location.href, etc.) to redirect to whatever URL you want. Of course, that depends on what HTML has already been sent down.
My Issue got resolved by adding the Exception Handler to handle
"Cannot redirect after HTTP headers have been sent". this Error as shown below code
catch (System.Threading.ThreadAbortException)
{
// To Handle HTTP Exception "Cannot redirect after HTTP headers have been sent".
}
catch (Exception e)
{//Here you can put your context.response.redirect("page.aspx");}
I solved the problem using:
Response.RedirectToRoute("CultureEnabled", RouteData.Values);
instead of Response.Redirect.
Be sure that you don't use Responses' methods like Response.Flush(); before your redirecting part.
Error
Cannot redirect after HTTP headers have been sent.
System.Web.HttpException (0x80004005): Cannot redirect after HTTP headers have been sent.
Suggestion
If we use asp.net mvc and working on same controller and redirect to different Action then you do not need to write.. Response.Redirect("ActionName","ControllerName"); its better to use only return RedirectToAction("ActionName"); or return View("ViewName");
The redirect function probably works by using the 'refresh' http header (and maybe using a 30X code as well). Once the headers have been sent to the client, there is not way for the server to append that redirect command, its too late.
If you get Cannot redirect after HTTP headers have been sent then try this below code.
HttpContext.Current.Server.ClearError();
// Response.Headers.Clear();
HttpContext.Current.Response.Redirect("/Home/Login",false);
There are 2 ways to fix this:
Just add a return statement after your Response.Redirect(someUrl);
( if the method signature is not "void", you will have to return that "type", of course )
as so:
Response.Redirect("Login.aspx");
return;
Note the return allows the server to perform the redirect...without it, the server wants to continue executing the rest of your code...
Make your Response.Redirect(someUrl) the LAST executed statement in the method that is throwing the exception. Replace your Response.Redirect(someUrl) with a string VARIABLE named "someUrl", and set it to the redirect location... as follows:
//......some code
string someUrl = String.Empty
.....some logic
if (x=y)
{
// comment (original location of Response.Redirect("Login.aspx");)
someUrl = "Login.aspx";
}
......more code
// MOVE your Response.Redirect to HERE (the end of the method):
Response.Redirect(someUrl);
return;

Resources