custom finder not working in CakePHP 3

2.1k Views Asked by At

I'm working on CakePHP 3.3. I want user to login using either email or mobile number along with password.

I have users table with email, mobile, password, etc fields.

According to CakePHP doc, I'm using custom finder auth to login.

Auth component in AppController.php

    $this->loadComponent('Auth', [
        'loginRedirect' => [
            'controller' => 'Dashboard',
            'action' => 'index'
        ],
        'logoutRedirect' => [
            'controller' => 'Pages',
            'action' => 'home'
        ],
        'authenticate' => [
            'Form' => [
                'finder' => 'auth'
            ]
        ]
    ]);

and findAuth() action in UsersTable.php

public function findAuth(Query $query, array $options)
{
    $query
        ->select(['id', 'email', 'mobile', 'password'])
        ->where(['Users.email' => $options['login']])
        ->orWhere(['Users.mobile' => $options['login']]);

    return $query;
}

and login() action in UsersController.php

public function login()
{
    if ($this->request->is('post')) {
        $user = $this->Auth->identify();
        if ($user) {
            $this->Auth->setUser($user);
            return $this->redirect($this->Auth->redirectUrl());
        }
        $this->Flash->registerError(__('Invalid Credentials, try again'), ['key' => 'registration']);
    }
}

login.ctp view contains

<?php
   echo $this->Form->create();
   echo $this->Form->input('login');
   echo $this->Form->input('password');
   echo $this->Form->submit();
   echo $this->Form->end();
?>

But this is not working and prints Invalid Credentials, try again

Update 2

Added a blank column username to users table.

login.ctp

<?php
   echo $this->Form->create();
   echo $this->Form->input('username');
   echo $this->Form->input('password');
   echo $this->Form->submit();
   echo $this->Form->end();
?>

AppController.php:authComponent

same as above

findAuth()

public function findAuth(Query $query, array $options)
{
    $query
        ->orWhere(['Users.email' => $options['username']])
        ->orWhere(['Users.mobile' => $options['username']]);

    return $query;
}

Now it's working. But why force to use username column even if not needed in application.

1

There are 1 best solutions below

11
On

You must SELECT all the fields you need to authenticate a user, as described on doc.

public function findAuth(Query $query, array $options)
{
    $query
        ->select(['id', 'email', 'mobile', 'username', 'password'])
        ->orWhere(['Users.email' => $options['login']])
        ->orWhere(['Users.mobile' => $options['login']]);

    return $query;
}

And be sure $options['login'] is on your form.

Update: If you are using 'login' as Form input try using:

        'authenticate' => [
            'Form' => [
                'finder' => 'auth',
                'fields' => ['username' => 'login', 'password' => 'password']
            ]
        ]

fields The fields to use to identify a user by. You can use keys username and password to specify your username and password fields respectively.

My own Query using my App (without fields => [username => login]):

SELECT 
  Users.id AS `Users__id`, 
  Users.username AS `Users__username`, 
  Users.password AS `Users__password`, 
  Users.role AS `Users__role`, 
  Users.email AS `Users__email` 
FROM 
  users Users 
WHERE 
  (
    Users.email = '[email protected]' 
    OR (
      Users.username = '[email protected]' 
      AND Users.username = '[email protected]'
    )
  ) 

My login is similar, but is using username and email instead of your fields.

Update 2: The documentation is not so great. So testing I figured that by default using a custom finder the query will be modified by Cake adding the first WHERE this = 'something', then the solution is using orWhere on all the others (findAuth modified).

New Query:

SELECT 
  Users.id AS `Users__id`, 
  Users.username AS `Users__username`, 
  Users.password AS `Users__password`, 
  Users.role AS `Users__role`, 
  Users.email AS `Users__email` 
FROM 
  users Users 
WHERE 
  (
    Users.email = 'user' 
    OR Users.username = 'user'
  )