- LoginTimeout is 0 (infinite logins never expire)
- The "Remember me" checkbox is not checked
I have fixed this by sacrificing a new security feature for usability, but wanted to post here with a little explanation ...
The design goal is to have more server-side control over authentication cookies. Particularly, logins should be expirable as per the following:
- the session is inactive for more than x minutes (when you haven't checked the 'Remember me' checkbox, or the option is not available)
- Up to a maximum of LoginTimeout (typically between 2 and 24 hours, depending on the level of security desired; Yahoo lets users pick a range of 2-8 hours)
- However, if the password is changed, all sessions and logins on other browsers than the one changing the password should expire immediately (within a few minutes)
- If the account is closed (e.g. banned), all sessions or logins for that account should expire imemdiately (within a few minutes).
On top of this, there are further security measures for handling authentication over HTTPS.
We have two cookies:
- ad_session_id is for the session, and includes information about the user_id and the login level. Login level is 0 = not logged in/authentication expired; 1 = authentication tip-top ok; 2 = authentication is tip-top okay, but your account is closed (e.g. banned). So only if user_id != 0 and login_level = 1 do we actually consider you officially and fully logged in.
The session cookie expires and is refreshed frequently.
It is expired by both server and client after "SessionTimeout" seconds (default value: 1200 = 20 minutes). When the session expires, we forget everything, including user_id.
It is refreshed much more frequently, though, namely when it is "SessionRenew" seconds old (default value: 300 = 5 minutes).
So if you keep hitting the site, the session will be refreshed every 5 minutes, meaning that the session will expire between 15 and 20 minutes after your last hit. And if you do keep hitting the site, then, after my fix above, you will stay logged in forever. Period.
As per the requirements above, my goal is the following changes:
1) if LoginTimeout is set (!=0), then this should be the maximum lifetime of your logged in session, regardless of whether you keep getting your session cookie refreshed. In other words, when refershing the session cookie, we need to check when you last authenticated, and if that's more than LoginTimeout ago, then we should issue a session cookie with user_id staying the same, but login_level = 0 (expired).
2) If the auth_token has change (which is our mechanism for expiring logins server-side, e.g. when changing password), we will also issue a new session cookie with the same user_id, but with login_level = 0 (expired).
3) If your account has been closed (e.g. banned), we'll issue a session cookie with same user_id, but with login_level = 2 (account closed).
So much for the session cookie. Now for the ad_user_login cookie.
The original idea was that this cookie will only get issued if you click "Remember me on this computer".
So if you visit the site with no session cookie, the ad_user_login cookie can be used to 'seed' the session cookie with a user_id from the ad_user_login cookie. If the ad_user_login cookie is expired, or the cookie's auth_token doesn't match the auth_token in the database (e.g. if the password has been changed), the session cookie's login_level is set to 0. If the account is closed, the session cookie's login_level is set to 2. Otherwise, the session cookie's login_level is set to 1, and the user is set to go.
One of the changes I made to the security system in 5.0 was to always create an ad_user_login cookie, even if you logged in without checking "Remember me". And then, whenever we refresh the session, we check that login cookie, including expiration, auth_token, and account status.
Then when you login without checking the "Remember me" box, instead of making the ad_user_login cookie be permanent, I set the login cookie to expire after LoginTimeout (defaults to 8 hours). Now, if you've set LoginTimeout to zero, it will expire immediately.
The problem with this design is that it kills the ability for a session to expire based on a certain period of inactivity.
Hence, some slight modifications to the design are required for 5.1:
- include the login issue_time in the session cookie, so we can stop refreshing your session when we reach LoginTimeout, without having to set the ad_user_login cookie at all.
- include auth_token the session cookie, so we can expire your session server side, withouth having to set the ad_user_login cookie.
- check and verify time sine login issue time, auth_token, and account_status when refreshing the session cookie.