Skip to content

fix: 30 day sliding session TTL instead of a hard 24h expiry - #22

Merged
ConalMullan merged 1 commit into
developfrom
fix/session-ttl-sliding
Aug 12, 2026
Merged

fix: 30 day sliding session TTL instead of a hard 24h expiry#22
ConalMullan merged 1 commit into
developfrom
fix/session-ttl-sliding

Conversation

@ConalMullan

Copy link
Copy Markdown
Collaborator

Problem

OAuth sessions expired after 24h, so every hosted user had to complete a full browser sign-in daily.

The interesting part is why. Decoding the DS access token from a live dev session:

DS access token issued : 2026-08-12
DS access token expires: 2027-08-12   (life: 365 days)
our session expires    : 2026-08-13   (life: 24 hours)
BINDING CONSTRAINT     : our 24h session TTL

The DS token is good for a year. Our own TTL was forcing re-authentication — and since we issue no refresh_token and advertise only the authorization_code grant, a client has no way to renew without user interaction.

This also corrects the earlier plan to implement token refresh: refresh solves an expiry we don't actually have.

Change

  • TTL.SESSION: 24h → 30 days
  • Slid forward on each authenticated request (touchSession), so an actively used connection never expires. 30 days becomes an inactivity window rather than a hard cap.

Kept well inside the DS token's 365-day life because a session ID is a bearer credential.

Two implementation details:

  • The slide only writes when the expiry has moved by more than an hour, so a busy session touches the store roughly hourly rather than on every request.
  • It is fire-and-forget — a failed extension logs a warning and never fails the request the user actually made.

Verification

Built server reports TTL.SESSION = 2592000s (30 days), which is also the expires_in we advertise to clients.

New tests cover both directions:

  • an active session survives 50 days of use (5× the old TTL, past a static 30-day one)
  • an unused session still expires
  • touchSession will not resurrect an already-expired session
  • it skips the write when the expiry has barely moved, and reports whether it extended

The existing exposes TTLs in seconds test is updated, plus a new intent-based assertion (> 7 days, < 365 days) so the reasoning is pinned, not just the constant.

553 tests passing (was 547). Lint and format clean.

Sessions expired after 24h, so every hosted OAuth user had to complete a
full browser sign-in daily. The DS access token behind the session is
valid for 365 days, so our own TTL - not the DS token - was forcing it:

  DS access token life : 365 days
  our session TTL      : 24 hours   <- binding constraint

We issue no refresh_token and advertise only the authorization_code
grant, so a client has no way to renew without user interaction.

Raise TTL.SESSION to 30 days and slide it forward on each authenticated
request, so an actively used connection never expires; 30 days becomes
an inactivity window rather than a hard cap. Kept well inside the DS
token's life because a session ID is a bearer credential.

The slide only writes when the expiry has moved by more than an hour, so
a busy session touches the store about hourly rather than per request,
and it is fire-and-forget: a failed extension never fails the user's
request.

Verified: TTL.SESSION = 2592000s (30d), which is also the expires_in we
advertise. Sliding is covered in both directions - an active session
survives 50 days of use, an unused one still expires, and touchSession
will not resurrect an already-expired session.
@ConalMullan
ConalMullan merged commit 68c650d into develop Aug 12, 2026
3 checks passed
@ConalMullan
ConalMullan deleted the fix/session-ttl-sliding branch August 12, 2026 10:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant