In my application, I have set up multi-auth using multiple guards, as well as a separate users
and admins
table.
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
],
Further to this, admin users can have an access level of admin, full or partial.
This is because full and partial admins can only view candidate applications, they cannot create new admins, or manage job listings
In order to achieve the separation I desire I have set up a custom Middleware called Role, which is located here:
app\Http\Middleware\Role.php
The code for which is as follows
<?php
namespace App\Http\Middleware;
use Closure;
use Auth;
class Role
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next, ...$roles)
{
if (Auth::guard('admin')->user()) {
$user = Auth::guard('admin')->user();
if ($user->is_admin) {
return $next($request);
} else {
if ($user->hasAnyRole($roles)) {
return $next($request);
} else {
return response('You do not have sufficient priveledges to perform this action.', 403);
}
}
}
}
}
To compliment this I have also set up custom Blade Directives
Blade::if('user', function () {
return Auth::guard('web')->check();
});
Blade::if('admin', function () {
return Auth::guard('admin')->check() && Auth::guard('admin')->user()->is_admin;
});
Blade::if('full', function () {
return Auth::guard('admin')->check() && Auth::guard('admin')->user()->is_full;
});
Blade::if('partial', function () {
return Auth::guard('admin')->check() && Auth::guard('admin')->user()->is_partial;
});
The attributes in each admin role check are defined in my Admin model as follows:
/**
* Check whether this admin has full admin status
*
* @return void
*/
public function getIsAdminAttribute()
{
return $this->access_level == 'admin' ? true : false;
}
/**
* Check whether this user has Partial status
*
* @return void
*/
public function getIsFullAttribute()
{
return $this->access_level == 'full' ? true : false;
}
/**
* Check whether this user has Partial status
*
* @return void
*/
public function getIsPartialAttribute()
{
return $this->access_level == 'partial' ? true : false;
}
/**
* Check whether this admin has a particular role
*
* @param [type] $role
* @return void
*/
public function hasRole($role)
{
if ($this->access_level == $role) {
return true;
}
return false;
}
/**
* Check whether this admin user has a role within a list of roles
*
* @param [type] $roles
* @return boolean
*/
public function hasAnyRole($roles)
{
if (is_array($roles)) {
foreach ($roles as $role) {
if ($this->hasRole($role)) {
return true;
}
}
} else {
if ($this->hasRole($roles)) {
return true;
}
}
return false;
}
Finally, and here's where I get stuck: I have one navigation bar for all users, but I only wanted to display guest routes to users who aren't logged in, but you can be logged in as an admin and a user at the same time.
Given this, I only wanted to display navigation items if you're a guest and not logged in as an admin.
Code below.
<nav class="navbar navbar-expand-md " id="generic">
<div class="container">
<a class="navbar-brand" href="/">
<img src="https://www.newable.co.uk/assets/img/common/newable-logo-blue.svg" width="auto" height="45" alt="">
</a>
<div class="navbar-toggler-right">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbarTogglerDemo02"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</div>
<div class="collapse navbar-collapse flex-column " id="navbar">
<ul class="navbar-nav w-100 justify-content-end ">
{{-- Admin navigation section --}}
@auth('admin')
<li class="nav-item">
<a class="nav-link" href="{{route('admins.dashboard')}}">Dashboard</a>
</li>
@admin
<li class="nav-item">
<a class="nav-link" href="{{route('vacancies.create')}}">Post vacancy</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{route('vacancies.index')}}">Manage vacancies</a>
</li>
@endadmin
<li class="nav-item">
<a class="nav-link" href="{{route('applications.index')}}">View applications</a>
</li>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
@csrf
</form>
<li class="nav-item">
<a class="nav-link" href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>
</li>
@endauth
{{-- User navigation section --}}
@user
<li class="nav-item">
<a class="nav-link" href="{{route('user-dashboard')}}">Dashboard</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{route('application-form')}}">Job Profile</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{route('user-job-applications')}}">Your Applications</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{route('user-account-settings')}}">Settings</a>
</li>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
@csrf
</form>
<li class="nav-item">
<a class="nav-link" href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>
</li>
@enduser
@guest
@if(!Auth::guard('admin')->check())
<li class="nav-item">
<a class="nav-link" href="{{route('login')}}">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{route('register')}}">Register</a>
</li>
@endif
@endguest
</ul>
</div>
</div>
</nav>
Is this a dirty way of doing things, is there a way I can improve this structure so I don't have to wrap navigation items in so many authentication checks?