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
Download code
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.
Download code
Download code
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).
Download code
Download code
* 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:
Download code
<?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.Download code
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.
Download code
<?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().Download code
<?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).
Download code
<?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.Download code
<?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) {
}
}
Comments
Comment
1 Auth defaults
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.
Comment
2 Reply
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))Comment
3 Works beautifully
Bug
4 AutoLogin Cookie
To replicate, just delete the session cookie.
Comment
5 Whats your cookie settings?
I have yet to receive this problem. What is your expires date, and your cookie settings in your browser?
Comment
6 Redirection problem
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.
Question
7 Visiting page doesnt log me in, not untill I click on a link.
**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()?
Comment
8 Hmm
It runs after beforeFilter() so that all your component settings are set. If you make it run during init, no settings will be applied.
Question
9 Same issue as #4-6
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
Comment
10 Didn't work off the bat
Thanks! I was spending as much time logging in as working.
Comment
11 AutoLogin Cookie
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();
}
?>
Bug
12 headers already sent
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
}?>Question
13 Do you really need a component for this?
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
}
}
?>
Comment
14 Autologin cookie deleted after successful autologin
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();
}
}
Comment
15 Didn't work for me
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
Comment
16 Annoying warning
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?