Re: [cherrypy-users] Session Timeouts and the "Remember Me" check box

564 views
Skip to first unread message

Michiel Overtoom

unread,
Feb 25, 2013, 3:50:51 AM2/25/13
to cherryp...@googlegroups.com

On Feb 24, 2013, at 00:27, Nick Repole wrote:

> I'm having a bit of a hard time trying to get a handle on how CherryPy handles sessions. I'm attempting to implement a log in form with a "remember me" checkbox [...]

I think this is usually accomplished by using cookies, not with a session.

Greetings,

--
"If you don't know, the thing to do is not to get scared, but to learn." - Ayn Rand



Nick Repole

unread,
Feb 25, 2013, 10:58:15 AM2/25/13
to cherryp...@googlegroups.com
I was under the impression that cherrypy sessions already used cookies to communicate?

For what it's worth, this seems to be working:
    cherrypy.config.update({'tools.sessions.timeout': 60 })
    cherrypy.session.regenerate()
    cherrypy.session.save()

I'm also locking that section since it's a change to the global config and I want to ensure there aren't any threading issues there. I feel like there has to be a better way of doing this, but perhaps not.

Nick Repole

unread,
Feb 25, 2013, 11:24:44 AM2/25/13
to cherryp...@googlegroups.com
Part of what confuses me, for example, is if in my login function I run this code:

print str(cherrypy.session.timeout) #prints original value of 60
cherrypy.session.timeout = 2
print str(cherrypy.session.timeout) #prints new value of 2
cherrypy.session.save()
print str(cherrypy.session.timeout) #still prints new value of 2


and yet when I request a new page, if I print the timeout there, I get back the original value of 60.

Tim Roberts

unread,
Feb 25, 2013, 1:07:06 PM2/25/13
to cherryp...@googlegroups.com
The code you have above modifies the parameters for the current session
only. If you request a new page in a different session, it will not
inherit these values. It starts out with the default.

What session back-end are you using?

Remember that there are two uses for this "timeout" value. When
CherryPy sends the session cookie, that cookie includes an expiration
date derived from the session timeout. That expiration is all handled
by the browser. The browser is supposed to check the expiration dates
on all of its cookies, and stop sending one after it has expired. That
process can fail; early versions of Safari had a bug in that, and the
process breaks down if the client's clock is very wrong.

The other place is in the server. Every N minutes, the server runs
through the list of stored sessions and deletes any of them that have
expired. By default, that clean-up process only runs every 5 minutes,
so your 2-minute expiration won't be caught immediately.

--
Tim Roberts, ti...@probo.com
Providenza & Boekelheide, Inc.

Nick Repole

unread,
Feb 25, 2013, 1:47:00 PM2/25/13
to cherryp...@googlegroups.com
So the goal is only to affect the timeout on an individual user, that way I can set the timeout dynamically upon a user logging in ideally. I understand that if I start a new session I'm going to get my original timeout (60) again, but what I've been experiencing is that under the same session it appears the timeout never truly gets saved.

Here's a couple functions I've been using to test:

      def session_debug(self):
        result = "Session ID: " + str(cherrypy.session._id) + ", "
        result += "Session timeout: " + str(cherrypy.session.timeout) #still prints new value of 2
        return result
    session_debug.exposed = True


    def login_test(self, username):
        cherrypy.session[SESSION_KEY] = cherrypy.request.login = username
        result = "Session Name: " + cherrypy.session[SESSION_KEY] + ", "
        result += "Session ID: " + str(cherrypy.session._id) + ", "
        result += "Session original timeout: " + str(cherrypy.session.timeout) + ", " #prints original value of 60
        cherrypy.session.timeout = 2
        cherrypy.session.save()
        result += "Session timeout after save: " + str(cherrypy.session.timeout) #still prints new value of 2
        return result
    login_test.exposed = True


I go to /login_test?username=test and get back:
Session Name: Test, Session ID: 4c60fea8882050288e1b4a42055508973ff3eb47, Session original timeout: 60, Session timeout after save: 2

I then go to /session_debug and get back:
Session ID: 4c60fea8882050288e1b4a42055508973ff3eb47, Session timeout: 60


The session is clearly the same, and yet even though I saved the new timeout in the login, it seems to have been set back to 10. I assume I'm missing something simple here? Do I need to call session.load() in the log in? Or regenerate?

Thanks for following up,
Nick

Nick Repole

unread,
Feb 25, 2013, 1:51:42 PM2/25/13
to cherryp...@googlegroups.com
Forgot to add, I'm using the default session back end. I think I'm capable of changing the session expire date on the client side, it's getting things to stick on the server side that is causing issues.

Joel Rivera

unread,
Feb 25, 2013, 2:07:24 PM2/25/13
to cherryp...@googlegroups.com
Hi Nick

I think that the point of Michel is that the expiration date of the
session gets updated every time that the user make a request with the
same session_id, so for example:

tools.session.timeout = 300 # 5hours
1. User <no cookies>
Server response with "Set-Cookie" sess_id=1 and expires=now()+timeout
2. User <cookie: session_id=1>
Server response
with "Set-Cookie" sess_id=1 and expires = now() + timeout
== After 4 hours:
The same session id is "refreshed",
3. User <cookie: session_id=1>
Server response
with "Set-Cookie" sess_id=1 and expires = now() + timeout
== After 6 hours:
4. User <cookie: session_id=1>
# session_id is EXPIRED!, more than 5 hours since the last visit.
Server response
with "Set-Cookie" sess_id=58 and expires = now() + timeout
# Now the user had a new session id, because the first one has expired,
# The used did not visit the page in less than `timeout`.

You can check the headers is every request there is a "Set-Cookie" with
the same session_id but with a new expires.

So that's the reason that it will be easier to implement with a
particular cookie with fixed expires, you control the expiration date
not the session (which update the expiration date of the cookie in every
request).

At least that what is see in the cherrypy source code,
`cherrypy.lib.sessions.init` gets hooked to the point
'before_request_body' in the session tool and the last statement of that
function is `set_response_cookie` with the new timeout.

I hope this clarify a little bit more the situation.

Cheers.
> --
> You received this message because you are subscribed to the Google
> Groups "cherrypy-users" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to cherrypy-user...@googlegroups.com.
> To post to this group, send email to cherryp...@googlegroups.com.
> Visit this group at
> http://groups.google.com/group/cherrypy-users?hl=en.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>

--
Rivera²

Tim Roberts

unread,
Feb 25, 2013, 2:20:18 PM2/25/13
to cherryp...@googlegroups.com
Joel Rivera wrote:
> I think that the point of Michel is that the expiration date of the
> session gets updated every time that the user make a request with the
> same session_id, so for example:

Exactly. The "timeout" value is an inactivity timeout, so each new
request starts the counter again. If you go look at the code, you'll
see that "cherrypy.session.timeout" value is not stored in the session
database. When the session is created or updated, it computes an
absolute "expiration date" and stores that. So, for each new request,
"cherrypy.session.timeout" always starts again from the default, which
is exactly what you are seeing.

Nick Repole

unread,
Feb 25, 2013, 3:00:56 PM2/25/13
to cherryp...@googlegroups.com
Ahhh, that explains a lot.

Not totally sure where I'll go from here though. Accomplishing what I'd like to seems to be a whole lot more difficult than I had expected, looks like I'll have to get my hands dirty in the actual sessions code.

Thanks Joel, Michael, and Tim for the help, appreciate it.

Tim Roberts

unread,
Feb 25, 2013, 3:47:51 PM2/25/13
to cherryp...@googlegroups.com
Nick Repole wrote:
>
> Not totally sure where I'll go from here though. Accomplishing what
> I'd like to seems to be a whole lot more difficult than I had
> expected, looks like I'll have to get my hands dirty in the actual
> sessions code.

What it is you are trying to accomplish? If you want the timeout to
apply only from the initial login, then all you need to do is store the
login time and the timeout in your session as session variables. Then,
on each request, you can decide for yourself if the session is stale,
and if so, force the session to expire immediately.

Nick Repole

unread,
Feb 25, 2013, 4:35:30 PM2/25/13
to cherryp...@googlegroups.com
I don't mind the timeout being refreshed, my basic goal is:

Remember me not checked in  [default case]: User logged in for whatever amount is specified in the config (60 minutes by default)
Remember me checked [special case]: User logged in for a month or some other long time

I've found somewhat of a solution it seems. On login I set a "remember me" cookie (cherrypy.serving.response.cookie["remember_me"] = True), and then I modified init() in sessions.py to check for that flag and act accordingly when setting the timeout value. This way both the response cookie and timeout value on the server should be synchronized. I'd of liked to have avoided touching the actual CherryPy code, but three lines in sessions.py saves me a whole lot of book keeping in other places it seems.

Tim Roberts

unread,
Feb 25, 2013, 6:26:00 PM2/25/13
to cherryp...@googlegroups.com
Nick Repole wrote:
> I don't mind the timeout being refreshed, my basic goal is:
>
> Remember me not checked in [default case]: User logged in for
> whatever amount is specified in the config (60 minutes by default)
> Remember me checked [special case]: User logged in for a month or some
> other long time

You need to think of these as two separate features. The first case is
a login session. The second case is "logging in without specifying a
password", and should be done with a custom cookie. Your login code
would then check for that cookie and proceed to logging in without
presenting the dialog. You don't want to have to keep sessions alive
forever.

Nick Repole

unread,
Feb 25, 2013, 7:37:27 PM2/25/13
to cherryp...@googlegroups.com
That makes sense, though if I go that route I suppose I might as well ditch the sessions tool all together if I'm issuing my own auth cookies. Unless I'm missing something?

Nick Repole

unread,
Feb 25, 2013, 7:56:54 PM2/25/13
to cherryp...@googlegroups.com
I take that back, sessions are still useful as opposed to going full auth cookie mode.

Onwards and upwards, now I just have to figure out some of that fun encryption stuff to keep the remember me cookie secure.


Thanks again for all the help, really do appreciate it.

Tim Roberts

unread,
Feb 26, 2013, 12:47:43 PM2/26/13
to cherryp...@googlegroups.com
Nick Repole wrote:
> That makes sense, though if I go that route I suppose I might as well
> ditch the sessions tool all together if I'm issuing my own auth
> cookies. Unless I'm missing something?

Well, the only reason that you have people "log in" is so you can
maintain some state for them while they are logged in. That's what
sessions do for you -- provide a place to hold state. If the only thing
you need to maintain is a user name, then no, you don't really need
sessions.

Tim Roberts

unread,
Feb 26, 2013, 12:49:09 PM2/26/13
to cherryp...@googlegroups.com
Nick Repole wrote:
>
> Onwards and upwards, now I just have to figure out some of that fun
> encryption stuff to keep the remember me cookie secure.

If you need the user name to be secure, you need to be using https.
Everything else is hackable.

Nick Repole

unread,
Feb 26, 2013, 4:30:11 PM2/26/13
to cherryp...@googlegroups.com
Understood, and even then I know it's best to encrypt the username/any other id info to prevent easy replication (the same way a session_id shouldn't be easily guessable). Beyond that I'm sure it's typically a good idea for important functions (like changing passwords) require an additional log in.
Reply all
Reply to author
Forward
0 new messages