mewc
mewc

Reputation: 1447

Validate user when email and password are in seperate tables cakephp

A form takes in an email and pw

<?= $this->Form->create() ?>
<?= $this->Form->control('email') ?>
<?= $this->Form->control('password') ?>
<?= $this->Form->button('Login') ?>
<?= $this->Form->end() ?>

The email is stored as an ID in Users, and Password is in password table Address is the attribute in Emails table that stores the actual email address Password is where pw is stores

The authenticate component takes in address - which

$this->loadComponent('Auth', [
        'authenticate' => [
            'Form' => [
                'fields' => [
                    //here we define what is compared to be authenticated
                    'username' => 'address',    
                    'password' => 'password'
                ]
            ]...

The login function is like normal:

public function login()
{
    if ($this->request->is('post')) {

        //PUT IN STUFF HERE
        $user = $this->Auth->identify();


        if ($user) {
            $user->last_login = Time::now();//dont put this above uif statement, will automatically create a default object
            $this->Auth->setUser($user);
            $this->Flash->success('You have been successfully logged in.');
            $this->log("Login success", 'debug');
          //redirect after login
            return $this->redirect($this->Auth->redirectUrl('/users/index'));
        }
        $this->Flash->error('Your username or password is incorrect.');
        $this->log("Login FAILURE", 'debug');
    }
}`

How i see it, we either compare email id's or get the form to look directly at the Associated classes 'address' attribute. How does one point the authentication to the attribute in another table like that

Thanks

Upvotes: 3

Views: 630

Answers (2)

ndm
ndm

Reputation: 60503

I would suggest a less intrusive way, that is, using a custom finder that contains/joins the users table, and sets the password field on the main query using an alias, or on the main entity as a virtual field, that way the built-in authenticator retrieves the data it needs, which is all that matters for the authenticator.

For example in your EmailsTable class, add a finder like this, which selects the proper value for the password field:

public function findAuth(\Cake\ORM\Query $query, array $options)
{
    return
        $this
            ->find()
            ->select([
                'Emails.id',
                'Emails.address', // you may want to alias this one too
                'password' => 'Users.password'
            ])
            ->leftJoinWith('Users')
            ->where([
                // the options is always named `username`, this is
                // not affected by the `fields` configuration
                'Emails.address' => $options['username']
            ]);
}

With such a finder, all you then need to do is to configure the fields, userModel, and finder options for the auth component, like:

$this->loadComponent('Auth', [
    'authenticate' => [
        'Form' => [
            'fields' => [
                // set the field to `email`, just like in your form
                'username' => 'email'
            ],
            'userModel' => 'Emails',
            'finder' => 'auth'
        ]
        // ...
    ]
]);

This example assumes that Emails is associated with Users via a belongsTo or hasOne association, using the join strategy.

Also note that the username field is set to email just like in your example form, you could as well set both to address (or anything you like actually), it will not affect the finders query, as it creates a new query, and uses the username value that was extracted from the request data via the configured field (the extracted value will always be passed in the username key of the $options array, unless the finder configuration would be an array that already has a key named username).

See also

Upvotes: 1

Aman Rawat
Aman Rawat

Reputation: 2625

You have to create Custom Authentication Objects for that

On load component

$this->loadComponent('Auth', [
        'authenticate' => [
            'CustomForm' => [
                'fields' => [
                    'username' => 'address',// Field in your emails table
                    'password' => 'password',// Field in your users table
                    'myAssoc'=>'Users'// Custom Filed to get association
                ],
                'userModel' => 'Emails'
            ]...

Create a file CustomFormAuthenticate.php in /src/Auth/ folder

<?php

namespace App\Auth;

use Cake\Auth\FormAuthenticate;
use Cake\Utility\Inflector;

class CustomFormAuthenticate extends FormAuthenticate
{
    public function _findUser($username, $password = null)
    {
        $result = $this->_query($username);
        $myAssoc = false;
        if (!empty($this->_config['fields']['myAssoc'])) {
            $myAssoc = $this->_config['fields']['myAssoc'];
            $result->contain([$myAssoc]);
        }

        $result = $result->first();

        if (empty($result)) {
            return false;
        }

        if ($password !== null) {
            $hasher = $this->passwordHasher();
            if($myAssoc !== false){
                $hashedPassword = $result->{Inflector::underscore(Inflector::singularize($myAssoc))}[$this->_config['fields']['password']];
            } else {
                $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']);
        }
        debug($result);
        return $result->toArray();
    }

}

Make sure you have association of model Users with Email in your EmailTable.php

$this->hasOne('Users', [
    'foreignKey' => 'email_id'
]);

In your login page

<?= $this->Form->create() ?>
<?= $this->Form->control('address') ?> // Field in your email table
<?= $this->Form->control('password') ?>// Field in your users table
<?= $this->Form->button('Login') ?>
<?= $this->Form->end() ?>

I have tested it and its working for me.

Upvotes: 3

Related Questions