Setting persistent cookie from Java doesn't work in IE - cookies

All,
Although I see related topics on the forum, but I don't see a clear solution on this issue.
I am trying to set a javax.servlet.http.Cookie with an expiration time (so that it persists across browser sessions). Code:
public void respond(HttpServletRequest req, HttpServletResponse resp) {
int expiration = 3600;
Cookie cookie = new Cookie("TestCookie", "xyz");
cookie.setDomain("");
cookie.setVersion(0);
cookie.setPath("/");
cookie.setMaxAge(expiration);
cookie.setSecure(false);
resp.addCookie(cookie);
}
I don't see this cookie being set when I check in IE developer tools. Searching on the internet gave me clues that IE doesn't consider Max-Age, but only works with Expires. If this does not work for IE, then is there a proven way of setting the HTTP response headers for a persistent cookie so that it works for IE?
PS: This works fine on all other browsers.
I tried creating a string for the cookie having expires attribute. IE succeeded in creating it, but it lost the domain (default - "") and showed ".com" and turned it into a session cookie instead of a persistent cookie. This again works fine on all other browsers.
Please help.
Thanks.

Working with IE9, I found that it was the HttpOnly attribute that was required in order to get it to echo the cookie value on subsequent posts, e.g:
Set-Cookie: autologCk1=ABCD; Path=/autolog/; HttpOnly

The answer is at Persistent cookies from a servlet in IE.
Your case may be a different flavour of the same issue: that is, by prefixing the domain with a "." (which I'm pretty sure is a version 1 cookie feature), something in the Java stack decides it's a version 1 cookie (unrecognized and not persisted by IE, even IE8) and sends that cookie format.
Or, as that answer suggests, is there something in your cookie value that contains an unrecognized character?

As javax.servlet.http.Cookie does not allow you to set Expires attribute to the cookie, you should set it manually.
You also need to know that Expires must be specified in the form of Wdy, DD Mon YYYY HH:MM:SS GMT following RFC-2616 Full Date section (more info).
In Java you can do it this way:
public void respond(HttpServletRequest req, HttpServletResponse resp) {
int expiration = 3600;
StringBuilder cookie = new StringBuilder("TestCookie=xyz; ");
DateFormat df = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss 'GMT'", Locale.US);
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 3600);
cookie.append("Expires=" + df.format(cal.getTime()) + "; ");
cookie.append("Domain=; ");
cookie.append("Version=0; ");
cookie.append("Path=/; ");
cookie.append("Max-Age=" + expiration + "; ");
cookie.append("Secure; ");
resp.setHeader("Set-Cookie", cookie.toString());
}

Related

Adding Same-site; Secure to Cookies in Classic ASP

We are running a classic ASP website, and having issues with Cookies in Chrome browser. Chrome is enforcing the cookie to be set securely (https://www.chromestatus.com/feature/5633521622188032)
We are setting a cookie as follows:
Response.AddHeader "Set-Cookie", "TestCookie=This is a Test; path=/; SameSite=None; Secure"
Response.Cookies("TestCookie").Expires = Date + 1
However, this has issues with Chrome, where sessions end abruptly when a resource of a different domain is called.
Chrome's cookie details show this:
Send for
Same-site connections only
Note there is no mention of "secure" as I think there should be.
What is the correct way of setting the Cookie in classic ASP for this?
There is a problem with your current approach to setting the Response Cookie.
By using Response.Cookies after setting the header using Set-Cookie you are in effect creating a new empty cookie called "TestCookie". Instead, you want to incorporate the expiry into the existing Set-Cookie header.
Testing your code, this is the Response header contents:
<%
Function FormatCookieDateTime(interval, value, tz)
Dim dt: dt = DateAdd(interval, value, Date())
Dim tm: tm = Time()
Dim result: result = WeekDayName(WeekDay(dt), True) & ", " & _
Right("00" & Day(dt), 2) & "-" & _
MonthName(Month(dt), True) & "-" & _
Year(dt) & " " & _
Right("00" & Hour(Time()), 2) & ":" & _
Right("00" & Minute(Time()), 2) & ":" & _
Right("00" & Second(Time()), 2) & " " & tz
FormatCookieDateTime = result
End Function
Response.AddHeader "Set-Cookie", "TestCookie=This is a Test; path=/; SameSite=None; Secure; expires=" & FormatCookieDateTime("d", 1, "GMT")
%>
Built a function that makes setting the expiry using the correct format easier.
Remember Secure is for Secure Connections
Because you are setting two cookies (one via AddHeader() and one via Response.Cookie) it might not be clear but the first cookie with Secure set will be ignored by chrome if the connection is not using HTTPS. In fact, if you look at the request in Chrome Dev Tools you should see a warning symbol next to the Set-Cookie header that says (when hovered over) something along the lines of;
This set-cookie had the "Secure" attribute but was not received over a secure connection.
The standard Response.Cookies method doesn't work reliably with cookies set by using the more low-level Reponse.Addheader. I have experienced the same thing.
I'm not able to test, but you might want to try two things:
don't use these two instructions in the same ASP codeblock. My guess is that setting the cookie using AddHeader() will bypass classic ASP's cookie collection. So Classic ASP will not know that this cookie has been set. What you could try is setting this cookie on one page, sending it to the browser, and on a different page set the expiration.
Try and set the expiration using the same AddHeader() instruction. You will have to look up how this is done on a header level, but it should certainly be possible.
I have some example code online that sets a secure and HTTPOnly cookie, using Response.AddHeader(), but it doesn't set an expiration, which results in a cookie that expires when the browser(tab) is closed:
https://gitlab.com/erik4/classic-asp-book-examples/-/blob/master/global.asa

Response cookies using for next requests

As I see from the answer for this question: Karate will automatically send any cookies returned by the server in the next request.
But when I send the request I see two sets of cookies in Set-Cookie of response: one is auto-created and another is real, that returned from the server.
When I printed responseCookies, I saw there only automatic cookies
and for the next request new cookies are generated and sent.
For my test I need to use cookies returned after the first request because it is a call to login service.
Feature: Using cookies in next request
Background:
Given url baseUrl
And path LOGOUT_SERVICE_ENDPOINT
And configure headers = read('classpath:headers.js')
And def filename = 'classpath:resources/users/' + brand.toLowerCase() + '/user.json'
And json user = read(filename)
Scenario: Login
When def login = callonce read('classpath:features/login_service/login.feature') user
* print login.responseCookies
And request { arg1: '#(brand)'}
And method post
Then status 200
What is wrong in my feature or it is Karate issue?
two sets of cookies in Set-Cookie of response:
Maybe that is a bug in the server ?
Also try using "shared scope", because cookies also will be part of the "global" variables etc.
* callonce read('classpath:features/login_service/login.feature') user
* request { arg1: '#(brand)'}
If you are still stuck, please follow this process: https://github.com/intuit/karate/wiki/How-to-Submit-an-Issue

Set cookie to show div only once per session

I have a div with a Welcome message that shows up before the index page and I want that div to appear only once per session.
How would I set a cookie to show a div only once?
I never used cookies before for something like this, normally I would use Local Storage but this website is visited frequently by older browsers and that's a problem.
You may take a look at the documentation of the document.cookie and more specifically example 3:
if (document.cookie.replace(/(?:(?:^|.*;\s*)someCookieName\s*\=\s*([^;]*).*$)|^.*$/, "$1") !== "true") {
alert("Do something here!");
document.cookie = "someCookieName=true; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/";
}
In this example the cookie is created with expires flag meaning that it will be persistent and survive browser restarts. If you want to perform the action only once per browser session simply remove the expires flag when setting the cookie:
if (document.cookie.replace(/(?:(?:^|.*;\s*)someCookieName\s*\=\s*([^;]*).*$)|^.*$/, "$1") !== "true") {
alert("Do something here!");
document.cookie = "someCookieName=true; path=/";
}

Django cookie age not setting

I am trying to create a way for my users to have a "remember me" option when they log in. However as it stands, no matter what I try, when I view the cookie in my browser, it just shows Expires: At end of session. So once I close my browser and come back to my page, I am logged out.
In my settings.py I have set
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
SESSION_COOKIE_AGE = 10000000
Which I assume are what I need to do...
On the front-end (which is in AngularJS) I have the following for the cookie storage:
$http.post('http://localhost:8000/auth/login/', {email: this.login['arguments'][0]['email'], password: this.login['arguments'][0]['password']})
.success(function(response){
if (response.token){
$cookieStore.put('djangotoken', response.token);
$http.defaults.headers.common['Authorization'] = 'Token ' + response.token;
}
})
.error(function(data, status, headers, config){
$cookieStore.remove('djangotoken');
});
If someone could show me how to get my cookies to just stay for the designated age I set that would be greatly appreciated!
You're setting the cookie directly from Angular, so it has nothing to do with the Django session cookie age. You need to pass the expiration time when you set the cookie.
Unfortunately, the built-in Angular cookieStore api is unnecessarily restrictive and does not support this. It looks like a good alternative is angular-cookie.

what are the values in _ga cookie?

I am using universal analytics. universal analytics creates first party cookie _ga
_ga=1.2.286403989.1366364567;
286403989 is clientId
1366364567 is timestamp
what is 1 and 2 in _ga cookie?
_ga=1.2.286403989.1366364567;
1st Field
This is a versioning number. In case the cookie format changes in the future. Seems to be fixed at 1 at the moment. The one above is an old format. Newer cookies have this value set at "GA1"
2nd Field
This field is used to figure out the correct cookie in case multiple cookies are setup in different paths or domains.
By default cookie are setup at path / and at the domain on document.location.hostname (with the www. prefix removed).
You could have a _ga cookie set at sub.example.com and another cookie set at example.com. Because the way the cookie API on browsers works there's no way to tell which is the correct cookie you use.
So the second number is the number of components (dot separated) at the domain.
for sub.example.com the number would be 3
for example.com the number would be 2
The path defaults to / but you can also change it by passing the cookiePath option to the ga.create method. If you pass it this field becomes 2 numbers dash separated. And the second number is the number slashes in the path.
Using these numbers the analytics.js script can correctly identify the cookie to be used in case there are multiple cookies set.
eg:
Imagine that you have a site that lives at sub1.sub2.example.com/folder1 in case you want to store the cookie only on your site and not make it visible to other subdomains or folders you can use the following configs:
ga('create', 'UA-XXXX-Y', {
'cookiePath': '/folder1/',
'cookieDomain': 'sub1.sub2.example.com'
});
In this case the cookie will look somoething like this;
_ga=1.4-2.XXXXXXXX.YYYYYYY
3rd Field
This is a random generated user ID. Used to identify different users.
4th Field
It's a timestamp of the first time the cookie was set for that user.
new Date(1366364567*1000)
> Fri Apr 19 2013 06:42:47 GMT-0300 (BRT)
This is also used to uniquely identify users in case of userId collisions.
Worth mentioning that a cookie is not an API. In the future it may completely change. Google doesn't recommend reading/writing the _ga cookie directly. You should interact with Google Analytics through one of the tracking libraries such as analytics.js. There's not a lot of use for this information other than curiosity.
If you are reading/writing directly the cookie you are doing it wrong.
I think this would be helpful.
/**
* Get Google Analytics UID
* #return int
*/
public function getGAUID() {
$uid = 0;
if ($_COOKIE['__utma'])
list($hash_domain, $uid, $first_visit, $prew_visit, $time_start, $num_visits) = sscanf($_COOKIE['__utma'], '%d.%d.%d.%d.%d.%d');
elseif ($_COOKIE['_ga'])
list($c_format, $c_domain, $uid, $first_visit) = sscanf($_COOKIE['_ga'], 'GA%d.%d.%d.%d');
return $uid;
}
Written in NodeJS with ES6 Syntax. Might help someone?
// Example: GA1.2.494614159.1574329064
const gaCookieGeneration = ({ version = 1, domain, rootpath = '/' }) => {
const subdomains = (domain.match(/./) || []).length + 1;
const rootpathDirs = (rootpath.match(/\//) || []).length;
const cookiePath = rootpathDirs > 1 ? `-${rootpathDirs}` : '';
const uniqueId = Math.random().toString().substr(2, 9);
const timeStamp = (+new Date()).toString().substr(0, 10);
return `GA${version}.${subdomains}${cookiePath}.${uniqueId}.${timeStamp}`;
};
const gaCookie = gaCookieGeneration({
domain: '.example.com',
});