In this tutorial, we’ll look at how to implement an application that lets users view all logged-in devices as well as log out from all or specific devices.

Laravel provides a fully-featured authentication scaffolding in the form of laravel-ui package. It has features such as user login, registration, session management, password recovery, and email confirmation. However, this authentication package does not provide any means of managing currently logged in devices in Laravel, which is an essential feature to have so that users can view the logged-in devices and easily log out from a specific device.

Overview

I’ll start with a new Laravel project and demonstrate the following things:

  • Implement a view which displays currently logged in devices for a user.
  • Log out from any specific device from the list of currently logged-in devices.
  • An option to log out from all other devices except the currently logged-in device.
  • Log out from all devices, including the currently logged-in device.
  • Disable multiple device logins altogether. This means that a user can only be logged in from one device at a time.

Here’s how the final result will look like:

Setting up a New Laravel Project

  • Install a new Laravel project using Composer’s create-project command:
composer create-project --prefer-dist laravel/laravel laravel-manage-logged-in-devices

The above command creates a new Laravel project and pulls in all the required dependencies along with it.

  • Install Larvel’s UI package, which includes the authentication scaffolding, upon which we’ll build our login management mechanism.
 composer require laravel/ui
  • Generate the authentication scaffolding:
php artisan ui vue --auth
  • Compile all the related assets:
npm install && npm run dev
  • Next, set up the database. We created a MySQL database named laravel-manage-logged-in-devices and we’ll add the database details to the .env file.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel-manage-logged-in-devices
DB_USERNAME=root
DB_PASSWORD=
  • Finally, we complete the setup process by running our migrations, which creates the authentication-related tables in our database.
php artisan migrate

To verify if the setup works properly, start the webserver using the following command and visit http://127.0.0.1:8000.

php artisan serve

Track User Logins in Database

HTTP is a stateless protocol. In other words, HTTP cannot store user login information across multiple requests. So, how does Laravel manage to keep users logged in? Well, Laravel uses PHP Sessions. Sessions contain information related to a user and they are stored in an encrypted object in the browser and file or database in the backend.

Therefore, we can get a user’s login information from their session. However, Laravel stores the session data in a file by default. We have to change this so that Laravel stores it in the database rather than a file.

Set Session driver to database in .env

 BROADCAST_DRIVER=log
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=database
SESSION_LIFETIME=120

This ensures that our sessions are stored in our database tables and are much more readable in this format.

Generate Session related migrations

Laravel’s artisan provides a handy command to generate the session related table that is required to store the session information. This table contains columns such as user_agent, user_id, last_activity_time etc.

Let’s create this migration:

php artisan session:table

Reload Composer autoload file

Composer loads every dependency of your project into a file called autoload.php; this resides in the memory and provides faster access to APIs exposed by the dependencies for the Laravel application.

composer dump-autoload

Run migrations

Now that we have generated the migration file and reloaded composer’s autoload file, let’s run this migration and have it create the sessions table in the database.

php artisan migrate

Restart your webserver

For these changes to take effect, you would have to restart your webserver. Note that this clears your currently active sessions and logs out all your users.

Manage Currently Logged-in Devices

Now let’s create views and define controller methods that enable listing and management of logged-in devices for a user.

Create LoggedInDeviceManager Controller

This controller contains all our device management logic. Therefore, let’s create this controller using artisan.

php artisan make:controller LoggedInDeviceManager --resource

Define device management routes

Next, we have to define the routes required for this application. Go to web.php and add the following routes:

<?php

use Illuminate\Support\Facades\Route;


Route::get('/', function () {
    return view('welcome');
});

Auth::routes();

Route::get('/home', 'HomeController@index')->name('home');

Route::get('/logged-in-devices', 'LoggedInDeviceManager@index')
		->name('logged-in-devices.list')
		->middleware('auth');

Route::get('/logout/all', 'LoggedInDeviceManager@logoutAllDevices')
		->name('logged-in-devices.logoutAll')
		->middleware('auth');

Route::get('/logout/{device_id}', 'LoggedInDeviceManager@logoutDevice')
		->name('logged-in-devices.logoutSpecific')
		->middleware('auth');

Now let’s see what each of these routes do:

  • GET /logged-in-devices: Shows a bootstrap table that contains all your currently logged-in devices. Also, it includes button actions to remove each device or all devices at once.
  • GET /logout/all: Remove all logged-in devices, except the current device.
  • GET /logout/{device_id}: Logout a specific device using its session identifier.

Define Controller logic

We define controller methods for all the three routes defined above. After that, we will also create a view that exposes all these functions to the user.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class LoggedInDeviceManager extends Controller
{

    /**
     * Display a listing of the currently logged in devices.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {

        $devices = \DB::table('sessions')
            ->where('user_id', \Auth::user()->id)
            ->get()->reverse();

        return view('logged-in-devices.list')
                ->with('devices', $devices)
                ->with('current_session_id', \Session::getId());
    }


    /**
     * Logout a session based on session id.
     *
     * @return \Illuminate\Http\Response
     */
    public function logoutDevice(Request $request, $device_id)
    {

        \DB::table('sessions')
            ->where('id', $device_id)->delete();

        return redirect('/logged-in-devices');
    }



    /**
     * Logouts a user from all other devices except the current one.
     *
     * @return \Illuminate\Http\Response
     */
    public function logoutAllDevices(Request $request)
    {
        \DB::table('sessions')
            ->where('user_id', \Auth::user()->id)
            ->where('id', '!=', \Session::getId())->delete();

        return redirect('/logged-in-devices');
    }

}

Explanation:

  • index(): This method simply fetches all the sessions of the currently logged in user along with the current session ID and passes them to a view that displays the sessions in a tabular format. We will create this view shortly.
  • logoutDevice(): This method takes $session_id as input and removes that particular session entry from the session table. This effectively logs out the user from that specific device.
  • logoutAllDevices(): We simply remove all the session IDs except the current session from the sessions table. This essentially logs out all devices from the application except the current device.

Create a view to managed logged-in devices.

Create a logged-in-devices folder in resources/views path and create a blade file named list.blade.php. In this, we will define a bootstrap table that will display all the logged-in devices along with options to log out all or specific devices.

@extends('layouts.app')
@section('content')
<div class="container">
  <div class="row justify-content-center">
    <div class="col-lg">
      <div class="card">
        <div class="card-header d-flex justify-content-between">
          Currently Logged In Devices
          <a href="{{ count($devices) > 1 ? '/logout/all' : '#' }}" class="btn btn-danger {{ count($devices) == 1 ? 'disabled' : '' }}">Remove All Devices</a>
        </div>
        <table class="table  table-hover">
          <thead class="thead-dark">
            <tr>
              <th>Device</th>
              <th>IP</th>
              <th style="width:12%" >Last Activity</th>
              <th style="width:12%" >Actions</th>
            </tr>
          </thead>
          <tbody>
            @foreach ($devices as $device)
            <tr>
              <td>{{ $device->user_agent }}</td>
              <td>{{ $device->ip_address }}</td>
              <td>{{ Carbon\Carbon::parse($device->last_activity)->diffForHumans() }}</td>
              @if ($current_session_id == $device->id)
              <td><button type="button" :disabled="true" class="btn btn-primary">This Device</button></td>
              @else
              <td><a href="/logout/{{$device->id}}" class="btn btn-danger">Remove</a></td>
              @endif
            </tr>
            @endforeach
          </tbody>
        </table>
      </div>
    </div>
  </div>
</div>
@endsection

The result looks something like this:

laravel-manage-logged-in-devices
A list of currently logged-in devices.

You can try to verify the functionality and it should all work as expected. Go ahead and login from a couple of different browser windows (incognito mode should work too), and try logging out of each device using our newly created dashboard.

Here is how the view looks when your device is the only one that is currently logged-in:

laravel-logout-all-devices

Log out all devices, including currently logged-in device:

If you want to log out all users including the currently logged-in device, you can modify the logoutAllDevices method and delete all the sessions of the user regardless of their current session ID.

    /**
     * Logouts a user from all other devices except the current one.
     *
     * @return \Illuminate\Http\Response
     */
    public function logoutAllDevicesIncludingMyself(Request $request)
    {
        \DB::table('sessions')
            ->where('user_id', \Auth::user()->id)->delete();

        return redirect('/login');
    }

Disable Multiple Logins:

Sometimes, you don’t want your users to be able to log in from multiple devices at once. For instance, if your website is a subscription-based website that relies on having as many individual users as possible. In such cases, allowing multiple logins is not a good idea.

You can do this by listening for auth.login event, which is triggered whenever a user logs into the system. To do so, add this code to the boot() method of EventServiceProvider.php:

    /**
     * Register any events for your application.
     *
     * @return void
     */
    public function boot()
    {
        parent::boot();

        // Fired on successful logins...
        $events->listen('auth.login', function ($user, $remember) {
            \DB::table('sessions')
            ->where('user_id', \Auth::user()->id)
            ->where('id', '!=', \Session::getId())->delete();
        });
    }

That’s it! we have successfully implemented the management of logged-in devices in Laravel. I hope this was as interesting to you as it was for me! If you have any doubts or questions, please ask them in the comment section below. Also, the source code of this project can be found in my GitHub repository.

You can also check out our other Laravel tutorials if you wish to learn more about it.