Cómo modificar el password broker de la recuperación de contraseñas de laravel

Spread the love

Hace unos días me encontré con esta pregunta en un grupo de Laravel.

Hola. Estoy intentando cambiar las reglas de validación del password cuando se produce la recuperación de contraseña. Tengo entendido que hay que sobre escribir el método rules del trait ResetsPasswords que usa ResetPasswordController. Copio y pego el método del trait en mi controlador y lo único que hago es cambiar el atributo min de 8 (que por defecto en el trait a 6:

La parte de código que viene en la pregunta es la siguiente.

/**
     * Get the password reset validation rules.
     *
     * @return array
     */
    protected function rules()
    {
        return [
            'token' => 'required',
            'email' => 'required|email',
            'password' => 'required|confirmed|min:8',
        ];
    }

Si cambias la validación min:8, vas a notar que de todas formas no puedes cambiar la contraseña de un usuario, porque sin importar que pongas menor a 8 en la validación, Seguirás recibiendo un mensaje de error y eso genera un poco de confusión sobre todo si estas comenzando a usar Laravel.

Error de validación.
Figura 1

Muy posiblemente te preguntes, si no es la regla de validación entonces que es lo que sucede y el misterio se resuelve revisando el componente que se encarga de hacer el cambio de contraseña la clase Password Broker.

Este componente valida que las contraseñas tenga una longitud mínima de 8 caracteres, si no se cumple regresa un error en la validación.

/**
     * Determine if the passwords are valid for the request.
     *
     * @param  array  $credentials
     * @return bool
     */
    protected function validatePasswordWithDefaults(array $credentials)
    {
        [$password, $confirm] = [
            $credentials['password'],
            $credentials['password_confirmation'],
        ];

        return $password === $confirm && mb_strlen($password) >= 8;
    }

Esto se hace por seguridad y en realidad no recomiendo que le metas mano, pero a veces se tiene que buscar la manera de acceder al Password Broker para personalizarlo, el detalle es que esto no es tan sencillo.

Así que me tome el tiempo de escribir esto, para explicar como puedes hacer cambios en esta parte de Laravel.

Análisis.

Cuando usas la recuperación de contraseñas de Laravel, se utilizan dos controladores: El ForgotPasswordController y el ResetPasswordController

Ambos controladores ocupan el Password Broker para hacer su tarea.

De tal forma que, cuando llamamos a este método.

/**
     * Get the broker to be used during password reset.
     *
     * @return \Illuminate\Contracts\Auth\PasswordBroker
     */
    public function broker()
    {
        return Password::broker();
    }

Regresa la siguiente estructura de objetos.

UML Password Broker
Figura 2

Es decir, el Facade Password te regresa un PasswordBrokerManager que es un Factory, que crea instancias de PasswordBroker y que se almacena cada una de ellas en el arreglo $brokers[].

El asunto es que este Manager tiene un pequeño detalle.

    /**
     * Resolve the given broker.
     *
     * @param  string  $name
     * @return \Illuminate\Contracts\Auth\PasswordBroker
     *
     * @throws \InvalidArgumentException
     */
    protected function resolve($name)
    {
        $config = $this->getConfig($name);
        if (is_null($config)) {
            throw new InvalidArgumentException("Password resetter [{$name}] is not defined.");
        }
        // The password broker uses a token repository to validate tokens and send user
        // password e-mails, as well as validating that password reset process as an
        // aggregate service of sorts providing a convenient interface for resets.
        return new PasswordBroker(
            $this->createTokenRepository($config),
            $this->app['auth']->createUserProvider($config['provider'] ?? null)
        );
    }

Observa la linea 18 del código; no existe forma de extender a otras implementaciones de PassworBroker!

Así que, si quieres cambiar la regla de la que hemos estado hablando desde el principio, requieres dos cosas, extender el PasswordBrokerManager y el PasswordBroke

adult chill connection

Así que, vamos a ver como podemos hacer esto.

Implementación

Aquí viene la parte divertida, para realizar los cambios requerimos extender la clase PasswordBrokerManager, la clase PasswordBroker, decirle a Laravel que reconozca nuestros cambios mediante el uso del ServiceProvider .

Extender la clase PasswordBroker

Debido a que solo vamos a sobreescribir un método, solo tienes que extender de la clase original.

use Illuminate\Auth\Passwords\PasswordBroker;

class CustomPasswordBroker extends PasswordBroker
{
    /**
     * Determine if the passwords are valid for the request.
     *
     * @param array $credentials
     * @return bool
     */
    protected function validatePasswordWithDefaults(array $credentials)
    {
        [$password, $confirm] = [
            $credentials['password'],
            $credentials['password_confirmation'],
        ];

        return $password === $confirm && mb_strlen($password) >= 6;
    }

}

Observa que la validación ahora acepta una longitud mayor o igual a 6.

Extender la clase PasswordBrokerManager

Aquí vamos a extender y sobreescribir el método resolve.

use Illuminate\Auth\Passwords\PasswordBrokerManager;
use InvalidArgumentException;

class CustomPasswordBrokerManager extends PasswordBrokerManager
{

    /**
     * Resolve the given broker.
     *
     * @param  string  $name
     * @return \Illuminate\Contracts\Auth\PasswordBroker
     *
     * @throws \InvalidArgumentException
     */
    protected function resolve($name)
    {
        $config = $this->getConfig($name);
        if (is_null($config)) {
            throw new InvalidArgumentException("Password resetter [{$name}] is not defined.");
        }
        // The password broker uses a token repository to validate tokens and send user
        // password e-mails, as well as validating that password reset process as an
        // aggregate service of sorts providing a convenient interface for resets.
        return new CustomPasswordBroker(
            $this->createTokenRepository($config),
            $this->app['auth']->createUserProvider($config['provider'] ?? null)
        );
    }

}

Ahora este método regresa nuestra versión del PasswordBroker.

Crear clase CustomPasswordResetServiceProvider

Esta clase se encargara de decirle al Contenedor de dependencias, que ahora tiene que ocupar nuestra implementación en lugar de la original.

use Illuminate\Support\ServiceProvider;
use App\Password\CustomPasswordBrokerManager;
use Illuminate\Contracts\Support\DeferrableProvider;

class CustomPasswordResetServiceProvider extends ServiceProvider implements DeferrableProvider
{
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->registerPasswordBroker();
    }
    /**
     * Register the password broker instance.
     *
     * @return void
     */
    protected function registerPasswordBroker()
    {
        $this->app->singleton('auth.password', function ($app) {
            return new CustomPasswordBrokerManager($app);
        });
        $this->app->bind('auth.password.broker', function ($app) {
            return $app->make('auth.password')->broker();
        });
    }
    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        return ['auth.password', 'auth.password.broker'];
    }
}

Finalmente edita el archivo config/app.php y cambia esta linea del arreglo providers

Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,

Por nuestra implementación.

App\Providers\CustomPasswordResetServiceProvider::class,

Y eso es todo, la siguiente vez que uses la recuperación de contraseñas ya no tendrás problemas con la validación.

Si tienes alguna duda deja tu comentario y recuerda compartir este artículo!