laravel-liberu/laravel-two-factor

Laravel应用的二次验证实现。

v0.0.9 2023-10-06 10:18 UTC

README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

Two-Factor-Laravel 是一个用于为您的 Laravel 应用实现二次验证的包。

如果启用,用户在认证过程中将被要求输入一个六位数字令牌。此令牌使用基于时间的单次密码 (TOTP) 生成,该密码可以从任何 TOTP 兼容的移动认证应用程序中获取,例如 Google Authenticator。

您还可以检索当前的单次密码并通过 SMS/电子邮件将其发送给用户。

安装

首先,使用 composer 将包安装到您的项目中

composer require emargareten/two-factor-laravel

接下来,您应该使用 vendor:publish Artisan 命令发布配置和迁移文件

php artisan vendor:publish --provider="Emargareten\TwoFactor\ServiceProvider"

最后,您应该运行应用程序的数据库迁移。这将向 users 表添加二次验证列

php artisan migrate

配置

发布资产后,您可以审查 config/two-factor.php 配置文件。此文件包含几个选项,允许您自定义二次验证功能的操作行为。

用法

要开始使用二次验证认证,您应该首先将 TwoFactorAuthenticatable 特性添加到您的 User 模型中

use Emargareten\TwoFactor\TwoFactorAuthenticatable;

class User extends Authenticatable
{
    use TwoFactorAuthenticatable;
}

启用二次验证认证

此包提供了使用二次验证认证用户的功能逻辑。但是,您需要提供启用和禁用二次验证认证的用户界面和控制器的实现。

要为用户启用二次验证认证,您应该在用户模型上调用 enableTwoFactorAuthentication 方法。这将生成用户的安全密钥和恢复代码,并将它们存储在数据库中(加密)

use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

class TwoFactorAuthenticationController extends Controller
{
    /**
     * Enable two-factor authentication for the user.
     */
    public function store(Request $request): RedirectResponse
    {
        $user = $request->user();

        if ($user->hasEnabledTwoFactorAuthentication()) {
            return back()->with('status', 'Two-factor authentication is already enabled');
        }

        $user->enableTwoFactorAuthentication();

        return redirect()->route('account.two-factor-authentication.confirm.show');
    }
}

确认二次验证认证

启用二次验证认证后,用户还必须通过提供有效的二次验证代码来“确认”他们的二次验证配置。您应该提供一种方式让用户这样做。例如,您可以提供一个视图,显示用户扫描到他们的认证应用程序中的 QR 码和密钥

use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class TwoFactorAuthenticationConfirmationController extends Controller
{
    /**
     * Get the two-factor authentication confirmation view.
     */
    public function show(Request $request): View|RedirectResponse
    {
        $user = $request->user();

        if ($user->hasEnabledTwoFactorAuthentication()) {
            return back()->with('status', 'Two-factor authentication is already enabled');
        }

        if (! $user->two_factor_secret) {
            return back()->with('status', 'Two-factor authentication is not enabled');
        }

        return view('account.two-factor-confirmation.show', [
            'qrCodeSvg' => $user->twoFactorQrCodeSvg(),
            'setupKey' => $user->two_factor_secret,
        ]);
    }

    /**
     * Confirm two-factor authentication for the user.
     */
    public function store(Request $request): RedirectResponse
    {
        $request->validate([
            'code' => ['required', 'string'],
        ]);

        $request->user()->confirmTwoFactorAuthentication($request->code);

        return redirect()
            ->route('account.two-factor-authentication.recovery-codes.index')
            ->with('status', 'Two-factor authentication successfully confirmed');
    }
}

如果您希望使用不同的方法接收单次密码,例如 SMS/电子邮件,您可以使用用户模型上的 getCurrentOtp 方法检索当前的单次密码

$user->getCurrentOtp();

注意 当通过 SMS/电子邮件发送单次密码时,您应该将窗口设置为更高的值,以便用户在发送后能够输入单次密码。

confirmTwoFactorAuthentication 方法接受一个可选的第二个参数来指定二次验证方法,这是完全可选的,如果您有多个接收单次密码的方法,这可能很有用。

禁用二次验证认证

您还应该提供一种方式让用户禁用二次验证认证。这可以通过在用户模型上调用 disableTwoFactorAuthentication 方法来完成

/**
 * Disable two-factor authentication for the user.
 */
public function destroy(Request $request): RedirectResponse
{
    $request->user()->disableTwoFactorAuthentication();

    return back()->with('status', 'Two-factor authentication disabled successfully');
}

用户认证

一旦用户确认启用二次验证认证,每次他们登录时,他们将被重定向到一个页面,在该页面中,他们将被要求输入由他们的认证应用程序生成的单次密码。

use Emargareten\TwoFactor\Actions\TwoFactorRedirector;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

public function login(Request $request, TwoFactorRedirector $redirector): Response
{
    // do login stuff...

    return $redirector->redirect($request);
}

这将重定向用户到 two-factor-challenge.create 路由。

如果用户被重定向到二次验证挑战页面,将触发 TwoFactorAuthenticationChallenged 事件,您可以监听此事件以添加额外的逻辑,例如,您可以通过 SMS/电子邮件发送单次密码。

public function handle(TwoFactorAuthenticationChallenged $event): void
{
    $event->user->notify(new CompleteSignInOTP);
}

您需要为 two-factor-challenge.create 路由提供一个视图。这个视图应该包含一个表单,用户可以在其中输入一次性密码。您应该通过调用 TwoFactor::challengeView() 方法在您的 AppServiceProviderregister 方法中绑定视图

/**
 * Register any application services.
 */
public function register(): void
{
    TwoFactor::challengeView('two-factor-challenge.create');
}

或者使用闭包来生成自定义响应

TwoFactor::challengeView(function (Request $request)  {
    return Inertia::render('TwoFactorChallenge/Create');
});

表单应提交到 two-factor-challenge.store 路由。

一旦用户输入了有效的一次性密码,他们将被重定向到目标URL(如果没有设置目标URL,则重定向到配置文件中定义的首页路由)。

恢复码

此包还提供了生成和使用恢复码的逻辑。恢复码可以在用户失去对其认证应用程序的访问权限时用于访问应用程序。

在启用双因素认证后,您应将用户重定向到一个可以查看其恢复码的页面。您也可以通过在用户模型上调用 generateNewRecoveryCodes 方法来生成一组新的恢复码

use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class TwoFactorAuthenticationRecoveryCodeController extends Controller
{
    /**
     * Get the two-factor authentication recovery codes for authenticated user.
     */
    public function index(Request $request): View|RedirectResponse
    {
        if (! $request->user()->hasEnabledTwoFactorAuthentication()) {
            return back()->with('status', 'Two-factor authentication is disabled');
        }

        return view('two-factor-recovery-codes.index', [
            'recoveryCodes' => $request->user()->two_factor_recovery_codes,
        ]);
    }

    /**
     * Generate a fresh set of two-factor authentication recovery codes.
     */
    public function store(Request $request): RedirectResponse
    {
        if (! $request->user()->hasEnabledTwoFactorAuthentication()) {
            return back()->with('status', 'Two-factor authentication is disabled');
        }

        $request->user()->generateNewRecoveryCodes();

        return redirect()->route('account.two-factor-authentication.recovery-codes.index');
    }
}

要使用恢复码,您应在 two-factor-challenge-recovery.create 路由上添加一个视图。这个视图应该包含一个表单,用户可以在其中输入恢复码。您应通过调用 TwoFactor::challengeRecoveryView() 方法在您的 AppServiceProviderregister 方法中绑定视图

表单应提交到 two-factor-challenge-recovery.store 路由。

测试

composer test

更新日志

请参阅 更新日志 了解最近的变化信息。

鸣谢

许可协议

MIT 许可协议 (MIT)。请参阅 许可文件 了解更多信息。