Laragear / TwoFactor

Two-Factor Authentication for all your users out-of-the-box.
https://github.com/sponsors/DarkGhostHunter
MIT License
261 stars 20 forks source link

[2.0] Include `SerializesModels` in events. #82

Closed aon4o closed 3 months ago

aon4o commented 4 months ago

PHP & Platform

8.3.6 - Linux Mint 21.3 Cinnamon x64

Database

MySQL 8.0.32

Laravel version

11.7.0

Have you done this?

Expectation

Your package has several events. When I write an event Listener for those, I expect to be able to run the Listeners in a queue like this:

<?php

namespace App\Listeners;

use App\Notifications\TwoFactorRecoveryCodesGeneratedNotification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Laragear\TwoFactor\Events\TwoFactorRecoveryCodesGenerated;

class TwoFactorRecoveryCodesGeneratedListener implements ShouldQueue
{
    use InteractsWithQueue;

    public function handle(TwoFactorRecoveryCodesGenerated $event): void
    {
        // Some logic...
    }
}

In the example code, the ShouldQueue interface and the InteractsWithQueue trait make the listener execute in a queue.

Description

The problem is that the package events don't use the SerializesModels trait which leads to the following problem: Serialization of 'Closure' is not allowed.

Reproduction

// For reproduction, just make an Event listener for any of the package events.
// The listener must implement `ShouldQueue` and use `InteractsWithQueue`.
// After that dispatch the event and it should result in an exception `Serialization of 'Closure' is not allowed`.

Stack trace & logs

{
  "message": "Serialization of 'Closure' is not allowed",
  "exception": "Exception",
  "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Queue/Queue.php",
  "line": 160,
  "trace": [
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Queue/Queue.php",
      "line": 160,
      "function": "serialize"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Queue/Queue.php",
      "line": 129,
      "function": "createObjectPayload",
      "class": "Illuminate\\Queue\\Queue",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Queue/RedisQueue.php",
      "line": 212,
      "function": "createPayloadArray",
      "class": "Illuminate\\Queue\\Queue",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/horizon/src/RedisQueue.php",
      "line": 86,
      "function": "createPayloadArray",
      "class": "Illuminate\\Queue\\RedisQueue",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Queue/Queue.php",
      "line": 107,
      "function": "createPayloadArray",
      "class": "Laravel\\Horizon\\RedisQueue",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/horizon/src/RedisQueue.php",
      "line": 46,
      "function": "createPayload",
      "class": "Illuminate\\Queue\\Queue",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Queue/Queue.php",
      "line": 59,
      "function": "push",
      "class": "Laravel\\Horizon\\RedisQueue",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php",
      "line": 633,
      "function": "pushOn",
      "class": "Illuminate\\Queue\\Queue",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php",
      "line": 552,
      "function": "queueHandler",
      "class": "Illuminate\\Events\\Dispatcher",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php",
      "line": 478,
      "function": "Illuminate\\Events\\{closure}",
      "class": "Illuminate\\Events\\Dispatcher",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php",
      "line": 286,
      "function": "Illuminate\\Events\\{closure}",
      "class": "Illuminate\\Events\\Dispatcher",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php",
      "line": 266,
      "function": "invokeListeners",
      "class": "Illuminate\\Events\\Dispatcher",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php",
      "line": 453,
      "function": "dispatch",
      "class": "Illuminate\\Events\\Dispatcher",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laragear/two-factor/src/TwoFactorAuthentication.php",
      "line": 205,
      "function": "event"
    },
    {
      "file": "/var/www/html/app/Http/Controllers/API/Admin/ProfileController.php",
      "line": 136,
      "function": "generateRecoveryCodes",
      "class": "App\\Models\\User",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php",
      "line": 46,
      "function": "generateRecoveryCodes",
      "class": "App\\Http\\Controllers\\API\\Admin\\ProfileController",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
      "line": 260,
      "function": "dispatch",
      "class": "Illuminate\\Routing\\ControllerDispatcher",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
      "line": 206,
      "function": "runController",
      "class": "Illuminate\\Routing\\Route",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
      "line": 806,
      "function": "run",
      "class": "Illuminate\\Routing\\Route",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 144,
      "function": "Illuminate\\Routing\\{closure}",
      "class": "Illuminate\\Routing\\Router",
      "type": "->"
    },
    {
      "file": "/var/www/html/app/Http/Middleware/Confirm2FAMiddleware.php",
      "line": 28,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 183,
      "function": "handle",
      "class": "App\\Http\\Middleware\\Confirm2FAMiddleware",
      "type": "->"
    },
    {
      "file": "/var/www/html/app/Http/Middleware/PermissionMiddleware.php",
      "line": 48,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 183,
      "function": "handle",
      "class": "App\\Http\\Middleware\\PermissionMiddleware",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/rakutentech/laravel-request-docs/src/LaravelRequestDocsMiddleware.php",
      "line": 49,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 183,
      "function": "handle",
      "class": "Rakutentech\\LaravelRequestDocs\\LaravelRequestDocsMiddleware",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php",
      "line": 50,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 183,
      "function": "handle",
      "class": "Illuminate\\Routing\\Middleware\\SubstituteBindings",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php",
      "line": 161,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php",
      "line": 127,
      "function": "handleRequest",
      "class": "Illuminate\\Routing\\Middleware\\ThrottleRequests",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php",
      "line": 89,
      "function": "handleRequestUsingNamedLimiter",
      "class": "Illuminate\\Routing\\Middleware\\ThrottleRequests",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 183,
      "function": "handle",
      "class": "Illuminate\\Routing\\Middleware\\ThrottleRequests",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php",
      "line": 64,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 183,
      "function": "handle",
      "class": "Illuminate\\Auth\\Middleware\\Authenticate",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/sanctum/src/Http/Middleware/EnsureFrontendRequestsAreStateful.php",
      "line": 25,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 144,
      "function": "Laravel\\Sanctum\\Http\\Middleware\\{closure}",
      "class": "Laravel\\Sanctum\\Http\\Middleware\\EnsureFrontendRequestsAreStateful",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 119,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/sanctum/src/Http/Middleware/EnsureFrontendRequestsAreStateful.php",
      "line": 24,
      "function": "then",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 183,
      "function": "handle",
      "class": "Laravel\\Sanctum\\Http\\Middleware\\EnsureFrontendRequestsAreStateful",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 119,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
      "line": 805,
      "function": "then",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
      "line": 784,
      "function": "runRouteWithinStack",
      "class": "Illuminate\\Routing\\Router",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
      "line": 748,
      "function": "runRoute",
      "class": "Illuminate\\Routing\\Router",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
      "line": 737,
      "function": "dispatchToRoute",
      "class": "Illuminate\\Routing\\Router",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
      "line": 200,
      "function": "dispatch",
      "class": "Illuminate\\Routing\\Router",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 144,
      "function": "Illuminate\\Foundation\\Http\\{closure}",
      "class": "Illuminate\\Foundation\\Http\\Kernel",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/barryvdh/laravel-debugbar/src/Middleware/InjectDebugbar.php",
      "line": 59,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 183,
      "function": "handle",
      "class": "Barryvdh\\Debugbar\\Middleware\\InjectDebugbar",
      "type": "->"
    },
    {
      "file": "/var/www/html/app/Http/Middleware/LocalizationMiddleware.php",
      "line": 44,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 183,
      "function": "handle",
      "class": "App\\Http\\Middleware\\LocalizationMiddleware",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php",
      "line": 121,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php",
      "line": 64,
      "function": "handleStatefulRequest",
      "class": "Illuminate\\Session\\Middleware\\StartSession",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 183,
      "function": "handle",
      "class": "Illuminate\\Session\\Middleware\\StartSession",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
      "line": 21,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php",
      "line": 31,
      "function": "handle",
      "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 183,
      "function": "handle",
      "class": "Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
      "line": 21,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php",
      "line": 51,
      "function": "handle",
      "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 183,
      "function": "handle",
      "class": "Illuminate\\Foundation\\Http\\Middleware\\TrimStrings",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePostSize.php",
      "line": 27,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 183,
      "function": "handle",
      "class": "Illuminate\\Http\\Middleware\\ValidatePostSize",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php",
      "line": 110,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 183,
      "function": "handle",
      "class": "Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php",
      "line": 62,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 183,
      "function": "handle",
      "class": "Illuminate\\Http\\Middleware\\HandleCors",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 119,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
      "line": 175,
      "function": "then",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
      "line": 144,
      "function": "sendRequestThroughRouter",
      "class": "Illuminate\\Foundation\\Http\\Kernel",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Application.php",
      "line": 1172,
      "function": "handle",
      "class": "Illuminate\\Foundation\\Http\\Kernel",
      "type": "->"
    },
    {
      "file": "/var/www/html/public/index.php",
      "line": 19,
      "function": "handleRequest",
      "class": "Illuminate\\Foundation\\Application",
      "type": "->"
    },
    {
      "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/resources/server.php",
      "line": 16,
      "function": "require_once"
    }
  ]
}
aon4o commented 4 months ago

I'm not particularly sure why the problem happens, to be honest. I tried updating the package's event code to use the SerializesModels trait and my code started working.

I also tried reproducing it in a Vanilla Laravel installation and couldn't do it. This may be because the User model in my original project is too complex. It utilizes 10 different traits, attribute functions, scopes, and many more.

But I found this in the Laravel 11 docs: Events - Defining Events Queue - Class Structure

These two paragraphs in the documentation make me believe it won't be bad for the package's events to start using the SerializesModels trait.