-
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
Description
Description
Coming back from Laravel (11/12), one thing that still feels missing in Symfony 7.4 is a way to configure which authentication mechanism / firewall is used directly at the route level, close to the controller / route definition, similar to Laravel's route middleware.
Symfony already allows route-level authorization via #[IsGranted] and via access_control rules that can target specific routes by path or route name. However, the authentication setup (firewalls, authenticators, token handlers, etc.) still has to live in config/packages/security.*, which becomes painful in a modular monolith setup where each module wants to encapsulate its own authentication concerns.
As of Symfony 7.4:
- Authentication is configured via firewalls under security.firewalls in config/packages/security.yaml (or PHP equivalent). A request matches exactly one firewall, typically by URL pattern, host, and/or HTTP method.
- Access rules are configured via access_control, which can match by path, methods, ip, route name, etc. This controls who can access (roles, PUBLIC_ACCESS, etc.), but still relies on the firewall that already matched the request.
- Route-level authorization can be expressed with attributes like
#[IsGranted(...)], which in 7.4 even gained a methods option to restrict checks to specific HTTP methods. But this only handles authorization; it does not select the firewall or authenticator. - Different authentication mechanisms (session, JWT, access tokens, login links, OIDC, etc.) are configured per firewall or as authenticators under a firewall, and are not selectable per route via attributes or configuration.
So while I can say "this route requires ROLE_USER" or "this route is PUBLIC_ACCESS" close to the controller, I cannot say "this route must use the api_jwt firewall / authenticator and nothing else" close to the route itself. That wiring must currently be expressed in security.* for URL patterns, and often involves tweaking shared firewalls.
Comparison with Laravel
In recent Laravel versions you can keep authentication concerns local to modules by defining a middleware class (e.g. JwtTokenValidationMiddleware) in a module and then attaching it directly to routes or route groups:
Route::get('/profile', function () {
// ...
})->middleware(JwtTokenValidationMiddleware::class);The key points:
- The route itself declares which authentication layer applies, which is very natural for modular / package-style route files
- Modules can ship their own middleware and route definitions without forcing the main application to know about all their internals in a central security config file
Problem in modular monoliths
In a modular monolith using Symfony 7.4 if each module may expose its own API with its own authentication mechanism, for example:
- Module A: service API key auth
- Module B: JWT bearer tokens
- Module C: OIDC / login-link based auth
To wire these up, I have to:
- Add or adjust one or more firewalls in config/packages/security.* (path patterns, stateless flags, authenticators, token handlers, etc.).
- Possibly add additional access_control rules.
This has several downsides:
- The central security.* config ends up knowing a lot about each module and its URL structure.
- Moving a module, changing its prefix, or extracting it into a package requires touching global security config.
- Modules cannot really be "plug-and-play" because their authentication cannot be configured close to their routes.
#[IsGranted] and access_control help with authorization, but they do not solve the per-route "which firewall / authenticator" concern.
Feature request
I would like Symfony 7.4+ to support a way to configure the authentication mechanism / firewall per route, co-located with the route definition, similar in spirit to Laravel's route middleware.
Conceptually, something along these lines:
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
// imaginary attribute / option:
use Symfony\Component\Security\Http\Attribute\UseFirewall;
#[Route('/profile', name: 'api_profile')]
#[UseFirewall('api_jwt')] // or #[UseAuthenticator(JwtTokenAuthenticator::class)]
#[IsGranted('ROLE_USER')]
public function profile()
{
// ...
}or as a Route option
#[Route(
path: '/profile',
name: 'api_profile',
// pseudo-configuration:
options: ['firewall' => 'api_jwt']
)]
public function profile() { /* ... */ }I am not proposing a specific concrete API; I am mainly asking whether Symfony would consider bringing the choice of firewall/authenticator closer to the route, similar to how #[IsGranted] brought authorization closer to the route, and similar to Laravel's route middleware.
If there is already a supported pattern to achieve this without pushing module-specific auth details into the central security.* configuration, I would be happy to adopt it instead.
Example
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
// imaginary attribute / option:
use Symfony\Component\Security\Http\Attribute\UseFirewall;
#[Route('/profile', name: 'api_profile')]
#[UseFirewall('api_jwt')] // or #[UseAuthenticator(JwtTokenAuthenticator::class)]
#[IsGranted('ROLE_USER')]
public function profile()
{
// ...
}