Permalink
Browse files

Adds custom failure app, makes all error messages consistent w/ OAuth…

… 2.0 specs.
  • Loading branch information...
1 parent 44c8535 commit 1257cb0fbfba76ee421d9475e0fd9d5c1f6b2eb0 Michael Bleigh committed Dec 3, 2011
@@ -20,7 +20,9 @@ def self.configure
yield config
end
+ autoload :FailureApp, 'warden/oauth2/failure_app'
module Strategies
+ autoload :Base, 'warden/oauth2/strategies/base'
autoload :Public, 'warden/oauth2/strategies/public'
autoload :Token, 'warden/oauth2/strategies/token'
autoload :Client, 'warden/oauth2/strategies/client'
@@ -0,0 +1,22 @@
+require 'warden-oauth2'
+
+module Warden
+ module OAuth2
+ class ErrorApp
+ def self.call(env)
+ new.call(env)
+ end
+
+ def call(env)
+ warden = env['warden']
+ strategy = warden.winning_strategy
+ status = strategy.respond_to?(:error_status) ? strategy.error_status : 401
+ headers = {'Content-Type' => 'application/json'}
+ headers['X-Accepted-OAuth-Scopes'] = (strategy.scope || :public).to_s
+ body = "{\"error\":\"#{strategy.message}}"
+
+ Rack::Response.new(body, status, headers).finish
+ end
+ end
+ end
+end
@@ -0,0 +1,22 @@
+module Warden
+ module OAuth2
+ class FailureApp
+ def self.call(env)
+ new.call(env)
+ end
+
+ def call(env)
+ warden = env['warden']
+ strategy = warden.winning_strategy
+
+ body = '{"error":"' + strategy.message.to_s + '"}'
+ status = strategy.error_status rescue 401
+ headers = {'Content-Type' => 'application/json'}
+
+ headers['X-Accepted-OAuth-Scopes'] = (strategy.scope || :public).to_s
+
+ Rack::Response.new(body, status, headers).finish
+ end
+ end
+ end
+end
@@ -0,0 +1,17 @@
+require 'warden-oauth2'
+
+module Warden
+ module OAuth2
+ module Strategies
+ class Base < Warden::Strategies::Base
+ def store?
+ false
+ end
+
+ def error_status
+ 400
+ end
+ end
+ end
+ end
+end
@@ -3,7 +3,7 @@
module Warden
module OAuth2
module Strategies
- class Bearer < Warden::OAuth2::Strategies::Token
+ class Bearer < Token
def valid?
!!token_string
end
@@ -4,17 +4,17 @@
module Warden
module OAuth2
module Strategies
- class Client < Warden::Strategies::Base
+ class Client < Base
attr_reader :client, :client_id, :client_secret
def authenticate!
@client = client_from_http_basic || client_from_request_params
if self.client
- fail "Insufficient scope." and return if scope && client.respond_to?(:scope) && !client.scope?(scope)
- success! self.client, "Authorized with client credentials."
+ fail "insufficient_scope" and return if scope && client.respond_to?(:scope) && !client.scope?(scope)
+ success! self.client
else
- fail "No client credentials provided."
+ fail "invalid_client"
end
end
@@ -33,6 +33,14 @@ def client_from_request_params
def public_client?
client && !client_secret
end
+
+ def error_status
+ case message
+ when "invalid_client" then 401
+ when "insufficient_scope" then 403
+ else 400
+ end
+ end
end
end
end
@@ -3,14 +3,18 @@
module Warden
module OAuth2
module Strategies
- class Public < Warden::Strategies::Base
+ class Public < Base
def authenticate!
if scope && scope.to_sym != :public
- fail! "The requested resource requires a '#{scope}' authorization scope." and return
+ fail! "insufficient_scope" and return
end
success! nil
end
+
+ def error_status
+ 401
+ end
end
end
end
@@ -3,14 +3,18 @@
module Warden
module OAuth2
module Strategies
- class Token < Warden::Strategies::Base
+ class Token < Base
+ def valid?
+ !!token_string
+ end
+
def authenticate!
if token
- fail "Expired access token." and return if token.respond_to?(:expired?) && token.expired?
- fail "Insufficient scope." and return if scope && token.respond_to?(:scope?) && !token.scope?(scope)
+ fail! "invalid_token" and return if token.respond_to?(:expired?) && token.expired?
+ fail! "insufficient_scope" and return if scope && token.respond_to?(:scope?) && !token.scope?(scope)
success! token
else
- fail "Invalid access token." and return unless token
+ fail! "invalid_request" and return unless token
end
end
@@ -21,6 +25,15 @@ def token
def token_string
raise NotImplementedError
end
+
+ def error_status
+ case message
+ when "invalid_token" then 401
+ when "insufficient_scope" then 403
+ when "invalid_request" then 400
+ else 400
+ end
+ end
end
end
end
View
@@ -8,5 +8,5 @@
require 'warden-oauth2'
RSpec.configure do |config|
-
+ config.include Rack::Test::Methods
end
@@ -0,0 +1,30 @@
+require 'spec_helper'
+
+describe Warden::OAuth2::FailureApp do
+ let(:app){ subject }
+ let(:warden){ mock(:winning_strategy => @strategy || strategy) }
+ let(:strategy){ mock(:message => 'invalid_request') }
+
+ context 'with all info' do
+ before do
+ @strategy = mock(:error_status => 502, :message => 'custom', :scope => 'random')
+ get '/unauthenticated', {}, 'warden' => warden
+ end
+
+ it 'should set the status from error_status if there is one' do
+ last_response.status.should == 502
+ end
+
+ it 'should set the message from the message' do
+ last_response.body.should == '{"error":"custom"}'
+ end
+
+ it 'should set the content type' do
+ last_response.headers['Content-Type'].should == 'application/json'
+ end
+
+ it 'should set the X-OAuth-Accepted-Scopes header' do
+ last_response.headers['X-Accepted-OAuth-Scopes'].should == 'random'
+ end
+ end
+end
@@ -56,7 +56,8 @@
it 'should fail if no credentials are passed' do
subject._run!
subject.result.should == :failure
- subject.message.should == "No client credentials provided."
+ subject.message.should == "invalid_client"
+ subject.error_status.should == 401
end
it 'should fail if insufficient scope is provided' do
@@ -65,7 +66,8 @@
subject.stub!(:scope).and_return(:confidential_client)
subject._run!
subject.result.should == :failure
- subject.message.should == "Insufficient scope."
+ subject.message.should == "insufficient_scope"
+ subject.error_status.should == 403
end
end
end
@@ -20,7 +20,7 @@
subject.stub!(:scope).and_return(:user)
subject._run!
subject.should be_halted
- subject.message.should == "The requested resource requires a 'user' authorization scope."
+ subject.message.should == "insufficient_scope"
subject.result.should == :failure
end
end
@@ -30,15 +30,17 @@
subject.stub!(:token).and_return(nil)
subject._run!
subject.result.should == :failure
- subject.message.should == "Invalid access token."
+ subject.message.should == "invalid_request"
+ subject.error_status.should == 400
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."
+ subject.message.should == "invalid_token"
+ subject.error_status.should == 401
end
it 'should fail if there is insufficient scope' do
@@ -47,7 +49,8 @@
subject.stub!(:scope).and_return(:secret)
subject._run!
subject.result.should == :failure
- subject.message.should == "Insufficient scope."
+ subject.message.should == "insufficient_scope"
+ subject.error_status.should == 403
end
end
end

0 comments on commit 1257cb0

Please sign in to comment.