In my previous entry about SQL authentication with Solar I promised to tell about implementing Persistent Logins (a.k.a "Remember Me" -feature) with Solar. I will use the previous entry as a starting point and add functionality to it, so be sure to read it first. I will first describe the idea and then show how you can implement it on your site.
The code is part of the Lux project that I maintain together with Rodrigo Moraes.
Introduction
By "Persistent Login" people usually mean a system where a user will be re-authenticated back to a site even though his/her session has already expired. Let's look at how it works in practise:
A user logs in to your site and selects "Remember me". If the login is successful, an "auth" cookie is sent to the client along with the normal session cookie.
Let's say the user fills his/her shopping cart with some items. These items are stored in the session data. Let's say the user leaves the shopping cart as it is, never buying or emptying the cart. Finally, user leaves the site.
The user comes back in a day or few (a week maybe). At this point the session has expired. Now the other "auth" cookie is checked. If it has correct info in it, the user is re-authenticated back to the site. User sees his/her cart is empty (because the session had expired), but he is now logged in again. The "auth" cookie is renewed and a new session is started. This brings the user back at situation 2.
Now, let's look at this from a security perspective:
The "auth" cookie contains an identifier and a secret token. The identifier tells to the system who's cookie it is. It is created from the username's hash spiced with some salt. The secret token is a unique sudo-random string. The key here is not to store anything in the cookie that would allow permanent access to an account. That is why the only permanent info stored in there is a username, and even that is heavily hashed.
The cookie can only be used in a specified time-window. In other words, the cookie is not valid after a certain time. You can set this with a config key, more about that in a moment.
After a successful login using a cookie, the info about that cookie is deleted from the server so that it cannot be used ever again.
Even though this is really convenient for users, it is obviously less secure than not having this feature. That is why it is recommended that the system asks for username and password every time user wants to change sensitive information. You could also provide a feature which allows your users to invalidate (delete) all the cookies from the server rendering any "hacked" cookies useless. Maybe a "Log me out from all computers"-feature or something along those lines.
Charles Miller has written a pretty good explanation of the best practice with Persistent Logins. The practice he explains is almost identical to the way our implementation works.
Show me the code!
Solar doesn't have this feature (yet), and you need to grab the persistent login auth adapter from Lux. It extends the SQL auth adapter in Solar and adds a "remember me" feature to it.
First, we need to create a table that holds all the cookie information (identifiers and tokens). A separate table is needed so that one user can have many cookies which can all be used to authenticate with. This allows your users to be remembered on multiple machines.
Here's an example of what that table could look like:
CREATE TABLE `cookies` (
`handle` varchar(15) NOT NULL,
`identifier` varchar(32) default NULL,
`token` varchar(32) NOT NULL default '',
`timeout` int(11) default NULL,
PRIMARY KEY (`handle`,`token`)
);
Next, we need to add a "Remember me" -checkbox to our sign-in form. Here's the complete new form:
<?php
// login.php:
echo $this->form()
->text(array(
'name' => 'handle',
'label' => 'Username',
))
->password(array(
'name' => 'passwd',
'label' => 'Password',
))
->checkbox(array(
'name' => 'remember',
'label' => 'Remember me',
'value' => 0,
))
->submit(array(
'name' => 'process',
'value' => $this->getTextRaw('PROCESS_LOGIN'),
))
->fetch();
?>
Now let's configure the adapter. In my previous entry about SQL-based authentication with Solar I showed a basic configuration you need to have for just the authentication to work. Here's the configuration you need to add for the persistent login adapter:
<?php
$config = array();
// change authentication adapter!!
$config['Solar_Auth']['adapter'] = 'Lux_Auth_Adapter_Psql';
// this key is checked from $_POST to
// see if user wants to be remembered.
// (meaning he clicked "Remember me")
$config['Lux_Auth_Adapter_Psql']['source_persist'] = 'remember';
// table name for cookie info. this is the table we just created
$config['Lux_Auth_Adapter_Psql']['token_table'] = 'cookies';
// column names
$config['Lux_Auth_Adapter_Psql']['token_handle_col'] = 'handle';
$config['Lux_Auth_Adapter_Psql']['token_token_col'] = 'token';
$config['Lux_Auth_Adapter_Psql']['token_identifier'] = 'identifier';
$config['Lux_Auth_Adapter_Psql']['token_timeout_col'] = 'timeout';
// this is the time you want users to be remembered
// (minus the time the session is valid).
// I've set it to one week here.
$config['Lux_Auth_Adapter_Psql']['timeout'] = 604800;
return $config;
?>
What we did there was we changed the auth adapter and then added some configuration params for the new adapter. Here's the combined configuration from the previous entry and the new configuration needed for persistent logins (old first, then new):
<?php
$config = array();
// change authentication adapter!!
$config['Solar_Auth']['adapter'] = 'Lux_Auth_Adapter_Psql';
// use the table we just created
$config['Solar_Auth_Adapter_Sql']['table'] = 'auth';
// set the names of both handle (username) and password column
$config['Solar_Auth_Adapter_Sql']['handle_col'] = 'handle';
$config['Solar_Auth_Adapter_Sql']['passwd_col'] = 'password';
// use some salt for passwords and tokens
$config['Solar_Auth_Adapter_Sql']['salt'] = 'random noice';
// this key is checked from $_POST to
// see if user wants to be remembered.
// (meaning he clicked "Remember me")
$config['Lux_Auth_Adapter_Psql']['source_persist'] = 'remember';
// table name for cookie info. this is the table we just created
$config['Lux_Auth_Adapter_Psql']['token_table'] = 'cookies';
// column names
$config['Lux_Auth_Adapter_Psql']['token_handle_col'] = 'handle';
$config['Lux_Auth_Adapter_Psql']['token_token_col'] = 'token';
$config['Lux_Auth_Adapter_Psql']['token_identifier'] = 'identifier';
$config['Lux_Auth_Adapter_Psql']['token_timeout_col'] = 'timeout';
// this is the time you want users to be remembered
// (minus the time the session is valid).
// I've set it to one week here.
$config['Lux_Auth_Adapter_Psql']['timeout'] = 604800;
return $config;
?>
That's all there is to it. To recap a bit, this setup will allow users to be authenticated against an SQL table (with the help of Solar_Auth_Adapter_Sql) and then be re-authenticated back within a week (with the help of Lux_Auth_Adapter_Psql).
Hope this helps to get started! If you have any further technical questions you can use our mailing list.

Works great Antti! Glad to see the patch made it to SVN. I'll hae another patch to you today that will actually remove the token from the database on logout.