rack-oauth2 0.1.0 → 0.2.0

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/README.rdoc CHANGED
@@ -7,7 +7,7 @@ http://tools.ietf.org/html/draft-ietf-oauth-v2-10
7
7
 
8
8
  == Installation
9
9
 
10
- gem install fb_graph
10
+ gem install rack-oauth2
11
11
 
12
12
  == Resources
13
13
 
@@ -17,23 +17,27 @@ http://tools.ietf.org/html/draft-ietf-oauth-v2-10
17
17
 
18
18
  == Usage
19
19
 
20
- See examples
20
+ === Rails
21
21
 
22
- === End User Authorization Endpoint
22
+ ==== Resource Owner Authorization & Token Endpoint
23
23
 
24
- * example/server/oauth2_controller.rb (Rails)
25
- * example/server/authorize.rb (Sinatra)
24
+ http://gist.github.com/584594
26
25
 
27
- === Token Endpoint
26
+ ==== Protected Resource Middleware Setting
28
27
 
29
- * example/server/oauth2_controller.rb (Rails)
30
- * example/server/token.rb (Sinatra)
28
+ http://gist.github.com/584565
31
29
 
32
- === Protected Resource Endpoint
30
+ === Sinatra
33
31
 
34
- TODO:
32
+ ==== Resource Owner Authorization Endpoint
35
33
 
36
- === Note on Patches/Pull Requests
34
+ http://gist.github.com/584595
35
+
36
+ ==== Token Endpoint
37
+
38
+ http://gist.github.com/584574
39
+
40
+ == Note on Patches/Pull Requests
37
41
 
38
42
  * Fork the project.
39
43
  * Make your feature addition or bug fix.
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ begin
5
5
  require 'jeweler'
6
6
  Jeweler::Tasks.new do |gem|
7
7
  gem.name = 'rack-oauth2'
8
- gem.summary = %Q{Rack Middleware for OAuth2 Client & Server}
8
+ gem.summary = %Q{Rack Middleware for OAuth2 Server}
9
9
  gem.description = %Q{Rack Middleware for OAuth2. Currently support only Server/Provider, not Client/Consumer.}
10
10
  gem.email = 'nov@matake.jp'
11
11
  gem.homepage = 'http://github.com/nov/rack-oauth2'
@@ -30,6 +30,7 @@ Spec::Rake::SpecTask.new(:rcov) do |spec|
30
30
  spec.libs << 'lib' << 'spec'
31
31
  spec.pattern = 'spec/**/*_spec.rb'
32
32
  spec.rcov = true
33
+ spec.rcov_opts = ['--exclude spec,gems']
33
34
  end
34
35
 
35
36
  task :spec => :check_dependencies
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -7,24 +7,30 @@ module Rack
7
7
 
8
8
  def initialize(env)
9
9
  super
10
- verify_required_params!
11
- @client_id = params['client_id']
10
+ missing_params = verify_required_params
11
+ @client_id ||= params['client_id']
12
12
  @scope = Array(params['scope'].to_s.split(' '))
13
+ missing_params << :client_id if @client_id.blank?
14
+ unless missing_params.blank?
15
+ invalid_request!("'#{missing_params.join('\', \'')}' required.", :state => @state, :redirect_uri => @redirect_uri)
16
+ end
17
+ if params['client_id'].present? && @client_id != params['client_id']
18
+ invalid_client!("Multiple client credentials are provided.")
19
+ end
13
20
  end
14
21
 
15
22
  def required_params
16
- [:client_id]
23
+ []
17
24
  end
18
25
 
19
- def verify_required_params!
26
+ def verify_required_params
20
27
  missing_params = []
21
28
  required_params.each do |key|
22
29
  missing_params << key unless params[key.to_s]
23
30
  end
24
- unless missing_params.blank?
25
- raise BadRequest.new(:invalid_request, "'#{missing_params.join('\', \'')}' required", :state => @state, :redirect_uri => @redirect_uri)
26
- end
31
+ missing_params
27
32
  end
33
+
28
34
  end
29
35
  end
30
36
  end
@@ -11,6 +11,7 @@ module Rack
11
11
  end
12
12
 
13
13
  class Request < Abstract::Request
14
+ include Error::Authorize
14
15
  attr_accessor :response_type, :redirect_uri, :state
15
16
 
16
17
  def initialize(env)
@@ -32,9 +33,10 @@ module Rack
32
33
  when 'code_and_token'
33
34
  CodeAndToken
34
35
  else
35
- raise BadRequest.new(:unsupported_response_type, "'#{params['response_type']}' isn't supported.", :state => state, :redirect_uri => redirect_uri)
36
+ unsupported_response_type!("'#{params['response_type']}' isn't supported.")
36
37
  end
37
38
  end
39
+
38
40
  end
39
41
 
40
42
  class Response < Abstract::Response
@@ -14,14 +14,6 @@ module Rack
14
14
  @realm = options[:realm]
15
15
  @scope = Array(options[:scope])
16
16
  @redirect_uri = Util.parse_uri(options[:redirect_uri]) if options[:redirect_uri]
17
- @www_authenticate =
18
- @channel = if options[:www_authenticate].present?
19
- :www_authenticate
20
- elsif @redirect_uri.present?
21
- :query_string
22
- else
23
- :json_body
24
- end
25
17
  end
26
18
 
27
19
  def finish
@@ -35,39 +27,47 @@ module Rack
35
27
  value.blank?
36
28
  end
37
29
  response = Rack::Response.new
38
- case @channel
39
- when :www_authenticate
40
- response.status = status
41
- response.header['WWW-Authenticate'] = "OAuth realm='#{realm}' #{params.collect { |key, value| "#{key}='#{value.to_s}'" }.join(' ')}"
42
- response.write params.to_json
43
- when :query_string
30
+ if @redirect_uri.present?
44
31
  redirect_uri.query = if redirect_uri.query
45
32
  [redirect_uri.query, params.to_query].join('&')
46
33
  else
47
34
  params.to_query
48
35
  end
49
36
  response.redirect redirect_uri.to_s
50
- when :json_body
37
+ else
51
38
  response.status = status
52
39
  response.header['Content-Type'] = 'application/json'
40
+ if realm.present?
41
+ response.header['WWW-Authenticate'] = "OAuth realm='#{realm}' #{params.collect { |key, value| "#{key}='#{value.to_s}'" }.join(' ')}"
42
+ end
53
43
  response.write params.to_json
54
44
  end
55
45
  response.finish
56
46
  end
57
47
  end
58
48
 
49
+ class BadRequest < Error
50
+ def initialize(error, description = "", options = {})
51
+ super(400, error, description, options)
52
+ end
53
+ end
54
+
59
55
  class Unauthorized < Error
60
56
  def initialize(error, description = "", options = {})
61
57
  super(401, error, description, options)
62
58
  end
63
59
  end
64
60
 
65
- class BadRequest < Error
61
+ class Forbidden < Error
66
62
  def initialize(error, description = "", options = {})
67
- super(400, error, description, options)
63
+ super(403, error, description, options)
68
64
  end
69
65
  end
70
66
 
71
67
  end
72
68
  end
73
- end
69
+ end
70
+
71
+ require 'rack/oauth2/server/error/authorize'
72
+ require 'rack/oauth2/server/error/token'
73
+ require 'rack/oauth2/server/error/resource'
@@ -0,0 +1,54 @@
1
+ module Rack
2
+ module OAuth2
3
+ module Server
4
+ class Error
5
+ module Authorize
6
+
7
+ DEFAULT_DESCRIPTION = {
8
+ :invalid_request => "The request is missing a required parameter, includes an unsupported parameter or parameter value, or is otherwise malformed.",
9
+ :invalid_client => "The client identifier provided is invalid.",
10
+ :unauthorized_client => "The client is not authorized to use the requested response type.",
11
+ :redirect_uri_mismatch => "The redirection URI provided does not match a pre-registered value.",
12
+ :access_denied => "The end-user or authorization server denied the request.",
13
+ :unsupported_response_type => "The requested response type is not supported by the authorization server.",
14
+ :invalid_scope => "The requested scope is invalid, unknown, or malformed."
15
+ }
16
+
17
+ def error!(error, description = nil, options = {})
18
+ description ||= DEFAULT_DESCRIPTION[error]
19
+ raise BadRequest.new(error, description, options.merge(:state => state, :redirect_uri => redirect_uri))
20
+ end
21
+
22
+ def invalid_request!(description = nil, options = {})
23
+ error!(:invalid_request, description, options)
24
+ end
25
+
26
+ def invalid_client!(description = nil, options = {})
27
+ error!(:invalid_client, description, options)
28
+ end
29
+
30
+ def unauthorized_client!(description = nil, options = {})
31
+ error!(:unauthorized_client, description, options)
32
+ end
33
+
34
+ def redirect_uri_mismatch!(description = nil, options = {})
35
+ error!(:redirect_uri_mismatch, description, options)
36
+ end
37
+
38
+ def access_denied!(description = nil, options = {})
39
+ error!(:access_denied, description, options)
40
+ end
41
+
42
+ def unsupported_response_type!(description = nil, options = {})
43
+ error!(:unsupported_response_type, description, options)
44
+ end
45
+
46
+ def invalid_scope!(description = nil, options = {})
47
+ error!(:invalid_scope, description, options)
48
+ end
49
+
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,50 @@
1
+ module Rack
2
+ module OAuth2
3
+ module Server
4
+ class Error
5
+ module Resource
6
+
7
+ DEFAULT_DESCRIPTION = {
8
+ :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.",
9
+ :invalid_token => "The access token provided is invalid.",
10
+ :expired_token => "The access token provided has expired.",
11
+ :insufficient_scope => "The request requires higher privileges than provided by the access token."
12
+ }
13
+
14
+ def error!(error, description = nil, options = {})
15
+ description ||= DEFAULT_DESCRIPTION[error]
16
+ options[:realm] = realm
17
+ exception = case error
18
+ when :invalid_token, :expired_token
19
+ Unauthorized
20
+ when :insufficient_scope
21
+ Forbidden
22
+ when :invalid_request
23
+ BadRequest
24
+ else
25
+ raise Error.new(options[:status] || 400, error, description, options)
26
+ end
27
+ raise exception.new(error, description, options)
28
+ end
29
+
30
+ def invalid_request!(description = nil, options = {})
31
+ error!(:invalid_request, description, options)
32
+ end
33
+
34
+ def invalid_token!(description = nil, options = {})
35
+ error!(:invalid_token, description, options)
36
+ end
37
+
38
+ def expired_token!(description = nil, options = {})
39
+ error!(:expired_token, description, options)
40
+ end
41
+
42
+ def insufficient_scope!(description = nil, options = {})
43
+ error!(:insufficient_scope, description, options)
44
+ end
45
+
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,59 @@
1
+ module Rack
2
+ module OAuth2
3
+ module Server
4
+ class Error
5
+ module Token
6
+
7
+ DEFAULT_DESCRIPTION = {
8
+ :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.",
9
+ :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.",
10
+ :unauthorized_client => "The authenticated client is not authorized to use the access grant type provided.",
11
+ :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).",
12
+ :unsupported_grant_type => "The access grant included - its type or another attribute - is not supported by the authorization server.",
13
+ :unsupported_response_type => "The requested response type is not supported by the authorization server.",
14
+ :invalid_scope => "The requested scope is invalid, unknown, malformed, or exceeds the previously granted scope."
15
+ }
16
+
17
+ def error!(error, description = nil, options = {})
18
+ description ||= DEFAULT_DESCRIPTION[error]
19
+ exception = if options.delete(:unauthorized)
20
+ Unauthorized
21
+ else
22
+ BadRequest
23
+ end
24
+ raise exception.new(error, description, options)
25
+ end
26
+
27
+ def invalid_request!(description = nil, options = {})
28
+ error!(:invalid_request, description, options)
29
+ end
30
+
31
+ def invalid_client!(description = nil, options = {})
32
+ error!(:invalid_client, description, options.merge(:unauthorized => via_authorization_header))
33
+ end
34
+
35
+ def unauthorized_client!(description = nil, options = {})
36
+ error!(:unauthorized_client, description, options)
37
+ end
38
+
39
+ def invalid_grant!(description = nil, options = {})
40
+ error!(:invalid_grant, description, options)
41
+ end
42
+
43
+ def unsupported_grant_type!(description = nil, options = {})
44
+ error!(:unsupported_grant_type, description, options)
45
+ end
46
+
47
+ def unsupported_response_type!(description = nil, options = {})
48
+ error!(:unsupported_response_type, description, options)
49
+ end
50
+
51
+ def invalid_scope!(description = nil, options = {})
52
+ error!(:invalid_scope, description, options)
53
+ end
54
+
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -5,13 +5,13 @@ module Rack
5
5
  module Server
6
6
  class Resource < Abstract::Handler
7
7
 
8
- def initialize(app, realm=nil, &authenticator)
8
+ def initialize(app, realm = nil, &authenticator)
9
9
  @app = app
10
10
  super(realm, &authenticator)
11
11
  end
12
12
 
13
13
  def call(env)
14
- request = Request.new(env)
14
+ request = Request.new(env, realm)
15
15
  if request.oauth2?
16
16
  authenticate!(request)
17
17
  env[ACCESS_TOKEN] = request.access_token
@@ -29,9 +29,13 @@ module Rack
29
29
  end
30
30
 
31
31
  class Request < Rack::Request
32
+ include Error::Resource
32
33
 
33
- def initialize(env)
34
+ attr_accessor :realm
35
+
36
+ def initialize(env, realm)
34
37
  @env = env
38
+ @realm = realm
35
39
  @auth_header = Rack::Auth::AbstractRequest.new(env)
36
40
  end
37
41
 
@@ -40,15 +44,14 @@ module Rack
40
44
  end
41
45
 
42
46
  def access_token
43
- @access_token ||= case
44
- when access_token_in_haeder.present? && access_token_in_payload.blank?
45
- access_token_in_haeder
46
- when access_token_in_haeder.blank? && access_token_in_payload.present?
47
- access_token_in_payload
48
- when access_token_in_haeder.present? && access_token_in_payload.present?
49
- raise BadRequest.new(:invalid_request, 'Both Authorization header and payload includes oauth_token.', :www_authenticate => true)
50
- else
47
+ tokens = [access_token_in_haeder, access_token_in_payload].compact
48
+ case Array(tokens).size
49
+ when 0
51
50
  nil
51
+ when 1
52
+ tokens.first
53
+ else
54
+ invalid_request!('Both Authorization header and payload includes oauth_token.', :realm => realm)
52
55
  end
53
56
  end
54
57
 
@@ -1,3 +1,5 @@
1
+ require 'rack/auth/basic'
2
+
1
3
  module Rack
2
4
  module OAuth2
3
5
  module Server
@@ -11,12 +13,21 @@ module Rack
11
13
  end
12
14
 
13
15
  class Request < Abstract::Request
14
- attr_accessor :grant_type, :client_secret
16
+ include Error::Token
17
+
18
+ attr_accessor :grant_type, :client_secret, :via_authorization_header
15
19
 
16
20
  def initialize(env)
17
- super
18
- @grant_type = params['grant_type']
19
- @client_secret = params['client_secret']
21
+ auth = Rack::Auth::Basic::Request.new(env)
22
+ if auth.provided? && auth.basic?
23
+ @client_id, @client_secret = auth.credentials
24
+ @via_authorization_header = true
25
+ super
26
+ else
27
+ super
28
+ @client_secret = params['client_secret']
29
+ end
30
+ @grant_type = params['grant_type']
20
31
  end
21
32
 
22
33
  def required_params
@@ -34,7 +45,7 @@ module Rack
34
45
  when 'refresh_token'
35
46
  RefreshToken
36
47
  else
37
- raise BadRequest.new(:unsupported_grant_type, "'#{params['grant_type']}' isn't supported.")
48
+ unsupported_grant_type!("'#{params['grant_type']}' isn't supported.")
38
49
  end
39
50
  end
40
51