Symfony5: login throttling failing must be an instance of RequestRateLimiterInterface, instance of RateLimiterFactory given

1.8k views Asked by At

In order to satisfy a security audit, I need to implement a feature where a user can have at most 3 attempts of login per 5 minutes

fortunately, it is now built-in Symfony 5.2: https://symfony.com/blog/new-in-symfony-5-2-login-throttling

As the parameter max_attempts is not enough for me (it's per 1 minute), I want to use the second option to provide my owner limiter

so I use this https://symfony.com/blog/new-in-symfony-5-2-rate-limiter-component

my configuration looks like this

framework:
    rate_limiter:
        max_login_failure:
            policy: fixed_window
            limit: 3
            interval: '10 minutes'
security:
    firewalls:
        admins:
            host: admin.*
            pattern: ^/
            provider: admins
            login_throttling:
                limiter: limiter.max_login_failure

but it fails with the error

Argument 2 passed to Symfony\Component\Security\Http\EventListener\LoginThrottlingListener::__construct() must be an instance of Symfony\Component\HttpFoundation\RateLimiter\RequestRateLimiterInterface, instance of Symfony\Component\RateLimiter\RateLimiterFactory given,

My understanding is that I'm giving it a factory instead of an instance of what I want, but there's nowhere in the doc where I can find how to get an instance from the factory

1

There are 1 answers

2
shevon On BEST ANSWER

If you want to use a custom limiter you need to create an own limiter. Then define as service in services.yml An example that works for me

<?php

declare(strict_types=1);

namespace App\Limiter;

use Symfony\Component\HttpFoundation\RateLimiter\RequestRateLimiterInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\RateLimiter\LimiterInterface;
use Symfony\Component\RateLimiter\RateLimit;
use Symfony\Component\RateLimiter\RateLimiterFactory;

    class CustomLimiter implements RequestRateLimiterInterface
    {
        /**
         * @var LimiterInterface
         */
        private $limiter;
    
        public function __construct(RateLimiterFactory $factory)
        {
            $this->limiter = $factory->create('a_unique_identifier');
        }
    
        public function consume(Request $request): RateLimit
        {

    
            return $this->limiter->consume();
        }
    
        public function reset(Request $request): void
        {
            $this->limiter->reset();
        }
    
    }

In services.yml define a custom limiter. You should pass as an argument your max_limiter configuration

   app.custom.limiter:
    class: App\Limiter\CustomLimiter
    arguments:
        - '@limiter.max_login_failure'

In the end, you should paste your new limiter to the security configuration

security:
firewalls:
    admins:
        host: admin.*
        pattern: ^/
        provider: admins
        login_throttling:
            limiter: app.custom.limiter

That's all! I hope this helps you