Skip to main content
CYPEX Documentation
Support
v2.0.0 Latest stable release View changelog ->

Sessions and tokens

5 min read

v2.0.0 replaces the single long-lived JWT with an access token / refresh token pair. This page explains the model, the settings that govern it, and the two configuration mistakes that break a deployment.

Why it matters

Under the old model a token was minted at login and stayed valid for its whole lifetime. Deactivating a user or changing their role did nothing until that token expired — potentially days later.

In v2.0.0 the access token is short-lived and the session is bounded by a separate refresh token. Renewal is not a rubber stamp: POST /app/auth/refresh re-checks the user against the database on every renewal — still active, role unchanged. That is what turns “revoke this user” into an action that takes effect within one access-token lifetime instead of within one session.

How it works

TokenLifetimeAudienceWhere it livesWhat it does
AccessJWT_EXPIRES_IN, default 15mcypex-platformtoken cookieAuthenticates API and PostgREST requests
Refreshthe session length — cypex.t_config.jwt_expcypex-refreshrefresh_token cookieBuys a new access token, nothing else

Consequences worth understanding:

  • The refresh token is never returned in a JSON body. It is cookie-only, to keep it off the XSS surface.
  • The refresh audience is deliberately different (cypex-refresh, not configurable). PostgREST is configured to require cypex-platform (PGRST_JWT_AUD), so a stolen refresh token cannot be spent as a database credential at the data API.
  • The refresh token is not rotated on renewal. Only the access cookie is rewritten.
  • Logout clears both cookies. Clearing only the access cookie would leave the session alive, since the refresh cookie alone can mint a new one.
  • Sessions are synchronised across browser tabs, and the session identity is reset on re-login.

Settings

Session length — admin panel

Platform Admin → Configuration → Session length writes cypex.t_config.jwt_exp. The field label is Session length; the storage key remains jwt_exp for compatibility. This is the refresh token lifetime — the session length — not the access-token lifetime.

Warning
The value is validated. It must be at least 2× the access-token lifetime (30m at the default 15m access TTL). A shorter session is incoherent: it would end when the access token expires rather than when you configured it to, and no renewal would ever run — which means the database re-check that bounds a revoked user would never fire either. Values below the minimum are rejected with too_short; malformed values are rejected with malformed.

Accepted format: a whole number followed by s, m, h, or d30m, 12h, 7d. Nothing else parses.

Environment variables

Set in .env. These are operator-level and are not editable from the admin panel.

VariableDefaultNotes
JWT_EXPIRES_IN15mAccess-token lifetime. Short on purpose — it bounds how long a deactivated user or a changed role survives.
JWT_REFRESH_EXPIRES_IN7dRefresh lifetime, used as the fallback default. Precedence is cypex.t_config.jwt_exp (the admin-panel session length) → this variable → the built-in default. Set it to define the starting value; once an administrator saves a session length in the admin panel, that value wins. The SSO gateway reads the same variable, so both services agree by default.
JWT_ISSUERcypex-apiIssuer stamped on minted tokens.
JWT_AUDIENCEcypex-platformAudience on access tokens. Must match PGRST_JWT_AUD in docker-compose.yml.
BCRYPT_ROUNDS10Password-hashing cost. Valid range 10–15, clamped rather than rejected. Each increment roughly quadruples login CPU cost — raise it behind a load test, not on principle. Existing hashes keep working at the cost they were created with.
REDIS_URLredis://cypex_redis:6379See below.
Warning
JWT_ISSUER is not free-form. Verifiers accept the configured issuer plus a fixed list of accepted names. Setting it to something outside that list produces a deployment that signs tokens it then rejects. Leave it alone unless you have a specific reason.
Info
The issuer default changed from cypex-sso-gateway (v1.9.x) to cypex-api. This is not breaking: cypex-sso-gateway is retained as a legacy accepted issuer, so live sessions survive the upgrade.

Redis

The SSO Gateway writes OAuth state, sessions, and refresh-token records to Redis. Before renewing a gateway-issued refresh token, the backend checks it against that revocation list.

The check applies to SSO-origin tokens only. Password sessions do not consult Redis in any state — a deployment that does not use SSO is unaffected by Redis entirely, and REDIS_URL can be left unset.

Where it matters, the two failure modes are not the same:

Redis stateResult
Reachable, record presentRenewal proceeds
Reachable, record missing or flagged revokedRenewal refused
Unreachable or not configuredThe check is skipped and renewal proceeds; a warning is logged once, not per request
Warning
The middle row is the operational trap. A missing record is read as revoked — so a Redis that is reachable but has lost its dataset signs out every SSO user at their next renewal. Run it with persistence; the bundled compose service uses --appendonly yes on a named volume.

Upgrading from v1.9.x

Confirm each of these before cutting traffic:

  • JWT_AUDIENCE and PGRST_JWT_AUD agree. A mismatch produces authenticated users who get 401s from the data API only — the admin panel appears to work.
  • cypex.t_config.jwt_exp is at least 2× JWT_EXPIRES_IN. An existing value below the new minimum will be refused on the next edit.
  • Redis is running and persistent if you use SSO.
  • External JWT verifiers accept the new claims — see Upgrade to v2.0.0.
  • Any integration that read a long-lived token from a login response has been updated. Access tokens now expire in 15 minutes and the refresh token is cookie-only.
Tip
A service-to-service integration that logged in once and reused the token for days will now break every 15 minutes. Either call POST /app/auth/refresh on 401, or give the integration its own role and a token issued outside the interactive login flow.

See also