Search code examples
phplaravelhashlaravel-7bcrypt

How to edit Laravel 7 AbstractHasher or BcryptHasher?


I'm having some problems trying to extend or change a part of the Laravel Framework. Simply I cannot figure out where I can add or edit so that my changes will not be made in the vendor folder.

Essentially my issue was implementing Password Reset functionality to my React/Laravel application. I use inertia to post the reset password form, and it gives the error as follows:

ErrorException password_verify() expects parameter 1 to be string, array given

I use Bcrypt to hash the passwords, so as a workaround I added a couple lines of code to the check function of vendor/laravel/framework/src/Illuminate/Hashing/BcryptHasher.php:

    if (is_array($value)) {
       $value = array_values($value)[0];
  }

So now the whole function looks like this:

public function check($value, $hashedValue, array $options = [])
{
    if ($this->verifyAlgorithm && $this->info($hashedValue)['algoName'] !== 'bcrypt') {
        throw new RuntimeException('This password does not use the Bcrypt algorithm.');
    }
    if (is_array($value)) {
        $value = array_values($value)[0];
    }
    return parent::check($value, $hashedValue, $options);
}

I can also make the same change in the parent, AbstractHasher, within its check function and this also resolves the error.

This works, but I need to find a solution to implement this fix without making the changes inside the vendor folder. I deploy the app to google app engine so the changes within the vendor folder will not work in my situation. I just need to know how I can either extend or overwrite the BcryptHasher.php or AbstractHasher.php file properly.

Note: This is my first post here, so hopefully I did not format the message too poorly. If any additional information is required I will provide it. Thanks in advance.


Solution

  • To resolve the issue, I adapted @apokryfos answer How to change login hash bcrypt to hash256 to work with BcryptHasher instead of trying to add a new hasher such as Sha256.

    To do this I made the following changes:

    In config/hashing.php Set the driver as custombcrypt for my implementation.

    'driver' => 'custombcrypt',

    In app folder

    I created a folder named Libraries to hold my custom hasher classes. This folder can be named anything.

    In created Libraries folder at app/Libraries

    I created a file CustomBcryptHasher.php

    <?php
    
    namespace App\Libraries;
    
    use Illuminate\Hashing\BcryptHasher;
    use RuntimeException;
    
    class CustomBcryptHasher extends BcryptHasher
    {
        /**
         * Check the given plain value against a hash.
         *
         * @param  string  $value
         * @param  string  $hashedValue
         * @param  array  $options
         * @return bool
         *
         * @throws \RuntimeException
         */
        public function check($value, $hashedValue, array $options = [])
        {
            if ($this->verifyAlgorithm && $this->info($hashedValue)['algoName'] !== 'bcrypt') {
                throw new RuntimeException('This password does not use the Bcrypt algorithm.');
            }
            if (is_array($value)) {
                $value = array_values($value)[0];
            }
            return parent::check($value, $hashedValue, $options);
        }
    
    }
    

    The most important part of this file is this small portion near the bottom

        if (is_array($value)) {
           $value = array_values($value)[0];
      }
    

    This checks if the value is an array, and if so will extract the first value and use this for the Bcrypt check.

    In created Libraries folder at app/Libraries I add created a file named Hash.php which is a copy of /vendor/laravel/framework/src/Illuminate/Support/Facades/Hash.php

    <?php
    
    namespace Illuminate\Support\Facades;
    
    /**
     * @method static array info(string $hashedValue)
     * @method static bool check(string $value, string $hashedValue, array $options = [])
     * @method static bool needsRehash(string $hashedValue, array $options = [])
     * @method static string make(string $value, array $options = [])
     *
     * @see \Illuminate\Hashing\HashManager
     */
    class Hash extends Facade
    {
        /**
         * Get the registered name of the component.
         *
         * @return string
         */
        protected static function getFacadeAccessor()
        {
            return 'hash';
        }
    }
    

    In app/Providers I edited the AuthServiceProvider.php to return my CustomBcryptHasher.php when the custombcrypt driver is called.

    To do so, I added this code to the boot() function:

    Hash::extend('custombcrypt', function () {
        return new CustomBcryptHasher();
    });