Skip to content

Remove Cookies from JSON responses and configure session timeouts#425

Open
P4sca1 wants to merge 4 commits into
Shuffle:mainfrom
PROCYDE:improve-session-handling
Open

Remove Cookies from JSON responses and configure session timeouts#425
P4sca1 wants to merge 4 commits into
Shuffle:mainfrom
PROCYDE:improve-session-handling

Conversation

@P4sca1

@P4sca1 P4sca1 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

This pull request introduces robust session management improvements, focusing on enforcing session idle timeouts and maximum lifetimes, as well as standardizing session expiration handling across authentication and login flows. The changes ensure that user sessions are automatically expired both in the database and at the HTTP cookie level based on configurable environment variables.

Session Timeout and Expiration Management:

  • Added functions to retrieve session idle timeout and maximum lifetime from environment variables (SHUFFLE_SESSION_IDLE_TIMEOUT, SHUFFLE_SESSION_MAX_LIFETIME), and to calculate session expiration times, enabling configurable session duration and inactivity limits.
  • Enforced session idle timeout and max lifetime in HandleApiAuthentication, expiring sessions both in the database and via cookies if limits are exceeded, and refreshing cookies on each request to slide the expiration window.
  • Introduced the expireSession helper to centralize logic for clearing sessions from both server and client, ensuring consistent cleanup and error reporting.

Consistent Session Handling in Authentication Flows:

  • Replaced hardcoded 8-hour session expiration logic with the new expiration calculation in all authentication and login handlers, including 2FA, login, OpenID, and organization switching endpoints.
  • Updated all calls to SetSession to use a pointer to the user object, ensuring session metadata is properly updated and persisted.

Session Metadata Tracking:

  • Updated session creation and expiration logic to record SessionCreatedAt and SessionLastActivityAt timestamps, supporting accurate enforcement of idle and max lifetime policies.
  • Throttled database writes for session activity updates to once per minute per session to reduce backend load.

Remove cookies from JSON responses:
Sending the cookies in a JSON response completely defeats the security benefits of HTTPOnly cookies. Cookies are no longer returned in JSON responses after this PR gets merged. This is technically a breaking change, as it requires changes to the Shuffle frontend (which I propose in a PR to Shuffle/Shuffle).

These changes collectively enhance the security and reliability of session management throughout the application.

P4sca1 added 4 commits July 6, 2026 17:01
- Remove Cookies field from HandleInfo struct in structs.go
- Remove all JSON cookie emission in HandleSet2fa and HandleLogin
- Session cookies are already set via http.SetCookie() headers
Signed-off-by: Pascal Sthamer <pascal+github@sthamer.xyz>
Signed-off-by: Pascal Sthamer <pascal+github@sthamer.xyz>
Signed-off-by: Pascal Sthamer <pascal+github@sthamer.xyz>
Comment thread shared.go
func expireSession(ctx context.Context, resp http.ResponseWriter, user *User, reason string) error {
if resp != nil {
newCookie := constructSessionDeleteCookie()
http.SetCookie(resp, newCookie)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

False positive. constructSessionDeleteCookie() calls ConstructSessionCookie under the hood, which sets the Secure Attribute when SHUFFLE_COOKIE_SECURE=true.

Comment thread shared.go
newCookie := constructSessionDeleteCookie()
http.SetCookie(resp, newCookie)
newCookie.Name = "__session"
http.SetCookie(resp, newCookie)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

False positive. constructSessionDeleteCookie() calls ConstructSessionCookie under the hood, which sets the Secure Attribute when SHUFFLE_COOKIE_SECURE=true.

Comment thread shared.go
// Slide the cookie's Expires forward so the browser also enforces the idle timeout at the HTTP level.
if resp != nil {
refreshedCookie := ConstructSessionCookie(user.Session, getSessionExpiration())
http.SetCookie(resp, refreshedCookie)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

False positive. ConstructSessionCookie sets the Secure Attribute when SHUFFLE_COOKIE_SECURE=true.

Comment thread shared.go
refreshedCookie := ConstructSessionCookie(user.Session, getSessionExpiration())
http.SetCookie(resp, refreshedCookie)
refreshedCookie.Name = "__session"
http.SetCookie(resp, refreshedCookie)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

False positive. ConstructSessionCookie sets the Secure Attribute when SHUFFLE_COOKIE_SECURE=true.

Comment thread shared.go
return
}

loginData = fmt.Sprintf(`{"success": true, "cookies": [{"key": "session_token", "value": "%s", "expiration": %d}], "region_url": "%s"}`, sessionToken, expiration.Unix(), regionUrl)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh well, we will need region_url for the cloud frontend.

i went through everything, my fears for this PR begins with the way this fits into the cloud multi-region system.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we just remove region_url, this would screw up the frontend getting region_url

the multi-region system is basically the same backend spread across many regions. looking at your PR, from what i see, cookies should carry over and be passed to the other backends too (since they are hosted on .shuffler.io)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. Let me rework this PR so that cloud stays unchanged.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @0x0elliot, I rechecked this PR. The loginData assignment was removed, because it gets overriden with string(newData) anyways and is not used in the meantime.

@yashsinghcodes

Copy link
Copy Markdown
Member

@0x0elliot

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.

4 participants