jafar/guarded-authentication-bundle
A PHP authentication bundle that adds guarded authentication to your application, with configurable guards, access checks, and a straightforward setup for integrating secure login flows into existing projects. Designed to keep authentication logic organized and easy to extend.
First, a framing note: despite the "bundle" naming and the fact you're asking as a Laravel developer, this is a Symfony bundle — it hooks into security.yaml, the Symfony authenticator system, and the DI container. There is no Laravel service provider here. If you're on Laravel, nothing in this package transfers directly; the equivalent stack is tymon/jwt-auth (or Sanctum) plus a custom guard. If you're on Symfony, read on.
Minimal path to first login:
composer.json before anything else. v4.0.0 states Symfony 6 support and PHP 8+, so expect something like symfony/*: ^5.4|^6.0 and php: ^8.0 (Symfony 6.0 itself requires PHP 8.0.2+, so verify the exact floor). Note the notes say nothing about Symfony 7 — assume 6.x is the ceiling until proven otherwise.composer require jafar/guarded-authentication-bundle:^4.0, then register it in config/bundles.php.config/packages/. Don't guess the root key — open DependencyInjection/Configuration.php, which is the authoritative tree of every option the bundle accepts.config/security.yaml using the Symfony 5.3+/6 style — custom_authenticators: under the firewall — not the old guard: key.User entity implements UserInterface and PasswordAuthenticatedUserInterface, and that a user provider is configured.Where to look first, in order: the README (expect it to be thin), then DependencyInjection/Configuration.php (what you can configure), then the Security/ directory (what actually authenticates), then Resources/config/services.yaml (service IDs you can override or decorate). If there's a Tests/ directory, that's your best usage documentation.
Verify the v4 claim immediately. "Works with Symfony 6" is a one-line release note. Symfony 6 removed the Guard component outright, so if Security/ still contains classes extending AbstractGuardAuthenticator, this release is mislabelled and you should stop there. Run grep -rn "Guard" src/ and composer why-not symfony/framework-bundle 6.0 before writing any config.
Your first real use case is the one this bundle was built for: a JSON login endpoint (POST /api/login_check style) that validates credentials and returns a signed token, plus a stateless API firewall that authenticates subsequent requests from an Authorization header. Get that working end-to-end on a scratch app before touching a production firewall.
Two firewalls, two authenticators. This is the pattern the bundle is designed around: a main firewall with form_login (session, cookies, CSRF) for the browser, and a separate api firewall with stateless: true for token auth. Do not try to run both authenticators on one firewall — the login authenticator's supports() should match only the login path, and the token authenticator should match only requests carrying the header.
Login authenticator vs. token authenticator. Expect the bundle to ship both. The first reads POSTed credentials, calls the user provider, verifies the password hash, and returns a response containing the JWS. The second is header-driven and runs on every API request. Keep them on separate firewalls and keep the login route outside the protected path prefix, or you'll get redirect loops. In v4 these should be registered via custom_authenticators, and the login one will typically be an entry point too.
Override, don't fork. The bundle's services are the extension point. To change the token payload (add roles, tenant ID, custom claims) or the response shape, decorate the relevant service in services.yaml (decorates: <bundle.service.id>) rather than editing vendor code. Use bin/console debug:container --tag=security.authenticator and debug:container <service.id> to discover exactly what's registered.
Integration with the rest of your app. Once authenticated, the token subject resolves to your User through the provider, so normal $this->getUser(), isGranted(), and voters work unchanged. Symfony's security.access_control rules apply to the API firewall too — put ^/api rules there rather than hand-rolling checks in controllers.
Testing. Use KernelBrowser (or ApiTestCase if you're on API Platform): post credentials to the login route, assert a token in the body, then set HTTP_AUTHORIZATION on subsequent requests. Write one test that asserts an expired/malformed token yields 401 — that's the path most likely to regress and the one no one tests. On v4, add a test that boots the kernel on Symfony 6.4 (the last 6.x LTS) so a future PHP/Symfony bump doesn't silently break DI wiring.
Key management. Whatever option holds the signing key/secret, source it from an environment variable or Symfony Secrets Vault. Never commit it, and never share it between environments.
json_login authenticator covers the login half, and lexik/jwt-authentication-bundle covers the token half. Your exit path remains replace, not upgrade.DependencyInjection/Configuration.php against the 3.x tree — if config keys moved, a silent misconfiguration is easy. Also re-pin your composer.json and clear the container cache (bin/console cache:clear) after upgrading; stale compiled containers are the usual cause of "the new version still behaves like the old one."Security/ before production, and vendor-pin the exact version — ^4.0 will pick up whatever 4.0.x ships next.bin/console debug:firewall <name> to see authenticators and the entry point, bin/console debug:config security to confirm your YAML actually merged, the profiler's Security panel for token/roles at request time, and monolog at debug on the security channel to trace why an authenticator returned false from supports().OPTIONS requests carry no Authorization header. Make sure your CORS listener runs at high priority so preflight short-circuits before the authenticator 401s.stateless: true means no session. If you see session cookies being set on your API firewall, the config isn't taking — check for security.firewalls.api.stateless and any listener accidentally touching the session. Also confirm security.enable_authenticator_manager is not lingering in your config; that toggle was for the 5.x transition and is gone in 6.ReturnTypeWillChange noise should be gone on 8.1+, but you may still see deprecations from Symfony internals the bundle calls. Don't silence them globally; fix locally or upgrade.How can I help you explore Laravel packages today?