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 Laravel Package

knpuniversity/guard

KnpUniversity Guard adds simple, flexible guard-based authentication to Symfony. Define a guard class to handle credentials, user loading, success or failure, and token creation, making custom login flows and API authentication easier.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: knpuniversity/guard is a Symfony bundle, not a Laravel package. It backports Symfony's Guard authentication system (native from Symfony 2.8) to Symfony 2.6/2.7. There is no service provider, no facade, no Auth::extend integration, and no path to composer require it into a Laravel 9/10/11 app. For a Laravel developer, day-to-day leverage from this repository is conceptual reference, not code reuse.

Where to look first

  1. README.md — shows the single-class GuardAuthenticatorInterface contract. Skim it in 10 minutes; that flow is the design you'll mirror.
  2. KnpUGuardBundle / DI extension — only if you're actually maintaining a Symfony 2.6/2.7 app. Modern readers can skip it.
  3. The Symfony docs for native Guard (2.8+) and AuthenticatorInterface (5.3+) — this is where the package's ideas actually live and get maintained.

Your first use case (Laravel)

Extract the flow model, then implement it with first-party Laravel tooling. The Guard lifecycle is:

getCredentials → getUser → checkCredentials
   → createAuthenticatedToken  (onAuthenticationSuccess)
   → onAuthenticationFailure / start (challenge)

Laravel counterpart:

// config/auth.php
'guards' => [
    'api-key' => ['driver' => 'api-key', 'provider' => 'users'],
],
// app/Providers/AuthServiceProvider.php
Auth::extend('api-key', function ($app, $name, array $config) {
    return new ApiKeyGuard(
        Auth::createUserProvider($config['provider']),
        $app['request'],
    );
});
class ApiKeyGuard implements Guard
{
    use GuardHelpers;

    public function __construct(protected UserProvider $provider, protected Request $request) {}

    public function user(): ?Authenticatable
    {
        if ($this->user !== null) return $this->user;

        $key = $this->request->header('X-Api-Key');
        if (!$key) return null;

        $user = $this->provider->retrieveByCredentials(['api_key' => $key]);

        return $this->user = $user; // checkCredentials equivalent
    }

    public function validate(array $credentials = []): bool { /* ... */ }
    public function check(): bool { return $this->user() !== null; }
}

If you're on a Laravel version where Fortify/Sanctum already covers the flow (session login, API tokens, 2FA, remember-me), use those instead of writing a custom guard — the same advice this package's own assessment gives: use the framework.

Only-if-you-must (legacy Symfony 2.6/2.7)

composer require knpuniversity/guard
// app/AppKernel.php
$bundles[] = new KnpU\GuardBundle\KnpUGuardBundle();
# app/config/security.yml
security:
    providers:
        users: { entity: { class: AppBundle\Entity\User, property: username } }
    firewalls:
        main:
            anonymous: ~
            guard:
                authenticators:
                    - app.form_authenticator
                    - app.api_token_authenticator

Then implement one authenticator class per flow. Treat this as a stopgap with a scheduled exit, not a foundation.

Implementation Patterns

Pattern 1 — One authenticator per flow, not per endpoint. The whole point of Guard is that a single class owns credentials, user loading, credential checking, success, failure, and the challenge response. In Laravel that maps to one custom Guard (or one Authenticator) per flow. Keep them small and flow-specific: FormLoginGuard, ApiTokenGuard, SocialLoginGuard.

class ApiTokenGuard implements Guard
{
    public function user(): ?Authenticatable
    {
        if ($this->user) return $this->user;

        $token = $this->request->bearerToken();
        if (!$token) return null;

        return $this->user = $this->provider->retrieveByCredentials(['api_token' => $token]);
    }
}

Pattern 2 — Multiple authenticators on one firewall (Symfony). Guard's real value is letting several authenticators coexist (form + remember-me + token) with a first-class challenge flow:

firewalls:
    main:
        guard:
            entry_point: app.form_authenticator
            authenticators:
                - app.api_token_authenticator
                - app.form_authenticator

Laravel equivalent: multiple guards on request via middleware, or one guard that inspects the request shape.

// app/Http/Middleware/ChooseAuthGuard.php
public function handle($request, Closure $next)
{
    config(['auth.defaults.guard' => $request->expectsJson() ? 'api' : 'web']);
    return $next($request);
}

Pattern 3 — Stateless vs stateful split. Guard's start() is what renders the challenge (redirect to /login or 401). Mirror that with Laravel middleware branching on $request->expectsJson() or Accept headers:

class Authenticate
{
    protected function redirectTo($request)
    {
        return $request->expectsJson() ? null : route('login');
    }
}

Pattern 4 — Remember-me. Guard supports remember-me tokens in the authenticator. In Laravel this is Auth::attempt($creds, $remember = true) plus a remember_token column — the built-in SessionGuard handles it. Don't roll it yourself.

Pattern 5 — Social login. Guard-style social authenticators (getCredentials reads the provider code, getUser exchanges it, checkCredentials is usually return true) map cleanly to Laravel Socialite:

Route::get('/auth/{provider}/callback', function ($provider) {
    $social = Socialite::driver($provider)->user();
    $user = User::firstOrCreate(['email' => $social->getEmail()], [
        'name'  => $social->getName(),
        'provider_id' => $social->getId(),
    ]);
    Auth::login($user, remember: true);
    return redirect('/dashboard');
});

Pattern 6 — Migration mapping (legacy Symfony → modern). If your job is removing this dependency:

Guard method (this package) Native Symfony Guard (2.8+) Modern AuthenticatorInterface (5.3+)
getCredentials(Request) same getCredentials(Request)
getUser($credentials, UserProviderInterface) same getUser(Passport)
checkCredentials($user, $credentials) same Passport validation
createAuthenticatedToken same createToken(Passport)
onAuthenticationSuccess same onAuthenticationSuccess
onAuthenticationFailure same onAuthenticationFailure
start(Request) start(Request) start(Request)
supportsRememberMe() same via RememberMeBadge

The work is mechanical: one file per authenticator, re-point method signatures, replace the DI tags.

Pattern 7 — CI guard against reintroduction. If this package shows up in a scan, treat it as a legacy marker. Add a lint rule:

# scripts/check-forbidden-deps.sh
if grep -q 'knpuniversity/guard' composer.json; then
    echo "Forbidden dependency: knpuniversity/guard (use native auth)" >&2
    exit 1
fi

Gotchas and Tips

  • It will not install on modern stacks. Constraint is pinned to Symfony 2.6/2.7 and a PHP 5-era toolchain. composer require fails on anything current; --ignore-platform-reqs gets you a file that cannot bootstrap because there is no Symfony kernel, container, or event dispatcher. If a teammate proposes it for a Laravel app, stop the conversation there.
  • Archived, single-release, zero dependents. Last release was September 2015. No security-advisory channel, no maintainer. Authentication is the highest-consequence surface in any app — do not adopt an unmaintained component there.
  • Names collide with Laravel's Guard. Illuminate\Contracts\Auth\Guard, Auth::guard(), and $request->guard() have nothing to do with this Symfony Guard. Keep the vocabulary separate in reviews and tickets; confusion here wastes real time.
  • Don't copy checkCredentials code verbatim. The 2015 idioms use hash_equals patterns and sometimes plain == comparisons. Copying them into a modern codebase imports a timing-safe-comparison bug class with no tests to catch it. In Laravel, use Hash::check($plain, $user->password) — and let SessionGuard do it.
  • getUser() is where user enumeration defects hide. Guard distinguishes "user not found" from "bad password" through onAuthenticationFailure. In your Laravel rewrite, return the same generic error message and log the specific reason:
    if (! $user || ! Hash::check($password, $user->password)) {
        Log::warning('auth.failed', ['email' => $email, 'ip' => $request->ip()]);
        return back()->withErrors(['email' => __('auth.failed')]);
    }
    
  • start() is the challenge, not the failure. It runs before any credentials are checked — the "you must authenticate first" response, not "your password was wrong." In Laravel that's middleware('auth') redirecting to route('login'); the "wrong password" case is a separate flash-and-back. Getting these two conflated produces infinite-redirect bugs in both frameworks.
  • Remember-me interacts with the firewall, not the authenticator. In Symfony, remember_me: is a separate firewall key alongside guard:. Forgetting it and expecting supportsRememberMe() to work is a common trap. In Laravel, Auth::attempt($creds, true) plus a remember_token column does the whole job — the SessionGuard handles it.
  • Firewall ordering matters with multiple authenticators. Guard tries them in the listed order and the first non-null getCredentials() wins. Put token authenticators before form authenticators or every JSON request will be redirected to login. The Laravel analogue is guard middleware order — same class of bug.
  • provider is required on every guard block. A guard without a provider fails at request time with a confusing container error, not at boot. This bites in both Symfony (guard.provider) and Laravel ('api-key' => ['driver' => ..., 'provider' => 'users']).
  • Test the failure path, not just login. Guard's most under-tested surfaces are onAuthenticationFailure and start(). Write tests for: bad password, unknown user, already-authenticated user re-authenticating, and JSON vs HTML request. These are the tests that survive a migration.
  • Extension points are the migration target. GuardAuthenticatorInterface methods are almost 1:1 with the modern AuthenticatorInterface. If you must maintain a legacy app on this, keep each authenticator a thin adapter so a later upgrade is a rename, not a rewrite.
  • Keep the repo open as documentation. Bookmark it for conceptual reference when reading old Symfony security configs or designing a new auth flow. Do not list it in composer.json, do not vendor it, do not open an advisory ticket expecting a maintainer — there isn't one.
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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package