For TaskTHIS, I've implemented a simple authentication cache. The "Remember me..." checkbox on the login form. I'm going to present to you, good or bad, how I decided to design it.
After looking at how it's done in other technologies and how secure (or actually insecure) it is, I decided to try something a little different. My goals were to 1) avoid setting cookies with any personal information, and 2) make it perishable. Meaning, I wanted the cookie value itself to expire.
High Level
When "Remember Me" is selected on the login screen the application creates a unique token and saves it into a cookie. The token is called a remembrall. The remembrall is essentially an SHA hash of the user email and the exact time the remembrall was created. In addition, the application also generates an expiration date for the remembrall. So it's only a valid token for a short while, satisfying my second goal.
It may appear that I have already broken my first goal, I have used the user's email address in the token. Looking back, I probably should have used the USER record's ID (usually just an integer that's unique within the system) as the information that's unique to the user. However, since it's hashing the email and timestamp, I believe it satisfactorily meets the first goal.
At this point, we need a way to recognize the remembrall on pages that are protected, and automatically log the user into the system if the remembrall is still 'fresh'. To accomplish this, TaskTHIS uses a filter that runs before every page is loaded. The filter checks to see if there's a remembrall set and if it's valid. The flow looks a little something like this:
Whew, that was a mouthful.
Less High Level
So let's look at some code, eh?
The initial user authentication was created with the excellent login_generator. What follows build upon that foundation.
We need to modify the User so that the remembrall can be saved. So, in Migration format, the user gets:
add_column :users, :remembrall, :string, :length=>40
add_column :users, :remembrall_expires, :datetime
One of the results of having an automatic login process is that the login procedure can be called from two different places. Each with a different destination after the successful login. What does that mean? It means I needed to abstract the code for logging in a user. For TaskTHIS, I put that into the AutoLogin module. I wouldn't really recommend handling it this way, in the future TaskTHIS won't. I think it should really be a protected method in your ApplicationController.
In the login process, TaskTHIS keeps track of the last time a user logs in. Also, each user's settings are stored as a YAML string. The login code needs to handle all of this, the code looks like this:
def handle_login( user )
@session[:user] = user
@session[:user_prefs] = YAML::load(user.prefs)
user.last_login = Time.now
user.save
end
The following is the filtering code used with :before_filter in the ApplicationController:
def login_from_cookie
user = User.find_by_remembrall( cookies[:remembrall] ) if cookies[:remembrall] and @session[:user].nil?
if user and active_remembrall? user
handle_login user
end
end
It's using a helper method in the same module for testing the remembrall timestamp:
def active_remembrall?( user )
return Time.now < user.remembrall_expires unless user.remembrall_expires.nil?
false
end
From here, it's just a matter of hooking it all up. In the , we'll need to make sure it calls our custom handle_login method. For clarity, here's the entire login method:
def login
case @request.method
when :post
if user = User.authenticate(@params[:user_login], @params[:user_password])
handle_login( user )
if @params[:remember_me]
create_remembrall( current_user )
end
flash['notice'] = “Login successful”
redirect_back_or_default :action => “welcome”
else
flash.now['notice'] = “Login unsuccessful”
@login = @params[:user_login]
end
end
end
You'll notice that it uses some things we haven't discussed: currentuser and createremembrall. They can be found in the lib/auto_login.rb file.
Now, in our ApplicationController, we'll need to add the call for the :before_filter:
class ApplicationController < ActionController::Base
include LoginSystem
include AutoLogin
before_filter :login_from_cookie
# the handle_login method and other stuff here...
end
So that, in a nutshell, is how TaskTHIS handles cached user authentication. I've not shown all of the code. For example, I didn't show the code that actually creates the remembrall. For all of that, you can download the code and have a look for yourself.
Let me know what you think or what you would have done differently.
Technorati Tags: rails
Update: Well, DreamHost wasn't my, uh, dream host for RoR applications. So taskthis.com is down for now. Sorry for any inconvenience this causes. taskthis.darthapo.com is still available.
TaskTHIS.com is alive! TaskTHIS is now being hosted at DreamHost (yes, that link has a referral code in it, every little thing I can do to keep eating
)
DreamHost will be whole lot more stable than my development server. I'm going to retire the old demo instance of TaskTHIS (taskthis.darthapo.com) very soon. But, in the meantime, I have upgraded it to 0.5 so that you can export all of your tasklists and import them into taskthis.com.
I've also created a TaskTHIS development blog at blog.taskthis.com. From this point on, information specific to TaskTHIS will be posted there. My general posts and other Rails posts will still be here.
If you need to move your tasklists from the demo instance to taskthis.com, there's a quick tutorial available.
Technorati Tags: rails
TaskTHIS 0.5.1 is ready for download. It implements the following:
vendor foldersend_emailsThat's pretty much it. If you have any issues, please feel free to post a comment here and I'll try and address them as soon as I can.
Update: Changed the URL to the download page. The link should work again.
Technorati Tags: rails
TaskTHIS 0.5 is officially released! You can download it from here. I don't have it hosted yet, but I'm working on that. I was able to get the taskthis.com domain name, I just haven't had the time to set it all up yet.
This new release contains:
It leverages the following technologies:
It also contains an early release of my theme engine work. Check back soon for an article explaining how I implement themes in Rails applications.
Enjoy!
Update: Updated the URL for downloading the source...
Technorati Tags: rails
Sin City is a dark, extremely stylized movie. Very noir with a modern twist.
Overall, I like this movie. It is very true to it's source material. However, after watching it again on DVD, I think Frank Miller has some issues that Freud would have had a field day with.
I read this and laughed. It's actually quite amusing. A guy does the math on why he will never have a girlfriend.