Multi-Authentication in Laravel
Multi-Authentication in Laravel
For any application, we have different numbers of users that have roles and permissions which differentiate the users, we need to have multi-auth functionality in our application
Laravel provides the ability to authenticate users with different user roles, permissions, multi-authentication, social login, and more.
Prerequisites:
- PHP 7.1
- MySQL
- Laravel 5.5
Run following commands on the terminal,
laravel new multi-auth
And go to your project by running below commands
cd multi-auth
Create the database
And configure the .env file by adding the credentials, for example:
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=laravel_multi_auth DB_USERNAME=root DB_PASSWORD=root
Migration
We are going to create migrations for both roles ‘admins and writers’ tables. Default Laravel comes with a users migration.
Now we will create a migration for admins, open a terminal and execute below commands
php artisan make:migration create_admins_table
As soon as you run the above command, add below lines into the migration file
public function up() { Schema::create('admins', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->string('email')->unique(); $table->string('password'); $table->boolean('is_super')->default(false); $table->rememberToken(); $table->timestamps(); }); }
By adding the above lines it will create a simple migration file, with defined columns that we to add in the admin table.
Create migration for writers
php artisan make:migration create_writers_table
Similarly, we add below line in the above migration file
public function up() { Schema::create('writers', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->string('email')->unique(); $table->string('password'); $table->boolean('is_editor')->default(false); $table->rememberToken(); $table->timestamps(); }); }
Now we will migrate the database by running the command:
php artisan migrate
Now we will set up the models for admin, run the below command:
php artisan make:model Admin
Now, open the Admin model which is inside the “app” and add the following code:
// app/Admin.php <?php namespace App; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; class Admin extends Authenticatable { use Notifiable; protected $guard = 'admin'; protected $fillable = ['name', 'email', 'password']; protected $hidden = [ 'password', 'remember_token', ]; }
When you are planning to use model for the authentication and not willing to use default user guard (will explain what are guard later in the document), we will use the admin guard and it is very important to specify the guard name
Will create writers model as well
To create model for the writers, run the below command:
php artisan make:model Writer
Now we will add the below code in “Writer” model
// app/Writer.php <?php namespace App; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; class Writer extends Authenticatable { use Notifiable; protected $guard = 'writer'; protected $fillable = ['name', 'email', 'password']; protected $hidden = [ 'password', 'remember_token', ]; }
It is very important to specify the guard which you want to use, in our case, we will sue the admin guard.
Writers model
run the command to make the model for the writer’s role
php artisan make:model Writer
open the Writer model and add below code
<?php namespace App; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; class Writer extends Authenticatable { use Notifiable; protected $guard = 'writer'; protected $fillable = [ 'name', 'email', 'password', ]; protected $hidden = [ 'password', 'remember_token', ]; }
Now, the most important thing “Guards”
Guards are basically to authenticate the user and we can also create our own as per the role and guards allow to use Laravel’s default authentication system with our Admin and Writer
Now, we will add our new guards, open the auth.php inside the config file directory
<?php 'guards' => [ 'admin' => [ 'driver' => 'session', 'provider' => 'admins', ], 'writer' => [ 'driver' => 'session', 'provider' => 'writers', ], ],
We have added 2 new guards “admin” and “writer” and we will set the providers
When we tried to use the guard, providers are basically to provide the information about what to use for authentication or validation. Now, we will add the following to the provider’s array,
'providers' => [ 'admins' => [ 'driver' => 'eloquent', 'model' => App\Admin::class, ], 'writers' => [ 'driver' => 'eloquent', 'model' => App\Writer::class, ], ],
Now, we have set up the providers along with the guards
Controller Setup:
For authentication, we are using guards, we can create the new one or we can change the existing one, so let’s modify the existing controller
Modifying the LoginController:
Inside “app/Http/Controllers/Auth” open login controller <?php namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Http\Request; use Auth; class LoginController extends Controller { public function __construct() { $this->middleware('guest')->except('logout'); $this->middleware('guest:admin')->except('logout'); $this->middleware('guest:writer')->except('logout'); } }
Just update the constructor in the LoginController
Set the middleware accordingly to restrict access for this controller or its methods.
Define all the types of guests in the controller as it is very important because, if 1 user is logged in and we tried with another user type, then it will redirect you to its authentication page.
Open Login controller inside “app/Http/Controllers/Auth” directory and add below code public function showAdminLoginForm() { return view('auth.login', ['url' => 'admin']); } public function adminLogin(Request $request) { $this->validate($request, [ 'email' => 'required|email', 'password' => 'required|min:6' ]); if (Auth::guard('admin')->attempt(['email' => $request->email, 'password' => $request->password], $request->get('remember'))) { return redirect()->intended('/admin'); } return back()->withInput($request->only('email', 'remember')); }
Above checks are very important, to avoid confusion,
After login we will redirect user to a specific URL and if the user is unauthenticated then it will back to the login page. Now, we will add the same thing but for the writers,
Open “app/Http/Controllers/Auth/LoginController.php” public function showWriterLoginForm() { return view('auth.login', ['url' => 'writer']); } public function writerLogin(Request $request) { $this->validate($request, [ 'email' => 'required|email', 'password' => 'required|min:6' ]); if (Auth::guard('writer')->attempt(['email' => $request->email, 'password' => $request->password], $request->get('remember'))) { return redirect()->intended('/writer'); } return back()->withInput($request->only('email', 'remember')); } Now, we have set the login :)
We will now Modify RegisterController:
Open “RegisterController.php” inside “app/Http/Controllers/Auth” directory <?php namespace App\Http\Controllers\Auth; use App\User; use App\Admin; use App\Writer; use App\Http\Controllers\Controller; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Http\Request; class RegisterController extends Controller { public function __construct() { $this->middleware('guest'); $this->middleware('guest:admin'); $this->middleware('guest:writer'); } }
We have set up the middleware similar we did with Login controller.
Open “RegisterController.php” inside “app/Http/Controllers/Auth/”, add below code public function showAdminRegisterForm() { return view('auth.register', ['url' => 'admin']); } public function showWriterRegisterForm() { return view('auth.register', ['url' => 'writer']); }
What we did for showing different login pages, this is also similar to that and now, we will define our own methods for creating an admin
Open app/Http/Controllers/Auth/RegisterController.php and add below code, protected function createAdmin(Request $request) { $this->validator($request->all())->validate(); $admin = Admin::create([ 'name' => $request['name'], 'email' => $request['email'], 'password' => Hash::make($request['password']), ]); return redirect()->intended('login/admin'); }
Next thing is, define methods for creating a writer, inside the “RegisterController”
// app/Http/Controllers/Auth/RegisterController.php protected function createWriter(Request $request) { $this->validator($request->all())->validate(); $writer = Writer::create([ 'name' => $request['name'], 'email' => $request['email'], 'password' => Hash::make($request['password']), ]); return redirect()->intended('login/writer'); } Hurray! registration is now completed!
Now, we will set up the authentication pages, run below command
php artisan make:auth
It will generate view files in resources/views/auth along with routes to handle basic authentication
Now, open the login.blade.php file and edit as follows:
<div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header"> {{ isset($url) ? ucwords($url) : ""}} {{ __('Login') }}</div> <div class="card-body"> @isset($url) <form method="POST" action='{{ url("login/$url") }}' aria-label="{{ __('Login') }}"> @else <form method="POST" action="{{ route('login') }}" aria-label="{{ __('Login') }}"> @endisset @csrf </div>
Open the register.blade.php file and edit as follows:
<div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header"> {{ isset($url) ? ucwords($url) : ""}} {{ __('Register') }}</div> <div class="card-body"> @isset($url) <form method="POST" action='{{ url("register/$url") }}' aria-label="{{ __('Register') }}"> @else <form method="POST" action="{{ route('register') }}" aria-label="{{ __('Register') }}"> @endisset @csrf </div>
Now, we have replicated what we did for login page here,
Create the pages that authenticated users will access:
touch resources/views/layouts/auth.blade.php touch resources/views/admin.blade.php touch resources/views/writer.blade.php touch resources/views/home.blade.php
Insert below code block into the auth.blade.php file:
// resources/views/layouts/auth.blade.php <!DOCTYPE html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- CSRF Token --> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>{{ config('app.name', 'Laravel') }}</title> <!-- Scripts --> <script src="{{ asset('js/app.js') }}" defer></script> <!-- Fonts --> <link rel="dns-prefetch" href="https://fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css?family=Raleway:300,400,600" rel="stylesheet" type="text/css"> <!-- Styles --> <link href="{{ asset('css/app.css') }}" rel="stylesheet"> </head> <body> <div id="app"> <nav class="navbar navbar-expand-md navbar-light navbar-laravel"> <div class="container"> <a class="navbar-brand" href="{{ url('/') }}"> {{ config('app.name', 'Laravel') }} </a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <!-- Left Side Of Navbar --> <ul class="navbar-nav mr-auto"> </ul> <!-- Right Side Of Navbar --> <ul class="navbar-nav ml-auto"> <!-- Authentication Links --> <li class="nav-item dropdown"> <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre> Hi There <span class="caret"></span> </a> <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown"> <a class="dropdown-item" href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();"> {{ __('Logout') }} </a> <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;"> @csrf </form> </div> </li> </ul> </div> </div> </nav> <main class="py-4"> @yield('content') </main> </div> </body> </html>
Next, insert this code block into the admin.blade.php file:
// resources/views/admin.blade.php @extends('layouts.auth') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">Dashboard</div> <div class="card-body"> Hi boss! </div> </div> </div> </div> </div> @endsection
Open the writer.blade.php file and edit as follows:
// resources/views/writer.blade.php @extends('layouts.auth') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">Dashboard</div> <div class="card-body"> Hi there, awesome writer </div> </div> </div> </div> </div> @endsection
Finally, open the home.blade.php file and replace with the following:
// resources/views/home.blade.php @extends('layouts.auth') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">Dashboard</div> <div class="card-body"> Hi there, regular user </div> </div> </div> </div> </div> @endsection
Now, we will set up the routes
Let us define the routing, we are almost ready to run the application.
Open the routes/web.php file and replace with the following: <?php Route::view('/', 'welcome'); Auth::routes(); Route::get('/login/admin', 'Auth\LoginController@showAdminLoginForm'); Route::get('/login/writer', 'Auth\LoginController@showWriterLoginForm'); Route::get('/register/admin', 'Auth\RegisterController@showAdminRegisterForm'); Route::get('/register/writer', 'Auth\RegisterController@showWriterRegisterForm'); Route::post('/login/admin', 'Auth\LoginController@adminLogin'); Route::post('/login/writer', 'Auth\LoginController@writerLogin'); Route::post('/register/admin', 'Auth\RegisterController@createAdmin'); Route::post('/register/writer', 'Auth\RegisterController@createWriter'); Route::view('/home', 'home')->middleware('auth'); Route::view('/admin', 'admin'); Route::view('/writer', 'writer');
Now, we modify how users are redirected if they are authenticated
Laravel by default redirects all authenticated users to /home page. If we do not modify the redirection, we will get the below error,
Open the app/Http/Controllers/Middleware/RedirectIfAuthenticated.php <?php namespace App\Http\Middleware; use Closure; use Illuminate\Support\Facades\Auth; class RedirectIfAuthenticated { public function handle($request, Closure $next, $guard = null) { if ($guard == "admin" && Auth::guard($guard)->check()) { return redirect('/admin'); } if ($guard == "writer" && Auth::guard($guard)->check()) { return redirect('/writer'); } if (Auth::guard($guard)->check()) { return redirect('/home'); } return $next($request); } }
The above middleware receives the auth guard as a parameter. This middleware is triggered when we try to visit any page for authenticated users.
We can then determine the type of authentication the user has and redirect them accordingly.
Now, will modify authentication exception handler:
To user tries to visit /writer they are redirected to /login/writer page and same for /admin, we have to modify the exception handler.
Open the handler file in app/Exceptions and add the following:
// app/Exceptions/Handler.php <?php namespace App\Exceptions; use Exception; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Auth\AuthenticationException; use Auth; class Handler extends ExceptionHandler { protected function unauthenticated($request, AuthenticationException $exception) { if ($request->expectsJson()) { return response()->json(['error' => 'Unauthenticated.'], 401); } if ($request->is('admin') || $request->is('admin/*')) { return redirect()->guest('/login/admin'); } if ($request->is('writer') || $request->is('writer/*')) { return redirect()->guest('/login/writer'); } return redirect()->guest(route('login')); } }
Lasty, run the application
php artisan serve
It should generally be available on http://localhost:8000
Don’t forget to visit to register writers and admins respectively.
http://localhost:8000/register/writer
http://localhost:8000/register/admin
Then visit below url for login the writers and admins respectively.
http://localhost:8000/login/writer
http://localhost:8000/login/admin
Conclusion
In this, we defined multiple guards to handle multiple authentications and access control. Also, handled redirection for authenticated user and redirection for an unauthenticated user.