hassox / merb-auth
- Source
- Commits
- Network (6)
- Issues (0)
- Downloads (0)
- Wiki (1)
- Graphs
-
Tree:
3b8a20b
merb-auth-core
MerbAuth is a very unopinionated authentication framework for use with the
Merb web framework.
MerbAuth does not try to dictate what you should use as a user type model, or how
it should authenticate. Instead it focuses on the logic required to check
that an object passes authentication, and store authenticated objects in the
session. This is in fact the guiding principle of MerbAuth. The controller is
not the place that an authenticated user of the system should live. A controller
is for directing traffic. Instead, the session is used as the place for
authentication, with a sprinkling of controller helpers. 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
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
taken to the login page.
To protect your controllers, add a simple before filter to your controller.
before :ensure_authenticated
It is possible to use MerbAuth with any object type as a user object, provided
that object does not evaluate to false and it can be retrieved with key.
For this reason, merb-auth-core does not try to implement even a simple login
form for you, since it may not be to your liking.
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 Authentication::Strategy and
define an instance method run!
class PasswordStrategy < Authentication::Strategy
def run!
if params[:login] && params[:password]
user_class.authenticate(params[:login], params[:password])
end
end
end
This login strategy uses the
authenticatefinder on the User class to
retrieve a user byloginandpassword. Remember, you can put as much logic
here as you require.
The strategy provides access to the current controller giving you access
to the params hash, request, session etc.
To pass authentication, simply return a non-nil
non-false object at the end of the run! method. Any false or nil value will cause
that strategy to fail. Then the next one will be tried :) wait… what?
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 < Authentication::Strategy
def run!
if controller.basic_authentication.provided?
controller.basic_authentication.authenticate do |login, password|
user = user_class.authenticate(login, password)
unless user
controller.basic_authentication.request!
throw(:halt, "Login Required")
end
user
end
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.
By default the strategy#user_class method will defer to Authentication#default_user_class
so you can overwrite this method to set the default user class for your application if required.
The default value is User
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.
To mark a class as abstract, use the is_abstract! class method.
class AbstractStrategy < Authentication::Strategy
is_abstract!
end
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.
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.
Specifying selected strategies per action
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 => [
Authenticated::BasicAuth,
Authenticated::OpenID,
Authenticated::OAuth
]
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 Authenticated::Oauth, Authenticated::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?
So far the thinking is that strategies should be setup in a directory for this purpose.
lib/strategies/
This is a good place to put everything together so you can see what you’re doing at a glance.
What Strategies are there?
See merb-auth-more
Storing you user object into the session
You need to tell MerbAuth how to represent your user object in the session
data store, and also how to reconstruct it from that data. You don’t
want to store complex objects in the session if you can avoid it.
To configure your user object to go in and out of the session, here’s how you
could do it.
class 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
Additional checks / actions to perform after the user is found
Sometimes you may need to perform additional operations on the user object
before or after you grab it out of the database when authenticating it. The
Authentication class implements Extlib::Hook so you can just setup hooks to
deal with this.
Here’s an example of checking that a user object is active after it’s been
found:
Notice that to fail the check we raised an Unauthenticated exception. The
session is available in that block assession
Really that’s all there is to it. By default this plugin doesn’t actually
authenticate anything ;) It’s up to you to get your model going, and add an
authentication strategy. Just remember that to login, you just use
session.authenticate(self) inside a controller. To logout use
session.abandon! and to force a login at any time use
raise Unauthenticated, "You Aren't Cool Enough"
Contributors
- Adam French – http://adam.speaksoutofturn.com/
- Daniel Neighman – http://merbunity.com
- Ben Burket – http://benburkert.com/


