9 Tips to Set Up Multiple Authentication in Laravel
View complete project here
What was I doing?
I was building a system that required users, doctors, and admins to register and have different authentications. I needed to set up different logins and tables for them and I wanted to make use of the Laravel App\User
. I got access to the default Eloquent authentication driver and started digging. I came across a few tips that got me on the road to success by setting up custom guards.
Here's what I did:
How I Approached Setting Up Multiple Authentications
-
First off, you should have Laravel auth set up. If you don't, you can run:
php artisan make:auth
php artisan migrate
This will scaffold the entire authentication system.
-
Create the admin and doctors model and migration.
php artisan make:model Models/Admins -m
This creates a migration file from the admins model.(/app/Models/)
To make it easier, you can just duplicate the 'create_users_table' migration file but remember to make the necessary modifications:
The Admin Model:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
class Admin extends Authenticatable
{
protected $guard = 'admin';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'firstname', 'midname', 'lastname', 'email', 'address', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token',
];
}
The Admins Migration file:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateAdminsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('admins', function (Blueprint $table) {
$table->increments('id');
$table->string('firstname');
$table->string('midname');
$table->string('lastname');
$table->string('email')->unique();
$table->string('address')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('admins');
}
}
- In the
config/auth.php
file, set up the custom guards and providers for doctors, users, and admins.
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
'admin-api' => [
'driver' => 'token',
'provider' => 'admins',
],
'doctor' => [
'driver' => 'session',
'provider' => 'doctors',
],
'doctor-api' => [
'driver' => 'token',
'provider' => 'doctors',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'admins' => [
'driver' => 'eloquent',
'model' => App\Models\Admin::class,
],
'doctors' => [
'driver' => 'eloquent',
'model' => App\Models\Doctor::class,
],
],
- Now we have our guards set up, we'll set up our admin login view.
<form id="sign_in_adm" method="POST" action="{{ route('admin.login.submit') }}">
{{ csrf_field() }}
<div class="input-group"> <span class="input-group-addon"> <i class="zmdi zmdi-account"></i> </span>
<div class="form-line">
<input type="email" class="form-control" name="email" placeholder="Email Address" value="{{ old('email') }}" required autofocus>
</div>
@if ($errors->has('email'))
<span class="text-danger"><strong>{{ $errors->first('email') }}</strong></span>
@endif
</div>
<div class="input-group"> <span class="input-group-addon"> <i class="zmdi zmdi-lock"></i> </span>
<div class="form-line">
<input type="password" class="form-control" name="password" placeholder="Password" required>
</div>
</div>
<div>
<div class="">
<input type="checkbox" name="remember" {{ old('remember') ? 'checked' : '' }} id="rememberme" class="filled-in chk-col-pink">
<label for="rememberme">Remember Me</label>
</div>
<div class="text-center">
<button type="submit" class="btn btn-raised waves-effect g-bg-cyan">SIGN IN</button>
</div>
</div>
</form>
</div>
The admin login is almost the same as the user login, but we'll change the action of the form to a route, which we will set up later in our routes file.
- We have our login views set up, so we can create the AdminController now
php artisan make:controller Auth/AdminController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class AdminController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth:admin');
}
/**
* show dashboard.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return view('admin');
}
}
- And the AdminLoginController,
php artisan make:controller Auth/AdminLoginController
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Auth;
use Route;
class AdminLoginController extends Controller
{
public function __construct()
{
$this->middleware('guest:admin', ['except' => ['logout']]);
}
public function showLoginForm()
{
return view('auth.admin_login');
}
public function login(Request $request)
{
// Validate the form data
$this->validate($request, [
'email' => 'required|email',
'password' => 'required|min:6'
]);
// Attempt to log the user in
if (Auth::guard('admin')->attempt(['email' => $request->email, 'password' => $request->password], $request->remember)) {
// if successful, then redirect to their intended location
return redirect()->intended(route('admin.dashboard'));
}
// if unsuccessful, then redirect back to the login with the form data
return redirect()->back()->withInput($request->only('email', 'remember'));
}
public function logout()
{
Auth::guard('admin')->logout();
return redirect('/admin');
}
}
- Now we will set our routes;
routes/web.php
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Route::get('/home', 'HomeController@index');
Route::prefix('admin')->group(function() {
Route::get('/login',
'Auth\AdminLoginController@showLoginForm')->name('admin.login');
Route::post('/login', 'Auth\AdminLoginController@login')->name('admin.login.submit');
Route::get('logout/', 'Auth\AdminLoginController@logout')->name('admin.logout');
Route::get('/', 'AdminController@index')->name('admin.dashboard');
})
- With the routes setup, we have to set up the different login views for the guards. We effect this in the
app/Exceptions/Handler.php
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['error' => 'Unauthenticated.'], 401);
}
$guard = array_get($exception->guards(), 0);
switch ($guard) {
case 'admin':
$login = 'admin.login';
break;
case 'doctor':
$login = 'doctor.login';
break;
default:
$login = 'login';
break;
}
return redirect()->guest(route($login));
}
- Finally, If the login is successful, redirect to the dashboard of the specific guard. This is done in the
RedirectIfAuthenticated.php
public function handle($request, Closure $next, $guard = null)
{
switch ($guard) {
case 'admin':
if (Auth::guard($guard)->check()) {
return redirect()->route('admin.dashboard');
}
break;
case 'doctor':
if (Auth::guard($guard)->check()) {
return redirect()->route('doctor.dashboard');
}
break;
case 'consultant':
if (Auth::guard($guard)->check()) {
return redirect()->route('consultant.dashboard');
}
break;
default:
if (Auth::guard($guard)->check()) {
return redirect('/');
}
break;
}
return $next($request);
}
Conclusion
It took me a few days to get this going, but hopefully, with this post, it will be a lot easier for the next person who runs into the same issue! Thanks to Alexander J. Curtis of devMarketer for his useful tips.
Awesome!!! Is it possible to use one login page to authenticate different users? Instead of just having separate login forms, can I use 1 form that will check each of these tables to see in which category (doctor, user or admin) is the user ?
Great post!
But what about policy for Admin model?
When I’ve created AdminPolicy, but I can’t pass Admin model instance in this policy(
Do you know how to do this?
this error is comin
“Declaration of App\Exceptions\Handler::unauthenticated($request, App\Exceptions\AuthenticationException $exception) should be compatible with Illuminate\Foundation\Exceptions\Handler::unauthenticated($request, Illuminate\Auth\AuthenticationException $exception) ◀”
Hello Devrah, you can look at the repository to help you get started.
https://github.com/1Rico/laravel-multi-auth