asbiin/laravel-webauthn

Laravel Webauthn 支持

4.6.0 2024-07-21 20:53 UTC

README

Latest Version Downloads Workflow Status Quality Gate Coverage Status

LaravelWebauthn 是用于在 Laravel 中使用 Webauthn 作为2FA(双因素认证)或无密码认证的适配器。

现在在演示应用程序上试用。

功能

  • 管理 Webauthn 密钥注册
  • 第二因素认证:添加中间件服务以使用 Webauthn 密钥作为 2FA
  • 使用 Webauthn 密钥登录,无需密码

安装

使用以下命令安装此包:

composer require asbiin/laravel-webauthn

配置

您可以将 LaravelWebauthn 配置发布到名为 config/webauthn.php 的文件中,并使用 vendor:publish 命令发布资源

php artisan vendor:publish --provider="LaravelWebauthn\WebauthnServiceProvider"

接下来,您应该迁移您的数据库

php artisan migrate

设置

选项1:添加 LaravelWebauthn 中间件

Webauthn 中间件将强制用户对某些路由进行 Webauthn 密钥认证。

将中间件分配给路由或路由组

use LaravelWebauthn\Http\Middleware\WebauthnMiddleware;

Route::get('/home', function () {
    // ...
})->middleware(WebauthnMiddleware::class);

当需要时,Webauthn 中间件将用户重定向到 Webauthn 登录页面。

通过记住密码登录

当会话过期,但用户设置了 remember cookie 时,您可以通过订阅 LaravelWebauthn\Listeners\LoginViaRemember 监听器来重新验证 Webauthn 会话

use Illuminate\Support\Facades\Event;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Event::listen(
            \Illuminate\Auth\Events\Login::class,
            \LaravelWebauthn\Listeners\LoginViaRemember::class
        );
    }
}

选项2:无密码认证

您可以使用 Webauthn 通过仅使用 Webauthn 密钥认证来认证用户,而无需密码。

要启用无密码认证,首先添加 Webauthn 用户提供者:更新您的 config/auth.php 文件并更改 users 提供者

'providers' => [
    'users' => [
        'driver' => 'webauthn',
        'model' => App\Models\User::class,
    ],
],

然后允许您的登录页面通过 email 标识符启动 Webauthn 登录。

您可以通过向 webauthn.auth.options 路由发送 POST 请求并带有 email 输入来获取挑战数据。有关更多详细信息,请参阅认证部分。

禁用视图

默认情况下,LaravelWebauthn 定义了用于返回认证和注册密钥视图的路由。

但是,如果您正在构建由 JavaScript 驱动的单页应用程序,则可能不需要这些路由。因此,您可以通过将应用程序的 config/webauthn.php 配置文件中的 views 配置值设置为 false 完全禁用这些路由。

'views' => false,

缓存

请注意,此包使用缓存在服务器请求和浏览器响应之间存储挑战数据。您需要从您的 config/cache.php 文件设置真实的缓存驱动程序,因此您不能使用 arraynull 驱动程序。

用法

您可以在 asbiin/laravel-webauthn-example 中找到用法示例。您现在可以在演示应用程序上尝试它。

认证

使用 Webauthn 密钥进行认证的工作流程如下

  1. 打开 webauthn.login 登录页面。您可以通过调用 Webauthn::loginViewResponseUsing 来自定义登录页面视图。请参阅 查看响应

    默认行为将打开 webauthn::authenticate 页面。您还可以在配置文件中更改 webauthn.views.authenticate 的值。

  2. 或者:通过调用 webauthn.auth.options 获取公钥挑战(如果未提供)。

  3. 启动 webauthn 浏览器身份验证。您可以使用 webauthn.js 库来完成此操作。

    将签名数据发送到 webauthn.auth 路由。

  4. POST 响应将是

    • 重定向响应
    • 或包含 callback 数据的 json 响应。

示例

  <!-- load javascript part -->
  <script src="{!! secure_asset('vendor/webauthn/webauthn.js') !!}"></script>
...
  <!-- script part to run the sign part -->
  <script>
    var publicKey = {!! json_encode($publicKey) !!};

    var webauthn = new WebAuthn();

    webauthn.sign(
      publicKey,
      function (data) {
        axios.post("{{ route('webauthn.auth') }}", data)
          .then(function (response) {
            if (response.data.callback) { window.location.href = response.data.callback;}
          });
      }
    );
  </script>

如果身份验证成功,服务器将使用 webauthn.redirects.login 配置

  • 在纯 http 调用中重定向响应
  • 或使用如下 json 响应,例如
    {
        result: true,
        callback: `webauthn.redirects.login` target url,
    }

注册新密钥

要注册新的 webauthn 密钥,工作流程如下

  1. 打开 webauthn.register 页面。您可以通过调用 Webauthn::registerViewResponseUsing 来自定义注册页面视图。请参阅 查看响应

    默认行为将打开 webauthn::register 页面。您还可以在配置文件中更改 webauthn.views.register 的值。

  2. 或者:通过调用 webauthn.store.options 获取公钥挑战(如果未提供)。

  3. 启动 webauthn 浏览器注册。您可以使用 webauthn.js 库来完成此操作。

    将签名数据发送到 webauthn.store 路由。数据应包含一个 name 字段,其中包含 webauthn 密钥名称。

  4. POST 响应将是

    • 重定向响应
    • 或包含 callback 数据的 json 响应。

示例

  <!-- load javascript part -->
  <script src="{!! secure_asset('vendor/webauthn/webauthn.js') !!}"></script>
...
  <!-- script part to run the sign part -->
  <script>
    var publicKey = {!! json_encode($publicKey) !!};

    var webauthn = new WebAuthn();

    webauthn.register(
      publicKey,
      function (data) {
        axios.post("{{ route('webauthn.store') }}", {
          ...data,
          name: "{{ $name }}",
        })
      }
    );
  </script>

如果注册成功,服务器将使用 webauthn.redirects.register 配置

  • 在纯 http 调用中重定向响应
  • 或使用如下 json 响应,例如
    {
        result: json serialized webauthn key value,
        callback: `webauthn.redirects.register` target url,
    }

路由

这些路由已定义

您可以通过在配置文件中设置 prefix 值来自定义 URL 的第一部分。

忽略路由创建

您可以通过在 AppServiceProvider 中添加以下内容来禁用路由创建

use LaravelWebauthn\Services\Webauthn;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        Webauthn::ignoreRoutes();
    }
}

自定义认证管道

Laravel Webauthn 身份验证管道高度受 Fortify 管道 启发。

如果您愿意,可以定义一个自定义的类管道,登录请求应通过该管道。每个类都应该有一个 __invoke 方法,该方法接收传入的 Illuminate\Http\Request 实例,并像中间件一样有一个 $next 变量,用于调用以将请求传递给管道中的下一个类。

要定义您自己的自定义管道,您可以使用 Webauthn::authenticateThrough 方法。此方法接受一个闭包,该闭包应返回要管道化登录请求的类数组。通常,此方法应在您的 App\Providers\FortifyServiceProvider 类的 boot 方法中调用。

以下示例包含默认管道定义,您可以在进行自己的修改时将其用作起点

use LaravelWebauthn\Actions\AttemptToAuthenticate;
use LaravelWebauthn\Actions\EnsureLoginIsNotThrottled;
use LaravelWebauthn\Actions\PrepareAuthenticatedSession;
use LaravelWebauthn\Services\Webauthn;
use Illuminate\Http\Request;

Webauthn::authenticateThrough(fn (Request $request) => array_filter([
    config('webauthn.limiters.login') !== null ? null : EnsureLoginIsNotThrottled::class,
    AttemptToAuthenticate::class,
    PrepareAuthenticatedSession::class,
]));

速率限制器

默认情况下,Laravel Webauthn 将每分钟为每个电子邮件和 IP 地址组合限制登录为五个请求。您可以使用其他指定来指定自定义速率限制器。

首先定义一个自定义速率限制器。按照 Laravel 速率限制器文档,在您的应用程序的 App\Providers\AppServiceProvider 类的 boot 方法中创建一个新的 RateLimiter。

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     */
    protected function boot(): void
    {
        RateLimiter::for('webauthn-login', function (Request $request) {
            return Limit::perMinute(1000);
        });
    }
}

然后在此 webauthn.limiters.login 配置中使用此新的自定义速率限制器

'limiters' => [
    'login' => 'webauthn-login',
],

事件

事件由 LaravelWebauthn 分发

  • \LaravelWebauthn\Events\WebauthnLogin 在使用 Webauthn 进行登录检查时。
  • \LaravelWebauthn\Events\WebauthnLoginData 在准备身份验证数据挑战时。
  • \Illuminate\Auth\Events\Failed 在登录检查失败时。
  • \LaravelWebauthn\Events\WebauthnRegister 在注册新密钥时。
  • \LaravelWebauthn\Events\WebauthnRegisterData 在准备注册数据挑战时。
  • \LaravelWebauthn\Events\WebauthnRegisterFailed 在注册新密钥失败时。

视图响应

您可以使用 Webauthn 服务轻松更改视图响应。

例如,在您的 App\Providers\AppServiceProvider 类中调用 Webauthn::loginViewResponseUsing

use LaravelWebauthn\Services\Webauthn;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        Webauthn::loginViewResponseUsing(LoginViewResponse::class);
    }
}

使用一个 LoginViewResponse 类。

use LaravelWebauthn\Http\Responses\LoginViewResponse as LoginViewResponseBase;

class LoginViewResponse extends LoginViewResponseBase
{
    public function toResponse($request)
    {
        return Inertia::render('Webauthn/WebauthnLogin', [
            'publicKey' => $this->publicKey
        ])->toResponse($request);
    }
}

方法列表及其预期的响应合约。

兼容性

Laravel 兼容性

此包具有以下 Laravel 兼容性。

浏览器兼容性

大多数浏览器 支持 Webauthn

但是,没有以下内容,您的浏览器将拒绝与您的安全设备协商中继:

  • 一个适当的域名(localhost 和 127.0.0.1 将被 webauthn.js 拒绝)
  • 您的浏览器信任的 SSL/TLS 证书(自签名证书是可以的)
  • 443 端口的 HTTPS 连接(除了 443 以外的端口将被拒绝)

Homestead

如果您是 Laravel Homestead 用户,默认情况下是转发端口。您可以使用与 Homestead.yaml 类似的选项从 NAT/端口转发切换到私有网络。

sites:
  - map: homestead.test
networks:
  - type: "private_network"
    ip: "192.168.254.2"

重新配置 Vagrant 将通知您的虚拟机新的网络并自动安装自签名的 SSL/TLS 证书:vagrant reload --provision

如果您还没有这样做,请描述您的站点域名和网络在您的 hosts 文件中。

192.168.254.2 homestead.test

许可证

作者: Alexis Saettler

版权 © 2019–2024。

许可协议:MIT 协议。 查看许可