Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Guard Bundle Laravel Package

knpuniversity/guard-bundle

KnpUGuardBundle is a Symfony bundle that integrates the Guard authentication component, making it easy to build custom authenticators for form login, API tokens, social sign-in, and other authentication flows with a flexible, well-documented interface.

View on GitHub
Deep Wiki
Context7

Getting Started

knpuniversity/guard-bundle backports Symfony's Guard authentication system to Symfony 2.3–2.7. It is archived (last release 2015-06-28) and is only appropriate for legacy projects still on Symfony < 2.8. If you are on 2.8+/3.x, use the built-in symfony/security-guard component instead; if you are on 5.3+, migrate to the new AuthenticatorInterface. Everything below applies equally to the bundle and to the built-in component, since the API is identical.

Minimal steps:

  1. Install and register the bundle. In composer.json require "knpuniversity/guard-bundle": "~1.0" (the constraint should mirror whatever your Symfony version allows), then add it to app/AppKernel.php:
$bundles = array(
    // ...
    new KnpU\GuardBundle\KnpUGuardBundle(),
);
  1. Create an authenticator. This is the whole point of the package: one class implements every authentication decision (extract credentials, load user, validate, respond on success/failure/challenge) instead of the old listener + provider + entry point trio.
// src/AppBundle/Security/FormLoginAuthenticator.php
use KnpU\GuardBundle\Guard\GuardAuthenticatorInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;

class FormLoginAuthenticator implements GuardAuthenticatorInterface
{
    private $encoderFactory;
    private $router;

    public function __construct($encoderFactory, $router)
    {
        $this->encoderFactory = $encoderFactory;
        $this->router = $router;
    }

    public function getCredentials(Request $request)
    {
        if ($request->getPathInfo() !== '/login_check' || !$request->isMethod('POST')) {
            return null; // skip this authenticator
        }

        return array(
            'username' => $request->request->get('_username'),
            'password' => $request->request->get('_password'),
        );
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        return $userProvider->loadUserByUsername($credentials['username']);
    }

    public function checkCredentials($credentials, $user)
    {
        $encoder = $this->encoderFactory->getEncoder($user);

        return $encoder->isPasswordValid($user->getPassword(), $credentials['password'], $user->getSalt());
    }

    public function createAuthenticatedToken($user, $providerKey)
    {
        // Not needed on Symfony >= 2.6; the bundle falls back to a sensible default.
        return null;
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        return new RedirectResponse($this->router->generate('dashboard'));
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        return new RedirectResponse($this->router->generate('login') . '?error=1');
    }

    public function start(Request $request, AuthenticationException $authException = null)
    {
        return new RedirectResponse($this->router->generate('login'));
    }

    public function supportsRememberMe()
    {
        return true;
    }
}
  1. Wire it into the firewall. Register the class as a service (services were public by default in these Symfony versions, so an id is enough) and reference it:
# app/config/security.yml
security:
    providers:
        db_provider:
            entity: { class: AppBundle:User, property: username }

    firewalls:
        main:
            pattern: ^/
            anonymous: ~
            guard:
                provider: db_provider
                authenticators:
                    - app.form_login_authenticator
                entry_point: app.form_login_authenticator
  1. First use case. Start with a form-login authenticator like above and get a full login flow working end-to-end before adding anything. The real payoff of Guard is the second use case: an API token authenticator on a stateless firewall — same class shape, radically different getCredentials/start.

Where to look first:

  • The GuardAuthenticatorInterface in the bundle — that is the contract, and the eight methods map 1:1 to the lifecycle.
  • The bundle's own tests and the Resources/doc examples (if present in your tag).
  • Ryan Weaver's "Guard: Simple and Lovely Symfony Authentication" screencast/blog post, which is the canonical walkthrough.

Implementation Patterns

One authenticator per auth mechanism, not per firewall. A form login, an API token, and an OAuth callback should be three classes, each returning null from getCredentials() when the request isn't theirs. Multiple authenticators on a single firewall is an explicitly supported pattern:

firewalls:
    main:
        pattern: ^/
        guard:
            provider: db_provider
            authenticators:
                - app.api_token_authenticator
                - app.form_login_authenticator
            entry_point: app.form_login_authenticator

Ordering matters only for the entry_point/challenge behavior and for remember-me token handling; credential extraction from each authenticator is attempted in order until one returns non-null.

Stateless API authenticator. The most common real-world use, and where Guard beats the old listener approach:

class ApiTokenAuthenticator implements GuardAuthenticatorInterface
{
    public function getCredentials(Request $request)
    {
        return $request->headers->get('X-AUTH-TOKEN');
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        return $userProvider->loadUserByUsername($credentials); // token == username in toy setups
    }

    public function checkCredentials($credentials, $user)
    {
        return true; // token validated by the provider above
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        return null; // continue the request
    }

    public function start(Request $request, AuthenticationException $authException = null)
    {
        return new JsonResponse(array('message' => 'Authentication required'), 401);
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        return new JsonResponse(array('message' => $exception->getMessageKey()), 401);
    }

    public function supportsRememberMe() { return false; }
}

Put this on a firewall with stateless: true so no session is started.

OAuth / social login (hybrid flow). start() is the "challenge" — for social login it redirects the browser to the provider's consent screen. getCredentials() then reads ?code= on the callback route, and getUser() exchanges the code for a profile and returns a user object (or creates one):

public function start(Request $request, AuthenticationException $e = null)
{
    return new RedirectResponse($this->oauth->getAuthorizationUrl());
}

public function getCredentials(Request $request)
{
    return $request->query->get('code'); // null on every other request
}

Base class to cut the boilerplate. In most apps you will write 3–4 authenticators and repeat createAuthenticatedToken, supportsRememberMe, and the onAuthenticationFailure shape every time. Create your own AbstractAppAuthenticator that implements those defaults and let concrete classes implement only getCredentials, getUser, checkCredentials, onAuthenticationSuccess, start. (Symfony 2.8's component ships AbstractGuardAuthenticator for the same purpose; the bundle's version predates it.)

Return null from onAuthenticationSuccess to continue the request. For an API authenticator, returning a Response short-circuits the controller — useful only when you want to terminate (e.g., redirect after form login). Returning null lets the normal controller run with the token in the security context.

Integrate via the firewall, not the event system. Resist the temptation to also register old-style listeners; Guard owns authentication for the firewall it is attached to. Things like "remember me" (supportsRememberMe()), CSRF (handle it yourself in the form or via _csrf_token check in getCredentials), and logout stay configured in security.yml as normal.

Testing. Because an authenticator is a plain service with constructor dependencies, unit-test it directly: mock Request, a UserProviderInterface, and the encoder factory. The awkward one is checkCredentials — inject the encoder factory rather than hard-coding security.encoder_factory lookups so you can stub it.

Gotchas and Tips

  • The bundle is dead weight on Symfony 2.8+. The Guard system was merged into symfony/security-guard; keeping the bundle means two copies of GuardAuthenticatorInterface and an autoload conflict or a confusing "which namespace?" moment. On upgrade, delete the bundle, drop the new KnpUGuardBundle() line, and change your use statements to Symfony\Component\Security\Guard\GuardAuthenticatorInterface. If a class implements both namespaces, remove the bundle one. Plan this migration before you invest heavily.

  • checkCredentials() is mandatory and does not delegate to the provider. The old UserAuthenticationProvider checked passwords for you. Guard does not. You must fetch the encoder via security.encoder_factory and call isPasswordValid() yourself. Forgetting this results in every password being accepted.

  • getUser() returning null is an error, not a "not found". Throw UsernameNotFoundException (or a subclass of AuthenticationException) so onAuthenticationFailure receives a meaningful exception. Returning null yields a generic BadCredentialsException.

  • getCredentials() returning null skips the authenticator entirely. This is the mechanism that makes multi-authenticator firewalls work, and it is also the most common source of "my authenticator never runs." Log at the top of getCredentials() while debugging.

  • onAuthenticationFailure() must return a Response. Returning null throws a RuntimeException from the Guard handler. onAuthenticationSuccess() and createAuthenticatedToken() may return null.

  • start() vs entry_point. start() is only called when the firewall needs to challenge an anonymous user (and no previous exception is stored). With several authenticators, entry_point: <service id> selects which one's start() runs. If you omit entry_point and have more than one authenticator, Symfony raises an error at container compile time.

  • Default redirect behavior is gone. With classic form login, Symfony redirected to default_target_path automatically. With Guard, if onAuthenticationSuccess() returns null, the request simply continues — on a /login_check route with no controller that produces a confusing blank page or 404. Either return a RedirectResponse yourself, or use the target-path session logic ($request->getSession()->get('_security.main.target_path')) explicitly.

  • CSRF is not handled for you. Add _csrf_token to your login form and validate it at the top of getCredentials() (return null or throw). Guard will otherwise happily authenticate a CSRF-less POST.

  • stateless: true + supportsRememberMe() === true is contradictory. On stateless firewalls return false; otherwise the token listener tries to write a remember-me cookie into a session that doesn't exist.

  • createAuthenticatedToken() version drift. On Symfony 2.6+ there is a default; on 2.3/2.4 you may need to override it to return the right token class (e.g., UsernamePasswordToken vs PreAuthenticatedToken). If roles behave oddly, check the token class here.

  • Debugging. Enable the web profiler's Security panel and inspect the token after login; for failure paths, AuthenticationException::getMessageKey() is what you want in your JSON/error output, not getMessage(). Also temporarily log inside getCredentials, getUser, and checkCredentials — the Guard flow is sequential and one null return kills the chain silently.

  • Extension point to prefer. Rather than subclassing bundle internals, wrap security.encoder_factory and the user provider into small injectable services and keep authenticators thin. When the eventual Symfony upgrade forces you off this package, the only thing you rewrite is the class declaration and a couple of method signatures.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core