Tutorial: Authlogic Basic Setup

November 3rd, 2008

I'm a machine today! 3 blog posts in one day. I can't be stopped.

Anyways, this past week I released Authlogic and it has been great. I've been getting a lot of positive feedback and it seems like people enjoy using it. That's the reason I release any of my libraries, to try and make your life a little easier. Awwww, how sweet. But I've also been getting emails asking me to put together a setup tutorial. So here we go...

What am I about to build?

Why not check it out for yourself...

A live example of this tutorial

To put it into words, we are going to setup basic user authentication for a new rails app. When it's all said and done, you will have an authentication system that provides:

  1. User registration.
  2. Automatically log users in upon successful registration.
  3. A my account area where the user can view / change details about their account, including their password.
  4. Automatically update the users session when they change their password.
  5. Logout functionality.
  6. Login functionality with a "remember me" option.
  7. Secure password / token system, based on proven principals used in ruby / rails for years.
  8. Automatically store information on the users and their session in the databases. Such as login count, IP address, when they logged in last, and when their last activity occurred.
  9. Count how many users are logged in / out in your system.

Assumptions

  1. You know how to set up a rails app
  2. This is a basic tutorial, therefore I won't cover advanced situations.
  3. You are familiar with basic authentication in rails and understand the "gist" of it.
  4. You are familiar with the RESTful development style and get the basic idea behind it. (you can set up a RESTful route and controller)

Helpful links

Migrating an existing app from restful_authentication?

It's simple, checkout my blog post about this. Also, keep reading this tutorial as this will help you understand the implementation.

1. Install Authglogic

Install the gem / plugin (recommended)

$ sudo gem install authlogic
# config/environment.rb
config.gem "authlogic"

Or as a plugin (you must have git installed to do this)

$ script/plugin install git://github.com/binarylogic/authlogic.git

2. Create your UserSession model

Now we need to create the user session model:

$ script/generate session user_session

This will create a file that looks like:

# app/models/user_session.rb
class UserSession < Authlogic::Session::Base
  # various configuration goes here, see AuthLogic::Session::Config for more details
end

Remember all of those repetitive authentication methods you added in your controllers? All of that "cruft" is taken care of in this file. It basically sits in between you and the cookies. Similar to how ActiveRecord sits in between you and the database.

3. Create your UserSessions controller

$ script/generate controller user_sessions

This is where people will be logging in / out. Name it after your UserSession model, just like you name all of your other controllers after your model.

Map the resource in config/routes.rb:

# config/routes.rb
map.resource :user_session
map.root :controller => "user_sessions", :action => "new"

Now setup your UserSessionsController just like you would with a model:

# app/controllers/user_sessions_controller.rb
class UserSessionsController < ApplicationController
  def new
    @user_session = UserSession.new
  end

  def create
    @user_session = UserSession.new(params[:user_session])
    if @user_session.save
      flash[:notice] = "Login successful!"
      redirect_back_or_default account_url
    else
      render :action => :new
    end
  end

  def destroy
    current_user_session.destroy
    flash[:notice] = "Logout successful!"
    redirect_back_or_default new_user_session_url
  end
end

What is going on here?

  1. Create is basically logging users in
  2. Destroy is basically logging users out

What is redirect_back_or_default? We will get to this method in a few steps and I will explain what it does there. So hold your horses.

4. Create and setup your User model

$ script/generate scaffold user \
  login:string \
  crypted_password:string \
  password_salt:string \
  persistence_token:string \
  login_count:integer \
  last_request_at:datetime \
  last_login_at:datetime \
  current_login_at:datetime \
  last_login_ip:string \
  current_login_ip:string

Every field, below persistence_token is a "magic" field. Authlogic will take care of keeping these up to date for you. They are optional, so add them if you want. They work the same way as created_at and updated_at.

Another thing to keep in mind is that there are other optional token field. You can add a "singleaccesstoken" and a "perishable_token" that Authlogic will maintain for you. There are different types of access for accounts, as a result we need different types of tokens. Take resetting a password, accessing a private feed, etc. I won't repeat myself here, but for more information checkout the "tokens" section in the README. It's explained in more detail there.

Now just run your migrations

$ rake db:migrate

Make your user act as authentic:

# app/models/user.rb
class User < ActiveRecord::Base
  acts_as_authentic
end

Basically acts_as_authentic adds all of the repetitive authentication code in your model:

  1. Common validations, such as validating the login field and the password field.
  2. Password accessor methods that encrypt the password and generate salt.
  3. after_save callbacks that keep the users session up to date as their password changes.
  4. Other various convenience methods such as reset_password! and forget!

Something to keep in mind is that by default Authlogic uses the Sha512 algorithm. you are NOT forced to use this, in fact Authlogic comes preloaded with some common encryption algorithms that you can choose from. I personally like the BCrypt method a lot. Let's say you wanted to use that instead. Just tell Authlogic about it:

# app/models/user.rb
class User < ActiveRecord::Base
  acts_as_authentic :crypto_provider => Authlogic::CryptoProviders::BCrypt
end

For more information on the available crypto providers, checkout the "Encryption methods" section in the read. Also, Authlogic provides a very easy way to migrate passwords to a new algorithm as well.

As you can see, acts_as_authentic accepts a number of options. Checkout Authlogic::ORMAdapters::ActiveRecordAdapter::ActsAsAuthentic::Config in the documentation for more details.

5. Create your UsersController

First let's set up some routes. Add these above your other routes:

# config/routes.rb
map.resource :account, :controller => "users"
map.resources :users

Basically registrations and the "my account" area are using the same controller: UsersController.

The last generator you ran should have added app/controllers/users_controller.rb. This is a typical CRUD controller, nothing new. But let's go ahead and modify it to look like the following:

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  def new
    @user = User.new
  end

  def create
    @user = User.new(params[:user])
    if @user.save
      flash[:notice] = "Account registered!"
      redirect_back_or_default account_url
    else
      render :action => :new
    end
  end

  def show
    @user = @current_user
  end

  def edit
    @user = @current_user
  end

  def update
    @user = @current_user # makes our views "cleaner" and more consistent
    if @user.update_attributes(params[:user])
      flash[:notice] = "Account updated!"
      redirect_to account_url
    else
      render :action => :edit
    end
  end
end

Let's talk a little bit about this controller:

  1. The new and create methods are for new user registrations and redirects them to their account when successful. Notice we didn't have to do anything with sessions or cookies. That's because Authlogic is "magical" and takes care of this for you when you successfully saved the user object.
  2. The edit and update methods are strictly for users logged in. You will also notice there is nothing going on with the sessions or cookies. Authlogic takes care of this for you when the user is successfully updated.

6. Setup your views

The views are very simple and don't introduce anything new, but obviously are necessary to get this app running. So instead of cluttering up this tutorial with all of the views, check out the source code. You can copy the views from that very easily.

Here is a direct link to the views folder.

7. Setup your ApplicationController

Your application controller will be responsible for persisting your session. This is my favorite part because it is so easy. I am able to do in 2 methods what used to take 5 to 6 different methods:

# app/controllers/application.rb
class ApplicationController < ActionController::Base
  filter_parameter_logging :password, :password_confirmation
  helper_method :current_user_session, :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
      return @current_user if defined?(@current_user)
      @current_user = current_user_session && current_user_session.user
    end
end

Keep in mind this is just a tutorial. You can accomplish the above any way you want. Authlogic lets you design your app how you want.

8. Restrict access

This is done the same way you have always restricted access. Here is how I do it. Add this into your ApplicationController:

# app/controllers/application.rb
class ApplicationController < ActionController::Base
  private
    def require_user
      unless current_user
        store_location
        flash[:notice] = "You must be logged in to access this page"
        redirect_to new_user_session_url
        return false
      end
    end

    def require_no_user
      if current_user
        store_location
        flash[:notice] = "You must be logged out to access this page"
        redirect_to account_url
        return false
      end
    end

    def store_location
      session[:return_to] = request.request_uri
    end

    def redirect_back_or_default(default)
      redirect_to(session[:return_to] || default)
      session[:return_to] = nil
    end
end

You will notice I added in some extra methods that you have probably seen before: store_location and redirect_back_or_default. These are pretty self explanatory, but they basically allow you to redirect the user back to where they were trying to go initially. For example, if they tried to access their account while logged out it would store that location and redirect them to their account once they logged in.

Now its time to restrict access. Add the following code:

# app/controllers/user_sessions_controller.rb
class UserSessionsController < ApplicationController
  before_filter :require_no_user, :only => [:new, :create]
  before_filter :require_user, :only => :destroy
end

Now for your users controller:

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_filter :require_no_user, :only => [:new, :create]
  before_filter :require_user, :only => [:show, :edit, :update]
end

You're done! You now have a working rails app with authentication.

If you go back through this tutorial you will notice you really didn't learn anything new. Creating RESTful routes and controllers is something you already knew. If anything this tutorial made you "unlearn" mundane tasks you had to previously perform with other authentication solutions.

Next Steps

You might be asking any of the following questions:

  1. What about scoping the user accounts to an account, or some parent relationship?
  2. What about open ID authentication?
  3. What about resetting passwords
  4. What about roles, permissions system, etc...?
  5. What if I want to log users in by email or username instead of login?
  6. How do I test all of this?

I will answer your questions in order:

  1. This is extremely simple. Checkout the readme in the repository, there is a "scoping" section that goes over this.
  2. Check out the tutorial on using OpenID with Authlogic
  3. I wrote a tutorial on resetting passwords with Authlogic.
  4. I feel roles are really a separate project. Your app could require a very intense / granular permissions system, or a very broad / simple permissions system. Who knows. A lot of these authentication solutions try to lump all of this into one library, when these things really aren't part of the actual user authentication, they are separate. There are a million roles / permissions systems that I do not have a problem with, so I see no reason to reinvent the wheel here.
  5. Authlogic favors convention over configuration, but it has a plethora of configuration options. You can change just about anything it does, including what field names it uses. Check out the documentation for a list of configuration options.
  6. Checkout the "Testing" section in the README, Authlogic comes with some helpers to make testing very easy.

Feedback

If you enjoyed this tutorial or hated it, please let me know. I am always interested in getting feedback and am looking to improve any tutorials I write. Hopefully you enjoyed this and authentication is now a lot easier for you.

34 Responses to “Tutorial: Authlogic Basic Setup”

  1. November 3rd, 2008 at 02:03 PM okr Says

    Thank you for this tutorial, authlogic looks like a breath of fresh air in rails authentication. I think I am definitely going to try using it on my next project but I usually need some sort of role based authentication for most of my projects. Is there any chance you could cover role integration or how to setup a basic admin user type in a separate tutorial? Maybe I missed something in the documentation?

  2. November 3rd, 2008 at 02:25 PM Ben Johnson Says

    okr, roles are really a permissions system that is separate from authentication. The problem with roles is that it depends on the app. Some apps might need very granular roles, others might need some more broad / simple. There are a lot of solutions on this and I view this as a completely separate project. Lastly, depending on the type of role system you need the implementation is different. So lumping this in with Authlogic doesn’t really make sense. I have both of the rails recipes books, and one of the has a great tutorial on this. You should check it out.

    Secondly, setting up a basic admin area is easy. Check out this screencast: http://www.vimeo.com/637894 . I think that is a great tutorial on how to set up an admin area using the REST development style. The screen cast uses resource_controller. You don’t have to use this, but it does help DRY up those repetitive admin controllers.

    Hope this helps.

  3. November 3rd, 2008 at 02:30 PM Ben Johnson Says

    okr, you might also want to check out: http://railscasts.com/episodes/19-where-administration-goes

  4. November 3rd, 2008 at 05:50 PM Evan Says

    Step 4 comes before step 3? lol

  5. November 3rd, 2008 at 06:01 PM Ben Johnson Says

    Yes, you also need to do the first step last. (I fixed the typo)

  6. November 4th, 2008 at 02:13 PM vince Says

    Any chance for a tutorial on how to migrate from Restful Authentication to Auth logic?

  7. November 4th, 2008 at 03:04 PM Tim S Says

    Has anyone interfaced this with DocSavage’s rails-authorization-plugin? Cursorily, it looks like they’ll play nice together, but I’d love some verification before I go ahead and try it for myself. :-)

    FTR, I prefered “authgasm”.

  8. November 5th, 2008 at 12:41 PM Ben Johnson Says

    Vince, I have that in my queue of articles to write. I recently converted 3 of my apps from restful_authentication to Authlogic, and it was pretty simple. Mainly removing a lot of code.

    Tim, I like Authgasm too, but the suits dont. I have not played around with the rails-authorization-plugin enough to answer this question properly. If you decide to mess around with it, let me know how it goes.

  9. November 5th, 2008 at 06:10 PM okr Says

    I just wanted to say in one day I got authlogic working with a namespaced admin area and some simple roles I built in and it works like a charm. Thank you for making such an easy to use authorization system. Now I just need to get my “forgot password” action working.

  10. November 6th, 2008 at 06:17 AM Spyou Says

    Thank for this plugin, awesome solution for a simple login.

    I’ll use it as a base on my future Twitter Mashup.

    Thank you very much.

  11. November 6th, 2008 at 09:57 AM Aneem Says

    Authologic will be nice when I get more built in features like below.

    http://github.com/ariejan/baseapp/tree/master looks promising to me. It has all features (open id, roles, user administration, restful auth etc.) built in. Plus some template features.

  12. November 11th, 2008 at 04:04 PM Peter Zingg Says

    I just did a personal review of 8 or 9 starter apps as well as a summary of the gems and plugins currently on display at the Rails Rumble 2008 site. I am so happy to find authlogic. Here are the pieces I think I am going to pull from these projects:

    authentication layer – authlogic configatron, roles, user adminstration and profile editing, forgot password – baseapp multiple openid identities (and idselector.com ui) – embark send user his lost (decrptyed) password – insoshi quick and dirty forms – awesome_fields and better_partials

    If I get it where I want it today, I’ll put it into github.

  13. November 14th, 2008 at 06:38 AM Stefan Weber Says

    Thank you very much for this plugin. Also iam totally fresh to Ruby and Ruby on Rails it is very easy to use and (thanks to your blog here) i understand whats going on ;-)

  14. November 17th, 2008 at 08:52 PM TW Scannell Says

    This looks great. I haven’t been able to install yet as I get an error:

    ERROR: Error installing authlogic: echoe requires RubyGems version >= 1.2

    I finally found this and it seemed to work.

    script/plugin install git://github.com/binarylogic/authlogic.git

  15. November 19th, 2008 at 03:50 PM Ben Johnson Says

    Yeah, rails gem support requires echoe. I will more than likely either switch to Hoe or require echoe as a dependency.

  16. November 24th, 2008 at 07:13 AM Michael Kovacs Says

    Hey Ben,

    Thanks so much for your efforts. Just came across this today on the recommendation from a friend last night. I was going through this tutorial and noticed that your user model spec in this tutorial isn’t complete. You’re missing the persistence_token. Might want to sync that up with your README in the gem.

    Thanks again.

  17. November 24th, 2008 at 12:48 PM akahn Says

    Thanks for this. I’m very new to Rails and I’m going to be using this for my first app. So far it seems simpler and easier than restful_authentication. One thing I’d like to point out is that the views in your sample app on github don’t work with the code here in this tutorial. E.g. _form.html.erb takes openid information and an email address, when that isn’t in the model created in this tutorial.

  18. November 24th, 2008 at 08:53 PM Ben Johnson Says

    Hey Michael, thanks for the heads up on this, I changed it. But it really doesn’t matter, you could call the field that and authlogic will adjust accordingly and work just fine. It does some “smart checking” for common field names.

    Akahn, yeah I had a feeling that might be a problem. I will put some notes in the views. It should be fairly obvious that there is OpenID support in the app. Simply removing the OpenID code would make the app work just fine.

  19. November 29th, 2008 at 03:57 PM Carlos A. da Silva Says

    Hello Ben,

    great jog with Authlogic.

    I was testing the plugin and it seems to work perfectly using this tutorial, but when I tried using it inside a namespace like Security::User and Security::UserSession, I had the following error:

    undefined local variable or method `user’ for Security::UserSession:Class

    The stack trace sent me to:

    vendor/plugins/authlogic/lib/authlogic/session/base.rb:359:in `create_configurable_methods!’

    So I took a look there and found this line

    alias_method :#{klass_name.underscore}, :record

    I figured out the problem might be the klass_name.underscore, so I tried changing it to:

    alias_method :#{klass_name.underscore.tr(’/’,’_‘)}, :record

    And it worked.

    I was wondering if I should create a ticket at lighthouse, because it seems a little bug.

  20. November 29th, 2008 at 04:18 PM Ron Damen Says

    Excellent plugin! Just went through the tutorial and imho it’s nicer than anything else out there at the moment.

    That said, just starting out with Rspec I’m having trouble writing specs for the users controller and controllers that require authentication.

    If anyone could share some examples I’d appreciate it.

  21. November 30th, 2008 at 03:36 PM Ron Damen Says

    I’ve made some progress on writing specs and forked the example app on Github for anyone interested and to hopefully receive some feedback.

    http://github.com/jxl/authlogic_example/tree/rspec

    If you see anything that’s just plain wrong or could be improved please fork or leave a comment.

  22. December 9th, 2008 at 09:28 PM James Lavin Says

    Hi, Ben:

    Your plugin looked interesting enough for me to rip out restful_authentication and give it a try. But I want to repeat Tim S’s question because (unless I made a mistake, which is possible) it doesn’t seem to work out of the box with rails-authorization-plugin. That’s a very popular plugin, so it would be great if they worked together without custom code.

    http://github.com/DocSavage/rails-authorization-plugin/tree/master says, “make sure your application provides a current_user method or something that returns the current user object (resful_authentication provides this out of the box).”

    Authlogic does provide a current_user method, but the authorization plugin can’t seem to use it. It keeps giving me the following error message:

    Authorization::CannotObtainUserObject (Couldn’t find #current_user or @user, and nothing appropriate found in hash): /vendor/plugins/rails-authorization-plugin/lib/authorization.rb:117:in `get_user’ /vendor/plugins/rails-authorization-plugin/lib/authorization.rb:74:in `has_permission?’

    Did I misconfigure Authlogic? If not, what would be your official recommendation for getting these two plugins to talk with each other? I don’t want to put in my own hack and then get screwed up later when Authlogic changes.

    Thanks!

    James Lavin

  23. December 9th, 2008 at 11:10 PM Joel Margolese Says

    Is there a way to do email confirmations for new accounts? (I know I can add it, just wondering if it’s builtin and/or documented somewhere?)

    Also any anti-bot technologies like CAPTIAs?

    Looks great!

    thanks

  24. December 10th, 2008 at 05:51 AM David Says

    Sort of annoyed. It took me an hour to find out I needed to install Git.

    Could you point things out to noobies like me in the future? Would take two lines of text.

    Thanks.

  25. December 10th, 2008 at 08:20 PM James B. Byrne Says

    I ran across a reference to this on the RSpec mailing list and am giving it a tryout. On first blush I am favourably impressed with the entire approach, taking the application coding out of the hands of generators while providing the background functionality necessary.

    The references to the view code present in the git repository may cause recent users of this tutorial some difficulty however. That code is now polluted with the changes introduced in the later tutorials OpenID and ResttingPasswords. If you do not wish to reproduce the original view code in line in this article then perhaps a different resource reference with the pristine versions might be provided instead?

  26. December 12th, 2008 at 04:26 AM Ben Johnson Says

    Hi everyone, I apologize for taking a few days for a response, this has been an unusually busy week for me. Anyways, here are my replies:

    Carlos, great find, I went ahead and fixed this, just update your gem.

    James Lavin, the current_user method is just a method you create yourself. Maybe you forgot to do helper_method :current_user? Anyways, I’ll install the authorization plugin and see what the deal is. Regardless, it should work because the current_user method is no different than the restful_auth one in terms of the value it returns.

    Joel, that’s really up to you. Authlogic doesn’t have this built in, but if gives you some helpful tools. Take a look at the resetting password tutorial. You can use the same things I use in there to confirm accounts, such as the perishable_token, etc.

    David, I will add that in, I didn’t really think about mentioning it since the plugin url begins with git://, but I’ll put something in here mentioning git is required.

    James Byrne, I will more than likely just create a branch for each tutorial and clean up the code respectively.

  27. December 15th, 2008 at 10:24 PM Jan Says

    This seems to be a great plugin/gem. But beware of a return statement in the block after the ”@user_session.save”. I wanted to do a simple “render :text => ‘success’ and return” as a temporary workaround but this skips the after_save filter(s) and thus persisting the session. Another issue is the ability to use authlogic in cocumber tests. I’m unable to stay logged in during a scenario.

  28. December 16th, 2008 at 01:04 AM Ivan Says

    why not use

    def current_user @current_user ||= current_user_session && current_user_session.user end

    instead of

    return @current_user if defined?(@current_user) @current_user = current_user_session && current_user_session.user

    and alike for current_user_session

  29. December 16th, 2008 at 06:42 PM Ivan Says

    me again in documentation there is login_field_validates_confirmation_of_options instead of password_field_validates_confirmation_of_options

  30. December 19th, 2008 at 04:25 AM Ben Johnson Says

    Thanks jan, I will look into that.

    Ivan, because if a current user is not found it will return nil, which means it will keep executing that code. You only want that statement executed once.

  31. December 27th, 2008 at 03:40 AM Keith Says

    Hi, my problem is that I don’t want the user to be automatically logged-in. When a user is created I can see that the cookies are set. How do i prevent it?

  32. December 27th, 2008 at 04:16 AM Keith Says

    Nevermind my comment. Found out about session_ids => []

  33. December 29th, 2008 at 09:37 PM Lee Says

    First off, I’ve been playing around with authlogic and I really like it so far. I appreciate your work Ben.

    One question though…say I want to load that flash message with user details (say, login, last_login_at, and last_login_ip) on successful creation of a user_session. At what point do I have access to “current_user”? Can I simply call current_user.last_login_at inside the create method of UserSessionsController to build my flash message text? Thanks for any suggestions.

  34. December 31st, 2008 at 06:11 AM Ben Johnson Says

    Lee, I don’t see any reason why you wouldn’t be able to. Try it out, keep in mind you are in control of the current_user method, so if it doesnt work because of caching (that first line: return @current_user if defined?(@current_user)), try clearing out the @current_user variable so it will try and find the user again. But I don’t see any reason why it wouldn’t work without doing this.

Leave a Reply