Laravel Multi Auth using different tables [part 2: Admin authentication]

Bool False
12 min readJul 1, 2019

--

Links:

The codes of this article here on GitHub;
Appropriate YouTube video;
YouTube video-lessons Playlist;
You can download the public resources from here;
Part 1: User authentication;
Part 3: Admin Roles/Permissions;

Introduction:

In the 1-st part of this article I’ve described how to make Laravel (5.6, 5.7, 5.8) default authentication system (login, register, etc), using “users” table (migrated from default migration).

Our purpose: to have Admin Login system from URI point “/admin”.
Later, in the 3-rd part we’ll make that path manageable.

In this 2-nd part we’ll continue our work on Admin part. It means we’ll create controllers (in separate directory), routes (separated from user routes by Route Service Provider), blade views (with AdminLTE template dashboard). Also we’ll work on such important files, which are middleware, kernel, handler.

So let’s get started!

Creating a Model:

Copy “app/Models/User.php” and past that in the same directory named as “Admin.php”. Also, soon we’ll use “admin” guard for Admin auth system, so we’ll add protected property. So, after that all, we got this:

<?php

namespace App\Models;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class Admin extends Authenticatable
{
use Notifiable;

protected $table = 'admins';
protected $guard = 'admin';

protected $fillable = [
'name',
'email',
'password',
];

protected $hidden = [
'password',
'remember_token',
];

protected $casts = [
'email_verified_at' => 'datetime',
];
}

Creating Controllers:

Copy “app/Http/Controllers/User/” directory and past that in the same directory named as “Admin”. So we’ve a new directory here “app/Http/Controllers/Admin” for the Admin auth.
We must change all namespaces and make them correct. Rename “app/Http/Controllers/Admin/UserController.php” to “AdminController” (don’t forget to rename the class name too).
Also replace all “return view(‘user” matches with “return view(‘admin” inside of “app/Http/Controllers/Admin/” directory. It means that we need to have other blades for admin views (we’ll create them soon).

In our article, as we need to have only Admin sign-in system (without any registration, password-resetting), so in this part we’ll concentrate on just Admin Login Controller. So you can just leave other controller files in “app/Http/Controllers/Admin/” directory, and take care only for “LoginController.php” at this time.

Optional (I’ll delete these):
If you want, you just can delete these following files from “Admin\Auth”:
ForgotPasswordController.php
RegisterController.php
ResetPasswordController.php
VerificationController.php

As we want to have Admin login system powered from “/admin” URI-point, then we need to init “$redirectTo” protected property.
We need to change middleware for admins as “guest:admin”.
Also we need to add “guard()” protected method.
So after that we’ll get this:
app\Http\Controllers\Admin\Auth\LoginController.php

<?php

namespace App\Http\Controllers\Admin\Auth;

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

class LoginController extends Controller
{
use AuthenticatesUsers;

protected $redirectTo = '/admin/dashboard';

public function __construct()
{
$this->middleware('guest:admin')->except('logout');
}

public function showLoginForm()
{
return view('admin.auth.login');
}

protected function guard(){
return Auth::guard('admin');
}
}

In “app/Http/Controllers/Admin/AdminController.php” we’ll have:

<?php

namespace App\Http\Controllers\Admin;

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

class AdminController extends Controller
{
public function __construct()
{
$this->middleware('auth:admin');

View::share('nav', 'dashboard'); //ss
}

public function index()
{
return view('admin.pages.dashboard'); //ss
}
}

If you can’t understand, what’s the sense of lines with commented staff, just keep in mind that we’ll use AdminLTE template for Admin blade views, so later we’ll get the reason, why it written like that.

Handler of Exceptions:

As I said, we’ll a use “admin” guard, it means we should work on “app\Exceptions\Handler.php”. Before editing that file, I just want to let you know some concepts of “Handler.php”.
As you can see, we have a class Handler extended from ExceptionHandler, which is the same as “Illuminate\Foundation\Exceptions\Handler” inside of “vendor\laravel\framework\src\”. Here we can find the method

protected function unauthenticated($request, AuthenticationException $exception)
{
return $request->expectsJson()
? response()->json(['message' => $exception->getMessage()], 401)
: redirect()->guest($exception->redirectTo() ?? route('login'));
}

As you can see, on each unauthenticated web request, laravel will run this method and redirect to login page. In our case we’ll override this method. We’ll decide, which page we want to redirect before returning to the login page.
So our Handler will be like:

<?php

namespace App\Exceptions;

use App\Models\Admin;
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Support\Facades\Auth;
use Illuminate\Auth\Access\AuthorizationException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Session\TokenMismatchException;
use Illuminate\Validation\ValidationException;

class Handler extends ExceptionHandler
{
protected $dontReport = [
AuthenticationException::class,
AuthorizationException::class,
HttpException::class,
ModelNotFoundException::class,
TokenMismatchException::class,
ValidationException::class,
];

protected $dontFlash = [
'password',
'password_confirmation',
];

public function report(Exception $exception)
{
parent::report($exception);
}

public function render($request, Exception $exception)
{
return parent::render($request, $exception);
}

protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['error' => 'Unauthenticated.'],401);
}
$guard = array_get($exception->guards(), 0);

if($guard == 'admin') {
return redirect()->guest(route('admin.login_page'));
}

return redirect()->guest(route('login_page'));
}
}

Redirect after Authentication:

Let’s understand our purpose. We want to have 2 types of clients (users and admins), which will have their separate login pages, home pages, routes. So we need to have a main page after their login process. In “handle()” public function of “App\Http\Middleware\RedirectIfAuthenticated.php” file we’ll decide where exactly to redirect the clients after logged in users.

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;

class RedirectIfAuthenticated
{
public function handle($request, Closure $next, $guard = null)
{
switch ($guard) {
case 'admin' :
if (Auth::guard($guard)->check()) {
return redirect()->route('admin.dashboard');
}
break;
default:
if (Auth::guard($guard)->check()) {
return redirect()->route('home');
}
break;
}
return $next($request);
}
}

As we can see, it depends from current guard.

New Configurations for Guards, Providers (and Passwords):

This step is a super important thing we must to do. We’ll add the new features for the new “admin” guard, provider (and also optional functionality configuration for password resets). We can make that configuration in the “config/auth.php”:

<?php

return [
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'admins' => [
'driver' => 'eloquent',
'model' => App\Models\Admin::class,
],
],
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
],
'admins' => [
'provider' => 'admins',
'table' => 'password_resets',
'expire' => 15,
],
],
];

Note:
Just don’t forget to clear your app caches after config changes, else it will not work as you want. You can do that with simple way (using my package installed in the previous 1-st part):
php artisan clearcaches
Or with default laravel ways:
php artisan optimize
php artisan config:cache

Creating a Migration:

In this step we’ll create a migration for “admins” table. For this we’ll copy existing migration of users, and rename that to “database/migrations/2014_10_12_000000_create_admins_table.php”:

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateAdminsTable extends Migration
{
public function up()
{
Schema::create('admins', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}

public function down()
{
Schema::dropIfExists('admins');
}
}

Now we can create “admins” table too. For that we need to run migrations via these two commands (emptying DB, and running migrations):
php artisan droptables
php artisan migrate

Or just can recreate our DB using migrations refreshing command:
php artisan migrate:refresh

Separating the Routes:

As I said we had to separate the routes. It means, that we should have “routes/admin.php” in the same directory like “web.php”. In there we need to have all the admin routes. We’ll make that happen from “app\Providers\RouteServiceProvider”.

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;

class RouteServiceProvider extends ServiceProvider
{
protected $namespace = 'App\Http\Controllers';

public function boot()
{
parent::boot();
}

public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
$this->mapAdminRoutes();
}

protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace . '\User')
->group(base_path('routes/web.php'));
}

protected function mapAdminRoutes()
{
Route::middleware('web')
->as('admin.')
->prefix('admin')
->namespace($this->namespace . '\Admin')
->group(base_path('routes/admin.php'));
}

protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
}

As you can see, we’ve created additional protected function “mapAdminRoutes()” for admin routes (with “admin.” prefix alias, “/admin” prefix URI-segment). It means, that we should have “routes/admin.php”.

<?php

Route::group([
'namespace' => 'Auth',
], function () {
// Authentication Routes...
Route::get('login', 'LoginController@showLoginForm')->name('login_page');
Route::post('login', 'LoginController@login')->name('login');
Route::post('logout', 'LoginController@logout')->name('logout');
});

Route::group([
'middleware' => [
'auth:admin',
],
], function () {
Route::get('/', 'AdminController@index')->name('dashboard');
Route::get('home', 'AdminController@index')->name('dashboard');
Route::get('dashboard', 'AdminController@index')->name('dashboard');
});

Also in Route Service Provider we’ve changed “mapWebRoutes()” function content. It shows that we’ve already initialized path prefix for User Controllers. So we should remove the “User\\” path prefix from “routes\web.php”.

Route::group([
'namespace' => 'Auth',
], function () {

// ... existing routes

});
Route::get('home', 'UserController@index')->name('home');

Creating Blade Views:

Admin blade views structure will be similar to “resources/views/user/” structure, but it will have additional files. It will looks something like this:

resources ->
views ->
admin ->
auth ->
login.blade.php
layouts
->
app.blade.php
pages
->
users ->
index.blade.php
dashboard
.blade.php
partials
->
aside.blade.php
flashes
.blade.php
footer
.blade.php
head
.blade.php
header
.blade.php
scripts
.blade.php

Now I’ll put here below all contents of blade views:

resources/views/admin/auth/login.blade.php

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{ config('app.name') }} | Log in</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

<!-- Bootstrap 4.0.0 -->
<link rel="stylesheet" href="{{ asset('css/admin/bootstrap-4.0.0.min.css') }}">

<!-- Font Awesome Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">

<!-- Theme style -->
<link href="{{ asset('css/admin/admin.min.css') }}" rel="stylesheet" type="text/css"/>
<link href="{{ asset('css/admin/style.css') }}" rel="stylesheet" type="text/css"/>
</head>
<body class="login-page" style="background-image: url({{ asset('images/admin.jpg') }})">
<div class="login-box">
<div class="login-logo">
<p><b>Multi</b> Auth</p>
</div>
<div class="login-box-body">
<p class="login-box-msg">Sign in to start your session</p>

<form action="{{ route('admin.login') }}" method="post">
{{ csrf_field() }}

<div class="form-group has-feedback">
<input type="text" name="email" class="form-control" placeholder="Email"/>
<i class="fa fa-envelope form-control-feedback"></i>
@if ($errors->has('email'))
<span class="invalid-feedback">
<strong>{{ $errors->first('email') }}</strong>
</span>
@endif
</div>

<div class="form-group has-feedback">
<input type="password" name="password" class="form-control" placeholder="Password"/>
<i class="fa fa-lock fa-fw"></i>
@if ($errors->has('password'))
<span class="invalid-feedback">
<strong>{{ $errors->first('password') }}</strong>
</span>
@endif
</div>

<div class="row">
<div class="col-8">
<div class="checkbox icheck">
<label>
<input type="checkbox"> Remember Me
</label>
</div>
</div>
<div class="col-4">
<button type="submit" class="btn btn-primary btn-block btn-flat">Sign In</button>
</div>
</div>
</form>

</div>
</div>

<!-- Optional JavaScript -->
<script src="{{ asset('js/admin/jquery-3.3.1.slim.min.js') }}"></script>
<script src="{{ asset('js/admin/bootstrap-4.1.1.min.js') }}"></script>

<!-- iCheck -->
<script src="{{ asset('js/admin/admin.min.js') }}" type="text/javascript"></script>
<script>
$(function () {
$('input').iCheck({
checkboxClass: 'icheckbox_square-blue',
radioClass: 'iradio_square-blue',
increaseArea: '20%' // optional
});
});
</script>

</body>
</html>

resources/views/admin/layouts/app.blade.php

<!DOCTYPE html>
<html>
<head>
@include('admin.partials.head')
@yield('custom_styles')
</head>
<body class="hold-transition skin-blue sidebar-mini">

<div class="wrapper">
@include('admin.partials.header')
@include('admin.partials.aside')

<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">

@include('admin.partials.flashes')

<!-- Content Header (Page header) -->
<section class="content-header">
<h1>
Dashboard <small>{{ $nav }}</small>
@if($nav != 'dashboard' && $action != 'create' && $action != 'no_add')
<a class="btn btn-success" href="{{ route('admin.' . $nav . '.create') }}">
<i class="fa fa-plus"></i> Add
</a>
@endif
</h1>
</section>

<!-- Main content -->
<section class="content">

@yield('content')

</section>
<!-- /.content -->
</div>
<!-- /.content-wrapper -->

@include('admin.partials.footer')
</div>

@include('admin.partials.scripts')

@yield('custom_scripts')

</body>
</html>

resources/views/admin/pages/dashboard.blade.php

@extends('admin.layouts.app')

@section('content')

<p>Welcome to main page of {{ config('app.name') }} admin panel!</p>

@endsection

resources/views/admin/partials/aside.blade.php

<aside class="main-sidebar">
<section class="sidebar">
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu" data-widget="tree">
<li class="header">Admin Dashboard</li>

<li class="{{ $nav == 'dashboard' ? 'active' : '' }}"><a href="{{ route('admin.dashboard') }}"><i class="fa fa-dashboard"></i> <span>Dashboard</span></a></li>
</ul>
</section>
</aside>

Optional (I’ll do):
resources/views/admin/partials/flashes.blade.php

@if(session('flash_message'))
<div id="flash_message" style="padding: 20px 30px; background: {{ session('success') ? 'green' : 'red' }}; z-index: 999999; font-size: 16px; font-weight: 600;">
<a class="pull-right" onclick="el=document.getElementById('flash_message');el.parentNode.removeChild(el);" href="#" data-toggle="tooltip" data-placement="left" title="Never show me this again!" style="color: rgb(255, 255, 255); font-size: 20px;">×</a>
<span style="color: rgba(255, 255, 255, 0.9); display: inline-block; margin-right: 10px; text-decoration: none;">
{{ session()->get('flash_message') }}
</span>
</div>
@endif
@if($errors->any())
<div id="flash_message" style="padding: 20px 30px; background: red; z-index: 999999; font-size: 16px; font-weight: 600;">
<a class="pull-right" onclick="el=document.getElementById('flash_message');el.parentNode.removeChild(el);" href="#" data-toggle="tooltip" data-placement="left" title="Never show me this again!" style="color: rgb(255, 255, 255); font-size: 20px;">×</a>
<span style="color: rgba(255, 255, 255, 0.9); display: inline-block; margin-right: 10px; text-decoration: none;">
{!! implode('', $errors->all('<div>:message</div>')) !!}
</span>
</div>
@endif

resources/views/admin/partials/footer.blade.php

<footer class="main-footer">
<div class="pull-right hidden-xs">
<b>{{ config('app.name') }}</b>
</div>
<strong>Copyright &copy; {{ '2019' . (date('Y') == 2019 ? '' : '-' . date('Y')) }}</strong> All rights reserved.
</footer>

resources/views/admin/partials/head.blade.php

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>{{ config('app.name') }} | {{ $nav }}</title>
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- Bootstrap 3.3.7 -->
<link rel="stylesheet" href="{{ asset('panel-resources/bower_components/bootstrap/dist/css/bootstrap.min.css') }}">
<!-- Font Awesome -->
<link rel="stylesheet" href="{{ asset('panel-resources/bower_components/font-awesome/css/font-awesome.min.css') }}">
<!-- Ionicons -->
<link rel="stylesheet" href="{{ asset('panel-resources/bower_components/Ionicons/css/ionicons.min.css') }}">
<!-- Theme style -->
<link rel="stylesheet" href="{{ asset('panel-resources/dist/css/BoolFalse.min.css') }}">
<!-- BoolFalse Skins. Choose a skin from the css/skins
folder instead of downloading all of them to reduce the load. -->
<link rel="stylesheet" href="{{ asset('panel-resources/dist/css/skins/_all-skins.min.css') }}">
<!-- Morris chart -->
{{--<link rel="stylesheet" href="{{ asset('panel-resources/bower_components/morris.js/morris.css') }}">--}}
<!-- jvectormap -->
{{--<link rel="stylesheet" href="{{ asset('panel-resources/bower_components/jvectormap/jquery-jvectormap.css') }}">--}}
<!-- Date Picker -->
{{--<link rel="stylesheet" href="{{ asset('panel-resources/bower_components/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css') }}">--}}
<!-- Daterange picker -->
{{--<link rel="stylesheet" href="{{ asset('panel-resources/bower_components/bootstrap-daterangepicker/daterangepicker.css') }}">--}}
<!-- bootstrap wysihtml5 - text editor -->
<link rel="stylesheet" href="{{ asset('panel-resources/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css') }}">
<!-- Select2 -->
<link rel="stylesheet" href="{{ asset('panel-resources/bower_components/select2/dist/css/select2.min.css') }}">

<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->

<!-- Google Font -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic">

resources/views/admin/partials/header.blade.php

<header class="main-header">
<!-- Logo -->
<a target="_blank" href="{{ route('front') }}" class="logo">
<!-- mini logo for sidebar mini 50x50 pixels -->
{{-- //ss TODO: App Names like this, we can do using these methods https://stackoverflow.com/questions/6572974/regex-to-break-up-camelcase-string-php/6572999#6572999 --}}
<span class="logo-mini"><b>M</b>A</span>
<!-- logo for regular state and mobile devices -->
<span class="logo-lg"><b>Multi</b> Auth</span>
</a>
<!-- Header Navbar: style can be found in header.less -->
<nav class="navbar navbar-static-top">
<!-- Sidebar toggle button-->
<a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button">
<span class="sr-only">Toggle navigation</span>
</a>

<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<li class="dropdown user user-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-user-circle"></i>
<span class="hidden-xs">{{ Auth::user()->name }}</span>
</a>
<ul class="dropdown-menu">
<!-- Menu Footer-->
<li class="user-footer">
<div class="pull-right">
<form id="logout-form" action="{{ route('admin.logout') }}" method="POST" style="display: none;">
{{ csrf_field() }}
</form>
<a class="btn btn-default btn-flat" onclick="event.preventDefault();document.getElementById('logout-form').submit();">
Logout
</a>
</div>
</li>
</ul>
</li>
</ul>
</div>
</nav>
</header>

resources/views/admin/partials/scripts.blade.php

<!-- jQuery 3 -->
<script src="{{ asset('panel-resources/bower_components/jquery/dist/jquery.min.js') }}"></script>
<!-- jQuery UI 1.11.4 -->
<script src="{{ asset('panel-resources/bower_components/jquery-ui/jquery-ui.min.js') }}"></script>
<!-- Resolve conflict in jQuery UI tooltip with Bootstrap tooltip -->
<script>
$.widget.bridge('uibutton', $.ui.button);
</script>
<!-- Bootstrap 3.3.7 -->
<script src="{{ asset('panel-resources/bower_components/bootstrap/dist/js/bootstrap.min.js') }}"></script>
<!-- Morris.js charts -->
{{--<script src="{{ asset('panel-resources/bower_components/raphael/raphael.min.js') }}"></script>--}}
{{--<script src="{{ asset('panel-resources/bower_components/morris.js/morris.min.js') }}"></script>--}}
<!-- Sparkline -->
{{--<script src="{{ asset('panel-resources/bower_components/jquery-sparkline/dist/jquery.sparkline.min.js') }}"></script>--}}
<!-- jvectormap -->
{{--<script src="{{ asset('panel-resources/plugins/jvectormap/jquery-jvectormap-1.2.2.min.js') }}"></script>--}}
{{--<script src="{{ asset('panel-resources/plugins/jvectormap/jquery-jvectormap-world-mill-en.js') }}"></script>--}}
<!-- jQuery Knob Chart -->
{{--<script src="{{ asset('panel-resources/bower_components/jquery-knob/dist/jquery.knob.min.js') }}"></script>--}}
<!-- daterangepicker -->
<script src="{{ asset('panel-resources/bower_components/moment/min/moment.min.js') }}"></script>
{{--<script src="{{ asset('panel-resources/bower_components/bootstrap-daterangepicker/daterangepicker.js') }}"></script>--}}
<!-- datepicker -->
{{--<script src="{{ asset('panel-resources/bower_components/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js') }}"></script>--}}
<!-- datetimepicker -->
{{--<script type="text/javascript" src="{{ asset('panel-resources/bower_components/bootstrap-datetimepicker/bootstrap-datetimepicker.min.js') }}"></script>--}}
<!-- Bootstrap WYSIHTML5 -->
<script src="{{ asset('panel-resources/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js') }}"></script>
<!-- Slimscroll -->
{{--<script src="{{ asset('panel-resources/bower_components/jquery-slimscroll/jquery.slimscroll.min.js') }}"></script>--}}
<!-- FastClick -->
{{--<script src="{{ asset('panel-resources/bower_components/fastclick/lib/fastclick.js') }}"></script>--}}
<!-- Select2 -->
<script src="{{ asset('panel-resources/bower_components/select2/dist/js/select2.full.min.js') }}"></script>
<!-- BoolFalse App -->
<script src="{{ asset('panel-resources/dist/js/boolfalse.min.js') }}"></script>
<!-- BoolFalse dashboard demo (This is only for demo purposes) -->
{{--<script src="{{ asset('panel-resources/dist/js/pages/dashboard.js') }}"></script>--}}
<!-- BoolFalse for demo purposes -->
{{--<script src="{{ asset('panel-resources/dist/js/demo.js') }}"></script>--}}

Public Resources for Admin (Panel) Blade Views:

In this section we’ll download and import some ready stuff for our app. I’ve used this resource in many projects. You may heard before about AdminLTE 2 open-source staff. So I’ve grabbed that for me.
For this article you can just download the .zip file from here, and extract that in your “public/” directory.
Note:
May you already have “public/css/” and “public/js/” folders. You need to override this due to putting the extracted files, or just remove that before importing.

Current Results:

Create an Admin in “admins” table for checking the admin auth sytem.
You can create a new admin record in “admins” table with different ways. One of that is running these commands (using “tinker”):

C:\OSPanel\domains\laravel-multiauth>php artisan tinker
Psy Shell v0.9.9 (PHP 7.3.2 — cli) by Justin Hileman
>>> App\Models\Admin::create(['name' => 'Admin', 'email' => 'admin@gmail.com', 'password' => bcrypt('secret')]);
=> App\Models\Admin {#2980
name: "Admin",
email: "admin@gmail.com",
updated_at: "2019-07-01 21:02:07",
created_at: "2019-07-01 21:02:07",
id: 1,
}
>>>

After these all steps described on top, we should get something like this for admin panel (admin home page after successfully logging):

Now we have two types of clients (users & admins), which can authenticate using different tables (behind the scenes).
We’ve worked on following staff:

Exceptions Handler
Controllers (Admin & User)
Middleware (RedirectIfAuthenticated)
Model (Admin, User)
Route Provider (with separated Admin & User routes)
Admin Migration
Public Staff (css, js, images, fonts, AdminLTE template, etc)
Blade Views (for Admin Panel)

Note:
FYI we just can’t be a logged in client as user and as admin at same time.

About this 2-nd part:

So , we did this! Congrats!

Now we have some simple and boilerplate app for further usage. You just can have your ready staff for yourself in somewhere (GitHub or something like that) and clone, fork, or download that in every time when you want to build a new app, which will have this multiauth functionality on it.

But you may will ask: “Is this the end?”. Actually I’ll say “No!”.
It means that I’ll continue to improve this boilerplate. I planning to use clients’ permissions in this app. May you’ve already used spatie’s package for permissions. So I’ll install, configure and use that in this app, also we’ll add some additional staff here too in the next 3-rd part.

Thanks!

--

--