rack-oauth2 0.2.3 → 0.3.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/README.rdoc +1 -0
- data/VERSION +1 -1
- data/lib/rack/oauth2.rb +1 -7
- data/lib/rack/oauth2/server.rb +0 -1
- data/lib/rack/oauth2/server/abstract.rb +2 -1
- data/lib/rack/oauth2/server/abstract/error.rb +55 -0
- data/lib/rack/oauth2/server/abstract/handler.rb +2 -3
- data/lib/rack/oauth2/server/abstract/request.rb +2 -3
- data/lib/rack/oauth2/server/abstract/response.rb +0 -5
- data/lib/rack/oauth2/server/authorize.rb +19 -14
- data/lib/rack/oauth2/server/authorize/code.rb +8 -19
- data/lib/rack/oauth2/server/authorize/error.rb +60 -0
- data/lib/rack/oauth2/server/authorize/token.rb +15 -24
- data/lib/rack/oauth2/server/resource.rb +1 -79
- data/lib/rack/oauth2/server/resource/bearer.rb +74 -0
- data/lib/rack/oauth2/server/resource/bearer/error.rb +80 -0
- data/lib/rack/oauth2/server/token.rb +12 -19
- data/lib/rack/oauth2/server/token/authorization_code.rb +4 -5
- data/lib/rack/oauth2/server/token/error.rb +54 -0
- data/lib/rack/oauth2/server/token/password.rb +0 -2
- data/lib/rack/oauth2/server/token/refresh_token.rb +1 -1
- data/lib/rack/oauth2/server/util.rb +29 -0
- data/rack-oauth2.gemspec +1 -1
- data/spec/rack/oauth2/server/abstract/error_spec.rb +51 -0
- data/spec/rack/oauth2/server/authorize/code_spec.rb +42 -28
- data/spec/rack/oauth2/server/authorize/error_spec.rb +103 -0
- data/spec/rack/oauth2/server/authorize/token_spec.rb +55 -26
- data/spec/rack/oauth2/server/authorize_spec.rb +24 -68
- data/spec/rack/oauth2/server/resource/bearer/error_spec.rb +118 -0
- data/spec/rack/oauth2/server/resource/bearer_spec.rb +117 -0
- data/spec/rack/oauth2/server/token/authorization_code_spec.rb +26 -109
- data/spec/rack/oauth2/server/token/error_spec.rb +77 -0
- data/spec/rack/oauth2/server/token/password_spec.rb +27 -47
- data/spec/rack/oauth2/server/token/refresh_token_spec.rb +22 -43
- data/spec/rack/oauth2/server/token_spec.rb +77 -116
- data/spec/rack/oauth2/server/util_spec.rb +75 -16
- data/spec/spec_helper.rb +0 -12
- metadata +25 -29
- data/lib/rack/oauth2/server/authorize/code_and_token.rb +0 -62
- data/lib/rack/oauth2/server/error.rb +0 -73
- data/lib/rack/oauth2/server/error/authorize.rb +0 -54
- data/lib/rack/oauth2/server/error/resource.rb +0 -50
- data/lib/rack/oauth2/server/error/token.rb +0 -59
- data/lib/rack/oauth2/server/token/assertion.rb +0 -29
- data/spec/rack/oauth2/server/authorize/code_and_token_spec.rb +0 -53
- data/spec/rack/oauth2/server/error/authorize_spec.rb +0 -102
- data/spec/rack/oauth2/server/error/resource_spec.rb +0 -69
- data/spec/rack/oauth2/server/error/token_spec.rb +0 -115
- data/spec/rack/oauth2/server/error_spec.rb +0 -107
- data/spec/rack/oauth2/server/resource_spec.rb +0 -141
- data/spec/rack/oauth2/server/token/assertion_spec.rb +0 -56
@@ -0,0 +1,74 @@
|
|
1
|
+
module Rack
|
2
|
+
module OAuth2
|
3
|
+
module Server
|
4
|
+
module Resource
|
5
|
+
class Bearer < Abstract::Handler
|
6
|
+
ACCESS_TOKEN = 'rack.oauth2.bearer.oauth_token'
|
7
|
+
|
8
|
+
def initialize(app, &authenticator)
|
9
|
+
@app = app
|
10
|
+
super(&authenticator)
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
request = Request.new(env)
|
15
|
+
if request.bearer?
|
16
|
+
authenticate!(request)
|
17
|
+
env[ACCESS_TOKEN] = request.access_token
|
18
|
+
end
|
19
|
+
@app.call(env)
|
20
|
+
rescue Rack::OAuth2::Server::Abstract::Error => e
|
21
|
+
e.finish
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def authenticate!(request)
|
27
|
+
@authenticator.call(request)
|
28
|
+
end
|
29
|
+
|
30
|
+
class Request < Rack::Request
|
31
|
+
def initialize(env)
|
32
|
+
@env = env
|
33
|
+
@auth_header = Rack::Auth::AbstractRequest.new(env)
|
34
|
+
end
|
35
|
+
|
36
|
+
def bearer?
|
37
|
+
access_token.present?
|
38
|
+
end
|
39
|
+
|
40
|
+
def access_token
|
41
|
+
tokens = [access_token_in_haeder, access_token_in_payload].compact
|
42
|
+
case Array(tokens).size
|
43
|
+
when 0
|
44
|
+
nil
|
45
|
+
when 1
|
46
|
+
tokens.first
|
47
|
+
else
|
48
|
+
invalid_request!('Both Authorization header and payload includes access token.')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def access_token_in_haeder
|
53
|
+
if @auth_header.provided? && @auth_header.scheme == :bearer
|
54
|
+
@auth_header.params
|
55
|
+
else
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def access_token_in_payload
|
61
|
+
if params['oauth_token'] && !params['oauth_signature_method']
|
62
|
+
params['oauth_token']
|
63
|
+
else
|
64
|
+
nil # This is OAuth1 request
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
require 'rack/oauth2/server/resource/bearer/error'
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Rack
|
2
|
+
module OAuth2
|
3
|
+
module Server
|
4
|
+
module Resource
|
5
|
+
class Bearer
|
6
|
+
class BadRequest < Abstract::BadRequest
|
7
|
+
end
|
8
|
+
|
9
|
+
class Unauthorized < Abstract::Unauthorized
|
10
|
+
def finish
|
11
|
+
super do |response|
|
12
|
+
response.header['WWW-Authenticate'] = if ErrorMethods::DEFAULT_DESCRIPTION.keys.include?(error)
|
13
|
+
header = "Bearer error=\"#{error}\""
|
14
|
+
header += " error_description=\"#{description}\"" if description.present?
|
15
|
+
header += " error_uri=\"#{uri}\"" if uri.present?
|
16
|
+
header
|
17
|
+
else
|
18
|
+
'Bearer'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Forbidden < Abstract::Forbidden
|
25
|
+
attr_accessor :scope
|
26
|
+
|
27
|
+
def initialize(error = :forbidden, description = nil, options = {})
|
28
|
+
super
|
29
|
+
@scope = options[:scope]
|
30
|
+
end
|
31
|
+
|
32
|
+
def protocol_params
|
33
|
+
super.merge(:scope => Array(scope).join(' '))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module ErrorMethods
|
38
|
+
DEFAULT_DESCRIPTION = {
|
39
|
+
:invalid_request => "The request is missing a required parameter, includes an unsupported parameter or parameter value, repeats the same parameter, uses more than one method for including an access token, or is otherwise malformed.",
|
40
|
+
:invalid_token => "The access token provided is expired, revoked, malformed or invalid for other reasons.",
|
41
|
+
:insufficient_scope => "The request requires higher privileges than provided by the access token."
|
42
|
+
}
|
43
|
+
|
44
|
+
def self.included(klass)
|
45
|
+
DEFAULT_DESCRIPTION.each do |error, default_description|
|
46
|
+
error_method = case error
|
47
|
+
when :invalid_request
|
48
|
+
:bad_request!
|
49
|
+
when :insufficient_scope
|
50
|
+
:forbidden!
|
51
|
+
else
|
52
|
+
:unauthorized!
|
53
|
+
end
|
54
|
+
klass.class_eval <<-ERROR
|
55
|
+
def #{error}!(description = "#{default_description}", options = {})
|
56
|
+
#{error_method} :#{error}, description, options
|
57
|
+
end
|
58
|
+
ERROR
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def bad_request!(error, description = nil, options = {})
|
63
|
+
raise BadRequest.new(error, description, options)
|
64
|
+
end
|
65
|
+
|
66
|
+
def unauthorized!(error = nil, description = nil, options = {})
|
67
|
+
raise Unauthorized.new(error, description, options)
|
68
|
+
end
|
69
|
+
|
70
|
+
def forbidden!(error, description = nil, options = {})
|
71
|
+
raise Forbidden.new(error, description, options)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
Request.send :include, ErrorMethods
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -4,26 +4,21 @@ module Rack
|
|
4
4
|
module OAuth2
|
5
5
|
module Server
|
6
6
|
class Token < Abstract::Handler
|
7
|
-
|
8
7
|
def call(env)
|
9
8
|
request = Request.new(env)
|
10
|
-
request.profile.new(
|
11
|
-
rescue Error => e
|
9
|
+
request.profile.new(&@authenticator).call(env).finish
|
10
|
+
rescue Rack::OAuth2::Server::Abstract::Error => e
|
12
11
|
e.finish
|
13
12
|
end
|
14
13
|
|
15
14
|
class Request < Abstract::Request
|
16
|
-
include Error::Token
|
17
|
-
|
18
15
|
attr_required :grant_type
|
19
16
|
attr_optional :client_secret
|
20
|
-
attr_accessor :via_authorization_header
|
21
17
|
|
22
18
|
def initialize(env)
|
23
19
|
auth = Rack::Auth::Basic::Request.new(env)
|
24
20
|
if auth.provided? && auth.basic?
|
25
21
|
@client_id, @client_secret = auth.credentials
|
26
|
-
@via_authorization_header = true
|
27
22
|
super
|
28
23
|
else
|
29
24
|
super
|
@@ -32,14 +27,12 @@ module Rack
|
|
32
27
|
@grant_type = params['grant_type']
|
33
28
|
end
|
34
29
|
|
35
|
-
def profile
|
30
|
+
def profile
|
36
31
|
case params['grant_type'].to_s
|
37
32
|
when 'authorization_code'
|
38
33
|
AuthorizationCode
|
39
34
|
when 'password'
|
40
35
|
Password
|
41
|
-
when 'assertion'
|
42
|
-
Assertion
|
43
36
|
when 'refresh_token'
|
44
37
|
RefreshToken
|
45
38
|
when ''
|
@@ -48,33 +41,33 @@ module Rack
|
|
48
41
|
unsupported_grant_type!("'#{params['grant_type']}' isn't supported.")
|
49
42
|
end
|
50
43
|
end
|
51
|
-
|
52
44
|
end
|
53
45
|
|
54
46
|
class Response < Abstract::Response
|
55
47
|
attr_required :access_token
|
56
48
|
attr_optional :expires_in, :refresh_token, :scope
|
57
49
|
|
58
|
-
def
|
59
|
-
|
50
|
+
def protocol_params
|
51
|
+
{
|
60
52
|
:access_token => access_token,
|
61
53
|
:expires_in => expires_in,
|
62
54
|
:scope => Array(scope).join(' ')
|
63
|
-
}
|
64
|
-
|
65
|
-
|
66
|
-
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def finish
|
59
|
+
write Util.compact_hash(protocol_params).to_json
|
67
60
|
header['Content-Type'] = "application/json"
|
61
|
+
attr_missing!
|
68
62
|
super
|
69
63
|
end
|
70
64
|
end
|
71
|
-
|
72
65
|
end
|
73
66
|
end
|
74
67
|
end
|
75
68
|
end
|
76
69
|
|
70
|
+
require 'rack/oauth2/server/token/error'
|
77
71
|
require 'rack/oauth2/server/token/authorization_code'
|
78
72
|
require 'rack/oauth2/server/token/password'
|
79
|
-
require 'rack/oauth2/server/token/assertion'
|
80
73
|
require 'rack/oauth2/server/token/refresh_token'
|
@@ -3,7 +3,6 @@ module Rack
|
|
3
3
|
module Server
|
4
4
|
class Token
|
5
5
|
class AuthorizationCode < Abstract::Handler
|
6
|
-
|
7
6
|
def call(env)
|
8
7
|
@request = Request.new(env)
|
9
8
|
@response = Response.new(request)
|
@@ -11,16 +10,16 @@ module Rack
|
|
11
10
|
end
|
12
11
|
|
13
12
|
class Request < Token::Request
|
14
|
-
attr_required :code
|
13
|
+
attr_required :code, :redirect_uri
|
15
14
|
|
16
15
|
def initialize(env)
|
17
16
|
super
|
18
|
-
@grant_type
|
19
|
-
@code
|
17
|
+
@grant_type = :authorization_code
|
18
|
+
@code = params['code']
|
19
|
+
@redirect_uri = params['redirect_uri']
|
20
20
|
attr_missing!
|
21
21
|
end
|
22
22
|
end
|
23
|
-
|
24
23
|
end
|
25
24
|
end
|
26
25
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Rack
|
2
|
+
module OAuth2
|
3
|
+
module Server
|
4
|
+
class Token
|
5
|
+
class BadRequest < Abstract::BadRequest
|
6
|
+
end
|
7
|
+
|
8
|
+
class Unauthorized < Abstract::Unauthorized
|
9
|
+
def finish
|
10
|
+
super do |response|
|
11
|
+
response.header['WWW-Authenticate'] = 'Basic realm="OAuth2 Token Endpoint"'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module ErrorMethods
|
17
|
+
DEFAULT_DESCRIPTION = {
|
18
|
+
:invalid_request => "The request is missing a required parameter, includes an unsupported parameter or parameter value, repeats a parameter, includes multiple credentials, utilizes more than one mechanism for authenticating the client, or is otherwise malformed.",
|
19
|
+
:invalid_client => "The client identifier provided is invalid, the client failed to authenticate, the client did not include its credentials, provided multiple client credentials, or used unsupported credentials type.",
|
20
|
+
:invalid_grant => "The provided access grant is invalid, expired, or revoked (e.g. invalid assertion, expired authorization token, bad end-user password credentials, or mismatching authorization code and redirection URI).",
|
21
|
+
:unauthorized_client => "The authenticated client is not authorized to use the access grant type provided.",
|
22
|
+
:unsupported_grant_type => "The access grant included - its type or another attribute - is not supported by the authorization server.",
|
23
|
+
:invalid_scope => "The requested scope is invalid, unknown, malformed, or exceeds the previously granted scope."
|
24
|
+
}
|
25
|
+
|
26
|
+
def self.included(klass)
|
27
|
+
DEFAULT_DESCRIPTION.each do |error, default_description|
|
28
|
+
error_method = if error == :invalid_client
|
29
|
+
:unauthorized!
|
30
|
+
else
|
31
|
+
:bad_request!
|
32
|
+
end
|
33
|
+
klass.class_eval <<-ERROR
|
34
|
+
def #{error}!(description = "#{default_description}", options = {})
|
35
|
+
#{error_method} :#{error}, description, options
|
36
|
+
end
|
37
|
+
ERROR
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def bad_request!(error, description = nil, options = {})
|
42
|
+
raise BadRequest.new(error, description, options)
|
43
|
+
end
|
44
|
+
|
45
|
+
def unauthorized!(error, description = nil, options = {})
|
46
|
+
raise Unauthorized.new(error, description, options)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Request.send :include, ErrorMethods
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -3,6 +3,12 @@ module Rack
|
|
3
3
|
module Server
|
4
4
|
module Util
|
5
5
|
class << self
|
6
|
+
def compact_hash(hash)
|
7
|
+
hash.reject do |key, value|
|
8
|
+
value.blank?
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
6
12
|
def parse_uri(uri)
|
7
13
|
case uri
|
8
14
|
when URI::Generic
|
@@ -13,6 +19,29 @@ module Rack
|
|
13
19
|
raise "Invalid format of URI is given."
|
14
20
|
end
|
15
21
|
end
|
22
|
+
|
23
|
+
def redirect_uri(base_uri, location, params)
|
24
|
+
redirect_uri = parse_uri base_uri
|
25
|
+
case location
|
26
|
+
when :query
|
27
|
+
redirect_uri.query = [redirect_uri.query, Util.compact_hash(params).to_query].compact.join('&')
|
28
|
+
when :fragment
|
29
|
+
redirect_uri.fragment = Util.compact_hash(params).to_query
|
30
|
+
end
|
31
|
+
redirect_uri.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
def verify_redirect_uri(registered, given)
|
35
|
+
registered = parse_uri(registered)
|
36
|
+
given = parse_uri(given)
|
37
|
+
registered.path = '/' if registered.path.blank?
|
38
|
+
given.path = '/' if given.path.blank?
|
39
|
+
[:scheme, :host, :port].all? do |key|
|
40
|
+
registered.send(key) == given.send(key)
|
41
|
+
end && /^#{registered.path}/ =~ given.path
|
42
|
+
rescue
|
43
|
+
false
|
44
|
+
end
|
16
45
|
end
|
17
46
|
end
|
18
47
|
end
|
data/rack-oauth2.gemspec
CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.add_runtime_dependency "json", ">= 1.4.3"
|
18
18
|
s.add_runtime_dependency "activesupport", ">= 2.3"
|
19
19
|
s.add_runtime_dependency "i18n"
|
20
|
-
s.add_runtime_dependency "attr_required", ">= 0.0.
|
20
|
+
s.add_runtime_dependency "attr_required", ">= 0.0.3"
|
21
21
|
s.add_development_dependency "rake", ">= 0.8"
|
22
22
|
s.add_development_dependency "rcov", ">= 0.9"
|
23
23
|
s.add_development_dependency "rspec", ">= 2"
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Rack::OAuth2::Server::Abstract::Error do
|
4
|
+
|
5
|
+
context 'when full attributes are given' do
|
6
|
+
subject do
|
7
|
+
Rack::OAuth2::Server::Abstract::Error.new 400, :invalid_request, 'Missing some required params', :uri => 'http://server.example.com/error'
|
8
|
+
end
|
9
|
+
its(:status) { should == 400 }
|
10
|
+
its(:error) { should == :invalid_request }
|
11
|
+
its(:description) { should == 'Missing some required params' }
|
12
|
+
its(:uri) { should == 'http://server.example.com/error' }
|
13
|
+
its(:protocol_params) do
|
14
|
+
should == {
|
15
|
+
:error => :invalid_request,
|
16
|
+
:error_description => 'Missing some required params',
|
17
|
+
:error_uri => 'http://server.example.com/error'
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'when optional attributes are not given' do
|
23
|
+
subject do
|
24
|
+
Rack::OAuth2::Server::Abstract::Error.new 400, :invalid_request
|
25
|
+
end
|
26
|
+
its(:status) { should == 400 }
|
27
|
+
its(:error) { should == :invalid_request }
|
28
|
+
its(:description) { should be_nil }
|
29
|
+
its(:uri) { should be_nil }
|
30
|
+
its(:protocol_params) do
|
31
|
+
should == {
|
32
|
+
:error => :invalid_request,
|
33
|
+
:error_description => nil,
|
34
|
+
:error_uri => nil
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
describe Rack::OAuth2::Server::Abstract::BadRequest do
|
42
|
+
its(:status) { should == 400 }
|
43
|
+
end
|
44
|
+
|
45
|
+
describe Rack::OAuth2::Server::Abstract::Unauthorized do
|
46
|
+
its(:status) { should == 401 }
|
47
|
+
end
|
48
|
+
|
49
|
+
describe Rack::OAuth2::Server::Abstract::Forbidden do
|
50
|
+
its(:status) { should == 403 }
|
51
|
+
end
|