vincent15000's avatar

How to prevent user from authenticating from multiple browsers / computers at the same time ?

Hello,

I need to prevent a user from authenticating from multiple browsers / computers at the same time, .

The authentication is done via Sanctum API token.

I already have added an IP address in the personal_access_tokens table, but it's the IP address retrieved from $request->ip() and 2 different computer in the same local network have the same $request->ip() address.

So how is it possible to check if a user tries to connect from another computer in the same local network ?

Thanks for your help.

V

0 likes
9 replies
Tray2's avatar

You can basically only check if the user is logged in already, the easiest for that would be to use the database driver for the session, and then check if the user trying to log in already exists there, and then either prevent login, or delete the already existing session.

1 like
vincent15000's avatar

@Tray2 I don't use any session, the user access the data with a Sanctum token.

Tray2's avatar

@vincent15000 Yes I did,

the easiest for that would be to use the database driver for the session, and then check if the user trying to log in already exists there, and then either prevent login, or delete

The same goes for the storing of tokens.

I realize that my comment sounded a bit snarky, that was not my intention.

LaryAI's avatar
Level 58

To prevent a user from authenticating from multiple browsers or computers at the same time using Laravel Sanctum, you can implement a mechanism to track and validate the user's session based on a unique identifier that is more specific than just the IP address. One approach is to use a combination of the user's device information and a unique session token.

Here's a step-by-step solution:

  1. Generate a Unique Device Identifier: When a user logs in, generate a unique identifier for the device. This can be done using a combination of user agent and a random token.

  2. Store the Device Identifier: Store this identifier in the personal_access_tokens table along with the token.

  3. Validate the Device Identifier on Each Request: On each request, check if the device identifier matches the one stored in the database.

  4. Invalidate Previous Sessions: If a new login is detected from a different device, invalidate the previous session.

Here is a code example to illustrate this approach:

Step 1: Generate a Unique Device Identifier

You can create a middleware to generate and attach a unique device identifier to each request.

// app/Http/Middleware/GenerateDeviceIdentifier.php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Str;

class GenerateDeviceIdentifier
{
    public function handle($request, Closure $next)
    {
        if (!$request->hasHeader('Device-Identifier')) {
            $deviceIdentifier = Str::random(40);
            $request->headers->set('Device-Identifier', $deviceIdentifier);
        }

        return $next($request);
    }
}

Step 2: Store the Device Identifier

Modify the Sanctum token creation process to store the device identifier.

// app/Http/Controllers/AuthController.php

use Illuminate\Http\Request;
use Laravel\Sanctum\PersonalAccessToken;

public function login(Request $request)
{
    // Validate the request...

    $user = User::where('email', $request->email)->first();

    if ($user && Hash::check($request->password, $user->password)) {
        // Generate a new token
        $token = $user->createToken('auth_token')->plainTextToken;

        // Store the device identifier
        $deviceIdentifier = $request->header('Device-Identifier');
        $tokenModel = PersonalAccessToken::findToken($token);
        $tokenModel->update(['device_identifier' => $deviceIdentifier]);

        return response()->json(['token' => $token]);
    }

    return response()->json(['message' => 'Invalid credentials'], 401);
}

Step 3: Validate the Device Identifier on Each Request

Create a middleware to validate the device identifier on each request.

// app/Http/Middleware/ValidateDeviceIdentifier.php

namespace App\Http\Middleware;

use Closure;
use Laravel\Sanctum\PersonalAccessToken;

class ValidateDeviceIdentifier
{
    public function handle($request, Closure $next)
    {
        $token = $request->bearerToken();
        $deviceIdentifier = $request->header('Device-Identifier');

        if ($token) {
            $tokenModel = PersonalAccessToken::findToken($token);

            if ($tokenModel && $tokenModel->device_identifier !== $deviceIdentifier) {
                return response()->json(['message' => 'Invalid device'], 401);
            }
        }

        return $next($request);
    }
}

Step 4: Invalidate Previous Sessions

When a new login is detected from a different device, invalidate the previous session.

// app/Http/Controllers/AuthController.php

public function login(Request $request)
{
    // Validate the request...

    $user = User::where('email', $request->email)->first();

    if ($user && Hash::check($request->password, $user->password)) {
        // Invalidate previous tokens
        $user->tokens()->delete();

        // Generate a new token
        $token = $user->createToken('auth_token')->plainTextToken;

        // Store the device identifier
        $deviceIdentifier = $request->header('Device-Identifier');
        $tokenModel = PersonalAccessToken::findToken($token);
        $tokenModel->update(['device_identifier' => $deviceIdentifier]);

        return response()->json(['token' => $token]);
    }

    return response()->json(['message' => 'Invalid credentials'], 401);
}

Register Middleware

Don't forget to register your middleware in app/Http/Kernel.php.

// app/Http/Kernel.php

protected $routeMiddleware = [
    // ...
    'generate.device.identifier' => \App\Http\Middleware\GenerateDeviceIdentifier::class,
    'validate.device.identifier' => \App\Http\Middleware\ValidateDeviceIdentifier::class,
];

Apply Middleware

Apply the middleware to your routes.

// routes/api.php

Route::middleware(['auth:sanctum', 'validate.device.identifier'])->group(function () {
    // Your protected routes
});

This approach ensures that each user session is tied to a specific device, and any attempt to use the same token from a different device will be rejected.

Please or to participate in this conversation.