Screencast: Using Clearance for authentication, Part 1 of 2

November 15th, 2009 Leave a comment Go to comments

Errata

(15-Nov-2009) Apparently the gem dependencies have changed for Clearance; you will also need to install the factory_girl gem. I’ve noted this below, so if you’re following only the screencast, make sure to see Step 3 below.


This is the first of two screencasts on using Clearance for authentication. The first screencast
presents the basics of getting Clearance to work with a new application; in the second screencast,
we’ll go over retro-fitting an existing application. The screencast link is at the bottom: I’d recommend that you read or skim the screencast steps before viewing.

  1. Let’s check your gem configuration.

    On Windows, or on Linux or OS/X and using ruby_switcher.sh, you do not need to run gem as the superuser. If you installed Ruby by hand on Linux or OS/X, you will need to prefix the gem command with “sudo”, for example “sudo gem install foobar”, not “gem install foobar”.

    First let’s make sure that your gem sources are all up to date. Type

    gem sources
    

    If http://gems.github.com is not listed, do

    gem source -a http://gems.github.com
    

    There is also a new gem source, which will supersede RubyForge. Do the
    following:

    gem install gemcutter
    gem tumble
    

    This will make gemcutter the top priority for gems. It has everything
    from RubyForge.

  2. To get a feel for how a new gem works, I would advise trying it out in
    “scratch” application that you intend to throw away. So first go into a
    temporary directory. Also, we’ll create a scaffold for a single table,
    just so we have something there that the application can manipulate.

    rails scratch
    cd scratch
    mv public/index.html public/index.html_orig
    script/generate scaffold Note title:string description:string
    rake db:migrate
    

    If you like, you can now do script/server, browse to http://localhost:3000/notes
    and create some sample data.

  3. Now let’s install the clearance gem. There are some brief install
    instructions here — http://github.com/thoughtbot/clearance — and you can
    also preview the code. (15-Nov-2009: You will also need the factory_girl gem.)

    gem install thoughtbot-clearance
    gem install thoughtbot-factory_girl
    
  4. We would like to have our application require a specific version. To do that:

    In config/environment.rb

    config.gem "thoughtbot-clearance",
      :lib     => 'clearance',
      :source  => 'http://gems.github.com',
      :version => '0.8.2'
    
  5. Note that you can use rake to ensure that all gems specific through config
    are installed; and you can also “unpack” them into your own application so that
    if you need to deploy to an environment that doesn’t have the gem, your app
    will still work.

    rake gems:install
    rake gems:unpack GEM=thoughtbot-clearance
    

    (rake gems:unpack will unpack ALL gems you’ve specified: Here, we’re just
    going to do it for clearance.)

  6. Generate clearance
    script/generate clearance
    
  7. FIRST, make sure you don’t lose track of the directions printed out
    after generating clearance. We want to go back to that. Before we go
    further, however, we should see what has happened to your application.
    Notice that right after the script/generate command, the generator tells
    us what files were added or modified:

    insert  include Clearance::Authentication into app/controllers/application_controller.rb
    exists  app/models
    create  app/models/user.rb
    create  test/factories
    create  test/factories/clearance.rb
    exists  db/migrate
    create  db/migrate/20091101174550_clearance_create_users.rb
    readme  README
    

    As you can see, the generator make some significant changes. We need to
    take a look at app/controllers/application_controller.rb, the User model that
    was created, and the migration.

    For application_controller.rb, we see that the module Clearance::Authentication
    has been included. This means that we are going to have access to some new
    methods in our application controller, and all controllers that subclass it.

    The created User model is quite simple:

    # Remember: Generated code: Don't add this yourself!
    class User < ActiveRecord::Base
      include Clearance::User
    end
    

    Finally, the migration is pretty significant:

    # Remember: Generated code: Don't add this yourself!
    class ClearanceCreateUsers < ActiveRecord::Migration
      def self.up
        create_table(:users) do |t|
          t.string   :email
          t.string   :encrypted_password, :limit => 128
          t.string   :salt,               :limit => 128
          t.string   :confirmation_token, :limit => 128
          t.string   :remember_token,     :limit => 128
          t.boolean  :email_confirmed, :default => false, :null => false
          t.timestamps
        end
    
        add_index :users, [:id, :confirmation_token]
        add_index :users, :email
        add_index :users, :remember_token
      end
    
      def self.down
        drop_table :users
      end
    end
    

    This last bit is important. It means that if we add clearance to an application
    that already has a users table and a User model, we will have to modify the
    migration so that it is not creating a users table, but adding columns.

  8. Follow the instructions given after script/generate:

    1. Define a HOST constant in your environments files.
    In config/environments/test.rb and config/environments/development.rb it can be:

    HOST = “localhost”

    In production.rb it must be the actual host your application is deployed to.
    The constant is used by mailers to generate URLs in emails.

    2. In config/environment.rb:

    DO_NOT_REPLY = “donotreply@example.com”

    3. Define root_url to *something* in your config/routes.rb:

    map.root :controller => ‘home’

    So, in our case, we’ll set HOST to localhost:3000, and the map.root to
    the “notes” controller.

  9. Migrate the database
    rake db:migrate
    
  10. Now you might think that clearance is working. Browse to http://localhost:3000/notes

    Nothing. The docs don’t tell you what to do. If you get caught in
    this situation, your best bet is to try to find the Clearance::Authentication
    module, and see if there are any interesting methods on it. You might
    also look at the tests.

    So, let’s look at vendor/gems/thoughtbot-clearance-0.8.2/lib/clearance/authentication.rb

    AHA! A method called authenticate. Now, we probably want this to run
    before any actions on protected resources.

    Notice as well that there is also an action called sign_out

  11. Now add to any controller that needs authentication . . .
    before_filter :authenticate
    

    An alternative is to create a controller that auth-protected controllers
    should subclass:

    class RequiresAuthenticationController < ApplicationController
      before_filter :authenticate
    end
    
  12. Try it. Browse back to http://localhost:3000/notes
  13. Ah. We’re being forced to log in. Let’s register. We’re sent back to
    login. Now, most authentication systems require you to click an authentication
    link; clearance is no different. Let’s check the log. And there it is. Let’s
    grab that link and paste it in.
  14. It works. We’re authenticated.
  15. The route for logout is . . . /sign_out, i.e., http://localhost:3000/sign_out
  16. At this point, we might add sign_in and sign_out links to our scaffold
    layout.

Click for screencast

  1. Gary
    November 11th, 2009 at 19:17 | #1

    Everything was going fine for me until I got to step 6. Any thoughts? Is this another windows thing?

    C:\cscie168\techTests\scratch>ruby script/generate clearance
    c:/ruby19/lib/ruby/gems/1.9.1/gems/activesupport-2.3.3/lib/active_support/dependencies.rb:156:in `require': no such file to load -- factory_girl (MissingSo
    urceFile)
    from c:/ruby19/lib/ruby/gems/1.9.1/gems/activesupport-2.3.3/lib/active_support/dependencies.rb:156:in `block in require'
    from c:/ruby19/lib/ruby/gems/1.9.1/gems/activesupport-2.3.3/lib/active_support/dependencies.rb:521:in `new_constants_in'
    from c:/ruby19/lib/ruby/gems/1.9.1/gems/activesupport-2.3.3/lib/active_support/dependencies.rb:156:in `require'
    from C:/cscie168/techTests/scratch/vendor/gems/thoughtbot-clearance-0.8.2/generators/clearance/clearance_generator.rb:3:in `'
    from c:/ruby19/lib/ruby/gems/1.9.1/gems/activesupport-2.3.3/lib/active_support/dependencies.rb:156:in `require'
    from c:/ruby19/lib/ruby/gems/1.9.1/gems/activesupport-2.3.3/lib/active_support/dependencies.rb:156:in `block in require'
    from c:/ruby19/lib/ruby/gems/1.9.1/gems/activesupport-2.3.3/lib/active_support/dependencies.rb:521:in `new_constants_in'
    from c:/ruby19/lib/ruby/gems/1.9.1/gems/activesupport-2.3.3/lib/active_support/dependencies.rb:156:in `require'
    from c:/ruby19/lib/ruby/gems/1.9.1/gems/rails-2.3.3/lib/rails_generator/spec.rb:17:in `klass'
    from c:/ruby19/lib/ruby/gems/1.9.1/gems/rails-2.3.3/lib/rails_generator/lookup.rb:140:in `instance'
    from c:/ruby19/lib/ruby/gems/1.9.1/gems/rails-2.3.3/lib/rails_generator/scripts.rb:31:in `run'
    from c:/ruby19/lib/ruby/gems/1.9.1/gems/rails-2.3.3/lib/commands/generate.rb:6:in `'
    from script/generate:3:in `require'
    from script/generate:3:in `'

    Thanks,
    Gary

  2. November 13th, 2009 at 15:11 | #2

    @Gary

    When you installed the gem for Clearance, FactoryGirl should have been installed automatically.

    I'll look at my Windows system, but it should be there.

  3. Gabriel
    November 15th, 2009 at 13:01 | #3

    I am having the exact same error on linux. maybe something with the version?

  4. Gabriel
    November 15th, 2009 at 13:06 | #4

    After install factory girl everything worked fine.
    http://github.com/thoughtbot/factory_girl

  5. November 15th, 2009 at 13:18 | #5

    @Gabriel

    OK.

    Just out of curiosity, did you say:

    rale gems:unpack

    OR

    rake gems:unpack GEM=thoughtbot-clearance

  6. Gabriel
    November 15th, 2009 at 13:28 | #6

    rake gems:unpack GEM=thoughtbot-clearance

  7. November 15th, 2009 at 13:49 | #7

    @Gabriel

    Harrumph. Looks like the gem dependencies are screwed up. I've added an errata to install factory_girl as well.

  8. Gabriel
    November 16th, 2009 at 13:51 | #8

    how would you add a validation for a minimum password length on the model? only the encrypted password is saved an that doesn't have the same length...

  9. November 16th, 2009 at 14:14 | #9

    In vendor/gems/clearance-0.8.3/lib/extensions/user.rb (might be slightly wrong), add

    validates_length_of :password, :minimum => 4, :if => :password_required?

    to the Validations module.

    (I think.)

  10. Gabriel
    November 16th, 2009 at 15:05 | #10

    so is it ok (rails way) to change the code in gems/plugins directly? I was figuring that I should somehow overwrite, include, extend something.
    Would I do the same thing if I wanted to change some logic (e.g. branch of for diferent login methods like OpenId and Clearane)?

  11. November 16th, 2009 at 15:12 | #11

    @Gabriel

    Once you unpack a gem, you essentially own it; do anything you want. I believe if you look through the wiki for Clearance, you'll seem them making various suggestions to tweak the gem, add tests, etc.

  12. November 17th, 2009 at 00:02 | #12

    @Gabriel

    Gabriel:

    Also: What about just putting the validation on the User model -- In app/models/user.rb, make it look like this:

    class User < ActiveRecord::Base
      include Clearance::User
      validates_length_of :password, :minimum => 4, :if => :password_required?
    end
    

    Now your gem is still pristine.

  13. Ben Ho
    November 30th, 2009 at 19:27 | #13

    Hi John,
    I followed your steps and got everything working. However, I noticed that after I sign up, I would get about a minute of hang time from my server before my request has been completed. Any thoughts on why this could be happening? Thanks.

    -Ben

  14. November 30th, 2009 at 21:30 | #14

    @Ben Ho

    Hangs are frequently caused by DNS issues.

    What is the value of HOST in environment.rb?

    If it is: localhost

    Try changing it to 127.0.0.1 (or 127.0.0.1:3000 if you give the port).

    And report back if this helps.

  15. Ben Ho
    November 30th, 2009 at 22:57 | #15

    @john
    Hey John,
    I tried your suggestions and was still getting the same result. So I restarted my server and then my requests was processed as expected. I think I left my server running when I was configuring my settings and the server didn't pick up the changes I made to the app?

  16. AdamK
    December 7th, 2009 at 21:46 | #16

    I would like to update clearance to support authentication via prototype calls. It looks like I need to go into the clearance controllers adding support for xhr requests, but would like to get confirmation that is the right approach.

    Also, as I've attempted to implement the approach above, but so far, my partial template that gets reloaded after authentication doesn't seem to recognize the sign in until after I do a full reload of the page. Could that be a caching issue or potentially something else? If it is caching, is there a way to force the browser to ignore the cached partail?

  17. December 7th, 2009 at 22:09 | #17

    @AdamK

    What is your use case?

    Can you pass basic auth params via Prototype? (I know you can do it with the underlying XMLHttpRequest.) Are you sure that you need to authenticate? Because if you are authenticating with JavaScript, then the credentials would be stored in the page, and that wouldn't be very secure.

  18. AdamK
    December 7th, 2009 at 22:30 | #18

    @john

    This would be the main mechanism to manage user authentication on the site. I was planning to create a partial which would dynamically display either a login form or a welcome message & logout link, depending on the current auth status. The login form submits the auth params using a remote_form_for call and then the clearance SessionController would essentially ask to reload the original partial, which should now display the logged in version.

    Are there security concerns with this approach that I'm not aware of? More generally am I thinking about this in the right way?

  19. December 7th, 2009 at 22:38 | #19

    @AdamK

    Mmm . . . I'm not sure you would need to do an xhr post. You should be able to accomplish the form submission with a remote submission and a partial . . . I think. I'll try to look at this a bit, if I can scrounge up some time.

    (I think you're trying to do a login in the manner of http://www.tourfilter.com ? (Register and see how they do it.)

    Unless your final project promised to handle login this way, you should try to get working code with the regular everyday login.

  20. AdamK
    December 7th, 2009 at 22:52 | #20

    @john
    Tourfilter is similar, but they do a full redirect after login.

    My final proposal promised to "Ajaxify", but didn't specifically call out authentication, so I'll move on with the regular everyday login for now & come back to this later.

    Thanks for all your prompt responses.

  21. Ken Busch
    December 16th, 2009 at 18:37 | #21

    This screencast has me adding a config.gem call to config/environment.rb for clearance. However, clearance depends on factory_girl.

    Shouldn't I also have an entry for each of the dependencies? Does the gem mechanism take care of tracking those down?

  22. December 16th, 2009 at 19:33 | #22

    @Ken Busch

    Yes, add a config.gem line for factory_girl, too -- I think there is actually a missing dependency in their gemspec.

  23. Ron Newman
    February 19th, 2010 at 21:50 | #23

    Looks like some time shortly after you wrote this, they renamed the gem from 'thoughtbot-clearance' to just 'clearance' . It's now been updated to version 0.8.6 -- but 'gem install' and 'gem update' will find the latest version only if you use the new name.

  1. No trackbacks yet.