Permalink
Browse files

Fleshes out Token strategy.

  • Loading branch information...
1 parent ee2f338 commit df8526d476b049854155fcdc722008fd7b26de22 Michael Bleigh committed Dec 2, 2011
Showing with 122 additions and 9 deletions.
  1. +59 −7 README.md
  2. +11 −2 lib/warden/oauth2/strategies/token.rb
  3. +52 −0 spec/warden/oauth2/strategies/token_spec.rb
View
@@ -32,11 +32,54 @@ end
### Configurable Options
-* **client_model:** A class that responds to `.locate(id,secret=nil)`
- represents a client application. Defaults to `ClientApplication`.
-* **token_model:** A class that responds to `.locate(token)` and
- represents a single access token. Should also respond to
- `#scope?(symbol)` for OAuth2 authorization scopes.
+* **client_model:** A client application class. See **Models** below.
+ Defaults to `ClientApplication`.
+* **token_model:** An access token class. See **Models** below. Defaults
+ to `AccessToken`.
+
+## Models
+
+You will need to supply data models to back up the persistent facets of
+your OAuth 2.0 implementation. Below are examples of the interfaces that
+each require.
+
+### Client Application
+
+```ruby
+class ClientApplication
+ # REQUIRED
+ def self.locate(client_id, client_secret = nil)
+ # Should return a client application matching the client_id
+ # provided, but should ONLY match client_secret if it is
+ # provided.
+ end
+end
+```
+
+### Access Token
+
+```ruby
+class AccessToken
+ # REQUIRED
+ def self.locate(token_string)
+ # Should return an access token matching the string provided.
+ # Note that you MAY include expired access tokens in the result
+ # of this method so long as you implement an instance #expired?
+ # method.
+ end
+
+ # OPTIONAL
+ def expired?
+ # True if the access token has reached its expiration.
+ end
+
+ # OPTIONAL
+ def scope?(scope)
+ # True if the scope passed in (usually a symbol) has been authorized
+ # for this access token.
+ end
+end
+```
## Strategies
@@ -48,6 +91,12 @@ supplied according to the OAuth 2.0 Bearer Token specification
token in string form and then calling the `.locate` method on your
access token model (see **Configuration** above).
+Token-based strategies will also fail if they are expired or lack
+sufficient scope. See **Models** above.
+
+**User:** The Warden user is set to the client application returned by
+`.locate`.
+
### Client
This strategy authenticates an OAuth 2.0 client application directly for
@@ -56,11 +105,14 @@ strategy when you want to create an API for client statistics or if you
wish to rate limit based on a client application even for publicly
accessible endpoints.
+**User:** The Warden user is set to the access token returned by `.locate`.
+
### Public
This strategy succeeds by default and only fails if the authentication
-scope is set and is something other than `:public`. The Warden user is
-set to nil when this strategy succeeds.
+scope is set and is something other than `:public`.
+
+**User:** The Warden user is set to `nil`.
[oauth2]: http://tools.ietf.org/html/draft-ietf-oauth-v2-22
[oauth2-bearer]: http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-08
@@ -5,11 +5,20 @@ module OAuth2
module Strategies
class Token < Warden::Strategies::Base
def authenticate!
- fail "Invalid access token." and return unless token
- success! token
+ if token
+ fail "Expired access token." and return if token.respond_to?(:expired?) && token.expired?
+ fail "Insufficient scope." and return if token.respond_to?(:scope?) && !token.scope?(scope)
+ success! token
+ else
+ fail "Invalid access token." and return unless token
+ end
end
def token
+ Warden::OAuth2.config.token_model.locate(token_string)
+ end
+
+ def token_string
raise NotImplementedError
end
end
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+describe Warden::OAuth2::Strategies::Token do
+ let(:token_model){ mock }
+ let(:strategy){ Warden::OAuth2::Strategies::Token }
+ subject{ strategy.new({'rack.input' => {}}) }
+
+ before do
+ Warden::OAuth2.config.token_model = token_model
+ end
+
+ describe '#token' do
+ it 'should call through to .locate on the token_class with the token string' do
+ token_model.should_receive(:locate).with('abc')
+ subject.stub!(:token_string).and_return('abc')
+ subject.token
+ end
+ end
+
+ describe '#authenticate!' do
+ it 'should be successful if there is a token' do
+ token_instance = mock
+ subject.stub!(:token).and_return(token_instance)
+ subject._run!
+ subject.result.should == :success
+ subject.user.should == token_instance
+ end
+
+ it 'should fail if there is not a token' do
+ subject.stub!(:token).and_return(nil)
+ subject._run!
+ subject.result.should == :failure
+ subject.message.should == "Invalid access token."
+ end
+
+ it 'should fail if the access token is expired' do
+ token_instance = mock(:respond_to? => true, :expired? => true, :scope? => true)
+ subject.stub!(:token).and_return(token_instance)
+ subject._run!
+ subject.result.should == :failure
+ subject.message.should == "Expired access token."
+ end
+
+ it 'should fail if there is insufficient scope' do
+ token_instance = mock(:respond_to? => true, :expired? => false, :scope? => false)
+ subject.stub!(:token).and_return(token_instance)
+ subject._run!
+ subject.result.should == :failure
+ subject.message.should == "Insufficient scope."
+ end
+ end
+end

0 comments on commit df8526d

Please sign in to comment.