Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Change the readme to refer to the wiki
- Loading branch information
Daniel Neighman
committed
Apr 19, 2009
1 parent
6472253
commit 5f0c2ff
Showing
5 changed files
with
31 additions
and
263 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,245 +1 @@ | ||
h1. Rack Auth | ||
|
||
A gem that provides authentication to an arbitrary rack application. | ||
|
||
This is still very experimental and incomplete in some pretty fundamental ways so please don't assume this is safe to use in a production application yet. It does however represent the concept. | ||
|
||
Based heavily on merb-auth this library acts as middleware in a rack application and operates at a fairly low level. It is intended that interfaces will be built to make sugary api's inside applications/frameworks. | ||
|
||
h2. Concepts | ||
|
||
The system acts as middleware, must be in the Rack stack after session middleware (require env['rack.session']). | ||
|
||
rack-auth, like merb-auth doesn't require any specific logic for authentication and instead, provides a | ||
mechanism that allows for custom logic to be used. | ||
|
||
The logic for authentication is called a Strategy. You can have multiple strategies and each will be attempted until either one is successful or all fail. If one is halted, or all fail, you should throw an :auth symbol. | ||
|
||
When a failure occurs, the result is generated by a rack application that you specify when configuring the stack as the :failure_app. This is where you would render any login forms etc that you may need e.g. | ||
|
||
<pre><code> | ||
fail_app = lambda{|e| [401, {"Content-Type" => "text/plain"}, ["Fail App"]]} | ||
|
||
@app = Rack::Builder.new do | ||
use Rack::Session::Cookie, :secret => "Foo" | ||
use Rack::Auth::Manager, :failure_app => fail_app | ||
run lambda { |env| [200, {'Content-Type' => 'text/plain'}, 'OK'] } | ||
end | ||
|
||
</code></pre> | ||
|
||
The application can look at the PATH_INFO variable in the rack env to see that it is a /unauthenticated | ||
|
||
You can also use the @:default@ option to specify an array of strategies to try by default | ||
|
||
h2. Strategies | ||
|
||
A strategy is a descendant of Rack::Auth::Strategies::Base and should implement an @authenticate!@ method. | ||
|
||
You declare a strategy like this: | ||
|
||
<pre><code> | ||
Rack::Auth::Strategies.add(:label) do | ||
def authenticate! | ||
# stuff in here for authentication | ||
end | ||
end | ||
</code></pre> | ||
|
||
You can also declare a strategy like this: | ||
|
||
<pre><code> | ||
class MyStrategy < Rack::Auth::Strategies::Base | ||
def authenticate! | ||
# stuff | ||
end | ||
end | ||
|
||
Rack::Auth::Strategies.add(:label, MyStrategy) | ||
</code></pre> | ||
|
||
|
||
And use it like | ||
<pre><code> | ||
env['rack-auth'].authenticated?(:label) | ||
</code></pre> | ||
|
||
The strategy is a class so you can mixin any logic you need to. | ||
|
||
Inside a strategy there are a number of methods available to control what happens: | ||
|
||
* @pass@ - ignore this strategy | ||
* @success!(user_object)@ - flags the strategy as successful, and stores the user_object into the session | ||
* @fail!(message)@ - flags the strategy as a failure with a message | ||
* @redirect!(url, params = {}, opts = {})@ - halts and sets up information to perform a redirect. Does not actually perform the redirect. | ||
* @custom!(rack_array)@ - halts and sets up a custom rack array to return | ||
* @headers(hash_to_merge)@ - access to the headers that will be returned by the strategy | ||
* @message=@ - set the response message directly | ||
* @halt!@ - halt the cascading of strategies. This will mean that this strategy will be treated as containing any information required. | ||
|
||
An example of a password / username strategy may look like this | ||
|
||
<pre><code> | ||
Rack::Auth::Strategies.add(:password) do | ||
def authenticate! | ||
return pass unless params[:username] || params[:password] | ||
u = User.authenticate(params[:username], params[:password]) | ||
u.nil? ? fail!("Could Not Login") : success(u) | ||
end | ||
end | ||
</code></pre> | ||
|
||
The checking code is pretty ugly though in there. You can seperate that out into a seperate method that will be checked before running the strategy. Simply return true to continue, or false to skip this stategy. | ||
|
||
<pre><code> | ||
Rack::Auth::Strategies.add(:password) do | ||
def valid? | ||
params[:username] || params[:password] | ||
end | ||
|
||
def authenticate! | ||
u = User.authenticate(params[:username], params[:password]) | ||
u.nil? ? fail!("Could Not Login") : success(u) | ||
end | ||
end | ||
</pre></code> | ||
|
||
|
||
h2. Checking for authentication | ||
|
||
rack-auth injects a lazy object into the env. If you don't use it it doesn't do anything. You can ask it if a request is authenticated, or, ask it to insist that a request be authenticated. | ||
|
||
Asking for authentication: | ||
<pre><code> | ||
stuff if env['rack-auth'].authenticated?(:strategy1, :strategy2) | ||
</code></pre> | ||
|
||
Insisting on authentication | ||
<pre><code> | ||
env['rack-auth'].authenticate!(:strategy1, :strategy2) | ||
</code></pre> | ||
|
||
When you insist on authentication, if no strategy is found to authenticate, an :auth symbol is thrown. This causes the failure app (login form) to be called. | ||
|
||
h2. Throwing when you want to refuse authentication | ||
|
||
To refuse authentication you can throw :auth. Here's an example: | ||
|
||
<pre><code> | ||
throw(:auth) | ||
throw(:auth, :some => :option) | ||
</code></pre> | ||
|
||
h2. Sessions | ||
|
||
Any kind of object can be used as an authenticated object in rack-auth. This of course means that you need to tell rack-auth how to serialize the objects that you're storing as "user" objects in and out of the session. | ||
|
||
Here's how to do that: | ||
|
||
<pre><code> | ||
class Rack::Auth::Manager | ||
def user_session_key(user) | ||
case user | ||
when ActiveRecord | ||
user.id | ||
when nil | ||
nil | ||
else | ||
raise "Unknown User Type" | ||
end | ||
end | ||
end | ||
</code></pre> | ||
|
||
You'll also need to tell rack-auth how to get the user out of the session: | ||
|
||
<pre><code> | ||
class Rack::Auth::Manager | ||
def user_from_session(key) | ||
return nil if key.nil? | ||
User.find(:first, :id => key) | ||
end | ||
end | ||
</code></pre> | ||
|
||
h2. Scopes | ||
|
||
You can have multiple logins in a single session with rack-auth by using scopes. The default scope is :default | ||
|
||
<pre><code> | ||
# getting scoped users | ||
env['rack-auth'].user #=> default user | ||
env['rack-auth'].user(:sudo) #=> :sudo user | ||
|
||
# setting scoped users | ||
env['rack-auth'].authenticate!(:password, :basic, :scope => :sudo) | ||
env['rack-auth'].set_user(@user, :scope => :secure) | ||
</code></pre> | ||
|
||
h2. Errors | ||
|
||
rack-auth provides an error system that works with helpers like @error_messages_for@. You can set an error at any time during an authenticated request: | ||
|
||
<pre><code> | ||
Rack::Auth::Strategies.add(:foo) do | ||
def authenticate! | ||
# oops | ||
errors.add(:login, "That login didn't work foo!") | ||
end | ||
end | ||
|
||
# In your merb helper | ||
error_messages_for env['rack-auth'] | ||
|
||
# In your specs | ||
env['rack-auth'].errors.on(:login).should == ["That login didn't work foo!"] | ||
</code></pre> | ||
|
||
h2. Hooks | ||
|
||
There are a number of hooks available at key points of the authentication mechanism. | ||
|
||
* after_set_user | ||
* after_authentication | ||
* before_failure | ||
|
||
h3. after_set_user | ||
|
||
This hook is useful to add logic that executes every time a user is set. This happens when authentication occurs, when a page is viewed, or when you manually set a user with @set_user@ | ||
|
||
<pre><code> | ||
Rack::Auth::Manager.after_set_user do |user, auth, opts| | ||
# user is the actual user object | ||
# auth is the proxy authentication object | ||
# opts provides the options you provide, including the scope for this user | ||
end | ||
</code></pre> | ||
|
||
You can setup as many after_set_user hooks as you like. They will be run in order | ||
|
||
h3. after_authentication | ||
|
||
The after_authentication hook is useful for times when you want to execute code after a user is authenticated. This is not the same as when the user is set, as it's only done on the first authentication of the session. | ||
|
||
<pre><code> | ||
Rack::Auth::Manager.after_authentication do |user,auth,opts| | ||
# the opts are the same as after_set_user | ||
end | ||
</code></pre> | ||
|
||
You can setup as many of these hooks as you like | ||
|
||
h3. before_failure | ||
|
||
This hook is used to mutate the rack env hash if required so that the failure app knows what to execute. When the failure app is called, rack-auth sets the PATH_INFO setting to /unauthenticated This is required for rails where a rails controller will look for request.params[:action] to know which method to call. | ||
|
||
<pre><code> | ||
Rack::Auth::Manager.before_failure do |env, opts| | ||
# env is the rack env hash | ||
# opts is the options hash and includes the options you threw with the :auth symbol | ||
end | ||
</code></pre> | ||
|
||
h2. I know there's other stuff | ||
|
||
But I can't think of it right now. | ||
Please see the Rack::Auth Wiki for overview documentation at http://wiki.github.com/hassox/rack-auth |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,2 @@ | ||
* Allow a spec / test mode where a _spec_authenticate! method is called on a strategy instead if present | ||
* Implement back urls | ||
* Document the crap out of it | ||
* Implement back urls |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters