PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Wednesday, October 19, 2022

[FIXED] How to allow to use the master password in Laravel 8 by overriding Auth structure?

 October 19, 2022     admin, authentication, laravel, password-hash, php     No comments   

Issue

I've got a website written in pure PHP and now I'm learning Laravel, so I'm remaking this website again to learn the framework. I have used built-in Auth Fasade to make authentication. I would like to understand, what's going on inside, so I decided to learn more by customization. Now I try to make a master password, which would allow direct access to every single account (as it was done in the past).

Unfortunately, I can't find any help, how to do that. When I was looking for similar issues I found only workaround solutions like login by admin and then switching to another account or solution for an older version of Laravel etc.

I started studying the Auth structure by myself, but I lost and I can't even find a place where the password is checked. I also found the very expanded solution on GitHub, so I tried following it step by step, but I failed to make my own, shorter implementation of this. In my old website I needed only one row of code for making a master password, but in Laravel is a huge mountain of code with no change for me to climb on it.

As far I was trying for example changing all places with hasher->check part like here:

protected function validateCurrentPassword($attribute, $value, $parameters)
{
    $auth = $this->container->make('auth');
    $hasher = $this->container->make('hash');

    $guard = $auth->guard(Arr::first($parameters));

    if ($guard->guest()) {
        return false;
    }

    return $hasher->check($value, $guard->user()->getAuthPassword());
}

for

return ($hasher->check($value, $guard->user()->getAuthPassword()) || $hasher->check($value, 'myHashedMasterPasswordString'));

in ValidatesAttributes, DatabaseUserProvider, EloquentUserProvider and DatabaseTokenRepository. But it didn't work. I was following also all instances of the getAuthPassword() code looking for more clues.

My other solution was to place somewhere a code like this:

if(Hash::check('myHashedMasterPasswordString',$given_password))
   Auth::login($user);

But I can't find a good place for that in middlewares, providers, or controllers.

I already learned some Auth features, for example, I succeed in changing email authentication for using user login, but I can't figure out, how the passwords are working here. Could you help me with the part that I'm missing? I would appreciate it if someone could explain to me which parts of code should I change and why (if it's not so obvious).

I would like to follow code execution line by line, file by file, so maybe I would find a solution by myself, but I feel like I'm jumping everywhere without any idea, how this all is connected with each other.


Solution

First of all, before answering the question, I must say that I read the comments following your question and I got surprised that the test you made returning true in validateCredentials() method in EloquentUserProvider and DatabaseUserProvider classes had failed.

I tried it and it worked as expected (at least in Laravel 8). You just need a an existing user (email) and you will pass the login with any non-empty password you submit.

Which of both classes are you really using (because you don't need to edit both)? It depends of the driver configuration in your auth.php configuration file.

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

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],

As you already thought, you can simply add an "or" to the validation in the validateCredentials() method, comparing the $credentials['password'] to your custom master password.

Having said that, and confirming that's the place where you'd have to add your master password validation, I think the best (at least my recommended) way to accomplish your goal is that you track the classes/methods, starting from the official documentation, which recommends you to execute the login through the Auth facade:

use Illuminate\Support\Facades\Auth;

class YourController extends Controller
{
    public function authenticate(Request $request)
    {
        //
        if (Auth::attempt($credentials)) {
            //
        }
        //
    }
}

You would start by creating your own controller (or modifying an existing one), and creating your own Auth class, extending from the facade (which uses the __callStatic method to handle calls dynamically):

use YourNamespace\YourAuth;

class YourController extends Controller
{
    //
    public function authenticate(Request $request)
    {
        //
        if (YourAuth::attempt($credentials)) {
            //
        }
        //
    }
}
//
 * @method static \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard guard(string|null $name = null)
//

class YourAuth extends Illuminate\Support\Facades\Facade
{
// 
}

And use the same logic, overriding all the related methods in the stack trace until you get to use the validateCredentials() method, which in the end will also be overrided in your own CustomEloquentUserProvider class which will be extending fron the original EloquentUserProvider.

This way, you will have accomplished your goal, and kept a correct override of the whole process, being able to update your laravel installation without the risk of loosing your work. Worst case scenario? You'll have to fix any of your overriding methods in case that any of them has drastically changed in the original classes (which has a ver low chance to happen).

Tips

When making the full overriding, maybe you'll prefer to add some significant changes, like evading the interfaces and going straight for the classes and methods you really need. For example: Illuminate/Auth/SessionGuard::validate.

You would also wish to save your master password in an environment variable in your .env file. For example:

// .env
MASTER_PASSWORD=abcdefgh

and then call it with the env() helper:

if ($credentials['password'] === env('MASTER_PASSWORD')) {
//
}

Nice journey!



Answered By - Anibal E. Alvarez Sifontes
Answer Checked By - Clifford M. (PHPFixing Volunteer)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing