-
Notifications
You must be signed in to change notification settings - Fork 73
Description
Checklist
- I have looked into the Readme and have not found a suitable solution or answer.
- I have searched the issues and have not found a suitable solution or answer.
- I have searched the Auth0 Community forums and have not found a suitable solution or answer.
- I agree to the terms within the Auth0 Code of Conduct.
Description
The Auth0\Symfony\Service class caches the Auth0 SDK instance in a private property without implementing Symfony's ResetInterface. This causes a critical security vulnerability when running Symfony applications in PHP worker mode runtimes (FrankenPHP, RoadRunner, Swoole, etc.).
The problem:
// src/Service.php
final class Service implements ServiceInterface
{
private ?Auth0 $sdk = null;
public function getSdk(): Auth0
{
if (! $this->sdk instanceof Auth0) {
$this->warmUp();
$this->sdk = new Auth0($this->configuration);
}
return $this->sdk; // Returns CACHED instance with previous user's session
}
}In worker mode, PHP processes persist between HTTP requests. The cached $sdk instance (which contains user credentials and session data) is shared across multiple users' requests, causing User A to be authenticated as User B.
Reproduction
Environment:
- PHP 8.4+ with FrankenPHP in worker mode (
FRANKENPHP_CONFIG="worker ./public/index.php") - Symfony 8.0 (also affects 7.x)
- auth0/symfony 5.x
Steps to reproduce:
- Configure a Symfony application with Auth0 authentication
- Run the application in FrankenPHP worker mode (or RoadRunner/Swoole)
- Open Browser A and log in as User A
- Open Browser B (different browser or incognito) and log in as User B
- Refresh both browsers
Expected behavior:
- Browser A shows User A's profile
- Browser B shows User B's profile
Actual behavior:
- Both browsers may show the same user's profile (whichever was cached in the worker)
- Users are randomly authenticated as other users depending on which worker handles their request
Additional context
Why this happens:
Traditional PHP (PHP-FPM) starts a fresh process for each request, so cached properties are naturally cleared. Worker mode runtimes reuse PHP processes across multiple requests for performance. Symfony provides ResetInterface to handle this - services implementing it have their reset() method called between requests.
The fix:
The Service class should implement Symfony\Contracts\Service\ResetInterface:
use Symfony\Contracts\Service\ResetInterface;
final class Service implements ServiceInterface, ResetInterface
{
private ?Auth0 $sdk = null;
// ... existing code ...
public function reset(): void
{
$this->sdk = null;
}
}Symfony automatically calls reset() between requests when running in worker mode (via the kernel.reset event).
Current workaround:
Users must create a custom resetter service using reflection (or could possibly replace with own service, but this workaround worked ffor me):
final class Auth0ServiceResetter implements ResetInterface
{
public function __construct(private readonly Service $auth0Service) {}
public function reset(): void
{
$reflection = new \ReflectionClass($this->auth0Service);
$property = $reflection->getProperty('sdk');
$property->setValue($this->auth0Service, null);
}
}Related issues:
- Worker reuses kernel with Security already configured php/frankenphp#1098
- Reset services after kernel handled request symfony/symfony#59997
- FrankenPHP worker mode; what are the best practices regarding services resetting? php/frankenphp#1486
jwt-auth-bundle version
5.6.0
Symfony version
8.0.3
PHP version
8.5.1