Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tag: 1.1.0

Fetching latest commit…

Cannot retrieve the latest commit at this time

..
Failed to load latest commit information.
lib
spec
LICENSE
README.textile
Rakefile
TODO
merb-auth-core.gemspec

README.textile

merb-auth-core

MerbAuth is an authentication framework for use with the
Merb web framework.

MerbAuth does not try to dictate what you should use as a user model, or how
it should authenticate. Instead it focuses on the logic required to check
that an object passes authentication, and store the keys of authenticated
objects in the session. This is in fact the guiding principle of MerbAuth. The Session is
used as the place for authentication, with a sprinkling of controller helpers.
You can choose to protect a controller action, or a route / group of routes.
This makes sense to talk about an authenticated session. For example, inside
your controller:

  • session.authenticated?
    returns true if the session has been
    authenticated. False otherwise # session.authenticate(controller)
    authenticates the session based on customizable user defined rules
  • session.user
    returns the currently authenticated user object
  • session.user=
    manually sets the currently authenticated user object
  • session.abandon!
    sets the session to unauthenticated, and clears all session data
  • session.authenticate!
    authenticates the session against the active strategies

MerbAuth makes use of Merb’s exception handling facilities which return correct
HTTP status codes when a 200 OK would be inappropriate. To fail a login, or
to force a login at any point in your controller code, simply raise an
Unauthenticated exception, with an optional message and the user will be
presented with login page. The login page is in fact the html view for
Extensions#unauthenticated

To protect your controllers, add a simple before filter to your controller.

before :ensure_authenticated

It is possible to use MerbAuth with any object as a user object, provided
that object does not evaluate to false and it can be serialized in an out of the session.
For this reason, merb-auth-core does not try to implement even a simple login
form for you, since it may not meet your requirements.

How Does It Authenticate my arbitrary user?

This is very similar to the BootLoader process in Merbs initialization.
You declare a class that inherits from Merb::Authentication::Strategy and
define an instance method run!


  class PasswordStrategy &lt Merb::Authentication::Strategy
  
    def run!
      if params[:login] && params[:password]
        user_class.authenticate(params[:login], params[:password])
      end
    end
  end
  

This login strategy uses the authenticate method on the User class to
retrieve a user by login and password. Remember, you can put as much logic
here as you require. The strategy uses an instance variable so the full power of
classes are available to your strategy.

The strategy provides access to the current request giving you access
to the params hash, session etc.

To pass authentication, simply return a non-nil
non-false value from the run! method. Any false or nil value will cause
that strategy to fail. Then the next strategy will be tried.

You can add as many strategies as you like and they will be tried one after
another until either one is found that works (login), or none of them have
passed (failed attempt).


    class PasswordLoginBasicAuth &lt Merb::Authentication::Strategy
      def run!
        if params[:api_key] && params[:api_token]
          Machine.api_authenticate(params[:api_key], params[:api_token])
        end
      end
    end

Now that we have two, they will be executed in the order that they were declared
when we call session.authenticate!(self). The first one that
returns a value that doesn’t evaluate to false, will be considered the winner.

Customizing the user_class

Notice the user_class method in the above strategy examples. This is a convenience method on a strategy
to provide you with the user_class to use for this strategy. You can overwrite
this method on a per strategy basis to use different user model types. You do not have to use this method
and it’s only there to keep track of the “default” user class. (if any)

By default the strategy#user_class method will defer to Merb::Authentication#user_class. You can
set which is the “default class” that Merb::Authentication will use in the provided strategies by
setting it in Merb.root/merb/merb-auth/setup.rb


    
    Merb::Authentication.user_class = Person

This will cascade throughout the default strategies, and your own strategies using the user class
that you defined. In this case Person.

There is no default class set for Merb::Authentication.user_class by default

Strategies and Inheritance

Strategies may be inherited multiple times to make the job of combining similar aspects easier.
You can inherit as many levels as you like and at any point you may mark a strategy as abstract

An abstract strategy just means that it will not be run when it comes time to authenticate.
Instead it’s good to put common logic in and then inherit from it to keep your strategies DRY.

To mark a class as abstract, use the abstract! class method.


  class AbstractStrategy < Merb::Authentication::Strategy
    abstract!
  end

At any point you can activate a registered strategy. You don’t need to register
your strategies, you just declare them, but plugin developers make life easier when they do.

To activate a registered strategy:


  Merb::Authentication.activate!(:defualt_password_form) 

You can easily mix this in with your own strategies. In you Merb.root/merb-auth/strategies.rb


  class MyStrategy < Merb::Authentication::Strategy
    def run!
      #...
    end
  end

Merb::Authentication.activate!(:default_openid) class AnotherStrategy < Merb::Authentication::Strategy def run! #… end end

This will collect them in order of declaration. i.e.:
MyStrategy, Merb::Authentication::Strategies::Basic::OpenID, AnotherStrategy

Customizing the order of the strategies

By default, strategies are run in the order they are declared. It’s possible
to customize the order that the strategies are called.

Merb::Authentication.default_strategy_order will return an array of
the strategy classes in the order that they will be run.
You can customize this by setting the default_strategy_order array
manually.

Authenticateion.default_strategy_order.order = [Second, First, Fourth]

It’s possible to leave some out, and re-order existing ones. It will error
out if you specify one that doesn’t exist though.

Authentication based on Routes

With MerbAuth you can protect routes rather than individual actions on a controller.
The benefit with doing this is that the request is stopped earlier in the request
process. This is a more efficient method of protection than controller based protection.

The downside is that a controllers action is not guaranteed to be protected. For example


  
  authenticate do 
    match("/one").to(:controller => "one", :action => "index")
  end
  
  match("/two").to(:controller => "one", :action => "index")
  

The /one route is protected, but you can see that both of these routes point to the same
controller action. If the One#index method is accessed through the /two route, there is
no protection. You can specify which strategies to use as arguments to the authenticate method.
The default strategies are used if there is no argument given.

Specifying selected strategies per action

If you need to protect an action regardless of which route leads to it, you can use
controller level protection. Use a before :ensure_authenticated filter to protect actions
in your controller whenever the are accessed.

It’s possible to configure each call to ensure_authenticated with a custom list
of strategies to run. These will be run in order and should have an instance method
of #run!


  class ApiMethods < Application
    before :ensure_authenticated, :with => [
                                            Merb::Authenticated::Strategies::Basic::Form,
                                            Merb::Authenticated::Strategies::Basic::BasicAuth, 
                                            Merb::Authenticated::Strategies::Basic::OpenID, 
                                           ],
                                  :only => [:index]
    before :machine_only, :only => [:create]
    
    def index
      display @stuff
    end

    def create
      stuff = Stuff.create(params[:stuff])
      display stuff
    end
    
    private
    def machine_only
      ensure_authentiated Merb::Authenticated::Strategies::Basic::OAuth, Merb::Authenticated::Strategies::Basic::BasicAuth
    end
  end

You can see in this example that you can specify a list of strategies to use.
These will be executed in the order of the array passed in, with the default order
ignored completely.

Where should Strategies be defined?

You should store your strategies in


  merb
  `-- merb-auth
      |-- setup.rb
      `-- strategies.rb

This is a good place to put everything together so you can see what you’re doing at a glance.
It is also auto included by merb-auth-core when you’re using it.

What Strategies are there?

See merb-auth-more

Storing you user object into the session

You need to tell MerbAuth how to serialize your object into
and out of the session. If possible try not to store large or complex
data items in the session but just store the objects key.

To configure your user object to go in and out of the session, here’s how you
could do it.


    class Merb::Authentication

      # return the value you want stored in the session 
      def store_user(user)
        return nil unless user 
        user.id
      end

      # session info is the data you stored in the session previously 
      def fetch_user(session_info)
        User.get(session_info)
      end
    end

Registering Strategies

Intended for plugin developers as a way to make it easy to use
strategies there is the possibility to register a strategy without loading it.


  Authentication.register(:my_strategy, "/absolute/path/to/strategy.rb")

This then allows developers to use


  Authentication.activate!(:my_strategy)

Providing feedback to users (Error Messages)

There’s at least 4 ways to provide feedback to users for failed logins.

  • Overwrite Merb::Authentication#error_message The return of this method is
    the default message that is passed to the Unauthenticated exception. Overwrite
    this to provide a very basic catch all message.
  • Provide a default message when you declare your before filter.
    
        before :ensure_authenticated, :with => [Openid, :message => "Could not log you in with open ID"]
  • OR
    before :ensure_authentication, :with => {:message => “Sorry Buddy… You Lose”}

    When you pass a message, it will replace the Merb::Authentication#error_message default for this
    action
  • Use an after filter for your login action. This can be used to set your messaging system. For example:
    
        after :set_login_message, :only => [:create]
private def set_login_message if session.authenticated? flash[:message] = “Welcome” else flash[:error] = “Bad.. You Fail” end end
  • Use the authentications error messaging inside your strategies to set error messages there.
    You can add to these errors just like adding to DataMappers validation errors.

session.authentication.errors.add(“Label”, “You Fail”)
Add as many as you like, ask session.authentication.errors.on(:label) to get specific errors etc Really… They’re just like the DataMapper validation errors. The bonus of using this system is that you can add messages inside your Strategies, and then in your views you can do this:

<%= error_messages_for sessions.authentication %>

Additional checks / actions to perform after the user is found

Sometimes you may need to perform additional operations on the user object
after you have found a valid user in the strategy. There is a hook method
Merb::Authentication.after_authentication which is designed for this.

Here’s an example of checking that a user object is active after it’s been
found:

Merb::Authentication.after_authentication do |user, request, params| user.active? ? user : nil end

Pass the user model on if everything is still ok. Return nil if you decide in the
after_authentication hook that the user should in fact not be allowed to be authenticated.

By default this plugin doesn’t actually
authenticate anything ;) It’s up to you to get your model going, and add an
authentication strategy.

To logout use
session.abandon! and to force a login at any time use
raise Unauthenticated, "You Aren't Cool Enough"

Contributors

  1. Adam French – http://adam.speaksoutofturn.com/
  2. Daniel Neighman – http://merbunity.com
  3. Ben Burket – http://benburkert.com/
Something went wrong with that request. Please try again.