Dynamically set session cookie max age - session

I'm using Jetty's session management and want to implement a simple login with a 'Remember me' option.
So if the user doesn't want to be remembered, I want the JSESSIONID cookie to live until the browser session is closed. If the user opts-in to be remembered, the cookie will expire within 30 days.
I'm using SessionCookieConfig to configure the cookie details on startup and I can't change that per request.
So is there a way to dynamically change the max age per login request?
The only way I can see is to get the cookie from the request and then change the max age:
//in LoginServlet
doPost(HttpServletRequest request, HttpServletResponse response) {
//... Get remember me option from request
request.getCookies();
//... Find cookie in array by name JSESSIONID
if (rememberMe) {
sessionCookie.setMaxAge(60 * 60 * 24 * 30);
} else {
sessionCookie.setMaxAge(-1);
}
}
However I want to refrain as much as possible from dealing with the session and leave it to the container.
Is there another option?

The jsessionid cookie is handled by the container and so I wouldn't recommend that you try and repurpose it. Instead, there's a wealth of info on stackoverflow on how to implement a "remember me" function: try here.

Related

Strategy for logging Tomcat expired sessions

My web application logs user logins and logouts on a database table, so that we keep track of user activity.
This works well when users explicitly login or logout, since the underlying authentication code is responsible for these logging events.
However, when the user leaves the computer idle for 30 minutes, the web session will expire and we'll have one Login event without the corresponding Logout event.
The only strategy I've thought for resolving this is to have a javascript event that triggers before the 30 minutes defined in the web.xml and that kicks the user out before the session expires. However, that has the limitation of needing javascript, and when someone uses multiple tabs it can have some unexpected results (i.e., user is using tab2 and session is expired in tab1).
What would you suggest for this case?
You want to use an HttpSessionListener. It's a standard interface defined by the Servlet Specification for exactly this kind of situation.
See below for a caveat.
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class LogoutListener implements HttpSessionListener {
#Override
public void sessionDestroyed(HttpSessionEvent event) {
HttpSession session = event.getSession();
// NOTE: You need to store some kind of user identification
// in the session. My example just has a "username"
// in here.
String username = session.getAttribute("username");
// If the username is null, there was no logged-in user
// to log-out (e.g. non-authenticated user).
if(null != username) {
// NOTE: Obviously, this needs to call your existing
// "logout"-recording code. Just an example.
MyDBUtilities.registerLogout(username, System.currentTimeMillis());
}
}
#Override
public void sessionCreated(HttpSessionEvent event) {
// Ignore
}
}
If you use the code above, you'll find that users who don't explicitly log out will get a single logout record, and users who do explicitly log out will get two. Why? Because you are likely using your "logout" action to write the "logout" record and then the session listener runs immediately afterward and makes a second audit entry.
There are two obvious solutions to this problem:
Stop recording the logout event in your "logout" action, and let the event handler log all logouts
Remove the user's "username" session attribute when you log them out and the event handler will ignore the event
use a session listener to update the database when the session expires.
This is a simple example of how to implement session.
https://www.mkyong.com/servlet/a-simple-httpsessionlistener-example-active-sessions-counter/

HttpServletRequest object change and Session drops after redirect

I've been working on implementing Punchout in my eCommerce Application. My implementation works as follows.
Everything was working fine till yesterday, then the session started to getting dropped when the redirection to store front took place.
My Observation:
The HttpServletRequest object is RequestFacade before the redirection takes place, but after I redirect, it becomes ApplicationHttpRequest. I can find the RequestFacade wrapped in ApplicationHttpRequest but I cannot find the object I put in the session. Below is the function I am using to put the object in session.
/**
* Creates an object of {#link PunchoutSessionDTO} and puts it in
* the session, making it a punchout session.
*
* #param punchoutTransaction
* The {#link PunchoutTransaction} object passed from the
* {#link PunchoutStoreEntryController}.
* #param session
* The {#link HttpSession}.
*/
public void createPunchoutSession(PunchoutTransaction punchoutTransaction, HttpSession session) {
// Create a PunchoutSessionDTO object.
PunchoutSessionDTO state = new PunchoutSessionDTO();
// Initialize it with the variables from the PunchoutTransaction
// object passed to it.
state.setBrowserFormPost(punchoutTransaction.getCallbackURL());
state.setBuyerCookie(punchoutTransaction.getBuyerCookie());
state.setFromId(punchoutTransaction.getFromId());
state.setToId(punchoutTransaction.getToId());
state.setPoTransId(punchoutTransaction.getTransactionId());
state.setOciPunchout(punchoutTransaction.getTransactionType() == PunchoutTransaction.TYPE_OCI);
// And put it in the session, so that the session could be
// identified as a punchout session.
session.setAttribute("PunchoutState", state);
// Set the max inactive interval of the session to the value
// provided in the store property. If such store property is
// not found, a default of 5 minutes is used.
/*String vid = punchoutTransaction.getVendorId();
Integer timeout = PunchoutStorePropertyFactory.getTimeoutPeriod(vid);
session.setMaxInactiveInterval( (timeout == null ? 5 : timeout) * 60); */
logger.info("Punchout Session Created for " + punchoutTransaction.getBuyerCookie());
}
Everything was working fine till I decided that I should set a timeout value for the session. After this point, problem started to occur. At first, I thought that I am messing it up by passing the incorrect value for setMaxInactiveInterval(), so I commented it. To my surprise, the session was getting dropped anyway.
Please Note:
We are using Apache Tomcat on Windows 8.1.
Server version: Apache Tomcat/7.0.54
Server built: May 19 2014 10:26:15
Server number: 7.0.54.0
OS Name: Windows 8
OS Version: 6.2
Architecture: amd64
JVM Version: 1.7.0_51-b13
JVM Vendor: Oracle Corporation
We are using Spring 2.5. Yes! And we cannot migrate since this application is very huge (Over 10,000 source files).
The URL patterns *.po and *.html are mapping to the same servlet, so the redirection occurs within the same servlet.
Google Searches:
Why does HttpServletRequest object changes.
HttpServletRequest changes to ApplicationHttpRequest
HttpServletRequest to ApplicationHttpRequest
Spring ServletRequest object changing
HttpServletRequest changes after redirection
This silly mistake is pissing us off since last 3 days. Any help would be appreciated! Please point out any silly mistake I have made, and some good tips related to session handling/management are the most welcome ones. If you think that I have not included enough information, please point it out as well. Thanks :)
Yes, I am answering my own question. I wish to go in details so that future comers may find their answers.
Turns out I am an idiot who is redirecting to absolute path.
Redirection is the key.
No matter whether the servlet you are redirecting to resides on the same application server, is within the same application or it even shares the same servlet. Any request redirected to absolute paths has their own context! So the request object created for them are completely new and has their separate sessions. A session is only maintained if the redirection is being sent on the same port, host and webapp. If redirection is done within the same application, use relative paths using relative paths is the best practice.
Redirecting from https://localhost/servlet1.html to http://localhost/servlet2.html are very different, even though the URLs map to the same servlet (Please notice the difference).
Redirecting from https://localhost/servlet1.html to https://192.168.x.x/servlet2.html or https://127.0.0.1/servlet2.html would bear the same results.
The best practice here would be redirecting to paths relative to your application. This would share the session object in the most efficient way.
Using response.sendRedirect("servlet2.html"); is best thing do here (in my opinion).
Understanding the Cookies
A session is identified by the JSESSIONID cookie. If the browser forwards this cookie, then the session is forwarded to the other servlet or controller (or whatever). HTTP and HTTPs are different protocols and hence, different cookies are used. Similarly, localhost and IP addresses are different hosts for a browser, hence different cookies are used. The JSESSIONID cookie is forwarded if the protocol, host and webapp context remains the same, which however, has the same meaning as using relative paths in the redirection. Redirecting to the relative addresses could be thought of as a safe way to redirect on the same host, protocol and application context. It is the browser that decides if it is going to forward your JSESSIONID cookie or not
This is how it works, It first checks whether relative redirect available or not then it else construct absolute path.
// Generate a temporary redirect to the specified location
try {
String locationUri;
// Relative redirects require HTTP/1.1
if (getRequest().getCoyoteRequest().getSupportsRelativeRedirects() &&
getContext().getUseRelativeRedirects()) {
locationUri = location;
} else {
locationUri = toAbsolute(location);
}
setStatus(status);
setHeader("Location", locationUri);
if (getContext().getSendRedirectBody()) {
PrintWriter writer = getWriter();
writer.print(sm.getString("coyoteResponse.sendRedirect.note",
Escape.htmlElementContent(locationUri)));
flushBuffer();
}
} catch (IllegalArgumentException e) {
log.warn(sm.getString("response.sendRedirectFail", location), e);
setStatus(SC_NOT_FOUND);
}

How to set expiration time to session in the controller?

I need to set a session with expiration time of 5min in controller. How do i do it?
I need something like:
$this->container->get('session')->set('mysession', 'value', 'expiration');
in symfony2 way?
Thanks!
Assuming your session is already created, you can achive your goal with:
$this->container->get('session')->migrate($destroy = false, $lifetime = null);
$destroy: Whether to delete the old session or leave it to garbage collection.
$lifetime: Sets the cookie lifetime for the session cookie. A null value will leave the system settings unchanged, 0 sets the cookie to expire with browser session. Time is in seconds, and is not a Unix timestamp.
This feature is added recently. You can update to this commit or patch. From the code it seems you can set expiry time by following way,
$this->container->get('session')->getMetadataBag()->stampNew(300);
To control the time of the active session (and idle time too) you have to do it in the controller this way (extract from: session configuration in the official doc):
$session->start();
if (time() - $session->getMetadataBag()->getCreated() > $maxTime) {
$session->invalidate();
throw new SessionExpired(); // redirect to expired session page
}
When time reaches your $maxTime session is "closed". You can put this code in the backend of your app as a function to call from the different methods to control the time.

ASP.NET MVC3 Forms Authentication user logon session renew

I have an AJAX method to call on server to return ".ASPXAUTH" cookie expiration time.
It works properly when the auth cookie presents.
Besides I want to renew user logon session with another AJAX call. I have a blank method "RenewSession" which is just for to make a call to the server. Is there any way to do this using Forms Authentication?
The problem is in that when I make a request to server to my "RenewSession" method to renew the session Response.Cookies array is always containing 0 items. But actually when the ".ASPXAUTH" cookie expiration time gets to 0 it renews.
So can anyone explain is it a browsers' or ASP.NET/MVCs' behaviour?
Maybe I need sliding expiration to be set to "true"?
Or maybe in my renew method I should re-login the user and put a new cookie in the response?
Thank you!
FormsAuthentication expiration is really a matter of two parts:
the expiration of the authentication ticket
the expiration of the cookie containing the ticket
If you want to leave sliding expiration off, and renew the ticket manually, you need to renew the ticket and return a new authentication cookie to the browser.
The Response.Cookies array is empty unless you (or other code) add something to it. It's only meant for adding cookies that are new or whose contents/expiration/whatever have changed. An empty Response.Cookies only means that nothing has changed - the browser will keep the cookies it already has (until they expire) and still send them on the next request.
The standard way of modifying cookie contents or expiration is to take a cookie the browser sent (from Request.Cookies), modify it, and then add it to Response.Cookies.
Here's a bit of sample code for manually renewing the authentication cookie (disclamer: Test thoroughly and think):
// You could also get the ticket from
// Request.Cookies using FormsAuthentication.Decode
FormsIdentity identity = HttpContext.Current.User.Identity as FormsIdentity;
if (identity == null) return; // User isn't authenticated
// Renew the ticket - you could also create a new ticket manually
// (see * below for an example), if you want to get rid of ASP.NET's
// rather confusing renew-if-old policy:
FormsAuthenticationTicket ticket =
FormsAuthentication.RenewTicketIfOld(identity.Ticket);
string encryptedTicket = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(
FormsAuthentication.FormsCookieName,
encryptedTicket
);
// Better keep this (see * below):
cookie.Secure = FormsAuthentication.RequireSSL;
cookie.HttpOnly = true;
// Isn't a security issue if this is set too long - the ticket contained
// within will still expire after the set time, and the server will timeout
// the auth session on the next request.
// But let's just keep cookie and ticket in sync:
cookie.Expire = ticket.Expiration;
// Add cookie to response to send the changes to the browser:
HttpContext.Current.Response.Cookies.Add(cookie);
Note that FormsAuthentication.RenewTicketIfOld() won't always renew the ticket. It will only renew if less than half of the expiration time is left. I.e., if your timeout in web.config is set to 20 minutes and RenewTicketIfOld is called 7 minutes after the ticket was created, the ticket won't be renewed, and there'll still be 13 minutes left. If it's called after e.g. 12 minutes, it will be renewed to 20 minutes.
The reason for this is because RenewTicketIfOld is used by slidingExpiration on every request and so would send back a new cookie on every request (to reset the expiration to [timeout] minutes). Only sending a new ticket cookie back when at least half the time has elapsed gets rid of a lot of cookie overhead - at the expense of being confusing to developers and end users.
*) On cookie.Secure, see Hanselman: Weird Timeouts - this simply makes sure that if RequireSSL is set in web.config, the cookie will honor that, which avoids many a debugging nightmare if you ever move the site to SSL.

How do we do authentications in web applications?

Sorry, I wrote a lot to explain my situation, If you don't have time or not in mood to read all these just jump to the questions. Even answering one of them helps my situation, thanks :D
I'm trying to write a web application in node.js, but since I'm too new to web, I don't know how to write the web login system. I don't want to use basic or digest http authentications, I want it like normal login systems with http forms and different pages shows different content for different types of users. I searched a bit, I know the basics, but they're not enough.
This is what I got:
We check the user and pass from the
POST data in the database
If correct we give the client a new session, and save it in a database.
The client saves the session as a cookie and sends it in each page request.
The server checks the session and gives the content intended for the user.
Here's the part I don't know:
How do we generate a session?
How do we send it to the client?
How is it saved in cookies?
When is it supposed to expire?
What happens if it is expired? What
should we do?
Is there anything else that I should
know?
If you can please, give me some examples in node.
Thank you in advance :D
A session is simply a unique key (session ID) associated with an object/array, this way you can connect data to a user of your site.
You send the session ID to the client as a cookie, once sent, the client sends its session ID to your server with every HTTP request.
You send the HTTP Set-Cookie header (Set-Cookie: sessionid=abcdefg38974).
You can make it expire when you want it to, when the browser closes or after for example a year (this would keep you logged in for a year, after which you'll have to login again).
When a cookie expires it is simply thrown away, on the serverside it will just look like the user doesn't have a session ID set yet so he has to log in again.
You should know of things like session hijacking (stealing someone else's session ID). Have a look at it.
Little example, it might increate the visit number twice each time you request because your browser also requests /favicon.ico. Keep in mind that this example is not very safe as session hijacking is possible, this also keeps all sessions in memory and forgets them when the server is restarted. It would be wise to save sessions to a database.
var http = require('http');
var sessions = {};
http.createServer(function (req, res) {
var ssid;
var session;
if (req.headers.cookie) {
// Cookie already set, read it
var parts = req.headers.cookie.split('=');
ssid = parts[1];
// Is the session id known to us?
if (typeof(sessions[ssid]) != "undefined") {
session = sessions[ssid];
console.log('Loaded session with id ' + ssid);
}
}
if (typeof(session) == "undefined") {
// No cookie set, make one
ssid = Math.random();
session = {
'visitNumber': 0
}
console.log('Created session with id ' + ssid);
}
session.visitNumber++;
// Respond to the browser
res.writeHead(200, {
'Content-Type': 'text/plain',
'Set-Cookie': 'ssid=' + ssid
});
res.end('Visit number: ' + session.visitNumber + '\n');
// Save the changes we have made to the session data
sessions[ssid] = session;
}).listen(8080);

Resources