I’m relatively new to using Laravel 5. Up until recently I managed a few applications that were Laravel 3 and 4.2, sadly we never had the time to migrate them.
When I started a new project I chose Laravel 5 (5.5 was the latest version as of starting this article, but has been updated for 5.6) and one of the best features was the ability to use multiple auth guards. This is great for applications that have 2 sets of users (e.g. customers, staff) and so needed their own login pages etc. In this article we’re going to go over setting them up and what other things need to be considered.
I’ve created an example app on Github that I’ll reference throughout the article:
Install Laravel
First thing we need to do is create a Laravel app (You can skip this part if you already have one).
composer create-project --prefer-dist laravel/laravel auth_app
Update .env file
Now you should update your .env folder to fit your needs. i.e setting a database to use. For this article the database is probably the only thing you’ll need to change. I’ve also setup the domain to be multi-auth.test, which I’ll refer to for the rest of the article.
Scaffold Basic Authentication
Next we’ll run the make auth command to give us migrations for our users as well as the routing and controllers we need. These will be used for the main user (customer) but we can copy some of the classes for use with the staff users.
php artisan make:auth
We’ll also need to create a migration for the staff table:
php artisan make:migration create_staff_table --create=staff
Which we’ll keep really simple like the users migration, so it should look like this:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateStaffTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('staff', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('staff');
}
}
Now we can migrate the database:
php artisan migrate
Add our admin staff user
Next we need to add a Staff class. We’ll create and Admin folder in app so we can namespace everything to that. Again our Staff model can be really simple for now:
<?php
namespace App\Admin;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class Staff extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token',
];
}
Add an admin guard
In the auth config (config/auth.php) we need to add our new guard for admin. We’ll call it web_admin. The driver is fine to be the same as the web guard but the provider will need to be for the staff users.
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'web_admin' => [
'driver' => 'session',
'provider' => 'staff',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],
Now we need to add a provider
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => \App\User::class,
],
'staff' => [
'driver' => 'eloquent',
'model' => \App\Admin\Staff::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
Update Guest Middleware
Now we need to update the guest middleware RedirectIfAuthenticated to account for the web_admin auth guard. Open app/Http/Middleware/RedirectIfAuthenticated.php and add a check for the web_admin guard, so the handle method will now look something like this:
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
if ('web_admin' === $guard) {
return redirect('/admin');
}
return redirect('/home');
}
return $next($request);
}
Add Auth routes and controller for Staff
Now from running php artisan make:auth we’re given some auth controllers in app/Http/Controllers/Auth and in routes/web.php theres a new line:
Auth::routes();
This will work fine for our customer user but we’ll need almost the same for the staff user.
We’ll copy the following files into app/Http/Controllers/Admin/Auth and sort out the class namespaces.
app/Http/Controllers/Auth/LoginController.php
app/Http/Controllers/Auth/RegisterController.php
Now I don’t usually have a register controller for staff users but for simplicity we’ll keep it in (I would usually create a console command for creating a staff user before the staff management section existed).
Update Admin LoginController
Now the first line we need update for now is this one:
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = '/admin';
The LoginController uses a AuthenticatesUsers trait which provides a lot of the functionality, but we’ll need to override some of it.
Once done The LoginController should look something like this:
<?php
namespace App\Http\Controllers\Admin\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = '/admin';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest:web_admin')->except('logout');
}
/**
* Show the application's login form.
*
* @return \Illuminate\Http\Response
*/
public function showLoginForm()
{
return view('admin.auth.login');
}
/**
* Log the user out of the application.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function logout(Request $request)
{
$this->guard()->logout();
$request->session()->invalidate();
return redirect()->route('admin.login');
}
/**
* Get the guard to be used during authentication.
*
* @return \Illuminate\Contracts\Auth\StatefulGuard
*/
protected function guard()
{
return Auth::guard('web_admin');
}
}
And the RegisterController should look like this:
<?php
namespace App\Http\Controllers\Admin\Auth;
use App\Staff;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
class RegisterController extends Controller
{
use RegistersUsers;
/**
* Where to redirect users after registration.
*
* @var string
*/
protected $redirectTo = '/admin';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest:web_admin');
}
/**
* Show the application registration form.
*
* @return \Illuminate\Http\Response
*/
public function showRegistrationForm()
{
return view('admin.auth.register');
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:staff',
'password' => 'required|string|min:6|confirmed',
]);
}
/**
* Create a new staff instance after a valid registration.
*
* @param array $data
* @return \App\Staff
*/
protected function create(array $data)
{
return Staff::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
}
}
Update Exception Handler
Now at the moment if a user is authenticated they will redirect back to the same place, no matter what the guard is.
Open app/Exceptions/Handler.php and add the following unauthenticated method, which will override the parent handler.
/**
* Convert an authentication exception into a response.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Auth\AuthenticationException $exception
* @return \Illuminate\Http\Response
*/
protected function unauthenticated(
$request,
AuthenticationException $exception
)
{
if (in_array('web_admin', $exception->guards())) {
return $request->expectsJson()
? response()->json([
'message' => $exception->getMessage()
], 401)
: redirect()->guest(route('admin.login'));
}
return $request->expectsJson()
? response()->json([
'message' => $exception->getMessage()
], 401)
: redirect()->guest(route('login'));
}
This will now make sure it redirects to the correct part of your app when logged in.
Add Admin Views
Next we need to add all the relevant views that admin will use. Due to size I’ve omitted the code for each view and provided the link to the file on Github.
Admin Layout
We need to create the following directory resources/views/admin/layouts and in it we’ll add a new file app.blade.php (This is essentially a clone of the layout in resources/views/layouts but with amendments for admin routes).
You can grab the code from the Git repo here: https://github.com/tomgrohl/laravel-multi-user-auth-example/blob/master/resources/views/admin/layouts/app.blade.php
We need to create the following directory resources/views/admin/auth and in it we’ll add a new file login.blade.php (Again this is essentially a clone of the login view in resources/views/auth but with amendments for admin routes).
You can grab the code from the Git repo here: https://github.com/tomgrohl/laravel-multi-user-auth-example/blob/master/resources/views/admin/auth/login.blade.php
Admin Register Page
We’ll add a new file register.blade.php (Again this is essentially a clone of the register view in resources/views/auth but with amendments for admin routes).
You can grab the code from the Git repo here: https://github.com/tomgrohl/laravel-multi-user-auth-example/blob/master/resources/views/admin/auth/register.blade.php
Testing the Application
Now we’re ready to test our application.
If we hit http://multi-auth.test/home in a browser you see it redirect us to the login page for the web guard.
And if we hit http://multi-auth.test/admin in a browser you see it redirect us to the login page for the web_admin guard.
Register Users
If you haven’t already register users for both guards via:
http://multi-auth.test/register
And
http://multi-auth.test/admin/register
Normally you would not have a register section for admin but has been used for simplicity.
This should log you in to the relevant sections.
Considerations
I did notice that logging out of one guard logs you out of all of them. This is because of the following line in the logout action in the login controller:
$request->session()->invalidate();
Not sure it its recommended but if you know what your doing you could remove it and deal with this another way.
Summary
And thats it, we’ve created a simple app with guard for multiple users.
Thanks for reading. Again the code for the example auth app can be found on Github here.
We need to create the following directory resources/views/admin/auth and in it we’ll add a new file login.blade.php (Again this is essentially a clone of the login view in resources/views/auth but with amendments for admin routes: