The Login Page (Ruby on Rails) Part 2

Doing It ‘The Rails Way’

Building the registration page and login the way we did was instructive. You got to see how to access the database using ActiveRecord, migrations, etc. All useful stuff, but building this particular code by hand is not how it’s done in Rails. It just doesn’t make sense to recreate all that code when you know there has to be code out there that is as good or better to fill such a common need as authentication. So we’re going to strike out on our own course, away from the PHP version and use the latest version of the restful_authentication plugin to get all our registration and login capabilities. To do so, we’re just going to start over with a fresh project and I’ll show you just how quickly and easily we can get back to the same spot, further in fact than we were before.

After all, one of the most fundamental ideas behind Rails is “Don’t Repeat Yourself” (abbreviated DRY) and that includes not repeating other people either. Why should all of us rebuild the same basic functionality to do signup, login, logout, etc. over and over again when we can get good quality reviewed code handed to us for free.

> rails --database=mysql pbbg

Install the MySQL gem if necessary, create your databases, and setup the config/database.yml file just like we did in the entry The Registration Page (Ruby on Rails). The only difference is that we specified explicitly this time that we were going to be using MySQL as a database so the database.yml will have the right fields in it from the start this time.

Now let’s get and install the plugin restful_authentication from GitHub. I’m going to assume that you don’t have Git installed (because you probably don’t) and instead you will just visit the GitHub repository and click the “download” button there to download a Zip file containing the latest version of the plugin.

Once you’ve downloaded the file, unzip it into the vendor/plugins directory under your pbbg project. Note that the directory name it will unzip to includes a lot of letters and numbers you don’t want. Rename the directory to restful_authentication and you’ll be ready to execute the following command (from the pbbg directory, not the vendor/plugins directory):

> ruby script/generate authenticated user sessions

Note: Full documentation for restful_authentication is available from the Wiki page on GitHub, it documents other flags you can add to get extra features (e.g. –include-activation to get email activation). With just the command I gave, you’ll see output like this:

Ready to generate.
———————————————————————-
Once finished, don’t forget to:

– Add routes to these resources. In config/routes.rb, insert routes like:
map.signup ‘/signup’, :controller => ‘users’, :action => ‘new’
map.login ‘/login’, :controller => ‘sessions’, :action => ‘new’
map.logout ‘/logout’, :controller => ‘sessions’, :action => ‘destroy’

———————————————————————-

We’ve create a new site key in config/initializers/site_keys.rb. If you have existing
user accounts their passwords will no longer work (see README). As always,
keep this file safe but don’t post it in public.

———————————————————————-
exists app/models/
exists app/controllers/
exists app/controllers/
exists app/helpers/
create app/views/sessions
exists app/controllers/
exists app/helpers/
create app/views/users
exists config/initializers
exists test/functional/
exists test/functional/
exists test/unit/
exists test/fixtures/
create app/models/user.rb
create app/controllers/sessions_controller.rb
create app/controllers/users_controller.rb
create lib/authenticated_system.rb
create lib/authenticated_test_helper.rb
create config/initializers/site_keys.rb
create test/functional/sessions_controller_test.rb
create test/functional/users_controller_test.rb
create test/unit/user_test.rb
create test/fixtures/users.yml
create app/helpers/sessions_helper.rb
create app/helpers/users_helper.rb
create app/views/sessions/new.html.erb
create app/views/users/new.html.erb
create app/views/users/_user_bar.html.erb
create db/migrate
create db/migrate/20080817172629_create_users.rb
route map.resource :session
route map.resources :users
route map.signup ‘/signup’, :controller => ‘users’, :action => ‘new’
route map.register ‘/register’, :controller => ‘users’, :action => ‘create’
route map.login ‘/login’, :controller => ‘sessions’, :action => ‘new’
route map.logout ‘/logout’, :controller => ‘sessions’, :action => ‘destroy’
The generator won’t just add a bunch of files, it will also add some configuration you need as well in order to use the newly added code. First we need to run the migration created for us by the code generator:

> rake db:migrate
Then we can move some code from the app/controllers/users_controller.rb and app/controllers/sessions_controller.rb to the app/controllers/application.rb. If you look at the first two controller files you’ll notice a section of text like this:

# Be sure to include AuthenticationSystem in Application Controller instead
  include

AuthenticatedSystem
Just like it says, we want to cut out that text from the two other controllers and put it (just once) in the ApplicationController (app/controllers/application.rb). After adding the include line, the beginning of the file will look like this:

# Filters added to this controller apply to all controllers in the application.
# Likewise, all the methods added will be available for all controllers.

class ApplicationController < ActionController::Base
include AuthenticatedSystem

helper :all # include all helpers, all the time
Let’s add an index page for our application, we can use it to replace the default index page for the Rails application and we can include a snippet of interface (called a “partial” in Rails terms) in our view to get something that shows which user is logged in and logout or else the login/signup links otherwise. Change this:

# You can have the root of your site routed with map.root -- just remember to delete public/index.html.
  # map.root :controller =&gt; "welcome"

to this:

# You can have the root of your site routed with map.root -- just remember to delete public/index.html.
  map.root :controller =&gt; "welcome"

and delete the public/index.html file. Now we create a controller and page which will appear when we go to either the root (i.e. http://localhost:3000) or specifically to the controller we named:

&gt; ruby script/generate controller Welcome index

Edit the newly created view (app/views/welcome/index.html.erb) and put the following into it:

&lt;%= render :partial =&gt; 'users/user_bar' %&gt;
 
&lt;% if flash[:notice] %&gt;
  &lt;%= h flash[:notice] %&gt;
&lt;% end %&gt;
 
&lt;h1&gt;Welcome To The Game!&lt;/h1&gt;

It looks just like the original view we created for when we registered and logged in, with the addition of the call to render the partial. If you look in the app/views/users directory you’ll see the partial it’s going to render. It’s called _user_bar.html.erb. The underscore at the beginning of the name is normal for partials and helps you distinguish views from partials in the directory. A partial is just a piece of HTML with embedded Rails code that you will be using from several pages. By putting a render :partial… on each page where you need it, you avoid repeating the same section of HTML + Rails. Remember, “Don’t Repeat Yourself.”

&lt;% if logged_in? -%&gt;
  &lt;div id="user-bar-greeting"&gt;Logged in as &lt;%= link_to_current_user :content_method =&gt; :login %&gt;&lt;/div&gt;
  &lt;div id="user-bar-action"  &gt;(&lt;%= link_to "Log out", logout_path, { :title =&gt; "Log out" }    %&gt;)&lt;/div&gt;
&lt;% else -%&gt;
  &lt;div id="user-bar-greeting"&gt;&lt;%= link_to_login_with_IP 'Not logged in', :style =&gt; 'border: none;' %&gt;&lt;/div&gt;
  &lt;div id="user-bar-action"  &gt;&lt;%= link_to "Log in",  login_path,  { :title =&gt; "Log in" } %&gt; /
                               &lt;%= link_to "Sign up", signup_path, { :title =&gt; "Create an account" } %&gt;&lt;/div&gt;
&lt;% end -%&gt;

Note: restful_authentication had a glitch that may or may not be corrected by the time you perform these steps. If so, your version may read abbr_tag_with_IP instead of link_to_login_with_IP. If it does, just correct your file to match the code above.

Now we can run the server and try out the new registration and login system.

&gt; ruby script/server

Now you can visit the welcome page (http://localhost:3000), signup, or login, after a successful signup or login you can logout. If you check the database after a signup you’ll find the new record added to the users table complete with encrypted password, salt, etc.

Not only do we save time and get better features by using restful_authentication, we also get better security. It offers a secondary level of salt (one for the entire site) in addition to per user salt values and it will apply the hash algorithm multiple times to make it much more difficult to brute force search for passwords. We’ll return to look at some more of the capabilities of restful_authentication in future entries.