Phx_gen_auth: Session cookies and remember-me cookies

Hello

In the generated authentication code by phx_gen_auth, I learn the following:

A “session cookie” maintains the user logged in for the duration of the browser session (until the browser is completely closed generally). The cookie contains the user session token.

In addition to the session cookie, and in order to keep the user logged in after browser closes/re-opens, I can use a “remember me cookie”, with a long expiration date. With the generated auth code, the user can check a checkbox for the server to maintain his session for e.g. 60 days through the remember me cookie.

Questions:

Renowned apps such as Facebook, StackOverflow, Gmail, (and most modern apps?) … do not provide the user with an option to keep longer session. The session are by default long so that the user stays logged in after browser closes. Correct me if I’m wrong, but this is mostly what we see today.

I was then wondering why didn’t we, by default, follow that approach of maintaining sessions, as it also allows for simpler code : working with one cookie vs two cookies (e.g. the current code is trying to fetch the logged in user from the session cookie, and if not found, try to find in the remember me cookie, and then if found, sets the session cookie, …).

Tokens in cookies are considered safe (as opposed as stored in browser), hence I don’t see the interest to have by default short-lived sessions that expire on closing the browser. Maybe in the case multiple users use the same computer?

However, what most app do though is prolonging the session automatically on logging in. With this mechanism, I didn’t have to enter my passwords in ages… This seems lacking in the generated authentication code, but again because another approach is used : have the remember me cookie expire after a precise amount of days.

So instead of a session cookie + remember me cookie expiring on a precise amount of days, on user’s consent, why not have a single session cookie expire after an amount of days, without user’s consent, and prolong that expiration date on login.

1 Like

The explicit “remember me” option is a safer default: if I am logging in to an application using a shared computer, should I forget to logout, I don’t want the session to be remembered after I close the browser.

Even when remembering users after the browser is closed, it is safer to rely on a remember token rather than having a long lasting session cookie. For example, if I manage to obtain the remember token of another user, I have a smaller window when I can use it (it cannot be reused, and it’s invalidated and refreshed upon login/logout). Conversely, stealing a session has much bigger impact.

1 Like

Actually I can’t find one website I regularly use right now that asks me for a log in after having closed my browser. And I’m pretty sure they don’t work with a remember-me option. Even this very forum keeps me logged in after closing/re-opening the browser. And even my Gmail account; and emails are sensitive data. The Google engineers deemed that ok.

You can impersonate a user if you have the remember me cookie, just like the session cookie. On logout all cookies are invalidated. So I don’t really see a difference.
I understand the advantage though, as you said, that if you use a public computer, you won’t check the checkbox “remember me”. Seems like most companies didn’t go that route, even though it makes sense.

Even when adopting the remember me checkbox, now I wonder, why not use only one configurable cookie. You check the “remember me” option, and the session cookie expires in 60 days. Not checked, your session expires when browser closes. Maybe because of Phoenix or Plug’s limitations to configure a cookie dynamically?

Also, I wonder why not have just a remember cookie that prolongs itself automatically on log in. If you use a public computer, you don’t check the remember me checkbox; if it’s a private computer, your session is now extended forever as long as you use the app from time to time, as most known apps do.

The remember me token scheme is a best practice for persistent login. The differences are:

  • if I steal a remember me cookie, the window of opportunity to use it is limited. It can only be used once, and if the user had used it already, it’s useless now. This is as opposed to a valid session cookie, that grants me access as many times as I want.

  • It is easily possible to remove all remember tokens for a specific user, invalidating all those that were issued.

What’s the cost of maintaining two cookies, especially when using a vetted authentication solution?

For reference, here’s a quite old but very often cited blog post that introduced that scheme.

1 Like

Many of those sites do not work with just one layer of security though. Many have layers in their application, where commonly used parts with low(er) impact on security are available through authentication via remember me tokens, while high impact parts of the application (e.g. saving passwords or showing credentials, …) are locked by actual session authentication, which is requested if you didn’t yet login in the current session. Given mix gen.auth is meant to be a “baseline” implementation and not a necessarily complete solution for all cases I feel it’s good to be on the safer side and let people adjust to be less save instead of being in a place where the generator default was not secure enough.

2 Likes

That is not specific to the remember-me cookie. Both remember me cookie and session cookie contain the user session token stored in the database. If you delete the user session token from the database, you have invalidated the session(s).

→ It can be used until its expiration (if it is valid for 1 month, you can use the token for 1 month), for any amount of times, until its expiration. That is, as long as the user session token has not been deleted from the database.
→ If a user logs-out explicitly (not many do), the previous user session tokens will be no longer be valid (they have been deleted). As this user session token is stored in the remember me cookie and the session cookie, I see no difference.

Here is the actual code that shows how the user session token is fetched:

defp ensure_user_token(conn) do
  if user_token = get_session(conn, :user_token) do
    {user_token, conn}
  else
    conn = fetch_cookies(conn, signed: [@remember_me_cookie])

    if user_token = conn.cookies[@remember_me_cookie] do
      {user_token, put_session(conn, :user_token, user_token)}
    else
      {nil, conn}
    end
  end
end

I think that we understand differently how a remember me cookie works. Looking at the code, the remember-me cookie is really just like a session cookie containing the user session token with longer expiration. The main advantage is to give the user the ability to limit the session duration : session is (if not explicitly logged out) maintained until browser is closed VS after a few days/weeks/… typically through the checking (or not) of a “remember me” checkbox for example. Except if I miss something big that I did not understand, or the differences you mentioned are incorrect.

I understand that; I was only wondering why choose that direction. I think my Gmail and Facebook are the most confidential apps I use (after banking ofc that don’t use remember-me-like cookies) and they maintain the session basically forever.

We talk about UX vs security (also not saying that going the more UX way is an insecure way). And I notice that 99% of the modern applications use the automatically prolonged sessions mechanism; therefore, to me, it makes more sense to follow that direction as well by default.

In any case anyone is free to modify the code as he wishes, which is the advantage of the code generation.

1 Like

This is incorrect. The token is changed upon every login, so for every usage of a remember cookie, the token just used is invalidated. The remember me token is single use, in all implementations following the best practices.

Also incorrect. The remember me cookie contains only the token, that can be exchanged for a session only once. The session cookie contains arbitrary information in encrypted form.

The point of having a generator is mainly to have a secure reference implementation following the best practices. You are free to modify the code and depart from those, but having secure defaults is definitely a better approach for something as critical as authentication.

Actually, @thojanssens1, while I was referring to generic best practices, looking at the specific implementation of phx_gen_auth it looks you are right on the first point: the remember token is not refreshed and invalidated after use. So I stand corrected.

It might be something missing in phx_gen_auth. As far as I know, best practice would be to delete the user token and issue a new remember me cookie after exchanging the old one for a logged-in session. As the blog post I linked before says, one of the main points of the remember me cookie approach was to be single-use. Many apps even remove all tokens for a user when an attempt to reuse an old one occurs, considering it the sign of a breach.

Re-issuing a new remember me cookie for every usage also has the advantage to remember the user for X days since the most recent session, as opposed to X days since the last explicit login (in other words, logging out the users after X days of inactivity).

If what said about phx_gen_auth is correct, you have a point that the remember cookie and the session cookie are basically equipotent in this implementation.

It would be interesting to know @josevalim take on this, if that was a deliberate choice and why.

3 Likes

I don’t think this would be desired. It means that if I forget to sign out somewhere, someone can continue logging in forever. We need to require re-authentication at some point.

Session tokens are short lived. Given people have multiple devices today, it can be very common for people to try to login with old tokens. So adopting such a policy can lead to bad UX as we would be constantly logging out users across devices.

3 Likes

You should look at the general authentication suite those apps provide. I have 2FA enabled and, at least for me, Google asks if I should trust the device for 60 days once I enter the code. This is quite similar to the remember me token.

Google also tracks my IP, user agent, and who knows what else. They e-mail me when they think something is amiss. I wouldn’t even be surprised if they have heuristics for “shared computer like usage”. For example, you can use a cookie to track all the different people who login to Gmail from a computer, and once you get 5 different people, they can assume it is a shared computer and automatically reduce how long the session lasts.

Therefore, I think it would be a mistake to look at Google/Facebook and say “hey, we can do the same”, without taking into account all of the other steps they may have taken to make this feature possible.

In any case, you can most likely change Plug.Session to make your session last longer by default. I will personally continue requiring this choice to be opt-in though.

5 Likes

Indeed I’m well aware that such known apps detect unusual browser usage; I saw it rather as an extra security layer on the top of the by-default-forever-lasting sessions. But now I understand that one doesn’t go without the other. And the approach taken in the code generation makes suddenly much more sense. Thank you for clarifying that!

The other question was why not go with a single cookie with a configurable expiration date, that change whether the user checks the remember me checkbox or not. I.e. user checks -> cookie expires after 60 days; doesn’t check -> cookie expires when browser closes. One cookie.

I think that @lucaong answered to that: session cookies can contain arbitrary data alongside the user session token, while the remember me cookie only stores the user session token.
It just makes me wonder, can there be anything wrong to just store all the session data in a cookie that lasts longer. The remember me cookie is actually a way (such as it is implemented in this design) to extract only that user session token, for it to be stored longer, without the other session data. I just wonder about the motivation doing so.

That would be ok. They are both cookies after all. As @lucaong said and you mentioned though, the session does contain other data that you may not want to persist. So having two separate cookies is less “coupling”.

2 Likes

Thank you and pretty sure this thread will be useful to others when the auth generation tool gains in usage :+1:

2 Likes