rack-oauth2 0.2.3 → 0.3.0.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|