homeASCIIcasts

160: Authlogic 

(view original Railscast)

Other translations: Es

One of the most popular plugins for Rails applications is Restful Authentication which was covered back in episode 67. Restful Authentication is a generator script that creates a lot of code that will handle the authentication in your Rails app. It works well as a quick solution to creating authentication for your application, but more flexible and extendable alternatives now exist.

One such alternative is Authlogic. It uses a different approach from Restful Authentication in that it doesn’t generate controllers or views but just deals with the code to handle the authentication for your users. Because of that it can take a bit longer to get your application running under Authlogic, as you have to write more code yourself, but you gain flexibility and more control over how the authentication is presented to the user.

Adding Authlogic To An Application

The application we’re going to add authentication to.

Above is a page from the application we’re going to add authentication to. In the top right corner of the page we want to add a link that shows “Register” and “Log in” links if there’s no currently logged-in user and “Edit Profile” and “Log out” links if there is.

Installing Authlogic

Authlogic can be installed either as a plugin or a gem; we’re going to install the gem. To add it to our application we just need to add the line below to /config/environment.rb.

config.gem "authlogic"

Then we’ll run rake to make sure that the gem is installed.

sudo rake gems:install

Creating The Model And Controller

With Authlogic installed the next thing to do is to create a model and controller to handle our users. First we’ll generate our User model.

script/generate model user

Next we’ll edit the generated migration file to configure our model’s properties. Authlogic works by looking for columns with a given name in the database. Our migration file will look like this:

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.string :username
      t.string :email
      t.string :crypted_password
      t.string :password_salt
      t.string :persistence_token
      t.timestamps
    end
  end

  def self.down
    drop_table :users
  end
end

Authlogic recognises the fields called email, crypted_password, password_salt and persistence_token and knows how to deal with them. You can find more details on the columns that Authlogic recognises on the Github page for the Authlogic example app.

Finally we’ll run rake to create the table in the database.

rake db:migrate

The User model we generated is just a standard model class. To add Authlogic to the class we just add the line acts_as_authentic.

class User < ActiveRecord::Base
  acts_as_authentic
end

You can pass a block to acts_as_authentic to specify configuration options, but the defaults are sensible so we won’t need to do that here.

Before we go any further we’ll also create our users controller. This will need to have new and edit views so we’ll get the generator to create those.

script/generate controller users new edit

We’ll be using the controller as a restful resource so we’ll also need to make a small addition to the routes file (/config/routes.rb).

map.resources :users

We can leave the controller as it is for now. We’ll come back and fill the controller methods and views shortly.

Adding The Links

Now that our model and controller are set up we’ll move on to creating the links that enable a user to sign up, log in or out, or change their profile. To start off we’ll create the “Register” link, which we’ll put in a div in our application’s layout file (/app/views/layouts/application.html.erb).

<div id="user_nav">
  <%= link_to "Register", new_user_path %>
</div>

We want the link to appear on the right-hand side of the page and in a slightly smaller size so we’ll use CSS to float it by modifying our stylesheet.

div#user_nav { float: right; font-size: 0.9em; }

And we now have our registration link on every page of our application.

The page now has a register link.

The “Register” link links to /users/new, so our next task is to write the code that creates a new user. We already have the controller and view files and the code we’ll need to put in them is fairly straightforward.

def new
    @user = User.new
end

def create
  @user = User.new(params[:user])
  if @user.save
    flash[:notice] = "Registration successful."
    redirect_to root_url
  else
    render :action => 'new'
  end
end

The controller code is standard RESTful code. The new action creates an empty user object while the create action creates a new user from the supplied parameters and saves it to the database if it is valid, or shows the new form again if it is not.

The registration form will be used when registering or editing a user’s profile so it will be best put in a partial file. Again, there’s nothing complicated.

<% title ("New User") %>
<%= render @user %>

The view code for new.html.erb.

<% form_for @user do |form| %>
<%= form.error_messages %>
<ol class="formList">
  <li>
    <%= form.label :username, "Username" %>
    <%= form.text_field :username %>
  </li>
  <li>
    <%= form.label :email, "Email" %>
    <%= form.text_field :email %>
  </li>
  <li>
    <%= form.label :password, "Password" %>
    <%= form.password_field :password %>
  </li>
  <li>
    <%= form.label :password_confirmation, "Password confirmation" %>
    <%= form.password_field :password_confirmation %>
  </li>
  <li>
    <%= form.submit "Submit" %>
  </li>
</ol>
<% end %>

The code for _user.html.erb.

Now we can click on the “Register” link now see our registration form. If we try to register without filling any of the fields in we’ll see that Authlogic automatically provides validations for the User model.

The user model is automatically validated when a user tries to register.

If we fill the form in correctly then we’ll be redirected to the home page and told that our registration has been successful.

Our user is now successfully registered.

Logging In

Along with the “Register” link we want a “Log in” one so that a registered user can log in. To do this in Authlogic we need to create another model called UserSession that represents the user’s current session. This means that to log a user in we just create a new UserSession record. Authlogic provides a generator script to generate this model.

$ script/generate session user_session
    exists  app/models/
    create  app/models/user_session.rb

The generator is simple and only creates the new UserSession model. If we look at that model we’ll see that it’s just an empty class that inherits from Authlogic::Session::Base, but that’s enough for it to handle all of the session logic for us.

class UserSession < Authlogic::Session::Base
end

As the generator script doesn’t create any controllers or views we still need to create a login form. We can do this in a RESTful manner by creating a UserSessions controller and giving it new, create and destroy actions to create and destroy sessions. Only the new action will have an associated view, so that’s the only action we’ll ask the generator to create.

script/generate controller UserSessions new

The three methods we need in the controller are, again, much as you’d see for any other controller. For new, we just create a new instance of UserSession.

def new
  @user_session = UserSession.new
end

For create we create a UserSession based on the passed parameters. If the user is valid then the UserSession object will be valid and can be saved.

def create
  @user_session = UserSession.new(params[:user_session])
  if @user_session.save
    flash[:notice] = "Successfully logged in."
    redirect_to root_url
  else
    render :action => 'new'
  end
end

Lastly, for destroy we just destroy the current UserSession. Note that we don’t need to find the session by an id as we’re just destroying the current one for that user.

def destroy
  @user_session = UserSession.find
  @user_session.destroy
  flash[:notice] = "Successfully logged out."
  redirect_to root_url
end

In the new view we’ll need a form so that a user can log themselves in.

<% title "Log in" %>

<% form_for @user_session do |form| %>
<%= form.error_messages %>
<ol class="formList">
  <li>
    <%= form.label :username, "Username" %>
    <%= form.text_field :username %>
  </li>
  <li>
    <%= form.label :password, "Password" %>
    <%= form.password_field :password %>
  </li>
  <li>
    <%= form.submit "Submit" %>
  </li>
</ol>
<% end %>

Again we’ll need to modify our routes file so that it treats the controller as a resource, and we’ll also add a couple of named routes to provide neat login and logout URLs.

map.login 'login', :controller => 'user_sessions', :action => 'new'
map.logout 'logout', :controller => 'user_sessions', :action => 'destroy'
map.resources :user_sessions

We can now go back to our application’s layout file and add the link for logging in.

<div id="user_nav">
  <%= link_to "Register", new_user_path %>
  <%= link_to "Log in", login_path %>
</div>

Now we can log in to the application. If we provide an invalid username or password then the UserSession model is considered invalid and we’re shown an error message.

An error message is shown if the credentials supplied are incorrect.

Otherwise we’re logged in and redirected back to the home page.

The flash message is shown when we successfully log in.

Logging Out

The code for logging in and out still isn’t complete. After we’ve logged in we still see the “Register” and “Log in” links where we should now see “Edit Profile” and “Log out”. To change the links we’ll need to modify our layout file again.

<div id="user_nav">
  <% if current_user %>
    <%= link_to "Edit profile", edit_user_path(:current) %>
    <%= link_to "Logout", logout_path %>
  <% else %>
    <%= link_to "Register", new_user_path %>
    <%= link_to "Log in", login_path %>
  <% end %>
</div>

There’s no need to pass a user id to the edit action as we’re always going to be editing the current user so we can just pass :current. The current_user method we’ve used in the if statement doesn’t as yet exist, so we’ll need to write it. We’ll want it to be available to all of the controllers so it will go in application_controller.rb.

helper_method :current_user

private
def current_user_session
  return @current_user_session if defined?(@current_user_session)
  @current_user_session = UserSession.find
end

def current_user
  @current_user = current_user_session && current_user_session.record
end

We’ve added two methods to the application controller. The first, current_user_session, gets the current user’s session, and the second, current_user, will return the User model for the currently logged-in user. We’ll make current_user a helper method so that it can be used in our views and our layout file.

Now when we log in we’ll see the correct links.

Our user is now successfully registered.

As we’ve defined the route and written the controller action to log a user out the “Log out” link will work, but we need to modify the UsersController to enable the profile to be edited.

def edit
  @user = current_user
end

def update
  @user = current_user
  if @user.update_attributes(params[:user])
    flash[:notice] = "Successfully updated profile."
    redirect_to root_url
  else
    render :action => 'edit'
  end
end

The edit and update methods vary slightly from normal in that instead of getting a model by its id they just get the current user, using the current_user method we wrote in the application controller.

Finally we just need to put the user form into the edit view (/app/views/users/edit.html.erb).

<% title ("Edit User") %>
<%= render @user %>

We can now edit our profile when we’re logged in which means that we’ve implemented all of the functionality we wanted in our site. Users can now register, log in, edit their profiles and log out again.

Further Reading

There’s much more functionality available in Authlogic than we’ve shown in this episode. For more information check out the Github page for Authlogic and the page for the example application. For a reference there’s the RDoc documentation too.