Skip to main content

Sessions and how long people stay logged in

When someone logs in, the site remembers them with a session, so they do not log in on every page. This page is for whoever runs the server. Most sites never change the defaults: the database on Node and Docker, KV on Cloudflare. Change them when you run more than one copy of the site, or when you want logins to last a different time.

How long people stay logged in​

VariableDefaultWhat it sets
ADMIN_SESSION_HOURS24How long an admin stays logged in, in hours, counted from the moment they log in.
MEMBER_SESSION_DAYS30How long a member stays logged in, in days. Members log in with an emailed link, so this is long on purpose.

A value that is not a whole number is ignored and the default is used. Changing either affects people who log in afterwards; a session already open keeps the lifetime it was given.

Whatever the lifetime, access ends sooner in two cases, checked on every request:

  • an admin who is disabled or deleted (when each admin has an account) is logged out at once;
  • a member who is disabled or deleted is logged out at once.

Admins and members are separate. One browser can be logged in as both, and logging out of one does not log out of the other.

Where sessions are kept​

SESSION_STORE chooses.

ValueWhereUse it when
databaseA table called sessions in the site's own database. Expired rows are cleared out now and then.The default on Node and Docker. No extra service, and it works with several copies of the site sharing one database.
redisA Redis server (or Valkey, KeyDB or another that speaks the same protocol), at REDIS_URL. Each session is a key beginning session: that expires on its own.You already run Redis, or want session traffic off the database. Node only.
memoryThe server's own memory.Development only. Every login is lost at each restart, and it cannot work with more than one process. In production the server logs a warning.
kvA Cloudflare KV namespace, bound as SESSIONS.The default on Cloudflare Workers.

Any other value stops the server at start on Node with Unknown SESSION_STORE "…". Use database, redis or memory (kv is for Cloudflare). On Node, kv gives the same message.

Database​

Nothing to configure. The table is created with the rest of the schema (How the database schema is kept up to date).

Redis​

SESSION_STORE=redis
REDIS_URL=redis://redis:6379

REDIS_URL is the address of the server, in the form redis://[user:password@]host:port. Without it the server logs SESSION_STORE=redis needs REDIS_URL. Redis only ever holds sessions, which are small. If Redis cannot be reached, the log says [sessions] redis error: followed by the reason, and people cannot log in until it is back. Docker Compose users: docker-compose.yml has a commented-out Redis service you can switch on.

Sessions are lost if Redis loses its data. Everyone then logs in again; nothing else is harmed.

Cloudflare KV​

On Workers the store is kv unless you say otherwise, and it needs this in wrangler.toml:

[[kv_namespaces]]
binding = "SESSIONS"
id = "YOUR_KV_NAMESPACE_ID"

Create the namespace with npx wrangler kv namespace create SESSIONS and copy the id it prints. Two ways to do without KV, both using D1 instead: set SESSION_STORE to database, or leave the binding out. On Workers, if SESSIONS is not bound, the site uses D1 whatever SESSION_STORE says.

The cookies​

The site sets two cookies, one for each kind of login:

CookieFor
cm_admin_sessionAdmins
cm_member_sessionMembers

Each holds only a random 64-character code. Nothing about the person is in it, and it is not signed: the code means something only to the session store. Each cookie is HttpOnly (scripts on the page cannot read it), SameSite=Lax, for the whole site, and has no Domain, so it is sent only to the exact host name. Max-Age equals the session's lifetime.

It is also Secure (sent over HTTPS only) whenever NODE_ENV is production, which is the default. That is why a production site served over plain HTTP, for instance when you test by address on your own network, cannot keep a login: most browsers refuse a Secure cookie over plain HTTP. Put the site behind HTTPS, or set NODE_ENV=development for a local trial only.

Log everyone out​

There is no button for this in the admin panel. Empty the session store instead; everyone, you included, is asked to log in again, and nothing else changes. Ending a single person's access is easier: disable or delete them.

StoreHow
database on NodeDelete every row of the sessions table. For SQLite, with the site stopped or running: node -e "const {DatabaseSync}=require('node:sqlite');new DatabaseSync('./data/choir.sqlite').exec('DELETE FROM sessions')" from the site's folder (change the path if you set SQLITE_PATH). For PostgreSQL or MySQL run DELETE FROM sessions; in psql or mysql.
database on D1npx wrangler d1 execute choir-db --remote --command "DELETE FROM sessions"
redisDelete the keys that begin session:. Untested here: redis-cli --scan --pattern 'session:*' | xargs redis-cli del. Avoid FLUSHALL if the Redis server holds anything else.
memoryRestart the server.
kvPoint wrangler.toml at a new, empty KV namespace and deploy again. Untested here; it follows from the way the store works, because a session is found only in the namespace it was written to.

The SQLite command was run against a copy of the table. The other rows follow the code and were not run.

Changing ADMIN_PASSWORD does not log anyone out: a session does not record the password.

If something goes wrong​

What you seeCause and fix
Logging in works, but the next page asks againThe cookie is not being kept. Usually a production site served over plain HTTP (see above).
Everyone is logged out after each restartSESSION_STORE=memory. Use database.
Logged in on one copy of the site, logged out on anothermemory with more than one process. Use database or redis.
[sessions] redis error: … in the logRedis is down or REDIS_URL is wrong.
SESSION_STORE=memory loses every login on restart and does not work with more than one process at startThe warning for a production site on memory. Not fatal.