h2. 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.

<code> before :ensure_authenticated </code>

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.

h3. 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!@

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

bq. 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).

<pre><code>
    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
</code></pre>

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.

h3. 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

<pre><code>
    
    Merb::Authentication.user_class = Person

</code></pre>

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

h3. 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.

<pre><code>
  class AbstractStrategy < Merb::Authentication::Strategy
    abstract!
  end
</code></pre>

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:
<pre><code>
  Merb::Authentication.activate!(:defualt_password_form) 
</code></pre>

You can easily mix this in with your own strategies. In you Merb.root/merb-auth/strategies.rb
<pre><code>
  class MyStrategy < Merb::Authentication::Strategy
    def run!
      #...
    end
  end
  
  Merb::Authentication.activate!(:default_openid)
  
  class AnotherStrategy < Merb::Authentication::Strategy
    def run!
      #...
    end
  end
</code></pre>

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

h3. 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.

h3. 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

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

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.

h3. 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!

<pre><code>
  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
</code></pre>

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.

h3. Where should Strategies be defined?

You should store your strategies in 
<pre><code>
  merb
  `-- merb-auth
      |-- setup.rb
      `-- strategies.rb
</code></pre>

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.

h3. What Strategies are there?

See merb-auth-more

h3. 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.

<pre><code>
    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
</code></pre>

h3. 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.

<pre><code>
  Authentication.register(:my_strategy, "/absolute/path/to/strategy.rb")
</code></pre>

This then allows developers to use

<pre><code>
  Authentication.activate!(:my_strategy)
</pre></code>

h3. 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.
  <pre><code>
    before :ensure_authenticated, :with => [Openid, :message => "Could not log you in with open ID"]
    # OR
    before :ensure_authentication, :with => {:message => "Sorry Buddy... You Lose"}
  </code></pre>
  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:
  <pre><code>
    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
  </code></pre>
* 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.
  
  <pre><code>
    session.authentication.errors.add("Label", "You Fail")
  </code></pre>
  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:
  <pre><code>
    <%= error_messages_for sessions.authentication %>
  </pre></code>
  

h3. 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"@

h3. Contributors 

# Adam French - "http://adam.speaksoutofturn.com/":http://adam.speaksoutofturn.com/
# Daniel Neighman - "http://merbunity.com":http://merbunity.com
# Ben Burket - "http://benburkert.com/":http://benburkert.com/
