Skip to content

fix: Sanctum middleware config mutation#387

Merged
binaryfire merged 11 commits into
0.4from
fix/session-middleware-config-mutation
May 14, 2026
Merged

fix: Sanctum middleware config mutation#387
binaryfire merged 11 commits into
0.4from
fix/session-middleware-config-mutation

Conversation

@binaryfire
Copy link
Copy Markdown
Collaborator

This PR cleans up Sanctum so it no longer mutate's configuration per-request. It also refactor's the previous extension point we added to a static method so it can be set in a service provider.

Summary

  • Add StartSession::configureSessionCookieUsing() so session cookie attributes can be customized per request from a service provider, without subclassing the session middleware.
  • Move Sanctum's stateful frontend cookie hardening onto that hook instead of mutating the global config repository during request handling.
  • Add static-state cleanup for the new session callback registry and SanctumGuard macros, with regression coverage for cookie hardening, callback composition, and test isolation.

Details

Sanctum previously called config() from EnsureFrontendRequestsAreStateful::handle() to force session.http_only = true and session.same_site = lax. That is safe in Laravel's request model, but not in Hypervel workers: the config repository is process-global, so request-time writes can leak into other requests handled by the same worker. Technically it wasn't causing problems but it's a worthwhile correctness cleanup.

This PR keeps Sanctum's behavior without the global mutation. StartSession now supports boot-time registration of request-aware cookie configuration callbacks:

StartSession::configureSessionCookieUsing(function (Request $request, array $cookie): array {
    return array_replace($cookie, [
        'domain' => tenant()->sessionCookieDomain(),
    ]);
});

Callbacks run when the session cookie is written, receive the current request and current cookie configuration, and compose in registration order. Sanctum registers one of these callbacks during provider boot and only applies its http_only / same_site hardening when the existing Sanctum request attribute is present.

This also replaces the older protected-method extension point. Customizing session cookie attributes no longer requires creating a child middleware class and swapping it into the middleware stack; apps can now configure the behavior directly from a service provider, which is closer to Laravel's ...Using() extension style and easier to use for multi-tenant cookie settings.

Tests

  • ./vendor/bin/phpunit tests/Session/Middleware/StartSessionTest.php
  • ./vendor/bin/phpunit tests/Session
  • ./vendor/bin/phpunit tests/Sanctum/EnsureFrontendRequestsAreStatefulTest.php tests/Sanctum/FrontendCookieHardeningTest.php tests/Sanctum/SanctumGuardStaticStateTest.php
  • ./vendor/bin/phpunit tests/Sanctum
  • ./vendor/bin/phpunit tests/Session tests/Sanctum
  • ./vendor/bin/phpstan analyse src/session/src src/sanctum/src tests/Session/Middleware/StartSessionTest.php tests/Sanctum tests/AfterEachTestSubscriber.php

binaryfire added 11 commits May 14, 2026 01:06
Replaces the subclass-based getSessionCookieConfig() override with a
static callback registry. Callbacks run in registration order, each
receiving the previous callback's output. Threads Request through to
the resolver so callbacks can vary cookie attributes per-request
without mutating worker-global config.
Replaces the subclass-override test with four callback tests covering
single-callback registration, per-request behavior via Request, chained
composition in registration order, and flushState clearing the
registry. Switches base class to Hypervel\Tests\TestCase and the
reflection helper to ClassInvoker.
Prevents the new StartSession cookie callback registry and SanctumGuard
Macroable state from leaking between tests.
Adds a Configuring the Session Cookie section covering the new
StartSession callback API, its boot-time registration pattern, and
chained registration semantics.
Drops the unconditional config([...]) mutation that fired on every
request through this middleware. Under Swoole the mutation persisted
on the worker-global config singleton and leaked across concurrent
requests. Cookie hardening is now applied via a StartSession callback
registered by SanctumServiceProvider.
Replaces the runtime config mutation with a boot-time
configureSessionCookieUsing callback that forces http_only=true and
same_site=lax only when the request has the sanctum attribute set by
EnsureFrontendRequestsAreStateful's prepended inline middleware.
SanctumGuard is effectively a worker-lifetime singleton (cached in
AuthManager's guards registry, and AuthManager is itself a singleton).
Without flushState, macros registered on the Macroable trait persisted
across tests and across coroutines for the worker lifetime.
Sets session.http_only=false and session.same_site=strict to non-default
values, runs EnsureFrontendRequestsAreStateful, and asserts both values
are still unchanged. Catches any regression that reintroduces the
removed configureSecureCookieSessions config([...]) call.
Covers three contracts: stateful Sanctum frontend requests force
http_only=true and same_site=lax on the session cookie; app-level
StartSession callbacks compose with Sanctum's hardening; non-Sanctum
requests bypass the hardening based on the sanctum request attribute.
Locks in that SanctumGuard::flushState() clears Macroable-registered
macros, guarding the test-isolation contract for the worker-lifetime
singleton guard.
@binaryfire binaryfire merged commit 237a082 into 0.4 May 14, 2026
34 checks passed
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