Skip to content

How Long Should a Login Session Last Before It Expires?

6 min read

For a beta app, a good default is a short access token that lasts about an hour paired with a refresh token that keeps the user signed in for one to two weeks (or up to 30 days if you add a "remember me" tick). That gives you sessions that survive a closed laptop but do not stay valid forever. If your users are getting logged out every few minutes, or never at all, one of those two numbers is wrong.

Why this happens

Most auth setups use two tokens, and vibe-coded apps usually leave both on whatever the library ships with. The access token is the short-lived pass that proves who you are on each request. The refresh token is the longer -lived one that quietly gets you a new access token when the old one expires, so you are not asked to log in again every hour.

When people say "I get logged out constantly," it usually means the refresh step is broken or missing, so the app falls back to the short access-token lifetime. When people say "I am never logged out," it usually means the refresh token never expires and is stored somewhere it can be reused indefinitely. Your agent built the login flow you asked for and picked defaults you never saw, so nobody decided how long a session should actually last.

How to check

  1. Log in, then leave the app open and untouched. Note how long until it forces you back to the login screen. Minutes means your access-token lifetime is doing all the work and refresh is not happening.
  2. Log in, close the browser completely, reopen it a day later. If you are still signed in, you have a persistent refresh token. If not, your session dies with the tab.
  3. Find the numbers. On Supabase, open Authentication > Settings and read the JWT expiry and refresh-token settings. On Firebase, the ID token is one hour by default and the refresh token lasts until revoked. In a custom setup, grep your code for expiresIn, maxAge, JWT_EXPIRY, or cookie options.

The fix

  1. Set the access token short. One hour is a sane default (expiresIn: "1h"). This limits the damage if a token is ever stolen, because it stops working quickly.
  2. Set the refresh token to a real window. One to two weeks for a normal session. If you offer "remember me," extend that path to 30 days and leave the unchecked path at a day or so.
  3. Make refresh actually work. Confirm the client silently exchanges the refresh token for a new access token before it expires. On Supabase and Firebase this is automatic once you use their client SDK; do not hand-roll it.
  4. Use a rolling window for sensitive apps. Reset the clock on activity so an engaged user is not kicked out mid-task, but an abandoned session still dies.
  5. Give logout real teeth. Ending a session should revoke or clear the refresh token, not just the access token, or the user is not really logged out. See why users are still logged in after logout.

The trap to avoid

Do not "fix" constant logouts by making tokens last a year. That turns a small annoyance into a real risk: a token copied from a shared or stolen device stays valid for months. The right fix is a short access token plus working refresh, not one giant token that never expires. Equally, do not store a long-lived token somewhere insecure to paper over a broken refresh flow; see is it safe to store a login token in localStorage.

Where this fits

Session length is one of those settings that feels invisible until a beta user either gets annoyed or gets exposed, and it never shows up in a demo because you are logged in the whole time. The free Readiness Report checks how your sessions expire and whether logout truly ends them, against your real project. If you would rather have the safe defaults set for you, in order, that is what the Finishing Pass is for.