Skip to content

Multi-user session bug: _security_main erased when num_threads > 1 (FrankenPHP worker mode) #1498

@annro-git

Description

@annro-git

Symptom

When two different users (e.g. user1 on PC A, user2 on PC B) are simultaneously
logged in, login of one user disconnects the other (their session is invalidated).

The bug occurs regardless of access method (Caddy reverse proxy, IP direct,
WireGuard tunnel). Affects different browsers (Chrome, Firefox). Bug is
intermittent timing-wise but systematic.

Investigation

Sessions stored in PostgreSQL/MariaDB show the disconnected user's session
still exists in DB but with _security_main removed. The session row remains,
but the auth token is silently erased from its sess_data.

Root cause

Bug only occurs in FrankenPHP multi-threaded worker mode (default num_threads=5).

Workaround: setting FRANKENPHP_CONFIG: "num_threads 1" resolves the bug.
This points to shared mutable state between FrankenPHP workers, most likely in
Symfony's TokenStorage or session lifecycle of the custom
UsernameOrEmailPasswordAuthenticator.

Setup

  • Koillection 1.8 (Docker image koillection/koillection:latest)
  • Sessions either default /tmp files or PDOSessionHandler MariaDB (same bug)
  • Behind Caddy reverse proxy or direct IP access (same bug)

Eliminated potential causes

  • TRUSTED_PROXIES configuration
  • session.gc_maxlifetime PHP value
  • cookie_samesite: strict vs lax
  • session_fixation_strategy: none
  • Removing remember_me from firewall main
  • Session storage backend (file vs PDO)

Suspicion

Symfony worker mode requires explicit reset of service state between requests
(see Symfony's kernel.reset event and ResetInterface). Koillection may have
a service that holds user-specific state across requests in the worker.

Workaround

Add FRANKENPHP_CONFIG: "num_threads 1" to docker-compose environment.
Trade-off: requests serialized, no real parallelism. Acceptable for low-usage
self-hosting.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions