AutoLogin Component - An Auth remember me feature
A user can save their login information by ticking off a checkbox in the login form and AutoLogin will store their information in a cookie to automatically log them in (using the Auth Component) on their next visit.
Below are a list of features present within the AutoLogin. Also thanks to all the other authors with similar scripts.
* Requires no installation except for adding the checkbox into your user login forms
* Automatically saves the cookie and info when a user logs in
* Automatically kills the cookie and session when a user logs out
* Inserts a hash within the cookie so that it cannot be hijacked
* Encrypts the cookie so the information cannot be harvested
* Configuration options for cookie name and length
* Functionality for additional user updating or error logging
The AutoLogin component will automatically save the user info to a cookie when they login at users/login/. It also works when logging out at users/logout/, by removing the cookie.
The final step is to create a checkbox in your login form named auto_login. The model used in the form should also match the User model you are using in your Auth.
If for some reason the controller name and the login/logout action names are not default (whats based in Auth), you can change them in the $settings array (in beforeFilter() of course).
* Requires no installation except for adding the checkbox into your user login forms
* Automatically saves the cookie and info when a user logs in
* Automatically kills the cookie and session when a user logs out
* Inserts a hash within the cookie so that it cannot be hijacked
* Encrypts the cookie so the information cannot be harvested
* Configuration options for cookie name and length
* Functionality for additional user updating or error logging
Code
You can view the original documentation and newer versions here!Component Class:
<?php
/**
* auto_login.php
*
* A CakePHP Component that will automatically login the Auth session for a duration if the user requested to (saves data to cookies).
*
* Copyright 2006-2009, Miles Johnson - www.milesj.me
* Licensed under The MIT License - Modification, Redistribution allowed but must retain the above copyright notice
* @link http://www.opensource.org/licenses/mit-license.php
*
* @package AutoLogin Component
* @created May 27th 2009
* @version 1.4
* @link www.milesj.me/resources/script/auto-login
* @changelog www.milesj.me/files/logs/auto-login
*/
class AutoLoginComponent extends Object {
/**
* Current version: www.milesj.me/files/logs/auto-login
* @var string
*/
var $version = '1.4';
/**
* Components
* @var array
*/
var $components = array('Cookie');
/**
* Cookie name
* @var string
*/
var $cookieName = 'autoLogin';
/**
* Cookie length (strtotime())
* @var string
*/
var $expires = '+2 weeks';
/**
* Settings
* @var array
*/
var $settings = array(
'controller' => '',
'loginAction' => '',
'logoutAction' => ''
);
/**
* Automatically login existent Auth session; called after controllers beforeFilter() so that Auth is initialized
* @param object $Controller
* @return void
*/
function startup(&$Controller) {
$cookie = $this->Cookie->read($this->cookieName);
if (!is_array($cookie) || $Controller->Auth->user()) {
return;
}
if ($cookie['hash'] != $Controller->Auth->password($cookie[$Controller->Auth->fields['username']] . $cookie['time'])) {
$this->delete();
return;
}
if ($Controller->Auth->login($cookie)) {
if (in_array('_autoLogin', get_class_methods($Controller))) {
call_user_func_array(array(&$Controller, '_autoLogin'), array($Controller->Auth->user()));
}
} else {
if (in_array('_autoLoginError', get_class_methods($Controller))) {
call_user_func_array(array(&$Controller, '_autoLoginError'), array($cookie));
}
}
return true;
}
/**
* Automatically process logic when hitting login/logout actions
* @param object $Controller
* @param array $url
* @param boolean $status
* @param boolean $exit
* @return void
*/
function beforeRedirect(&$Controller, $url, $status = null, $exit = true) {
$controller = $this->settings['controller'];
$loginAction = $this->settings['loginAction'];
$logoutAction = $this->settings['logoutAction'];
if (is_array($Controller->Auth->loginAction)) {
if (!empty($Controller->Auth->loginAction['controller'])) {
$controller = Inflector::camelize($Controller->Auth->loginAction['controller']);
}
if (!empty($Controller->Auth->loginAction['action'])) {
$loginAction = $Controller->Auth->loginAction['action'];
}
}
if (!empty($Controller->Auth->userModel) && empty($controller)) {
$controller = Inflector::pluralize($Controller->Auth->userModel);
}
if (empty($loginAction)) {
$loginAction = 'login';
}
if (empty($logoutAction)) {
$logoutAction = 'logout';
}
// Is called after user login/logout validates, but befire auth redirects
if ($Controller->name == $controller) {
$data = $Controller->data;
switch ($Controller->action) {
case $loginAction:
$username = $data[$Controller->Auth->userModel][$Controller->Auth->fields['username']];
$password = $data[$Controller->Auth->userModel][$Controller->Auth->fields['password']];
if (!empty($username) && !empty($password) && $data[$Controller->Auth->userModel]['auto_login'] == 1) {
$this->save($username, $password, $Controller);
} else if ($data[$Controller->Auth->userModel]['auto_login'] == 0) {
$this->delete();
}
break;
case $logoutAction:
$this->delete();
break;
}
}
}
/**
* Remember the user information
* @param string $username
* @param string $password
* @param object $Controller
* @return void
*/
function save($username, $password, $Controller) {
$time = time();
$cookie = array();
$cookie[$Controller->Auth->fields['username']] = $username;
$cookie[$Controller->Auth->fields['password']] = $password; // already hashed from auth
$cookie['hash'] = $Controller->Auth->password($username . $time);
$cookie['time'] = $time;
$this->Cookie->write($this->cookieName, $cookie, true, $this->expires);
}
/**
* Delete the cookie
* @return void
*/
function delete() {
$this->Cookie->del($this->cookieName);
}
}?>
Installation
If you haven't already, grab the script above and place the code in a file called auto_login.php within your app/controllers/components/ folder. Once you have done that, simply add AutoLogin into your controllers $components property. AutoLogin must be placed before Auth in the $components array or it will not work properly.
var $components = array('AutoLogin', 'Auth');
The AutoLogin component will automatically save the user info to a cookie when they login at users/login/. It also works when logging out at users/logout/, by removing the cookie.
The final step is to create a checkbox in your login form named auto_login. The model used in the form should also match the User model you are using in your Auth.
<?php echo $form->input('auto_login', array('type' => 'checkbox', 'label' => 'Log me in automatically?')); ?>
Configuration
If you would like to change the name of the cookie, or the duration until the cookie expires (defaults to 2 weeks), you can change it in your AppController's beforeFilter().
<?php
function beforeFilter() {
$this->AutoLogin->cookieName = 'rememberMe';
$this->AutoLogin->expires = '+1 month';
}
If for some reason the controller name and the login/logout action names are not default (whats based in Auth), you can change them in the $settings array (in beforeFilter() of course).
<?php
$this->AutoLogin->settings = array(
'controller' => 'Members',
'loginAction' => 'signin',
'logoutAction' => 'signout'
);
Adding your own logic or logging
If you need to do additional logging and updating that is not initially in Auths user login (for example updating a users last login time), you can place this extra code in a method called _autoLogin() within your AppController. Also if Auth login fails, you can do some error logging and reporting by creating a method called _autoLoginError(). Both of these will be called automatically and only if the method exists.
<?php
class AppController extends Controller {
/**
* Run whenever auto login is successful
* @param array $user - The Auth user session
* @access private
*/
function _autoLogin($user) {
}
/**
* Run whenever auto login fails
* @param array $cookie - The login cookie data
* @access private
*/
function _autoLoginError($cookie) {
}
}








$autoLogin = (isset($data[$Controller->Auth->userModel]['auto_login'])) ? $data[$Controller->Auth->userModel]['auto_login'] : 1;
so if we have data send from login form $autoLogin var will be = with checkbox value and else $autoLogin is 1
thanks for the component. I had a problem with $this->Cookie->del(), which did not work for me (CakePHP 1.3.2). I changed it to $this->Cookie->destroy(), what worked out.
Best wishes, Mathias Wellner
I had the same issue and it was gone when I added the Cookie component to my controller
I had the same issue and it was gone when I added the Cookie component to my controller
[end quote]
You can fix this by adding a 3rd param of false when you're setting/writing your cookie. So...
$this->Cookie->write('key', 'value', false);
This tells the Cookie component to not use any encryption
I tried the component today.
First, thanks for the work done, really useful.
Second, I found is a very very annoying warning :
Warning (512): You cannot use an empty key for Security::cipher()
coming from the writing of the cookie :
$this->Cookie->write($this->cookieName, $cookie, true, $this->expires);
Anybody ever experienced this problem?
Does anybody know how to solve this issue?
I'm pretty sure I did everything correctly. I put the AutoLogin component before the Auth in the AppController. I have a user model that has a username and password fields. But when I installed the autologin script I got the following three lines:
Notice (8): Undefined index: auto_login [APP\controllers\components\auto_login.php, line 128]
Notice (8): Undefined index: auto_login [APP\controllers\components\auto_login.php, line 130]
Warning (2): Cannot modify header information - headers already sent by (output started at C:\Users\david\Documents\sites\zipnit\cake\basics.php:109) [CORE\cake\libs\controller\controller.php, line 644
Controller Class:
<?php
class UsersController extends AppController {
function beforeFilter() {
// set this to have a custom login function
$this->Auth->autoRedirect = false;
}
function login() {
if (empty($this->data)) {
// read Auth Cookie and login
}
if ($this->Auth->user('id')) {
if ($this->data['User']['remember_me']) {
// set the cookie
}
}
}
function logout() {
// delete cookie
}
}
?>
Warning (2): Cannot modify header information - headers already sent by (output started at /var/www/mydomain/app/controllers/components/auto_login.php:167)this appears when i try to login.
my login works fine before ;-)
line 167 is the last line of the script with
}?>Thanks! I was spending as much time logging in as working.
I've got the same problem as Daniel Bard did above. Everything works great, but then I delete my PHP Session cookie. AutoLogin logs me back in properly, but also deletes the Cake[AutoLogin] cookie. So, if I repeat the delete-session-cookie event, I no longer have a valid autologin cookie.
Doing some investigating, it's on line 127 of auto_login.php that is causing this error. It seems my controller->data is null, which makes AutoLogin think that the cookie should be deleted, and so it does so. Note that $Controller inside the beforeRedirect method isn't null -- just the data attribute. Commenting out line 128 ($this->delete()) gets me the desired functionality, but I also think that it will cause all my users to autologin, whether or not they like it.
Any advice?
Thanks,
Travis
**UPDATE: It appears its because this component runs AFTER beforeFilter(), where I set user settings and do some Auth work. How would I go about changing the component to work with an "initialize()" method instead, so as to run BEFORE beforeFilter()?
**UPDATE: It appears its because this component runs AFTER beforeFilter(), where I set user settings and do some Auth work. How would I go about changing the component to work with an "initialize()" method instead, so as to run BEFORE beforeFilter()?
[end quote]
I also have the same problem, did some body solve this problem?
It runs after beforeFilter() so that all your component settings are set. If you make it run during init, no settings will be applied.
To replicate, just delete the session cookie.
I have yet to receive this problem. What is your expires date, and your cookie settings in your browser?
I have the same issue. Reproduction:
- I login and the cookie is nicely set with an expiration 2 week down the line.
- I select a page that has no public access
- I delete the session cookie
- I refresh the page, get a security warning that I have no access
- the AutoLogin cooke is gone, there is a new session cookie
- I refesh the page again, the page shows with full access
So, the cookie is set, the cookie is accepted but is deleted once accepted...
I traced the issue to the component, function BeforeRedirect:
Model Class:
<?php if (!empty($username) && !empty($password) && $data[$Controller->Auth->userModel]['auto_login'] == 1) {
The value of $data[$Controller->Auth->userModel]['auto_login'] seems to be blanc (not zero), but it does enter this 'else' block.$this->save($username, $password, $Controller);
} else if ($data[$Controller->Auth->userModel]['auto_login'] == 0) {
$this->delete();
}
?>
In AutoLogin Component v1.6:
try to replace code in rows 144-146:
if (!empty($username) && !empty($password) && $autoLogin == 1) {
$this->save($username, $password, $Controller);
} else if ($autoLogin == 0) {
$this->delete();
}
by these lines:
if (!empty($username) && !empty($password)) {
if ($autoLogin) {
$this->save($username, $password, $Controller);
} else {
$this->delete();
}
}
Sorry for not replying sooner, I never received an email when the comment was posted.
I've fixed the problem, I think it was how I was redirecting in my login action which was causing problems.
There is another problem though, if the user is accessing a protected area when the autologin occurs, the user is redirected back to the login box. Pressing F5 and the correct page is displayed.
I went on IRC for help and ADmad said "the autologin component is added after auth in the components list... so the startup method of auth compoent (which checks is logged in else redirects) is executed before the autologin components startup (which checks for cookie and logs in).. you must be encountering this only when you try to access a protected page directly first time in a browser session, right ?"
Everything is at defaults by the way.
Edit: It does seem like if you switch the two components around so that AutoLogin is first, it starts to work correctly.
I find it a bit weird that you only get the Auth default settings if the AutoLogin settings are empty, which, by default, are not.
So, in my application, I'd have to configure my controller and login action twice: once for the AuthComponent, and once for the AutoLoginComponent.
I've noticed, as well, that the "name" property of a controller is camelcase, and you compare it with an underscored word, and so the condition is always going to be false.
If you like my suggestions, here's a patch (which could probably be improven):
--- a/app/controllers/components/auto_login.php
+++ b/app/controllers/components/auto_login.php
@@ -46,9 +46,9 @@ class AutoLoginComponent extends Object {
* @var array
*/
var $settings = array(
- 'controller' => 'Users',
- 'loginAction' => 'login',
- 'logoutAction' => 'logout'
+ 'controller' => null,
+ 'loginAction' => null,
+ 'logoutAction' => null
);
/**
@@ -105,7 +105,7 @@ class AutoLoginComponent extends Object {
}
if (!empty($Controller->Auth->userModel) && empty($controller)) {
- $controller = Inflector::underscore(Inflector::pluralize($Controller->Auth->userModel));
+ $controller = Inflector::pluralize($Controller->Auth->userModel);
}
if (empty($loginAction)) {
--
Thanks.
Thanks Javier, you are correct about the first part, the settings should be empty.
As for the second part about inflecting the Controller name, I got it exactly from the Auth component. Its within __setDefaults(), line 422. But then again, I guess it wont match $this->name.
'controller'=> Inflector::underscore(Inflector::pluralize($this->userModel))Its an improvement but you still have login/logout action names hardcoded. Just as you use Auth->loginAction to check for controller name same way check for action name too and similarly for logout check Auth->logoutAction. Also this code
$controller = ucfirst(strtolower($this->Auth->loginAction['controller']));will produce incorrect controller name for multi-worded controller name, so please fix that.if ($controller->name == 'Users')Please make it customizable so that i can be used with any controllerComments are closed for articles over a year old