kinetis / session
Cookie-backed sessions and CSRF protection for Kinetis — file, Redis, and SQL storage behind one store interface.
Requires
- php: ^8.4
- kinetis/framework: ^1.5.1
- psr/http-message: ^2.0
- psr/http-server-middleware: ^1.0.2
- psr/simple-cache: ^3.0.0
Requires (Dev)
- infection/infection: ^0.35.0
- kinetis/cache-redis: ^1.2.0
- kinetis/persistence: ^1.2.1
- kinetis/redis: ^1.0.0
- phpstan/phpstan: ^2.2.8
- phpunit/phpunit: ^12.5.33
- vimeo/psalm: ^6.16.1
Suggests
- kinetis/cache-redis: Enables RedisSessionStore, the SESSION_DRIVER=redis backend, cluster and TLS included.
- kinetis/persistence: Enables SqlSessionStore, sessions in a kinetis_sessions table over the SQL contracts.
Provides
None
Conflicts
None
Replaces
None
README
kinetis/session
Cookie-backed sessions and CSRF protection for Kinetis
Part of Kinetis, a non-blocking PHP framework for API-first applications, developed in the kinetis-dev/kinetis monorepo.
The browser-application counterpart to the token-based kinetis/auth
and kinetis/auth-jwt packages: one store interface, three storage
drivers, and two route middlewares.
use Kinetis\Http\Attributes\Middleware; use Kinetis\Session\Middleware\CsrfMiddleware; use Kinetis\Session\Middleware\SessionMiddleware; use Kinetis\Session\Session; #[Middleware(SessionMiddleware::class)] #[Middleware(CsrfMiddleware::class)] final readonly class PreferencesController { public function __construct(private Session $session) {} }
Sessions load lazily and persist only when written: a route that never
touches its session performs no storage round trip and sends no cookie.
Session values are JSON-serializable application data: every store
projects the session to JSON, and a read decodes it back to plain
arrays and scalars. Concurrent updates of one live session are
last-write-wins by design (no session locking to serialize a browser's
parallel requests), and removing a stored id is terminal: a request
writing back the unchanged id it read has that write refused, and sends
no cookie, once a logout or rotation has removed it, so a stale write
cannot recreate a retired id. A concurrent request that calls
regenerate() itself writes under a new id and is not coordinated with
that logout.
A cookie id that is wellformed but unknown to the store — fabricated,
or expired — is never trusted as-is: the first real access rotates it
to a fresh id before anything can be exposed or written under it, and
that rotation persists nothing until something needs a stable identity,
so checking a CSRF token can never be what allocates a session.
Session::regenerate() is the complementary session-fixation defense
for a known, previously-issued id — call it on login. It rotates the
CSRF token with the id, keeping every application key, so a form
rendered before the privilege change needs re-rendering with the new
token. Full rules:
kinetis.dev/docs/session.html.
Provides
Installing this package auto-registers, via extra.kinetis:
- A container binding for
SessionStoreInterface, driven bySESSION_DRIVER—file(one JSON file per session, suited to development),redis(theCacheInterfacebinding Kinetis already builds from your Redis configuration, so one client serves the whole application — needskinetis/cache-redisandREDIS_URL,REDIS_HOST, orREDIS_CLUSTERwithREDIS_CLUSTER_SEEDS), orsql(akinetis_sessionstable over the persistence contracts, migration stubs shipped inresources/migrations/). Unset means the package binds nothing. - One command on
vendor/bin/kinetis:session:gc, deleting expired sessions from the bound store — schedule it with cron or an equivalent for thefileandsqldrivers. Theredisdriver needs no collection: the key's own TTL expires it.
Nothing else. Both middlewares are explicit opt-ins attached per route
or controller — SessionMiddleware registers the request's Session
on the request scope, CsrfMiddleware (stacked after it) enforces a
synchronizer token on state-changing methods via X-CSRF-Token or a
form's _token field, with hash_equals() comparison.
Configuration
| Key | Default | Purpose |
|---|---|---|
SESSION_DRIVER |
— | file, redis, or sql; unset = inert. |
SESSION_LIFETIME |
7200 |
Seconds a session stays readable from its last write — the cookie's Max-Age and the store's own TTL always refresh together. |
SESSION_COOKIE |
kinetis_session |
Cookie name. A __Host-/__Secure- prefix requires SESSION_SECURE. |
SESSION_SAMESITE |
Lax |
Cookie SameSite attribute: Strict, Lax, or None, any casing. None requires SESSION_SECURE. |
SESSION_SECURE |
true |
Cookie Secure attribute — false only for non-TLS local dev. |
SESSION_FILES_DIR |
system temp | The file driver's directory. |
Installation
composer require kinetis/session
Requires PHP 8.4+ and kinetis/framework.
Full documentation:
kinetis.dev/docs/session.html.
License
MIT — see LICENSE.