How to use Multiple Users Authentication Guards and Login Throttling in Laravel

Laravel provides all the tools from simple to advance authentication functionality and also enough space for customization. So in this post, you’ll be learning about How to use Multiple Users Authentication Guards and Login Throttling in Laravel.

First, let’s create an app\Http\Controllers\LoginController.php.

php artisan make:controller LoginController

Authenticating Users for Login

use Illuminate\Support\Facades\Auth;

if (Auth::attempt(
        [
            'email' => $request->email, 
            'password' => $request->password
        ]
    , $remember_me ) ) {
    
    // after true the users will automatically be remembered and you can access user instance using Auth::user() method.

}

You can also pass custom keys in an array such as username or status etc.

    Auth::attempt(
        [
            'username' => $request->username, 
            'password' => $request->password,
            'status' => 'active'
        ]
    , $remember_me )

The $remember_me takes a boolean value and is set to false by default.

Access User Instance after login

The Auth facade can be used to get the instance of the current logged in user for this you have to access a static method user().

Auth::user() // this will get you user instance object
Auth::user()->id // this will get you id of the user

Auth::id() // this method is short method to get id of the user

Check if a user logged in

If you just want to verify if the current user is logged in and authenticate then use Auth::check() which returns a boolean value.

use Illuminate\Support\Facades\Auth;

if (Auth::check()) {
    
}

Manually Authenticate a User via User Instance

Let’s say that you want to login as another user but don’t have the proper login credentials then you can pass the user object into the method Auth::login( $user ) and by doing this a session is set to that user instance.

use Illuminate\Support\Facades\Auth;

$user = User::find( 2 );

Auth::login( $user );
public function authenticateByInstance( Request $request ){

    $user = User::find( 2 );

    Auth::login( $user );
    
    $request->session()->regenerate();
    
    return redirect()->intended( route( 'dashboard' ) ); // intended() will redirect the user to the URL and will skip authentication middleware only once.

}
It is necessary to call the $request->session()->regenerate(); method as this will create a new session instead of overriding the existing one and also fixes session fixation issues.

Authenticate user via the id

Pass the user’s primary key to the method Auth::loginUsingId( $user_id ); and the session will be set to that particular user.

Auth::loginUsingId( $user_id );
public function authenticateById( Request $request ){
    Auth::loginUsingId( 1 );
    
    $request->session()->regenerate(); 
    
    return redirect()->intended( route( 'dashboard' ) ); 

}

Safely Logging out Authenticated user

To safely remove the authenticated user data from the session two additional steps must be followed. First, call Auth::logout(); then flush the current session by calling $request->session()->invalidate(); and generate a fresh session token via a method $request->session()->regenerateToken();.

Auth::logout();

$request->session()->invalidate();

$request->session()->regenerateToken();

Login Form Snippet

In resources\views\login.blade.php

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <form action="{{ route( 'login.verify' ) }}" method="post">
        @csrf
        <label for="">email</label><br>
        <input type="text" name="email" id="email" value="" > 
        <br>
        <label for="">Password</label><br>
        <input type="password" name="password" id="password" value="" > 
        <br>
        <label for="">Remember Me</label>
        <input type="checkbox" name="remember_me" id="remember_me" value="1" > 
        <br>
        <input type="submit" value="Submit" > 
    </form>
</body>
</html>

Login Method Snippet

In app\Http\Controllers\LoginController.php

public function verify( Request $request ){

    $query = [
        'email' => $request->email,
        'password' => $request->password,
    ];

    $remember_me = ( !empty( $request->remember_me ) )? true : false;

    if(  Auth::attempt( $query, $remember_me ) ){
        $request->session()->regenerate(); 

        return redirect()->intended( route( 'dashboard' ) );
    } 

    return back();
}

Login Throttling

Laravel login throttling enables an extra layer of security within your laravel application by limiting the rate of attempts accessing a specific URL to which data is submitted.
This feature can be used for limiting the number of incorrect login attempts from users or preventing the issue of multiple duplicate form submissions.

In this post, you’ll learn to implement throttling for application login. And for demonstration purposes, we’ll be limiting the rate of login to a single attempt per minute.

1) Start from app\Providers\RouteServiceProvider.php and within method configureRateLimiting() create a static method RateLimiter::for. The method for( 'rate limiter name', callback-function ).

2) In our case we give name as login and the second parameter will be an anonymous callback function that returns Limit class.
Here is a code explanation.

RateLimiter::for('login', function (Request $request) {
    return Limit::perMinute(1); 
});

Here we are limiting one attempt per minute using the method perMinute(1). You can also use perHour() the method to limit attempts in hours.

3) Now use login rate limiter in middleware or for a specific route you want to set.

Route::post('login/verify', [ LoginController::class, 'verify' ] )->name('login.verify')->middleware( [ 'throttle:login'] );

4) If you go back to your login page and try login in using incorrect credentials at first you will be redirected to the login page and after the second attempt an HTTP 429 – TOO MANY REQUESTS response is sent back.

laravel login throttling http code 429 too many requests
laravel login throttling HTTP code 429 too many requests

You can also change and provide a custom response message by returning a response method along with Limit::perMinute(1).

return Limit::perMinute(1)->response(function () {
    return response('Try login after 1 minute', 429);
});
laravel login throttling custom message for rate limiters
laravel login throttling custom message for rate limiters

What are authentication Guards in Laravel?

The guards are used to logically map and retrieve the authenticate user and also specify the ways in which they must be authenticated. Further, it also allows you to change drivers that are session-based or token-based.

So let’s assume a scenario where you have two tables which are admin and another is users tables and have to authenticate users from two different tables we have to specify a guard in config\auth.php file.
Since by default the users table is provided by laravel and now you must also want the admin table users also be authenticated by Laravel Authentication. So for this, we’ll start by creating a new guard and name it as admin and driver will be session.

Create AdminModel to use for guard

This model stores and accesses data from the admin database table.

php artisan make:model AdminModel

In app\Models\AdminModel.php file.

<?php

    namespace App\Models;
    
    use Illuminate\Contracts\Auth\MustVerifyEmail;
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Foundation\Auth\User as Authenticatable;
    use Illuminate\Notifications\Notifiable;
    
    class AdminModel extends Authenticatable  
    {
        use HasFactory, Notifiable;
    
        /**
         * The attributes that are mass assignable.
         *
         * @var array
         */
        
        protected $table = "admin";
        protected $fillable = [
            'name',
            'email',
            'password',
        ];
    
        /**
         * The attributes that should be hidden for arrays.
         *
         * @var array
         */
        protected $hidden = [
            'password',
            'remember_token',
        ];
    
        /**
         * The attributes that should be cast to native types.
         *
         * @var array
         */
        protected $casts = [
            'email_verified_at' => 'datetime',
        ];
    }
    

Now in the config\auth.php file add a provider admin and specify driver and model. The supported driver is eloquent and database which specifies how data is retrieved.
The key model takes the reflection of the AdminModel::class class. If you don’t want to specify the model then you can replace the model key with the table key and give the database table name.

Now Append below changes to your config\auth.php file.

use App\Models\AdminModel;    

'guards' => [ 
    //[...]

    'admin' => [
        'driver' => 'session',
        'provider' => 'admin',
    ],

    //[...]
],

'providers' => [
    //[...]
    
    'admin' => [
        'driver' => 'eloquent',
        'model' => AdminModel::class,
    ], 

    //[...]
],

Login using multiple authentication guards

After specifying a new guard called admin we have to mention it while authenticating a user so that laravel can access its provider and retrieve its composition. So till now, we have done all it’s required to specify a guard, and below you see how to apply them in case you have multiple of such guards.

Come to app\Http\Controllers\LoginController.php we have to refactor our LoginController verify() method to support multiple guards.

<?php

namespace App\Http\Controllers;

use App\Models\AdminModel;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{
    public function verify( Request $request ){

        $query = [
            'email' => $request->email,
            'password' => $request->password,
        ];

        $remember_me = ( !empty( $request->remember_me ) )? true : false;

        $is_authenticated = false;
        if(  Auth::guard('web')->attempt( $query, $remember_me ) ){
            Auth::shouldUse('web');
            $is_authenticated = true;
        }
        
        if(  Auth::guard('admin')->attempt( $query, $remember_me ) ){
            Auth::shouldUse('admin');
            $is_authenticated = true;
        }
    
        if( $is_authenticated ){
            $request->session()->regenerate(); 

            return redirect()->intended( route( 'dashboard' ) ); 
        } 

        return back();
    }
}

First, the login credentials are matched with the first once and it successful that session is automatically created for that users and if the user is invalid then the credentials are matched with the Auth::guard('admin')->attempt( $query, $remember_me ) now if this is true then the session will be generated for the admin guard.

Don’t miss this?

Did you see that I have called Auth::shouldUse('web'); method after the attempt() method on each. This is to set and tell laravel authentication that you’ll be using this particular guard throughout the application.
This is necessary to do so because Laravel Authentication by default uses web guard and if you log in via admin guard then the Auth::user() will return null.

Protecting Web Routes

Even though you have set Auth::shouldUse( ); it once during login verification. It is mandatory to set it on every request for that I have created a GuardAuth Middleware will set the guard on every request or else even after successful login you will receive user instance as NULL.

In app\Http\Middleware\GuardAuth.php.

<?php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;

class GuardAuth extends Middleware
{ 
    
    public function handle($request, Closure $next, ...$guards)
    {

        $guards = config( 'auth.guards' );

        foreach( $guards as $guard => $guard_arr ){
            if( Auth::guard( $guard )->check() ){
                Auth::shouldUse( $guard );
            }
        }

        if( !Auth::check() ){
            return redirect( route('login') );
        }
        
        return $next($request);
    }

}

Register the GuardAuth middleware into the app\Http\Kernal.php.

protected $routeMiddleware = [
      // [...]
      'guard_auth' => GuardAuth::class,
      // [...]
];

For authenticating users in very request use guard_auth middleware is theirs. You can apply this to a single or to a group of routes.
In routes\web.php

Route::get('dashboard', [ DashboardController::class, 'index' ] )->name('dashboard')->middleware( ['guard_auth'] ); // single route

// Multiple routes
Route::group(['middleware' => [ 'guard_auth' ] ], function ($router) {
    Route::get('dashboard', [ DashboardController::class, 'index' ] )->name('dashboard')
});

View Source Code in Github

Video Tutorial

Related Posts

Summary
Review Date
Reviewed Item
How to use Multiple Users Authentication Guards and Login Throttling in Laravel
Author Rating
51star1star1star1star1star
Software Name
Laravel Framework
Software Name
Windows Os, Mac Os, Ubuntu Os
Software Category
Web Development