Screencast: Using Clearance for authentication, Part 1 of 2
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.
- 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. - 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. - 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
- 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'
- 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.) - Generate clearance
script/generate clearance
- 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 endThis 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. - 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. - Migrate the database
rake db:migrate
- 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
- 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
- Try it. Browse back to http://localhost:3000/notes
- 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. - It works. We’re authenticated.
- The route for logout is . . . /sign_out, i.e., http://localhost:3000/sign_out
- At this point, we might add sign_in and sign_out links to our scaffold
layout.
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
@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.
I am having the exact same error on linux. maybe something with the version?
After install factory girl everything worked fine.
http://github.com/thoughtbot/factory_girl
@Gabriel
OK.
Just out of curiosity, did you say:
rale gems:unpack
OR
rake gems:unpack GEM=thoughtbot-clearance
rake gems:unpack GEM=thoughtbot-clearance
@Gabriel
Harrumph. Looks like the gem dependencies are screwed up. I've added an errata to install factory_girl as well.
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...
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.)
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)?
@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.
@Gabriel
Gabriel:
Also: What about just putting the validation on the User model -- In app/models/user.rb, make it look like this:
Now your gem is still pristine.
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
@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.
@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?
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?
@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.
@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?
@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.
@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.
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?
@Ken Busch
Yes, add a config.gem line for factory_girl, too -- I think there is actually a missing dependency in their gemspec.
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.