Overview
The Restaurant Management System uses Laravel Sanctum with Jetstream (Livewire stack) for authentication. The system implements role-based access control (RBAC) using a combination of Spatie Laravel Permission package and custom middleware.
Authentication is session-based for web routes using Laravel’s built-in session driver. Sanctum also supports API token authentication for external integrations.
Authentication Stack
Laravel Sanctum Configuration
Located in config/sanctum.php:
return [
'stateful' => explode ( ',' , env ( 'SANCTUM_STATEFUL_DOMAINS' ,
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1'
)),
'guard' => [ 'web' ],
'expiration' => null , // Tokens never expire by default
'middleware' => [
'authenticate_session' => Laravel\Sanctum\Http\Middleware\ AuthenticateSession :: class ,
'encrypt_cookies' => App\Http\Middleware\ EncryptCookies :: class ,
'verify_csrf_token' => App\Http\Middleware\ VerifyCsrfToken :: class ,
],
];
Jetstream Configuration
Located in config/jetstream.php:
return [
'stack' => 'livewire' , // Using Livewire stack
'middleware' => [ 'web' ],
'auth_session' => AuthenticateSession :: class ,
'guard' => 'sanctum' ,
'features' => [
Features :: accountDeletion (),
// API features disabled by default
],
];
Jetstream API features are currently disabled. To enable API token generation, uncomment Features::api() in the configuration.
User Model
The User model (app/Models/User.php) implements multiple traits:
use Laravel\Sanctum\ HasApiTokens ;
use Spatie\Permission\Traits\ HasRoles ;
class User extends Authenticatable
{
use HasApiTokens , HasFactory , Notifiable , HasRoles ;
protected $fillable = [
'name' ,
'email' ,
'password' ,
];
protected $hidden = [
'password' ,
'remember_token' ,
];
protected $casts = [
'email_verified_at' => 'datetime' ,
'password' => 'hashed' ,
];
}
Key Traits
HasApiTokens : Enables Sanctum token authentication
HasRoles : Provides Spatie Permission integration for role management
Notifiable : Enables notification sending
Authentication Guards
Configured in config/auth.php:
'defaults' => [
'guard' => 'web' ,
'passwords' => 'users' ,
],
'guards' => [
'web' => [
'driver' => 'session' ,
'provider' => 'users' ,
],
],
'providers' => [
'users' => [
'driver' => 'eloquent' ,
'model' => App\Models\ User :: class ,
],
],
Role-Based Access Control
The system implements RBAC using four primary roles:
Admin - Full system access
Chef - Menu management and profile
Mesero - Table and reservation management
Customer - Authenticated user (default)
User Type Field
Users have a usertype column that stores their role:
'admin'
'chef'
'mesero'
null or 'customer' (default)
Authentication Middleware
1. Authenticate Middleware
Located at app/Http/Middleware/Authenticate.php:
class Authenticate extends Middleware
{
protected function redirectTo ( Request $request ) : ? string
{
return $request -> expectsJson () ? null : route ( 'login' );
}
}
Behavior :
Unauthenticated web requests → Redirect to /login
Unauthenticated JSON requests → Return null (401 response)
Usage :
Route :: middleware ([ 'auth' ]) -> group ( function () {
// Protected routes
});
2. RoleMiddleware (Primary)
Located at app/Http/Middleware/RoleMiddleware.php:
class RoleMiddleware
{
public function handle ( Request $request , Closure $next , ... $roles )
{
$user = Auth :: user ();
if ( ! $user ) {
abort ( 403 , 'No tienes permiso para acceder a esta página. (no-auth)' );
}
// Normalize roles: handles 'admin,chef,mesero' or ['admin','chef']
$normalized = collect ( $roles )
-> flatMap ( function ( $r ) {
return array_map ( 'trim' , explode ( ',' , $r ));
})
-> filter ()
-> unique ()
-> values ()
-> all ();
// Check Spatie roles if available
if ( method_exists ( $user , 'hasAnyRole' )) {
if ( $user -> hasAnyRole ( $normalized )) {
return $next ( $request );
}
} else {
// Fallback to usertype column
if ( in_array ( $user -> usertype , $normalized , true )) {
return $next ( $request );
}
}
abort ( 403 , 'No tienes permiso para acceder a esta página. (sin-rol)' );
}
}
Key Features :
Supports multiple roles: role:admin,chef,mesero
Dual strategy: Spatie roles OR usertype fallback
Flexible role parsing (comma-separated or array)
Usage Examples :
// Single role
Route :: middleware ([ 'auth' , 'role:admin' ]) -> group ( function () {
// Admin only
});
// Multiple roles
Route :: middleware ([ 'auth' , 'role:admin,chef' ]) -> group ( function () {
// Admin or Chef
});
// Nested groups with different permissions
Route :: prefix ( 'admin' )
-> middleware ([ 'auth' , 'role:admin,chef,mesero' ])
-> group ( function () {
// All panel users can access dashboard
Route :: get ( '/dashboard' , [ DashboardController :: class , 'index' ]);
// Only admins
Route :: middleware ( 'role:admin' ) -> group ( function () {
Route :: resource ( 'users' , UserController :: class );
});
// Admins and chefs
Route :: middleware ( 'role:admin,chef' ) -> group ( function () {
Route :: resource ( 'foods' , FoodController :: class );
});
});
3. CheckRole Middleware
Located at app/Http/Middleware/CheckRole.php:
class CheckRole
{
public function handle ( $request , Closure $next , ... $roles )
{
if ( ! Auth :: check ()) {
return redirect ( '/login' );
}
if ( ! in_array ( Auth :: user () -> usertype , $roles )) {
abort ( 403 , 'Acceso no autorizado' );
}
return $next ( $request );
}
}
Differences from RoleMiddleware :
Redirects to /login instead of aborting with 403
Only checks usertype column (no Spatie integration)
Simpler implementation for basic role checking
4. EnsureRole Middleware
Located at app/Http/Middleware/EnsureRole.php:
class EnsureRole
{
public function handle ( Request $request , Closure $next , $role )
{
if ( ! $request -> user () || ! $request -> user () -> hasRole ( $role )) {
abort ( 403 , 'No autorizado.' );
}
return $next ( $request );
}
}
Features :
Only supports single role check
Requires Spatie’s hasRole() method
Used for strict single-role enforcement
5. RedirectIfAuthenticated
Located at app/Http/Middleware/RedirectIfAuthenticated.php:
class RedirectIfAuthenticated
{
public function handle ( Request $request , Closure $next , string ... $guards ) : Response
{
$guards = empty ( $guards ) ? [ null ] : $guards ;
foreach ( $guards as $guard ) {
if ( Auth :: guard ( $guard ) -> check ()) {
return redirect ( RouteServiceProvider :: HOME );
}
}
return $next ( $request );
}
}
Usage : Applied to login/register pages to prevent authenticated users from accessing them.
Authentication Flow
1. Login Process
2. Post-Login Redirect
Implemented in app/Http/Controllers/HomeController.php:38:
public function redirects ( Request $request )
{
$user = Auth :: user ();
if ( ! $user ) {
return redirect () -> route ( 'home' );
}
// Prefer Spatie roles if available
if ( method_exists ( $user , 'hasRole' )) {
if ( $user -> hasAnyRole ([ 'admin' , 'chef' , 'mesero' ])) {
return redirect () -> route ( 'admin.dashboard' );
}
}
// Fallback to usertype column
switch ( $user -> usertype ?? null ) {
case 'admin' :
return redirect () -> route ( 'admin.dashboard' );
case 'chef' :
return redirect () -> route ( 'chef.dashboard' );
case 'mesero' :
return redirect () -> route ( 'mesero.dashboard' );
default :
return redirect () -> route ( 'home' );
}
}
Route :
Route :: get ( '/redirects' , [ HomeController :: class , 'redirects' ])
-> middleware ( 'auth' )
-> name ( 'redirects' );
The /redirects route is the central hub for role-based routing after successful authentication.
3. Role-Based Dashboard Access
// Unified admin panel for all staff roles
Route :: prefix ( 'admin' )
-> name ( 'admin.' )
-> middleware ([ 'auth' , 'role:admin,chef,mesero' ])
-> group ( function () {
Route :: get ( '/dashboard' , [ AdminDashboardController :: class , 'index' ])
-> name ( 'dashboard' );
});
Protecting Routes
Pattern 1: Single Role
Route :: middleware ([ 'auth' , 'role:admin' ]) -> prefix ( 'admin' ) -> group ( function () {
Route :: get ( '/users' , [ AdminController :: class , 'user' ]);
Route :: post ( '/users' , [ AdminController :: class , 'createUser' ]);
});
Pattern 2: Multiple Roles
Route :: middleware ([ 'auth' , 'role:admin,mesero' ]) -> group ( function () {
Route :: resource ( 'tables' , AdminTableController :: class );
});
Pattern 3: Nested Role Restrictions
Route :: prefix ( 'admin' )
-> middleware ([ 'auth' , 'role:admin,chef,mesero' ])
-> group ( function () {
// Everyone in panel
Route :: get ( '/dashboard' , [ DashboardController :: class , 'index' ]);
// Admin only
Route :: middleware ( 'role:admin' ) -> group ( function () {
Route :: resource ( 'users' , UserController :: class );
});
// Admin and Chef
Route :: middleware ( 'role:admin,chef' ) -> group ( function () {
Route :: resource ( 'foods' , FoodController :: class );
});
});
Pattern 4: Guest Routes
Route :: get ( '/' , [ HomeController :: class , 'index' ]) -> name ( 'home' );
Route :: get ( '/menu' , [ HomeController :: class , 'comidaview' ]) -> name ( 'menu' );
Session Authentication
Jetstream Session Stack
Route :: middleware ([
'auth:sanctum' ,
config ( 'jetstream.auth_session' ),
'verified' ,
]) -> group ( function () {
Route :: get ( '/dashboard' , function () {
return view ( 'dashboard' );
}) -> name ( 'dashboard' );
});
Middleware Breakdown :
auth:sanctum - Sanctum authentication guard
config('jetstream.auth_session') - AuthenticateSession middleware
verified - Email verification (if enabled)
CSRF Protection
All state-changing operations require CSRF tokens:
<!-- In Blade templates -->
< form method = "POST" action = "/cart/1" >
@csrf
< button type = "submit" > Add to Cart </ button >
</ form >
<!-- For DELETE requests -->
< form method = "POST" action = "/cart/1" >
@csrf
@method('DELETE')
< button type = "submit" > Remove </ button >
</ form >
JavaScript CSRF Token
// Get token from meta tag
const token = document . querySelector ( 'meta[name="csrf-token"]' ). content ;
// Include in fetch requests
fetch ( '/api/endpoint' , {
method: 'POST' ,
headers: {
'X-CSRF-TOKEN' : token ,
'Content-Type' : 'application/json' ,
},
body: JSON . stringify ( data )
});
Password Security
Configured in config/auth.php:93:
'passwords' => [
'users' => [
'provider' => 'users' ,
'table' => 'password_reset_tokens' ,
'expire' => 60 , // 60 minutes
'throttle' => 60 , // 60 seconds between requests
],
],
'password_timeout' => 10800 , // 3 hours
Checking User Authentication
In Controllers
use Illuminate\Support\Facades\ Auth ;
public function index ()
{
$user = Auth :: user ();
if ( ! $user ) {
return redirect () -> route ( 'login' );
}
// Check role using Spatie
if ( $user -> hasRole ( 'admin' )) {
// Admin logic
}
// Check role using usertype
if ( $user -> usertype === 'chef' ) {
// Chef logic
}
}
In Blade Templates
@auth
< p > Welcome, {{ Auth :: user () -> name }} ! </ p >
@if ( Auth :: user () -> hasRole ( 'admin' ))
< a href = " {{ route ('admin.dashboard') }} " > Admin Panel </ a >
@endif
@endauth
@guest
< a href = " {{ route ('login') }} " > Login </ a >
@endguest
API Token Authentication (Optional)
API token features are currently disabled in Jetstream configuration. Enable Features::api() to use this feature.
Once enabled, users can generate API tokens:
// Generate token
$token = $user -> createToken ( 'token-name' );
// Use in requests
curl - H "Authorization: Bearer {token}" https : //example.com/api/endpoint
// Revoke token
$user -> tokens () -> where ( 'id' , $tokenId ) -> delete ();
Security Best Practices
Always Use Middleware Never rely on frontend checks alone. Always protect routes with auth and role middleware.
CSRF Protection All POST, PUT, PATCH, DELETE requests must include CSRF tokens.
Password Hashing Laravel automatically hashes passwords using bcrypt. Never store plain text passwords.
Session Security Configure appropriate session lifetimes and enable HTTPS in production.
Error Handling
Authentication Failures
// Middleware aborts
abort ( 403 , 'No tienes permiso para acceder a esta página. (sin-rol)' );
// Response
HTTP / 1.1 403 Forbidden
{
"message" : "No tienes permiso para acceder a esta página. (sin-rol)"
}
Unauthenticated Access
// Web routes
return redirect () -> route ( 'login' );
// JSON requests
HTTP / 1.1 401 Unauthorized
{
"message" : "Unauthenticated."
}
Testing Authentication
Testing with Sanctum
use Laravel\Sanctum\ Sanctum ;
public function test_admin_can_access_users ()
{
$admin = User :: factory () -> create ([ 'usertype' => 'admin' ]);
Sanctum :: actingAs ( $admin );
$response = $this -> get ( '/admin/users' );
$response -> assertStatus ( 200 );
}
public function test_chef_cannot_access_users ()
{
$chef = User :: factory () -> create ([ 'usertype' => 'chef' ]);
Sanctum :: actingAs ( $chef );
$response = $this -> get ( '/admin/users' );
$response -> assertStatus ( 403 );
}
Middleware Registration
Middleware must be registered in app/Http/Kernel.php:
protected $middlewareAliases = [
'auth' => \App\Http\Middleware\ Authenticate :: class ,
'role' => \App\Http\Middleware\ RoleMiddleware :: class ,
'check.role' => \App\Http\Middleware\ CheckRole :: class ,
'ensure.role' => \App\Http\Middleware\ EnsureRole :: class ,
// ...
];
Next Steps
Admin Controller Explore admin-specific API endpoints and permissions
Chef Controller Learn about chef menu management operations
Waiter Controller Discover waiter table and reservation management
Home Controller User-facing operations and public endpoints