rack-oauth2 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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