Remember MFA on device #2381

Open
opened 2026-02-05 03:53:05 +03:00 by OVERLORD · 2 comments
Owner

Originally created by @schnapsidee on GitHub (Sep 8, 2021).

Describe the feature you'd like
Currently you have to enter a new MFA Code everytime you login. It would be nice if there was an option to remember a device so you don't have to reauthenticate with MFA on every login.

Describe the benefits this feature would bring to BookStack users
We use MFA mostly to protect accounts in case of compromised passwords and outside access. Having to reauthentiate with 2FA from trusted devices at every login gets annoying very fast when we really just want to protect access from untrusted devices.
I believe making a security feature more user-friendly can only help adoption especially if the security risk isn't increased significantly. Most websites that offer 2FA already use this feature so it's a well established industry practice.

Additional context
Perhaps there could be an option in the admin pannel to allow or disallow trusting a device in case an admin wants the "full security" option of having users reauthenticate at every login.

Originally created by @schnapsidee on GitHub (Sep 8, 2021). **Describe the feature you'd like** Currently you have to enter a new MFA Code everytime you login. It would be nice if there was an option to remember a device so you don't have to reauthenticate with MFA on every login. **Describe the benefits this feature would bring to BookStack users** We use MFA mostly to protect accounts in case of compromised passwords and outside access. Having to reauthentiate with 2FA from trusted devices at every login gets annoying very fast when we really just want to protect access from untrusted devices. I believe making a security feature more user-friendly can only help adoption especially if the security risk isn't increased significantly. Most websites that offer 2FA already use this feature so it's a well established industry practice. **Additional context** Perhaps there could be an option in the admin pannel to allow or disallow trusting a device in case an admin wants the "full security" option of having users reauthenticate at every login.
OVERLORD added the 🔨 Feature Request🚪 Authentication labels 2026-02-05 03:53:05 +03:00
Author
Owner

@Adam0shka commented on GitHub (Nov 21, 2023):

Hi, BookStack team,

You remember about this issue? This is very important for our company)

@Adam0shka commented on GitHub (Nov 21, 2023): Hi, BookStack team, You remember about this issue? This is very important for our company)
Author
Owner

@eubi86 commented on GitHub (Aug 8, 2024):

You can skip 2Fa if you log in from the company network. As usual, 2fa is required from outside. You can set an IP range. This also works for individual users such as the admin. You just have to adjust it a bit. require composer Longman iptools
Here is the functions.php

Code Here

<?php

use BookStack\Facades\Theme;
use BookStack\Theming\ThemeEvents;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Longman\IPTools\Ip;
use Illuminate\Support\Facades\Auth;
use BookStack\Access\LoginService;
use BookStack\Access\Mfa\MfaSession;
use BookStack\Access\EmailConfirmationService;
use BookStack\Access\SocialDriverManager;

$companyNetworkRanges = [
    '172.30.246.0/23', // IP range
];

// Fetch dependencies from the container
$mfaSession = app(MfaSession::class);
$emailConfirmationService = app(EmailConfirmationService::class);
$socialDriverManager = app(SocialDriverManager::class);

Theme::listen(ThemeEvents::WEB_MIDDLEWARE_BEFORE, function (Request $request) use ($companyNetworkRanges, $mfaSession, $emailConfirmationService, $socialDriverManager) {
    $ip = $request->ip();
    Log::info('Received IP: ' . $ip);

    $isInCompanyNetwork = array_filter($companyNetworkRanges, function ($range) use ($ip) {
        return Ip::match($ip, $range);
    });

    $isInCompanyNetwork = !empty($isInCompanyNetwork);
    Log::info('Is IP in company network: ' . ($isInCompanyNetwork ? 'Yes' : 'No'));

    // Proceed only if user is in the company network
    if ($isInCompanyNetwork) {
        $loginService = new LoginService($mfaSession, $emailConfirmationService, $socialDriverManager);

        if (auth()->check()) {
            $user = auth()->user();
            Log::info('Authenticated user ID: ' . $user->id);

            // No additional actions if the user is authenticated and in the company network
            return null;
        }

        Log::info('User is not authenticated.');

        try {
            $credentials = $request->only('email', 'password');
            $remember = $request->boolean('remember', false);
            $result = auth()->attempt($credentials, $remember);

            if ($result) {
                $user = auth()->user();
                Log::info('Authenticated user ID: ' . $user->id);
                Auth::login($user, $remember);
            } else {
                Log::info('Authentication attempt failed.');
            }
        } catch (\Exception $e) {
            Log::error('Authentication error: ' . $e->getMessage(), ['exception' => $e]);
        }
    } else {
        Log::info('User is outside the company network. No actions performed.');
    }

    return null;
});

@eubi86 commented on GitHub (Aug 8, 2024): You can skip 2Fa if you log in from the company network. As usual, 2fa is required from outside. You can set an IP range. This also works for individual users such as the admin. You just have to adjust it a bit. require composer Longman iptools Here is the functions.php <details><summary>Code Here</summary> <p> ```php <?php use BookStack\Facades\Theme; use BookStack\Theming\ThemeEvents; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; use Longman\IPTools\Ip; use Illuminate\Support\Facades\Auth; use BookStack\Access\LoginService; use BookStack\Access\Mfa\MfaSession; use BookStack\Access\EmailConfirmationService; use BookStack\Access\SocialDriverManager; $companyNetworkRanges = [ '172.30.246.0/23', // IP range ]; // Fetch dependencies from the container $mfaSession = app(MfaSession::class); $emailConfirmationService = app(EmailConfirmationService::class); $socialDriverManager = app(SocialDriverManager::class); Theme::listen(ThemeEvents::WEB_MIDDLEWARE_BEFORE, function (Request $request) use ($companyNetworkRanges, $mfaSession, $emailConfirmationService, $socialDriverManager) { $ip = $request->ip(); Log::info('Received IP: ' . $ip); $isInCompanyNetwork = array_filter($companyNetworkRanges, function ($range) use ($ip) { return Ip::match($ip, $range); }); $isInCompanyNetwork = !empty($isInCompanyNetwork); Log::info('Is IP in company network: ' . ($isInCompanyNetwork ? 'Yes' : 'No')); // Proceed only if user is in the company network if ($isInCompanyNetwork) { $loginService = new LoginService($mfaSession, $emailConfirmationService, $socialDriverManager); if (auth()->check()) { $user = auth()->user(); Log::info('Authenticated user ID: ' . $user->id); // No additional actions if the user is authenticated and in the company network return null; } Log::info('User is not authenticated.'); try { $credentials = $request->only('email', 'password'); $remember = $request->boolean('remember', false); $result = auth()->attempt($credentials, $remember); if ($result) { $user = auth()->user(); Log::info('Authenticated user ID: ' . $user->id); Auth::login($user, $remember); } else { Log::info('Authentication attempt failed.'); } } catch (\Exception $e) { Log::error('Authentication error: ' . $e->getMessage(), ['exception' => $e]); } } else { Log::info('User is outside the company network. No actions performed.'); } return null; }); ``` </p> </details>
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/BookStack#2381