Laravel 7: How to override validateCredentials for custom Login Validation

The following Guide is optimized for Laravel 7, but it works for Laravel 6.x and Laravel 5.x, too. Differences should only be present in the paths.

A great way to start a simple web project with PHP which needs a login and / or registration form with permissions is by using the widespread Laravel Framework with the Laratrust Package. This yields to a quick set up working system with basic functionalities.

TL;DR

Check out the short version of my StackOverflow answer.

Customize Login Validation

If the project grows, there might be a time where you want to customize the authentication mechanism by defining your own login validation: Perhaps you want to create a new status field for users to forbid the login for specific values or you want to add a lifetime (date field) for accounts that will lead to a failed login when the date-value is in the past. Anyway, you have to extend the existing login validation process.

This validation process is made in the validateCredentials method in the EloquentUserProvider class, located at [PROJECTROOT]/vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php which looks as follows in Laravel 7:

/**
* Validate a user against the given credentials.
*
* @param  \Illuminate\Contracts\Auth\Authenticatable  $user
* @param  array  $credentials
* @return bool
*/
public function validateCredentials(UserContract $user, array $credentials)
{
	$plain = $credentials['password'];

	return $this->hasher->check($plain, $user->getAuthPassword());
}

Default validateCredentials method in EloquentUserProvider

This method must be extended to get things working. As a good and attentive developer, you know that the framework default methods should never be touched to be more stable for future updates and avoid side effects. As a result and a possible solution, a Custom Provider can be defined to override the existing function.

Define Custom Provider and override validateCredentials

First, create a new file called CustomUserProvider.php (or a better name, for example AuthValidateStatusServiceProvider.php if you want to check the user status before login) in the app/Providers directory with the following code:

<?php

namespace App\Providers;

use Illuminate\Auth\EloquentUserProvider as UserProvider;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;


class CustomUserProvider extends UserProvider {

    /**
     * Overrides the framework defaults validate credentials method 
     *
     * @param UserContract $user
     * @param array $credentials
     * @return bool
     */
    public function validateCredentials(UserContract $user, array $credentials) {
        $plain = $credentials['password'];

        // PUT YOUR CUSTOM VALIDATION HERE

        return $this->hasher->check($plain, $user->getAuthPassword());
    }

}

Custom Provider

Add your custom validation and save it, for example if ($user->status != 'active') return false; if you created a status attribute for the user.

After this, Laravel must call this provider at the correct execution step. Do this by extending the boot() method in the app/Providers/AuthServiceProvider.php file to register it:

/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
	$this->registerPolicies();

	\Illuminate\Support\Facades\Auth::provider('customuserprovider', function($app, array $config) {
		return new CustomUserProvider($app['hash'], $config['model']);
	});
}

boot() method in the AuthServiceProvider

Remember to change the provider class name on both points. Furthermore, the code can be shortened with a simple import statement.

We are not done yet! The last step will be in the config/auth.php file. Scroll to the User Providers part and change it to:

'providers' => [
    'users' => [
        'driver' => 'customuserprovider',
        'model' => App\User::class,
        'table' => 'users',
    ],
],

User Providers part in auth.php

Here again: Don't forget to change the driver name (provider class name)

That's it! Now, your custom login validation should automatically be called after the user hits the Login button.