phpauthenticationormcakephp-3.0

Pass extra data to finder auth


My finder from Auth has conditions that I need to access $this->request but I don't have access for that on UsersTable.

AppController::initialize

$this->loadComponent('Auth', [
        'authenticate' => [
            'Form' => [
                'finder' => 'auth',
            ]
        ]
    ]);

UsersTable

public function findAuth(Query $query, array $options)
{
    $query
        ->select([
            'Users.id',
            'Users.name',
            'Users.username',
            'Users.password',
        ])
        ->where(['Users.is_active' => true]); // If I had access to extra data passed I would use here.

    return $query;
}

I need pass extra data from AppController to finder auth since I don't have access to $this->request->data on UsersTable.

Update

People are saying on comments that is a bad design so I will explain exactly what I need.

I have a table users but each user belongs to a gym. The username(email) is unique only to a particular gym so I can have a example@domain.comfrom gym_id 1 and another example@domain.com from gym_id 2. On login page I have the gym_slug to tell to auth finder which gym the user username that I provided belongs.


Solution

  • To my knowledge, there is no way to do this by passing it into the configuration in 3.1. This might be a good idea submit on the cakephp git hub as a feature request.

    There are ways to do it by creating a new authentication object that extends base authenticate and then override _findUser and _query. Something like this:

    class GymFormAuthenticate extends BaseAuthenticate
    {
    
     /**
      * Checks the fields to ensure they are supplied.
      *
      * @param \Cake\Network\Request $request The request that contains login information.
      * @param array $fields The fields to be checked.
      * @return bool False if the fields have not been supplied. True if they exist.
      */
     protected function _checkFields(Request $request, array $fields)
     {
         foreach ([$fields['username'], $fields['password'], $fields['gym']] as $field) {
             $value = $request->data($field);
             if (empty($value) || !is_string($value)) {
                 return false;
             }
         }
         return true;
     }
    
     /**
      * Authenticates the identity contained in a request. Will use the `config.userModel`, and `config.fields`
      * to find POST data that is used to find a matching record in the `config.userModel`. Will return false if
      * there is no post data, either username or password is missing, or if the scope conditions have not been met.
      *
      * @param \Cake\Network\Request $request The request that contains login information.
      * @param \Cake\Network\Response $response Unused response object.
      * @return mixed False on login failure.  An array of User data on success.
      */
     public function authenticate(Request $request, Response $response)
     {
         $fields = $this->_config['fields'];
         if (!$this->_checkFields($request, $fields)) {
             return false;
         }
         return $this->_findUser(
             $request->data[$fields['username']],
             $request->data[$fields['password']],
             $request->data[$fields['gym']],
         );
     }
    
    /**
      * Find a user record using the username,password,gym provided.
      *
      * Input passwords will be hashed even when a user doesn't exist. This
      * helps mitigate timing attacks that are attempting to find valid usernames.
      *
      * @param string $username The username/identifier.
      * @param string|null $password The password, if not provided password checking is skipped
      *   and result of find is returned.
      * @return bool|array Either false on failure, or an array of user data.
      */
     protected function _findUser($username, $password = null, $gym = null)
     {
         $result = $this->_query($username, $gym)->first();
    
         if (empty($result)) {
             return false;
         }
    
         if ($password !== null) {
             $hasher = $this->passwordHasher();
             $hashedPassword = $result->get($this->_config['fields']['password']);
             if (!$hasher->check($password, $hashedPassword)) {
                 return false;
             }
    
             $this->_needsPasswordRehash = $hasher->needsRehash($hashedPassword);
             $result->unsetProperty($this->_config['fields']['password']);
         }
    
         return $result->toArray();
     }
    
    /**
      * Get query object for fetching user from database.
      *
      * @param string $username The username/identifier.
      * @return \Cake\ORM\Query
      */
     protected function _query($username, $gym)
     {
         $config = $this->_config;
         $table = TableRegistryget($config['userModel']);
    
         $options = [
             'conditions' => [$table->aliasField($config['fields']['username']) => $username, 'gym' => $gym]
         ];
    
         if (!empty($config['scope'])) {
             $options['conditions'] = array_merge($options['conditions'], $config['scope']);
         }
         if (!empty($config['contain'])) {
             $options['contain'] = $config['contain'];
         }
    
         $query = $table->find($config['finder'], $options);
    
         return $query;
     }
     }
    

    For more information see this: Creating Custom Authentication Objects