Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

jeroenvanrensen's avatar

Laravel Package Development - Adding an extra guard

Hi everyone,

I'm creating a package, and I want admins to login using other routes and to have other models and controllers.

I read this article but I don't understand it.

In the docs:

Auth::extend('jwt', function ($app, $name, array $config) {
    // Return an instance of Illuminate\Contracts\Auth\Guard...

   return new JwtGuard(Auth::createUserProvider($config['provider']));
});

Do I have to create a guard class?

And how should my login controller be?

Currently I have this:

<?php

namespace JeroenvanRensen\MoonPHP\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{
    public function show()
    {
        return view('moon::auth.login');
    }

    public function store()
    {
        Auth::guard('moon');

        if (Auth::attempt(['email' => request('email'), 'password' => request('password')])) {
            return redirect('/admin');
        }
        
        return back()->withInput(request()->only('email', 'remember'));
    }
}

But it does not work.

I hope someone can help me out.

Thank you! Jeroen

0 likes
9 replies
rodrigo.pedra's avatar
Level 56

Do I have to create a guard class?

Not exactly. As you are instantiating a new object you could use one of the guard drivers Laravel ships with.

As you seem to be willing to use the same session-based login you could add this at your package's Service Provider boot method:

<?php

namespace JeroenvanRensen\MoonPHP;

use Illuminate\Auth\EloquentUserProvider;
use Illuminate\Contracts\Hashing\Hasher;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\ServiceProvider;

class MoonServiceProvider extends ServiceProvider
{
    public function register()
    {
        // some code
    }

    public function boot()
    {
        // Register new user provider with AuthManager
        Auth::provider('moon', function ($app, $config) {
            // change this to your Admin model's FQCN
            $model = \JeroenvanRensen\MoonPHP\Models\Admin::class;

            return new EloquentUserProvider($app->make(Hasher::class), $model);
        });

        // Register new guard driver with AuthManager
        Auth::extend('moon', function ($app, $name, $config) {
            // as you are doing a package, we will override
            // the config that usually would come
            // from `./auth/config.php
            $config = [
                'provider' => 'moon',
            ];

            return Auth::createSessionDriver($name, $config);
        });
    }
}

This will register:

  1. A new User Provider using the EloquentUserProvider driver, but bound to the model your package provides.
  2. A new Guard Driver, using Laravel's SessionGuard, but configured using the new user provider just created.

If your IDE complaint at this line:

return Auth::createSessionDriver($name, $config);

Don't worry, the createSessionDriver() method is not listed in the Auth façade's doc-blocks, so IDE won't find it. But its a public method in the AuthManager class, which the Auth façade provides, so Laravel will find it.

Then in your controller you have a minor misconception:

<?php

namespace JeroenvanRensen\MoonPHP\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{
    public function show()
    {
        return view('moon::auth.login');
    }

    public function store()
    {
        // You were doing nothing with the result of this call
        $guard = Auth::guard('moon');

        // Use the guard retrieved above
        if ($guard->attempt(['email' => request('email'), 'password' => request('password')])) {
            return redirect('/admin');
        }
        
        return back()->withInput(request()->only('email', 'remember'));
    }
}

I added some comments to highlight what was missing.

But basically, calling Auth::attempt(...) without a guard, would use the default guard configured in the userś project ./config/auth.php file, it generally is the web guard.

A more concise version of your controller, that maybe will make things more clear would be this:

<?php

namespace JeroenvanRensen\MoonPHP\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{
    public function show()
    {
        return view('moon::auth.login');
    }

    public function store()
    {
        // Retrieve the wanted guard from Auth
        if (Auth::guard('moon')->attempt(['email' => request('email'), 'password' => request('password')])) {
            return redirect('/admin');
        }
        
        return back()->withInput(request()->only('email', 'remember'));
    }
}

This should do it.

As an additional remark, I see two main problems with you controller:

  1. It extends the Controller class from you package user's project. Your package should avoid depending on user code, and in this case there is no benefit extending from the user project.
  2. You are not doing any input validation.

Below is a suggestion addressing both remarks:

<?php

namespace JeroenvanRensen\MoonPHP\Http\Controllers\Auth;

use Illuminate\Support\Facades\Auth;

// Not extending code from the App namespace anymore
class LoginController
{
    public function show()
    {
        return view('moon::auth.login');
    }

    public function store()
    {
        // Added validation, the validated fields as 
        // an array is the return value
        $credentials = request()->validate([
            'email' => ['required', 'email'],
            'password' => ['required'],
        ]);

        if (Auth::guard('moon')->attempt($credentials, request('remember', false))) {
            return redirect('/admin');
        }

        return back()->withInput(request()->only(['email', 'remember']));
    }
}

Hope it makes thing clearer and helps you.

jeroenvanrensen's avatar

Hi @rodrigo.pedra,

Thank you very very very much for your very detailed answer!

I followed all steps, and now I'm understanding how it all works.

Unfortunately, it's not working yet. I can navigate to the login form, but when I hit Submit I get this error:

Auth guard [moon] is not defined. 

Do you know what's happening here?

Maybe there's something wrong in the MoonServiceProvider?

Thank you! Jeroen

rodrigo.pedra's avatar

How are you registering the Service Provider in the user's app?

For Laravel to run a Service Provider's code, it needs to be aware of its existence.

Any non-default Service Providers should be registered manually into the project's ./config/app.php.

But, as you are building a package, since version 5.5 Laravel allows packages to list their Service Providers in the package's package.json file under a special key for auto-discovery. This way the package's users don't need to go through this manual registration step.

Take a look at this section ate the docs about auto-discovery:

https://laravel.com/docs/8.x/packages#package-discovery

For a quick test to see if it the guard works as expected, you can add the Service Provider manually to your test app's ./config/app.php file:

<?php

return [
    // ... other config keys

    'providers' => [

        /*
         * Laravel Framework Service Providers...
         */

        ////////////////////////////////////////////////////////////
        // ... core service providers - DO NOT REMOVE
        ////////////////////////////////////////////////////////////

        /*
         * Package Service Providers...
         */

        ////////////////////////////////////////////////////////////
        // ... third-party Service Providers - ADD HERE
        ////////////////////////////////////////////////////////////

        \JeroenvanRensen\MoonPHP\MoonServiceProvider::class,


        /*
         * Application Service Providers...
         */

        ////////////////////////////////////////////////////////////
        // ... application service providers - DO NOT REMOVE
        ////////////////////////////////////////////////////////////

    ],

    // ... other config keys
];

Let me know if you already went through this step and it still doesn't work.

jeroenvanrensen's avatar

Hi @rodrigo.pedra,

This is currently my providers array in config/app.php:

/*
 * Application Service Providers...
 */
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
JeroenvanRensen\MoonPHP\MoonServiceProvider::class

So it still does not work unfortunately...

rodrigo.pedra's avatar

Turns out the way I suggested registering in the Service Provider would create a Auth Provider, and a Auth Guard driver. and both would need to be configured in the project's ./config/auth.php

As you want to use a session based guarde + eloquent model, the service provider is much simpler:

<?php

namespace JeroenvanRensen\MoonPHP;

use Illuminate\Support\Facades\Config;
use Illuminate\Support\ServiceProvider;

class MoonServiceProvider extends ServiceProvider
{
    public function register()
    {
        // some code
    }

    public function boot()
    {
        // Will use the SessionGuard driver with the moon provider
        Config::set('auth.guards.moon', [
            'driver' => 'session',
            'provider' => 'moon',
        ]);

        // Will use the EloquentUserProvider driver with the Admin model
        Config::set('auth.providers.moon', [
            'driver' => 'eloquent',
            'model' => \JeroenvanRensen\MoonPHP\Models\Admin::class,
        ]);
    }
}

Or you can instruct your package's user to configure it manually.

Sorry for over-complicating my first answer.

Please or to participate in this conversation.