Skip to content

Only rebind auth creators the AuthManager itself owns - #9

Merged
adhikjoshi merged 1 commit into
mainfrom
fix/auth-custom-creator-rebind
Aug 12, 2026
Merged

Only rebind auth creators the AuthManager itself owns#9
adhikjoshi merged 1 commit into
mainfrom
fix/auth-custom-creator-rebind

Conversation

@adhikjoshi

@adhikjoshi adhikjoshi commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

The bug

RequestScope::rebindAuthCustomCreators() rebound every entry in AuthManager::$customCreators onto the coroutine-local clone.

That is correct for Auth::viaRequest() — its closure is built inside AuthManager, so $this is the manager and $this->app['request'] must follow the coroutine.

It is wrong for any creator registered from a service provider. Sanctum's guard extension closes over the SanctumServiceProvider:

$auth->extend('sanctum', function ($app, $name, array $config) use ($auth) {
    return tap($this->createGuard($auth, $config), ...);   // $this = the provider
});

Rebinding $this to the AuthManager sends createGuard() into AuthManager::__call, which forwards unknown methods to the default guard:

BadMethodCallException: Method Lab404\Impersonate\Guard\SessionGuard::createGuard does not exist.
  #1 sanctum/src/SanctumServiceProvider.php(89): AuthManager->__call('createGuard')
  #2 AuthManager.php(112): {closure}(Laravel\Octane\Swoole\Coroutine\CoroutineApplication, 'sanctum', ...)

Every auth:sanctum request threw. In production this was a 5.26% 5xx rate within four minutes of rollout, across all authenticated API traffic, and required a rollback.

The fix

Rebind only when the closure's bound $this is already an AuthManager — precisely the viaRequest case the method exists for. Provider-owned creators keep their original binding.

Tests

tests/Unit/RequestScopeAuthCreatorRebindTest.php covers three cases: AuthManager-owned creators are rebound, provider-owned creators are left alone, and static/non-closure entries are skipped.

Verified it is a real regression test — with the fix reverted, test_it_leaves_service_provider_creators_alone fails with "Failed asserting that two variables reference the same object". Full suite: 139 tests, 882 assertions, green.

Note on scope

This class of bug only manifests under Swoole, so a host app's normal test suite cannot see it. Anything that resolves a guard registered by a package provider is affected — Sanctum here, but Passport and any Auth::extend() from a provider share the shape.


View with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is enabled.

rebindAuthCustomCreators() rebound every entry in AuthManager::$customCreators
to the coroutine-local clone. That is right for Auth::viaRequest(), whose
closure is built inside AuthManager and reads $this->app['request'] — without
the rebind the guard would see the worker's base request.

It is wrong for any creator registered from a service provider. Sanctum's
guard extension closes over the SanctumServiceProvider and calls
$this->createGuard($auth, $config). Rebinding $this to the AuthManager sends
that call into AuthManager::__call, which forwards unknown methods to the
default guard, so it surfaced as:

  BadMethodCallException: Method
  Lab404\Impersonate\Guard\SessionGuard::createGuard does not exist.

Every sanctum-authenticated request threw. In production this was a 5.26% 5xx
rate within four minutes of the rollout, on all authenticated API traffic.

Rebinding now applies only when the closure's bound $this is already an
AuthManager, which is exactly the viaRequest case the method was written for.
Provider-owned creators keep their original binding.
@adhikjoshi
adhikjoshi merged commit 23123dd into main Aug 12, 2026
3 checks passed
@adhikjoshi
adhikjoshi deleted the fix/auth-custom-creator-rebind branch August 12, 2026 02:23
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