- Session token has two timepoints: validUntil and renewableUntil. - If now > validUntil && now < renewableUntil - I'm regenerating session token.
This way user is not logged out periodically but session token is not staying the same for 5 years.
But maybe I'm just overthinking it. :)
For my application the token is valid for a few months, but we will automatically issue you a new one when you make requests. So the old token will expire eventually. But the client will update the token automatically making your "session" indefinite.
So when you throw away a drive that you had sitting in the junk drawer for a year that token is inert. Even if you are using a cloned machine that is still extending the same "session".
The link you posted shows code to extend the session, which is common practice (it's called rolling session), not to "extend" the token's life (which should be impossible, a token needs to be immutable in the first place, which is why refreshing a token gives you a new token instead of mutating the original).
In the first scenario, the attacker steals the token and uses it forever. In the second, the attacker steals the token and they'll get a fresh one near its expiry. They can still impersonate the user forever. The user might notice something when they get kicked out (because the attacker renewed the token, rendering the old one invalid) but it's unlikely. For good UX, you need a grace period anyway, otherwise legitimate users can have problems with parallel requests (request one causes a token refresh, request two gets rejected because it was initiated before the first one was completed).
You can use a second token (a refresh token) but it only pushes the risk to the second token. Now we need to worry about the second token being stolen and abused forever.
Refresh tokens are useful for not having to hit the database on every request though: Typically, the short lived session token can be validated without hitting the db (e.g. it's a signed JWT). But it means that you can't invalidate it when stolen, it will be valid until expiry time so the expiry time has to be short to limit the damage. For the refresh token, on the other hand, you do hit the db. Using a second token doesn't add any security, hitting the db does, because the refresh token can be invalidated (by deleting it from the db).
Lucia always hits the db (at least in their examples), so you can invalidate tokens anytime. To mitigate risks, you can allow the user to see and terminate their active sessions (preferably with time, location, and device info: "Logged in 12 AM yesterday from an iPhone in Copenhagen"). You could also notify the user when someone logs in from a new location or device.
That's about all you can do. There's simply no fully secure way of implementing long-lived sessions.
With all the respect, I'm a bit skeptical about this document for such reasons:
- Name is quite pompous. It's a very good marketing trick: calling some document like if it was written by group of researchers from a Copenhagen university. :)
Yes, Lucia is a relatively popular library but it doesn't mean that it is promoting best practices and that its author should be considered an authority in such important field unless opposite is proven.
- I don't like some aspects of Lucia library design: when user token is almost expired - instead of generating new security token Lucia suggesting just to extend life of existing one. I see it as a very insecure behavior: token lives forever and can be abused forever. This violates one of the security best practices of limited token lifetime.
But both Lucia and "Copenhagen Book" encourages this practice [1]:
``` if time.Now().After(session.expiresAt.Sub(sessionExpiresIn / 2)) { session.ExpiresAt = time.Now().Add( updateSessionExpiration(session.Id, session.ExpiresAt) } ```
[1]: https://thecopenhagenbook.com/sessions#session-lifetime