I'm going through the CakePHP tutorial and trying to test basic login functionality. I'm making slight tweaks along the way to match how my database needs to look (email and token instead of username and password as columns in the users table), I believe that I have messed something up when it comes to using Blowfish hashing. Can someone take a look and see if anything apparent pops out? Right now I can add new users, but their password in the database look to be plaintext. The token column is of type VARCHAR(75), is that enough space for Blowfish to work?
I'm getting the error:
**Warning (512): Invalid salt: pass for blowfish **
and then "Invalid username or password," when putting in a correct user/pass combo. When I put in incorrect credentials I only get the invalid user/pass error, so it looks like it is still getting through somewhere along the line.
app/Model/User.php
App::uses('AppModel', 'Model');
App::uses('BlowfishPasswordHasher', 'Controller/Component/Auth');
class User extends AppModel {
public $validate = array(
'email' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'An email is required'
)
),
'token' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A password is required'
)
),
'group' => array(
'valid' => array(
'rule' => array('inList', array('user', 'admin', 'manager')),
'message' => 'Please enter a valid group role',
'allowEmpty' => false
)
)
);
public function beforeSave($options = array()) {
if (isset($this->data[$this->alias]['token'])) {
$passwordHasher = new BlowfishPasswordHasher();
$this->data[$this->alias]['token'] = $passwordHasher->hash(
$this->data[$this->alias]['token']
);
}
return true;
}
}
app/Controller/AppController.php
class AppController extends Controller {
//...
public $components = array(
'Session',
'Auth' => array(
'loginRedirect' => array(
'controller' => 'posts',
'action' => 'index'
),
'logoutRedirect' => array(
'controller' => 'pages',
'action' => 'display',
'home'
),
'authenticate' => array(
'Form' => array(
'passwordHasher' => 'Blowfish',
'fields' => array('username' => 'email', 'password' => 'token')
)
)
)
);
public function beforeFilter() {
$this->Auth->allow('index', 'view');
}
//...
}
add.ctp
<div class="users form">
<?php echo $this->Form->create('User'); ?>
<fieldset>
<legend><?php echo __('Add User'); ?></legend>
<?php echo $this->Form->input('email');
echo $this->Form->input('token');
echo $this->Form->input('group', array(
'options' => array('admin' => 'Admin', 'manager' => 'Manager', 'user' => 'User')
));
?>
</fieldset>
<?php echo $this->Form->end(__('Submit')); ?>
</div>
login.ctp
<div class="users form">
<?php echo $this->Session->flash('auth'); ?>
<?php echo $this->Form->create('User'); ?>
<fieldset>
<legend>
<?php echo __('Please enter your username and password'); ?>
</legend>
<?php echo $this->Form->input('email');
echo $this->Form->input('token');
?>
</fieldset>
<?php echo $this->Form->end(__('Login')); ?>
</div>
Check the blowfish salt to make sure it has the correct number of characters, and use the add / edit form to set the password initally.
You should also set the token length in the db to 256 chars
Related
So, I'm trying to make an authentication in CakePHP that is using email instead of username, which field I don't even have in my users table in database. first i was trying to google that, and i already have tried these:
Cakephp 2.0 Authentication using email instead of username
Using email instead of username in CakePHP Auth Component
and few others, but nothing seems to work - still i get the "incorrect data" error. below is my code
AppController.php
class AppController extends Controller {
public $components = array(
'Session',
'Auth' => array(
'userModel' => 'User',
'loginRedirect' => array(
'controller' => 'pages',
'action' => 'display'
),
'logoutRedirect' => array(
'controller' => 'pages',
'action' => 'display',
'home'
),
'authenticate' => array(
'Form' => array(
'fields' => array(
'username' => 'email',
'password' => 'password'
),
'passwordHasher' => 'Blowfish'
)
)
)
);
public function beforeFilter(){
$this->Auth->allow('index', 'view');
}
UsersController.php
public function add() {
if ($this->request->is('post')) {
$this->User->create();
$this->request->data['User']['role'] = "user";
if(strcmp($this->request->data['User']['password'], $this->request->data['User']['repeat']) == 0){
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('Użytkownik zarejestrowany.'));
return $this->redirect(array('controller' => 'Pages', 'action' => 'display'));
}
else $this->Session->setFlash(__('Wystąpił błąd, spróbuj ponownie.'));
}
else $this->Session->setFlash(__('Wpisane hasła się nie zgadzają'));
}
}
public function login(){
if($this->request->is('post')){
if($this->Auth->login()){
return $this->redirect($this->Auth->redirectUrl());
}
$this->Session->setFlash(__('Nieprawidłowe dane, spróbuj ponownie'.$this->Auth->login()));
}
}
login.ctp
<div class="users form">
<?php echo $this->Session->flash('auth'); ?>
<?php echo $this->Form->create('User'); ?>
<fieldset>
<legend>
<?php echo __('Zaloguj się'); ?>
</legend>
<?php
echo $this->Form->input('email' /*I also tried with username instead of email here*/, array('label' => 'Email'));
echo $this->Form->input('password', array('label' => 'Hasło'));
?>
</fieldset>
<?php echo $this->Form->end(__('Zaloguj')); ?>
</div>
EDIT: Here is User.php
App::uses('AppModel', 'Model');
App::uses('BlowfishPasswordHasher', 'Controller/Component/Auth');
class User extends AppModel {
public $validate = array(
'email' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'Email jest wymagany'
)
),
'password' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'Hasło jest wymagane'
)
),
'repeat' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'Powtórz hasło'
)
),
'identicalFieldValues' => array(
'rule' => 'identicalFieldValues',
'message' => 'Wpisane hasła się nie zgadzają'
)
);
public function identicalFieldValues(){
return $this->data['User']['password'] === $this->data['User']['repeat'];
}
public function beforeSave($options = array()){
if(isset($this->data[$this->alias]['password'])){
$passwordHasher = new BlowfishPasswordHasher();
$this->data[$this->alias]['password'] = $passwordHasher->hash(
$this->data[$this->alias]['password']
);
}
return true;
}
}
2nd EDIT: I read that the problem can be in size of the varchar in sql, so I changed it from 50 to 130, deleted users, made a new one, but still I cannot log in.
3rd EDIT: I made a completly new, clean cake project, without password hashing, but still with login via email and still im getting "incorrect data" -.-
I'm new to CakePHP I tried to follow the sample authentication and login in CakePHP documentation and I still cannot login.
I saw same issues in others but I tried to work on it like they did but didn't work out for me. I can't login even if I am providing the correct credentials but $this->Auth->login() is returning false.
Here's my code
<?php
//UsersController
public function add() {
if ($this->request->is('post')) {
$this->User->create();
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('The user has been saved'));
return $this->redirect(array('action' => 'index'));
}
$this->Session->setFlash(
__('The user could not be saved. Please, try again.')
);
}
}
public function beforeFilter() {
parent::beforeFilter();
// Allow users to register and logout.
$this->Auth->allow('add', 'logout');
parent::beforeFilter();
}
public function login() {
if ($this->request->is('post')) {
debug($this->Auth->login());
if ($this->Auth->login()) {
return $this->redirect($this->Auth->redirect());
}
$this->Session->setFlash(__('Invalid username or password, try again'));
}
}
//User Model
public $validate = array(
'username' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A username is required'
)
),
'password' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A password is required'
)
),
'role' => array(
'valid' => array(
'rule' => array('inList', array('admin', 'author')),
'message' => 'Please enter a valid role',
'allowEmpty' => false
)
)
);
public function beforeSave($options = array()) {
if (isset($this->data[$this->alias]['password'])) {
$passwordHasher = new BlowfishPasswordHasher();
$this->data[$this->alias]['password'] = $passwordHasher->hash(
$this->data[$this->alias]['password']
);
}
return true;
}
// Login View
<div class="users form">
<?php echo $this->Session->flash('auth'); ?>
<?php echo $this->Form->create('User'); ?>
<fieldset>
<legend>
<?php echo __('Please enter your username and password'); ?>
</legend>
<?php echo $this->Form->input('username');
echo $this->Form->input('password');
?>
</fieldset>
<?php echo $this->Form->end(__('Login')); ?>
</div>
?>
it seems that you did not declared Auth Component. You can do that by simply refering to my sample code.
public $components = array('Auth' => array('authenticate' => array(
'Form' => array(
'userModel' => **YOUR MODEL**
)
),
'loginAction' => array(
'controller' => **YOUR CONTROLLER**,
'action' => 'login'
),
'loginRedirect' => array(
'controller' => **YOUR CONTROLLER**,
'action' => 'index'
),
'logoutRedirect' => array(
'controller' => **YOUR CONTROLLER**,
'action' => 'login'
)
)
);
You may want to read first the documentation or follow their blog tutorials because that helped me alot when I first started. :)
Cakephp's Documentation
Change this function:
public function beforeFilter() {
parent::beforeFilter();
// Allow users to register and logout.
$this->Auth->allow('add', 'logout', 'login'); //Add login
}
Update your AppController like that
public $components = array(
'Auth' => array(
'authenticate' => array(
'Form' => array(
'passwordHasher' => 'Blowfish'
)
)
)
);
Been trying to figure this out for plenty of hours, but without success.
$this->request->data['Login'] array contains the right username and hash which matches the entry in the database. $this->Auth->login(); is always returning false for some reason.
The Login table has a column called UserID and Password, Password is hashed using MD5 and a salt which is stored in the table Login as well.
I tried adding a beforeSave function in the Login model which hashed the password, but this didn't seem to work either?
/app/Controller/AppController.php
<?php
App::uses('Controller', 'Controller');
class AppController extends Controller {
public $helpers = array('App', 'Html', 'Form', 'Session');
public $components = array(
'Session',
'Auth' => array(
'loginAction' => array('controller' => 'login', 'action' => 'authenticate'),
'loginRedirect' => array('controller' => 'account', 'action' => 'index'),
'logoutRedirect' => array('controller' => 'home', 'action' => 'index'),
'authenticate' => array('Form' => array('userModel' => 'Login', 'fields' => array('username' => 'UserID', 'password' => 'Password'))))
);
// Execute on every page load.
public function beforeFilter(){
$this->Auth->allow('index', 'view');
}
}
/app/Controller/LoginController.php
<?php
class loginController extends AppController{
public $uses = array('Login');
public function index(){
$this->render('/login');
}
public function authenticate(){
if($this->request->is('post')){
$this->request->data['Login']['password'] = $this->hashPassword($this->data['Login']);
if($this->Auth->login()){
return $this->redirect($this->Auth->redirect());
}
$this->Session->setFlash('Invalid username or password.');
return $this->redirect('/login');
}
}
private function hashPassword($login){
// Get the salt of the user.
$salt = $this->Login->find('first', array(
'fields' => array(
'Salt'),
'conditions' => array(
'Login.UserID' => $login['username'])));
return md5(md5($login['password']) . $salt['Login']['Salt']);
}
}
/app/Model/Login.php
<?php
class Login extends AppModel{
public $useTable = 'Login';
public $validate = array(
'username' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'Please fill in all of the fields.'
)
),
'password' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'Please fill in all of the fields.'
)
));
}
/app/View/login.ctp
<?php
echo $this->Session->flash('flash', array('element' => 'fail'));
echo $this->Form->create('Login', array('url' => '/login/authenticate'));
echo $this->Form->input('username', array('label' => false, 'div' => false, 'autocomplete' => 'off'));
echo $this->Form->input('password', array('label' => false, 'div' => false));
echo $this->Form->end('Login');
?>
Per the incredibly detailed example in the CakePHP book:
http://book.cakephp.org/2.0/en/tutorials-and-examples/blog-auth-example/auth.html
You shouldn't be manually hashing your password prior to submitting.
Alright I solved it:
Changed the field names in the login view file to match the column names in the database.
Added a custom PasswordHasher
I have a very simple model User, and it's associated table users with fields 'id', 'username', 'password', 'role', 'created', 'modified'.
The view add.ctp contains:
<div class="users form">
<?= $this->Form->create('user'); ?>
<fieldset>
<legend><?= __('Add user');?></legend>
<?= $this->Form->input('user.username'); ?>
<?= $this->Form->input('password'); ?>
<?= $this->Form->input('role', array('options' => array('admin' => 'Admin', 'customer' => 'Customer'))); ?>
</fieldset>
<?= $this->Form->end(__('Submit')); ?>
</div>
The model is set up according to http://book.cakephp.org/2.0/en/tutorials-and-examples/blog-auth-example/auth.html:
class User extends AppModel {
public $validate = array(
'username' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'You must specify a username'
)
),
'password' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'You must specify a password'
)
),
'role' => array(
'valid' => array(
'rule' => array('inList', array('admin','customer')),
'message' => 'You must specify a valid role',
'allowEmpty'=> false
)
)
);
}
And lastly, the controller simply has:
public function add() {
if($this->request->is('post')) {
$this->User->create();
if ($this->User->save($this->request->data)) {
$this->set('stuff', $this->User->data);
$this->Session->setFlash(__('The user has been saved.'));
//return $this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The user could not be saved. Please try again.'));
}
}
}
I've commented out the redirect on success to be able to show the INSERT query on submit (if I allow the redirect, the only query being shown is SELECT's)
So, quite simple, the problem is, when the form is submitted, it generates this query:
INSERT INTO `mytable`.`users` (`modified`, `created`) VALUES ('2014-02-21 13:03:11', '2014-02-21 13:03:11')
How can I determine why it won't insert the submitted fields? And how can I make it?
use this <?= $this->Form->create('User'); ?> instead of <?= $this->Form->create('user'); ?>
and use this <?= $this->Form->input('User.username'); ?> instead of <?= $this->Form->input('user.username'); ?>
CakePHP is case sensitive model name must be in CamalCase
I have built a simple CakePHP app with a users login system and have hooked up the Cake Form plugin by Milesj.me (not sure if that's causing the problems).
However my validation seems to have applied itself to the login form as well as the signup form. So when I try and login, I am getting errors like 'Username already in use'.
Any ideas what would cause this? Has something changed in CakePHP that adds the validation to authentication forms as well?
Also why am I having to hash the password in the model? I was under the impression that CakePHP hashed passwords automatically? And I've not needed to do it before. However If I don't do it, then it was saving the password in the DB as in and not hashed...
Here is my view, controller and model:
<?php echo $this->Form->create(); ?>
<?php echo $this->Form->input('User.username',
array('tabindex'=>1, 'autofocus',
'label'=>array('class'=>'placeholder','text'=>'Username'))); ?>
<?php echo $this->Form->input('User.password',
array('tabindex'=>2, 'type'=>'password',
'label'=>array('class'=>'placeholder','text' =>'Password' ))); ?>
<div class="input button">
<button class="orangeButton" tabindex="3" type="submit"><span class="icon login">Log in</span></button>
</div>
<?php echo $this->Form->end(); ?>
controller:
public function login() {
if ($this->request->data) {
$this->User->set($this->request->data);
if ($this->User->validates() && $this->Auth->login()) {
if ($user = $this->Auth->user()) {
$this->User->Profile->login($user['id']);
$this->Session->delete('Forum');
$this->redirect($this->referer());
}
}
}
}
Note: the calls to Profile model for login just saves some data for when last logged in and other stuff and doesn't actually do anything regarding authentication!
and the model:
class User extends AppModel {
public $name = 'User';
public $hasOne = array(
'Profile' => array('className' => 'Forum.Profile')
);
public $hasMany = array(
'Access' => array('className' => 'Forum.Access'),
'Moderator' => array('className' => 'Forum.Moderator')
);
public $validate = array(
'email' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A valid email address is required'
),
'email' => array(
'rule' => array('email'),
'message' => 'This is not a valid email address'
),
'unique' => array(
'rule' => array('isUnique'),
'message' => 'This email is already in use'
)
),
'username' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A username is required'
),
'unique' => array(
'rule' => array('isUnique'),
'message' => 'This username is already in use'
),
'alphaNumeric' => array(
'rule' => array('alphaNumeric'),
'message' => 'Usernames must only contain letters and numbers'
),
'between' => array(
'rule' => array('between', 4, 20),
'message' => 'Usernames must be between 4 and 20 characters long'
)
),
'password' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A password is required'
)
)
);
public function beforeSave()
{
if (isset($this->data[$this->alias]['password']))
{
$this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']);
}
return true;
}
Just add to your validation rules 'on' => 'create'
http://book.cakephp.org/2.0/en/models/data-validation.html#on
As per cakephp
In case of multiple rules per field by default if a particular rule
fails error message for that rule is returned and the following rules
for that field are not processed.
But if first rule doesn't fail, then it continue to evaluate second
rule.
You can remove particular validation by following way
$this->validator()->remove('username', 'unique');