How to add the "active" field in authentication with AthenticationService? I use CakePHP 4.x

I request help with the following please:
I need to add a field to the authentication system and allow me to validate it with the isValid() function when calling

$result = $this->Authentication->getResult();
if ($result->isValid()) {
....code
}

I have tried the following but it doesn’t work:

public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
    {
        
        // Crea una nueva instancia de servicio de autenticación, definiendo la URL donde se debe 
        // redireccionar en caso de que se pretenda entrar a una vista protegida.
        $authenticationService = new AuthenticationService([
            'unauthenticatedRedirect' => '/'.basename(ROOT).'/users/login',
            'queryParam' => 'redirect',
        ]);

        // Carga los campos identificadores (logueo). 
        // Asegúrese de relacionar los campos con los que se valida el logueo
        $authenticationService->loadIdentifier('Authentication.Password', [
            'fields' => [
                            'username' => 'usuario',
                            'password' => 'password',
                        ],
            'where' => [    'estado' => 'Activo'    ],   // This is the other field that i need to validate
        ]);

        // Load the authenticators, you want session first
        $authenticationService->loadAuthenticator('Authentication.Session');

        // Configure form data check to pick email and password
        $authenticationService->loadAuthenticator('Authentication.Form', [
            'fields' => [
                            'username' => 'usuario',
                            'password' => 'password',
                        ],

            'loginUrl' => '/'.basename(ROOT).'/users/login',
        ]);


        return $authenticationService; 
    }

Add a custom finder to your Users table

   // src/Model/Table/UsersTable.php

   public function findActivo(Query $query, array $options): Query
    {
        return $query->where(['active' => true]);
    }

In your getAuthenticationService function in src/Application.php

public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
    {
// ... 
 $authenticationService->loadIdentifier('Authentication.Password', [
    'fields' => [
        'username' => 'usuario',
        'password' => 'passwd',
    ],
    'resolver' => [
        'className' => 'Authentication.Orm',
        'userModel' => 'Users',
        'finder' => 'activo', // default: 'all'
    ],
]);

// ...
2 Likes

@jmcd73 Thank you for your help, it was just what I needed, no more, no less.

Problem solved!

I want Userslogin only to be valid, if users.userstate_id == 2.
Therefore i followed the given example and added in “Application.php”:

    public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
    {
        $authenticationService = new AuthenticationService([
            //'unauthenticatedRedirect' => Router::url('/users/login'),
            'unauthenticatedRedirect' => Router::url(['_name'=>'login']),
            'queryParam' => 'redirect',
        ]);

        // Load identifiers, ensure we check email and password fields
        $authenticationService->loadIdentifier('Authentication.Password', [
            'fields' => [
                'username' => 'email',
                'password' => 'password',
            ],
           //**************from here on the custom check**********
            //custom check if userstate_id ==2
            'resolver' => [  //==> users table
                'className' => 'Authentication.Orm',
                'userModel' => 'Users',
                'finder' => 'allowed', // default: 'all'
            ],
            
        ]);

Then i added in UsersTable:

    public function findAllowed(Query $query, array $options): Query
    {
        return $query->where(['userstate_id' => 2]);
    }

This gives me the error:

App\Model\Table\UsersTable::findAllowed(): Argument #1 ($query) must be of type App\Model\Table\Query, Cake\ORM\Query\SelectQuery given, called in /var/www/html/recipes/vendor/cakephp/cakephp/src/ORM/Table.php on line 2697

I have no clue what and where to change this.
Can anyone maybe give me a hint or a link to a tutorial

Edit:
I forgot to add use App\Model\Entity\User; in UserTable.
After adding this the result of the login looks like this:

> **APP/Controller/UsersController.php** (line **31**)
> 
> object(Authentication\Authenticator\Result) id:0 { protected _status => 'FAILURE_CREDENTIALS_MISSING'protected _data => null protected _errors => [ (int) 0 => 'Login credentials not found', ] }

So it seems, I´m missing something else…

PPS:
Found it…
Finally in UsersTable I added use Cake\ORM\Query;

Reviewing your code a little, I notice that in the “finder” you assign the value “allowed”.

Question: Does the table where you store the login data have a field that stores the value “allowed”?

From what I see, the field you use for the filter is “userstate_id” which stores a value “2”.

Tip: Assign the value “2” in the “finder”, like this:

‘finder’ => ‘2’ // ‘allowed’

Thanks for your answer.
I finally found my mistake.
In UsersTable i had to add `

use Cake\ORM\Query;

And in UsersTable I put this method:

    public function findAllowed(Query $query, array $options): Query
    {
        return $query->where(['userstate_id' => 2]);
    }

And in Application.php I have this:

        $authenticationService->loadIdentifier('Authentication.Password', [
            'fields' => [
                'username' => 'email',
                'password' => 'password',
            ],
            //custom check if userstate_id ==2
            'resolver' => [  
                'className' => 'Authentication.Orm',
                'userModel' => 'Users',
                'finder' => 'allowed', 
            ],
        ]);

Seems to work:-)

This is the correct solution. Most examples you find will use the “short” name of the class, not the fully namespaced name, but if you just specify Query without telling it anywhere what Query class to use, it will assume one from the same namespace as the current class, i.e. App\Model\Table\Query, which is definitely wrong in this instance. Very standard PHP namespace issue, not anything CakePHP specific about it.

1 Like