Jumpstart Lab > Course Resources > Rails Jumpstart > JSBlogger > I5: Authentication

I5: Authentication

Authentication is an important part of almost any web application and there are several approaches to take. Thankfully some of these have been put together in plugins so we don’t have to reinvent the wheel.

The “flavor-of-the-week” is one named AuthLogic and I wrote up an iteration using it for the JSMerchant tutorial, but I think it is a little complicated for a Rails novice. You have to create several different models, controllers, and views manually. The documentation is kind of confusing, and I don’t think my tutorial is that much better.

So, instead, we’ll use the most common authentication plugin, restful_authentication.

Installing restful_authentication

In the previous iteration we installed libraries using RubyGems. This is the preferred way of managing plugins, but restful_authentication isn’t available through RubyGems. We’ll have to use the older method of installing a plugin.

At your terminal, enter the following:

ruby git clone git://github.com/technoweenie/restful-authentication.git restful_authentication

If you aren’t able to pull down the code from GitHub, you’ll need to download the code in a zip file from github, then extract the zip into /vendor/plugins/restful_authentication/.

Once you’ve installed the plugin, you can test that it’s available with this command at your terminal:

script/generate

In the output is a section title “Installed Generators”. It should have a line that says Plugins (vendor/plugins): authenticated. If it’s there, you’re ready to go!

Running the Generator

This plugin makes it easy to get up an running because one generator creates most of the code you’ll need. Run this from your terminal:

ruby script/generate authenticated user sessions

Take a look at the output and you’ll see it created about 16 files and added a few routes to the routes.rb file. The generator has a note that you shouldn’t forget to add the routes to routes.rb, but it’s already done that for you.

Let’s look at the CreateUsers migration that the generator created before we migrate the database. If you wanted your User models to have any additional information (like “deparment_name” or “favorite_color”) you could add columns for that. For our purposes these fields look alright and, thanks to the flexibility of migrations, if we want to add columns later it’s easy. So go to your terminal and enter:

rake db:migrate

Creating a First Account

First, stop then restart your server in RubyMine to make sure it’s picked up the plugin. Then go to http://localhost:3000/users/new and the new user form should popup.

Go ahead and create yourself an account. If you’re successful it will just bounce you to the http://localhost:3000/ root page. There isn’t any information about our login status in our views, though, so it’s hard to tell if we’re really logged in.

Let’s open /app/views/layouts/application.html.haml and add a little footer so the whole %body% chunk looks like this:

  %body
    #container
      #content
        = yield
        %hr
        %h6
          - if current_user
            = "Logged in as #{current_user.login}"
            = link_to "(logout)", logout_path
          - else
            = link_to "(login)", login_path

The go to http://localhost:3000/articles/ and you’ll get this error:

NameError in Articles#index
Showing app/views/layouts/application.html.haml where line #14 raised:
undefined local variable or method `current_user' for #<ActionView::Base:0x103147400>

We tried to use the current_user helper that comes with the restful_authentication plugin, but Rails isn’t recognizing it. We need to do one more setup step. Open /app/controllers/application_controller.rb, and underneath the protect_from_forgery line, add this: include AuthenticatedSystem.

Now refresh your browser and your articles list should come up along with the new footer at the bottom.

An Aside on the Site Root

It’s annoying me that we keep going to http://localhost:3000/ and seeing the Rails starter page. Let’s make the root show our articles index page.

First, delete the file /public/index.html. Files in the public directory will take precedence over routes in our application, so as long as that file exists we can’t route the root address anywhere.

Second, open /config/routes.rb and right above the other routes add in this one:

map.root :controller => 'articles', :action => 'index'

Now visit http://localhost:3000 and you should see your article list.

Securing New Users

It looks like we can create a new user, but right away I want to make some changes. We’re just going to use one layer of security for the app — a user who is logged in has access to all the commands and pages, while a user who isn’t logged in can only post comments and try to login. But that scheme will breakdown if just anyone can go to this URL and create an account, right?

Let’s add in a protection scheme like this to the new users form:

That way when the app is first setup we can create an account, then new users can only be created by a logged in user.

We can create a before_filter which will run before the new and create actions of our users_controller.rb. Open that controller and on the second line you’ll see include AuthenticatedSystem. You can remove that since we put it in the application_controller and, in it’s place, put all this code:

  before_filter :zero_users_or_authenticated, :only => [:new, :create]

  def zero_users_or_authenticated
    unless User.all.size == 0 || current_user
      redirect_to root_path
      return false
    end      
  end

The first line declares that we want to run a before filter named zero_or_authenticated when either the new or create methods are accessed. Then we define that filter, checking if there are either zero registered users OR if there is a user already logged in. If neither of those is true, we redirect to the root path (our articles list) and return false. If either one of them is true this filter won’t do anything, allowing the requested user registration form to be rendered.

With that in place, try accessing /users/new when you logged in and when your logged out. If you want to test that it works when no users exist, try this at your console:

User.destroy_all

Then try to reach the registration form and it should work! Create yourself an account if you’ve destroyed it.

Securing the Rest of the Application

The first thing we need to do is sprinkle before_filters on most of our controllers:

Now our app is pretty secure, but we should hide all those edit, destroy, and new article links from unauthenticated users.

Open /app/views/articles/index.html.erb and find the section where we output the “Actions”. Wrap that whole section in an if clause like this:

<% if current_user %>

<% end %>

Look at the article listing in your browser when you’re logged out and make sure those links disappear. Then use the same technique to hide the “Create a New Article” link.

If you look at the show view template, you’ll see that we never added an edit link! Add that link now, but protect it to only show up when a user is logged in.

Your basic authentication is done, and Iteration 5 is complete!