oauth2-provider 0.0.16

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.
Files changed (101) hide show
  1. data/.gitignore +8 -0
  2. data/Gemfile +3 -0
  3. data/README.md +49 -0
  4. data/Rakefile +24 -0
  5. data/examples/client/Gemfile +6 -0
  6. data/examples/client/Gemfile.lock +20 -0
  7. data/examples/client/README +8 -0
  8. data/examples/client/app.rb +59 -0
  9. data/examples/client/config.ru +3 -0
  10. data/examples/client/views/home.haml +3 -0
  11. data/examples/client/views/response.haml +11 -0
  12. data/examples/rails3-example/.gitignore +4 -0
  13. data/examples/rails3-example/Gemfile +10 -0
  14. data/examples/rails3-example/Gemfile.lock +82 -0
  15. data/examples/rails3-example/README +9 -0
  16. data/examples/rails3-example/Rakefile +7 -0
  17. data/examples/rails3-example/app/controllers/account_controller.rb +14 -0
  18. data/examples/rails3-example/app/controllers/application_controller.rb +18 -0
  19. data/examples/rails3-example/app/controllers/authorization_controller.rb +18 -0
  20. data/examples/rails3-example/app/controllers/home_controller.rb +4 -0
  21. data/examples/rails3-example/app/controllers/session_controller.rb +24 -0
  22. data/examples/rails3-example/app/helpers/application_helper.rb +2 -0
  23. data/examples/rails3-example/app/models/account.rb +6 -0
  24. data/examples/rails3-example/app/views/authorization/new.html.erb +5 -0
  25. data/examples/rails3-example/app/views/home/show.html.erb +1 -0
  26. data/examples/rails3-example/app/views/layouts/application.html.erb +16 -0
  27. data/examples/rails3-example/app/views/session/new.html.erb +7 -0
  28. data/examples/rails3-example/config.ru +4 -0
  29. data/examples/rails3-example/config/application.rb +42 -0
  30. data/examples/rails3-example/config/boot.rb +6 -0
  31. data/examples/rails3-example/config/database.yml +22 -0
  32. data/examples/rails3-example/config/environment.rb +5 -0
  33. data/examples/rails3-example/config/environments/development.rb +26 -0
  34. data/examples/rails3-example/config/environments/production.rb +49 -0
  35. data/examples/rails3-example/config/environments/test.rb +35 -0
  36. data/examples/rails3-example/config/initializers/backtrace_silencers.rb +7 -0
  37. data/examples/rails3-example/config/initializers/inflections.rb +10 -0
  38. data/examples/rails3-example/config/initializers/mime_types.rb +5 -0
  39. data/examples/rails3-example/config/initializers/secret_token.rb +7 -0
  40. data/examples/rails3-example/config/initializers/session_store.rb +8 -0
  41. data/examples/rails3-example/config/locales/en.yml +5 -0
  42. data/examples/rails3-example/config/routes.rb +9 -0
  43. data/examples/rails3-example/db/migrate/20110508151935_add_account_table.rb +12 -0
  44. data/examples/rails3-example/db/migrate/20110508151948_add_oauth2_tables.rb +43 -0
  45. data/examples/rails3-example/db/schema.rb +52 -0
  46. data/examples/rails3-example/db/seeds.rb +11 -0
  47. data/examples/rails3-example/doc/README_FOR_APP +2 -0
  48. data/examples/rails3-example/lib/tasks/.gitkeep +0 -0
  49. data/examples/rails3-example/public/404.html +26 -0
  50. data/examples/rails3-example/public/422.html +26 -0
  51. data/examples/rails3-example/public/500.html +26 -0
  52. data/examples/rails3-example/public/favicon.ico +0 -0
  53. data/examples/rails3-example/public/images/rails.png +0 -0
  54. data/examples/rails3-example/public/robots.txt +5 -0
  55. data/examples/rails3-example/public/stylesheets/.gitkeep +0 -0
  56. data/examples/rails3-example/script/rails +6 -0
  57. data/lib/oauth2-provider.rb +3 -0
  58. data/lib/oauth2/provider.rb +39 -0
  59. data/lib/oauth2/provider/models.rb +40 -0
  60. data/lib/oauth2/provider/models/access_token.rb +54 -0
  61. data/lib/oauth2/provider/models/active_record.rb +30 -0
  62. data/lib/oauth2/provider/models/active_record/access_token.rb +13 -0
  63. data/lib/oauth2/provider/models/active_record/authorization.rb +16 -0
  64. data/lib/oauth2/provider/models/active_record/authorization_code.rb +13 -0
  65. data/lib/oauth2/provider/models/active_record/client.rb +15 -0
  66. data/lib/oauth2/provider/models/authorization.rb +40 -0
  67. data/lib/oauth2/provider/models/authorization_code.rb +27 -0
  68. data/lib/oauth2/provider/models/client.rb +28 -0
  69. data/lib/oauth2/provider/models/mongoid.rb +30 -0
  70. data/lib/oauth2/provider/models/mongoid/access_token.rb +40 -0
  71. data/lib/oauth2/provider/models/mongoid/authorization.rb +32 -0
  72. data/lib/oauth2/provider/models/mongoid/authorization_code.rb +43 -0
  73. data/lib/oauth2/provider/models/mongoid/client.rb +40 -0
  74. data/lib/oauth2/provider/rack.rb +11 -0
  75. data/lib/oauth2/provider/rack/access_token_handler.rb +103 -0
  76. data/lib/oauth2/provider/rack/authorization_code_request.rb +74 -0
  77. data/lib/oauth2/provider/rack/authorization_codes_support.rb +25 -0
  78. data/lib/oauth2/provider/rack/middleware.rb +28 -0
  79. data/lib/oauth2/provider/rack/resource_request.rb +91 -0
  80. data/lib/oauth2/provider/rack/responses.rb +34 -0
  81. data/lib/oauth2/provider/rails.rb +37 -0
  82. data/lib/oauth2/provider/rails/controller_authentication.rb +21 -0
  83. data/lib/oauth2/provider/random.rb +30 -0
  84. data/lib/oauth2/provider/version.rb +5 -0
  85. data/oauth2-provider.gemspec +35 -0
  86. data/spec/models/access_token_spec.rb +123 -0
  87. data/spec/models/authorization_code_spec.rb +115 -0
  88. data/spec/models/authorization_spec.rb +110 -0
  89. data/spec/models/client_spec.rb +75 -0
  90. data/spec/requests/access_tokens_controller_spec.rb +360 -0
  91. data/spec/requests/authentication_spec.rb +150 -0
  92. data/spec/requests/authorization_codes_support_spec.rb +157 -0
  93. data/spec/schema.rb +38 -0
  94. data/spec/set_backend_env_to_mongoid.rb +1 -0
  95. data/spec/spec_helper.rb +27 -0
  96. data/spec/support/activerecord_backend.rb +18 -0
  97. data/spec/support/factories.rb +56 -0
  98. data/spec/support/macros.rb +46 -0
  99. data/spec/support/mongoid_backend.rb +34 -0
  100. data/spec/support/rack.rb +32 -0
  101. metadata +373 -0
@@ -0,0 +1,40 @@
1
+ class OAuth2::Provider::Models::Mongoid::AccessToken
2
+ module Behaviour
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ include ::Mongoid::Document
7
+ include OAuth2::Provider::Models::AccessToken
8
+
9
+ field :access_token
10
+ field :expires_at, :type => Time
11
+ field :refresh_token
12
+
13
+ referenced_in(:authorization,
14
+ :class_name => OAuth2::Provider.authorization_class_name,
15
+ :foreign_key => :oauth_authorization_id
16
+ )
17
+
18
+ referenced_in(:client,
19
+ :class_name => OAuth2::Provider.client_class_name,
20
+ :foreign_key => :oauth_client_id
21
+ )
22
+
23
+ before_save do
24
+ self.client ||= authorization.client
25
+ end
26
+ end
27
+
28
+ module ClassMethods
29
+ def find_by_refresh_token(refresh_token)
30
+ where(:refresh_token => refresh_token).first
31
+ end
32
+
33
+ def find_by_access_token(access_token)
34
+ where(:access_token => access_token).first
35
+ end
36
+ end
37
+ end
38
+
39
+ include Behaviour
40
+ end
@@ -0,0 +1,32 @@
1
+ class OAuth2::Provider::Models::Mongoid::Authorization
2
+ module Behaviour
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ include ::Mongoid::Document
7
+ include OAuth2::Provider::Models::Authorization
8
+
9
+ field :scope
10
+ field :expires_at, :type => Time
11
+ field :resource_owner_id
12
+ field :resource_owner_type
13
+
14
+ referenced_in(:client,
15
+ :class_name => OAuth2::Provider.client_class_name,
16
+ :foreign_key => :oauth_client_id
17
+ )
18
+
19
+ references_many(:access_tokens,
20
+ :class_name => OAuth2::Provider.access_token_class_name,
21
+ :foreign_key => :oauth_authorization_id
22
+ )
23
+
24
+ references_many(:authorization_codes,
25
+ :class_name => OAuth2::Provider.authorization_code_class_name,
26
+ :foreign_key => :oauth_authorization_id
27
+ )
28
+ end
29
+ end
30
+
31
+ include Behaviour
32
+ end
@@ -0,0 +1,43 @@
1
+ class OAuth2::Provider::Models::Mongoid::AuthorizationCode
2
+ module Behaviour
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ include ::Mongoid::Document
7
+ include OAuth2::Provider::Models::AuthorizationCode
8
+
9
+ field :code
10
+ field :expires_at, :type => Time
11
+
12
+ referenced_in(:authorization,
13
+ :class_name => OAuth2::Provider.authorization_class_name,
14
+ :foreign_key => :oauth_authorization_id
15
+ )
16
+
17
+ referenced_in(:client,
18
+ :class_name => OAuth2::Provider.client_class_name,
19
+ :foreign_key => :oauth_client_id
20
+ )
21
+
22
+ before_save do
23
+ self.client ||= authorization.client
24
+ end
25
+ end
26
+
27
+ module ClassMethods
28
+ def find_by_code_and_redirect_uri(code, redirect_uri)
29
+ where(:code => code, :redirect_uri => redirect_uri).first
30
+ end
31
+
32
+ def find_by_id(id)
33
+ where(:id => id).first
34
+ end
35
+
36
+ def find_by_code(code)
37
+ where(:code => code).first
38
+ end
39
+ end
40
+ end
41
+
42
+ include Behaviour
43
+ end
@@ -0,0 +1,40 @@
1
+ class OAuth2::Provider::Models::Mongoid::Client
2
+ module Behaviour
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ include ::Mongoid::Document
7
+ include OAuth2::Provider::Models::Client
8
+
9
+ field :oauth_secret
10
+ field :oauth_identifier
11
+
12
+ references_many(:authorizations,
13
+ :class_name => OAuth2::Provider.authorization_class_name,
14
+ :foreign_key => :oauth_client_id
15
+ )
16
+
17
+ references_many(:access_tokens,
18
+ :class_name => OAuth2::Provider.access_token_class_name,
19
+ :foreign_key => :oauth_client_id
20
+ )
21
+
22
+ references_many(:authorization_codes,
23
+ :class_name => OAuth2::Provider.authorization_code_class_name,
24
+ :foreign_key => :oauth_client_id
25
+ )
26
+ end
27
+
28
+ module ClassMethods
29
+ def find_by_oauth_identifier(identifier)
30
+ where(:oauth_identifier => identifier).first
31
+ end
32
+
33
+ def find_by_oauth_identifier_and_oauth_secret(identifier, secret)
34
+ where(:oauth_identifier => identifier, :oauth_secret => secret).first
35
+ end
36
+ end
37
+ end
38
+
39
+ include Behaviour
40
+ end
@@ -0,0 +1,11 @@
1
+ module OAuth2::Provider::Rack
2
+ autoload :AccessTokenHandler, 'oauth2/provider/rack/access_token_handler'
3
+ autoload :AuthenticationHandler, 'oauth2/provider/rack/authentication_handler'
4
+ autoload :AuthenticationMediator, 'oauth2/provider/rack/authentication_mediator'
5
+ autoload :AuthorizationCodeRequest, 'oauth2/provider/rack/authorization_code_request'
6
+ autoload :Middleware, 'oauth2/provider/rack/middleware'
7
+ autoload :Request, 'oauth2/provider/rack/request'
8
+ autoload :ResourceRequest, 'oauth2/provider/rack/resource_request'
9
+ autoload :Responses, 'oauth2/provider/rack/responses'
10
+ autoload :AuthorizationCodesSupport, 'oauth2/provider/rack/authorization_codes_support'
11
+ end
@@ -0,0 +1,103 @@
1
+ module OAuth2::Provider::Rack
2
+ class AccessTokenHandler
3
+ attr_reader :app, :env, :request
4
+
5
+ def initialize(app, env)
6
+ @app = app
7
+ @env = env
8
+ @request = env['oauth2']
9
+ end
10
+
11
+ def process
12
+ if request.post?
13
+ block_unsupported_grant_types || block_invalid_clients || handle_grant_type
14
+ else
15
+ Responses.only_supported 'POST'
16
+ end
17
+ end
18
+
19
+ def handle_grant_type
20
+ send grant_type_handler_method(request.params["grant_type"])
21
+ end
22
+
23
+ def handle_password_grant_type
24
+ with_required_params 'username', 'password' do |username, password|
25
+ if resource_owner = OAuth2::Provider.resource_owner_class.authenticate_with_username_and_password(username, password)
26
+ token_response OAuth2::Provider.access_token_class.create!(
27
+ :authorization => OAuth2::Provider.authorization_class.create!(:resource_owner => resource_owner, :client => oauth_client)
28
+ )
29
+ else
30
+ Responses.json_error 'invalid_grant'
31
+ end
32
+ end
33
+ end
34
+
35
+ def handle_authorization_code_grant_type
36
+ with_required_params 'code', 'redirect_uri' do |code, redirect_uri|
37
+ if token = oauth_client.authorization_codes.claim(code, redirect_uri)
38
+ token_response token
39
+ else
40
+ Responses.json_error 'invalid_grant'
41
+ end
42
+ end
43
+ end
44
+
45
+ def handle_refresh_token_grant_type
46
+ with_required_params 'refresh_token' do |refresh_token|
47
+ if token = oauth_client.access_tokens.refresh_with(refresh_token)
48
+ token_response token
49
+ else
50
+ Responses.json_error 'invalid_grant'
51
+ end
52
+ end
53
+ end
54
+
55
+ def with_required_params(*names, &block)
56
+ missing_params = names - request.params.keys
57
+ if missing_params.empty?
58
+ yield *request.params.values_at(*names)
59
+ else
60
+ if missing_params.size == 1
61
+ Responses.json_error 'invalid_request', :description => "missing '#{missing_params.join}' parameter"
62
+ else
63
+ describe_parameters = missing_params.map{|x| "'#{x}'"}.join(", ")
64
+ Responses.json_error 'invalid_request', :description => "missing #{describe_parameters} parameters"
65
+ end
66
+ end
67
+ end
68
+
69
+ def token_response(token)
70
+ json = token.as_json.tap do |json|
71
+ json[:state] = request.params['state'] if request.params['state']
72
+ end
73
+ [200, {'Content-Type' => 'application/json', 'Cache-Control' => 'no-cache, no-store, max-age=0, must-revalidate'}, [ActiveSupport::JSON.encode(json)]]
74
+ end
75
+
76
+ def block_unsupported_grant_types
77
+ with_required_params 'grant_type' do |grant_type|
78
+ unless respond_to?(grant_type_handler_method(grant_type), true)
79
+ Responses.json_error 'unsupported_grant_type'
80
+ end
81
+ end
82
+ end
83
+
84
+ def block_invalid_clients
85
+ with_required_params 'grant_type', 'client_id', 'client_secret' do |grant_type, client_id, client_secret|
86
+ @oauth_client = OAuth2::Provider.client_class.find_by_oauth_identifier_and_oauth_secret(client_id, client_secret)
87
+ if @oauth_client.nil?
88
+ Responses.json_error 'invalid_client'
89
+ elsif !@oauth_client.allow_grant_type?(grant_type)
90
+ Responses.json_error 'unauthorized_client'
91
+ end
92
+ end
93
+ end
94
+
95
+ def oauth_client
96
+ @oauth_client
97
+ end
98
+
99
+ def grant_type_handler_method(grant_type)
100
+ "handle_#{grant_type}_grant_type"
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,74 @@
1
+ module OAuth2::Provider::Rack
2
+ class AuthorizationCodeRequest
3
+ def initialize(env, params)
4
+ @env = env
5
+ @params = params
6
+ end
7
+
8
+ def validate!
9
+ unless redirect_uri
10
+ throw_response [400, {}, ['No redirect_uri provided']]
11
+ end
12
+
13
+ unless redirect_uri_valid?
14
+ throw_response [400, {}, ['Provided redirect_uri is invalid']]
15
+ end
16
+
17
+ unless client_id
18
+ throw_response Responses.redirect_with_error('invalid_request', redirect_uri)
19
+ end
20
+
21
+ unless client
22
+ throw_response Responses.redirect_with_error('invalid_client', redirect_uri)
23
+ end
24
+ end
25
+
26
+ def grant!(resource_owner = nil, authorization_expires_at = nil)
27
+ grant = client.authorizations.create!(
28
+ :resource_owner => resource_owner,
29
+ :client => client,
30
+ :scope => scope,
31
+ :expires_at => authorization_expires_at
32
+ )
33
+ code = grant.authorization_codes.create! :redirect_uri => redirect_uri
34
+ throw_response Responses.redirect_with_code(code.code, redirect_uri)
35
+ end
36
+
37
+ def deny!
38
+ throw_response Responses.redirect_with_error('access_denied', redirect_uri)
39
+ end
40
+
41
+ def invalid_scope!
42
+ throw_response Responses.redirect_with_error('invalid_scope', redirect_uri)
43
+ end
44
+
45
+ def client_id
46
+ @params['client_id']
47
+ end
48
+
49
+ def client
50
+ @client ||= OAuth2::Provider.client_class.from_param(client_id)
51
+ end
52
+
53
+ def redirect_uri
54
+ @params['redirect_uri']
55
+ end
56
+
57
+ def redirect_uri_valid?
58
+ Addressable::URI.parse(redirect_uri)
59
+ rescue
60
+ nil
61
+ end
62
+
63
+ def scope
64
+ @params['scope']
65
+ end
66
+
67
+ private
68
+
69
+ def throw_response(response)
70
+ @env['oauth2.response'] = response
71
+ throw :oauth2
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,25 @@
1
+ require 'addressable/uri'
2
+
3
+ module OAuth2::Provider::Rack::AuthorizationCodesSupport
4
+ protected
5
+
6
+ def oauth2_authorization_request
7
+ request.env['oauth2.authorization_request'] ||= OAuth2::Provider::Rack::AuthorizationCodeRequest.new(request.env, request.params)
8
+ end
9
+
10
+ def block_invalid_authorization_code_requests
11
+ oauth2_authorization_request.validate!
12
+ end
13
+
14
+ def grant_authorization_code(resource_owner = nil, authorization_expires_at = nil)
15
+ oauth2_authorization_request.grant! resource_owner, authorization_expires_at
16
+ end
17
+
18
+ def deny_authorization_code
19
+ oauth2_authorization_request.deny!
20
+ end
21
+
22
+ def declare_oauth_scope_invalid
23
+ oauth2_authorization_request.invalid_scope!
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ module OAuth2::Provider::Rack
2
+ class Middleware
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def call(env)
8
+ request = env['oauth2'] = ResourceRequest.new(env)
9
+
10
+ response = catch :oauth2 do
11
+ if request.path == "/oauth/access_token"
12
+ AccessTokenHandler.new(@app, env).process
13
+ else
14
+ @app.call(env)
15
+ end
16
+ end
17
+
18
+ thrown_response(env) || response
19
+ end
20
+
21
+ def thrown_response(env)
22
+ if env['oauth2.response']
23
+ env['warden'] && env['warden'].custom_failure!
24
+ env['oauth2.response']
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,91 @@
1
+ require 'rack/auth/abstract/request'
2
+
3
+ module OAuth2::Provider::Rack
4
+ class ResourceRequest < Rack::Request
5
+ delegate :has_scope?, :to => :authorization
6
+
7
+ def token
8
+ token_from_param || token_from_header
9
+ end
10
+
11
+ def has_token?
12
+ !token.nil?
13
+ end
14
+
15
+ def token_from_param
16
+ params["oauth_token"]
17
+ end
18
+
19
+ def token_from_header
20
+ if @env[authorization_key] =~ /OAuth (.*)/
21
+ $1
22
+ end
23
+ end
24
+
25
+ def authorization_key
26
+ @authorization_key ||= Rack::Auth::AbstractRequest::AUTHORIZATION_KEYS.detect do |key|
27
+ @env.has_key?(key)
28
+ end
29
+ end
30
+
31
+ def authenticate_request!(options, &block)
32
+ if authenticated?
33
+ if options[:scope].nil? || has_scope?(options[:scope])
34
+ yield
35
+ else
36
+ insufficient_scope!
37
+ end
38
+ else
39
+ authentication_required!
40
+ end
41
+ end
42
+
43
+ def authorization
44
+ validate_token!
45
+ @authorization
46
+ end
47
+
48
+ def authenticated?
49
+ authorization.present?
50
+ end
51
+
52
+ def resource_owner
53
+ authorization && authorization.resource_owner
54
+ end
55
+
56
+ def authentication_required!
57
+ throw_response Responses.unauthorized
58
+ end
59
+
60
+ def insufficient_scope!
61
+ throw_response Responses.json_error('insufficient_scope', :status => 403)
62
+ end
63
+
64
+ def validate_token!
65
+ if has_token? && @token_validated.nil?
66
+ @token_validated = true
67
+ block_bad_request
68
+ block_invalid_token
69
+ end
70
+ end
71
+
72
+ def block_bad_request
73
+ if token_from_param && token_from_header && (token_from_param != token_from_header)
74
+ throw_response Responses.json_error('invalid_request', :description => 'both authorization header and oauth_token provided, with conflicting tokens')
75
+ end
76
+ end
77
+
78
+ def block_invalid_token
79
+ access_token = OAuth2::Provider.access_token_class.find_by_access_token(token)
80
+ @authorization = access_token.authorization if access_token
81
+ throw_response Responses.unauthorized('invalid_token') if access_token.nil? || access_token.expired?
82
+ end
83
+
84
+ private
85
+
86
+ def throw_response(response)
87
+ @env['oauth2.response'] = response
88
+ throw :oauth2
89
+ end
90
+ end
91
+ end