Skip to content

Redis-backed sessions expire after 1 hour instead of persisting #4552

Description

@brantsrasmus

Summary

When REDIS_URL is set, a username/password login is stored in Redis with a 1 hour TTL that is never refreshed, so users are signed out roughly an hour after logging in regardless of activity. With Redis disabled the same login mints a JWT with no expiresIn and never expires.

Cause

saveAuth() in src/lib/auth.ts defaults expire = 0, and 0 is the "no expiry" sentinel:

await redis.client.set(authKey, data);

if (expire) {
  await redis.client.expire(authKey, expire);   // expire is 0 -> never runs
}

The intent is that no TTL is applied. But the Redis client in src/lib/redis.ts substitutes its own fallback whenever set() is called without one:

const ttl = time && time > 0 ? time : DEFAULT_TTL;   // DEFAULT_TTL = 3600

so the auth key is actually written with EX 3600.

Nothing renews it either — checkAuth() only does redis.client.get(authKey), never re-expires — so the hour is absolute from login, not a sliding window. Continuous use does not extend the session.

/api/auth/sso passes 86400 explicitly and is unaffected (24h). /api/auth/login and /api/2fa/verify both call saveAuth(data) with no expiry and get 1 hour.

Steps to reproduce

  1. Set REDIS_URL and start Umami.
  2. Log in with username and password.
  3. Find the session key and check its TTL:
    redis-cli --scan --pattern 'auth:*'
    redis-cli TTL auth:<key>
    
    It returns 3600 and counts down.
  4. Keep using the app. The TTL keeps counting down and is never reset.
  5. When it reaches 0 the next API request returns 401 and the app redirects to /login.

Expected vs. actual

Expected: the session persists until logout or a password change — matching both the expire = 0 contract in saveAuth() and the behavior when Redis is disabled.

Actual: hard logout roughly one hour after login, no matter how actively the app is being used.

Environment

  • Umami v3.4.0 (dev, 71f488f)
  • Self-hosted, PostgreSQL, Redis enabled
  • Affects any deployment with REDIS_URL set; especially noticeable when running several tenants, since each one signs you out on its own hourly clock

Notes

The DEFAULT_TTL fallback in set() was added in 8530329 ("deprecate @umami/redis-client, add redis lib"). The expire = 0 / if (expire) contract in saveAuth() predates it, going back to ede6587 (2023), which is why the two no longer line up.

I have a fix ready and will open a PR against dev.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions