Reputation: 1447
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
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
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