doorkeeper 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of doorkeeper might be problematic. Click here for more details.

Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -3
  3. data/CHANGELOG.md +19 -0
  4. data/README.md +6 -0
  5. data/app/views/doorkeeper/applications/_delete_form.html.erb +1 -1
  6. data/app/views/doorkeeper/applications/_form.html.erb +5 -6
  7. data/app/views/doorkeeper/applications/edit.html.erb +1 -1
  8. data/app/views/doorkeeper/applications/index.html.erb +5 -5
  9. data/app/views/doorkeeper/applications/new.html.erb +1 -1
  10. data/app/views/doorkeeper/applications/show.html.erb +7 -7
  11. data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
  12. data/app/views/doorkeeper/authorizations/new.html.erb +5 -5
  13. data/app/views/doorkeeper/authorizations/show.html.erb +1 -1
  14. data/app/views/doorkeeper/authorized_applications/_delete_form.html.erb +1 -1
  15. data/app/views/doorkeeper/authorized_applications/index.html.erb +4 -4
  16. data/app/views/layouts/doorkeeper/admin.html.erb +2 -2
  17. data/app/views/layouts/doorkeeper/application.html.erb +1 -1
  18. data/config/locales/en.yml +77 -0
  19. data/gemfiles/Gemfile.common.rb +1 -1
  20. data/lib/doorkeeper/config.rb +4 -6
  21. data/lib/doorkeeper/models/access_token_mixin.rb +16 -4
  22. data/lib/doorkeeper/oauth/client.rb +1 -1
  23. data/lib/doorkeeper/oauth/client_credentials/validation.rb +15 -3
  24. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +9 -8
  25. data/lib/doorkeeper/oauth/password_access_token_request.rb +1 -1
  26. data/lib/doorkeeper/oauth/pre_authorization.rb +5 -5
  27. data/lib/doorkeeper/oauth/refresh_token_request.rb +9 -1
  28. data/lib/doorkeeper/oauth/token_response.rb +2 -1
  29. data/lib/doorkeeper/version.rb +1 -1
  30. data/lib/generators/doorkeeper/templates/initializer.rb +8 -2
  31. data/spec/controllers/authorizations_controller_spec.rb +3 -0
  32. data/spec/dummy/config/environments/test.rb +0 -1
  33. data/spec/dummy/config/initializers/doorkeeper.rb +62 -18
  34. data/spec/lib/config_spec.rb +2 -6
  35. data/spec/lib/oauth/client_credentials/validation_spec.rb +26 -3
  36. data/spec/lib/oauth/helpers/scope_checker_spec.rb +25 -35
  37. data/spec/lib/oauth/password_access_token_request_spec.rb +8 -1
  38. data/spec/lib/oauth/pre_authorization_spec.rb +2 -0
  39. data/spec/lib/oauth/refresh_token_request_spec.rb +1 -1
  40. data/spec/lib/oauth/token_request_spec.rb +5 -1
  41. data/spec/lib/oauth/token_response_spec.rb +15 -9
  42. data/spec/models/doorkeeper/access_token_spec.rb +23 -8
  43. data/spec/requests/endpoints/authorization_spec.rb +6 -12
  44. data/spec/requests/flows/implicit_grant_errors_spec.rb +1 -0
  45. data/spec/requests/flows/implicit_grant_spec.rb +1 -0
  46. data/spec/requests/flows/password_spec.rb +1 -1
  47. data/spec/requests/flows/refresh_token_spec.rb +3 -4
  48. data/spec/spec_helper_integration.rb +1 -1
  49. data/spec/support/helpers/authorization_request_helper.rb +9 -0
  50. metadata +3 -4
@@ -52,7 +52,18 @@ module Doorkeeper
52
52
  resource_owner_or_id
53
53
  end
54
54
  token = last_authorized_token_for(application.try(:id), resource_owner_id)
55
- token if token && Doorkeeper::OAuth::Helpers::ScopeChecker.matches?(token.scopes, scopes)
55
+ if token && scopes_match?(token.scopes, scopes, application.try(:scopes))
56
+ token
57
+ end
58
+ end
59
+
60
+ def scopes_match?(token_scopes, param_scopes, app_scopes)
61
+ (!token_scopes.present? && !param_scopes.present?) ||
62
+ Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
63
+ token_scopes.to_s,
64
+ param_scopes,
65
+ app_scopes
66
+ )
56
67
  end
57
68
 
58
69
  def find_or_create_for(application, resource_owner_id, scopes, expires_in, use_refresh_token)
@@ -92,10 +103,11 @@ module Doorkeeper
92
103
 
93
104
  def as_json(_options = {})
94
105
  {
95
- resource_owner_id: resource_owner_id,
96
- scopes: scopes,
106
+ resource_owner_id: resource_owner_id,
107
+ scopes: scopes,
97
108
  expires_in_seconds: expires_in_seconds,
98
- application: { uid: application.try(:uid) }
109
+ application: { uid: application.try(:uid) },
110
+ created_at: created_at.to_i,
99
111
  }
100
112
  end
101
113
 
@@ -17,7 +17,7 @@ module Doorkeeper
17
17
  end
18
18
  end
19
19
 
20
- delegate :id, :name, :uid, :redirect_uri, to: :@application
20
+ delegate :id, :name, :uid, :redirect_uri, :scopes, to: :@application
21
21
 
22
22
  def initialize(application)
23
23
  @application = application
@@ -13,19 +13,31 @@ module Doorkeeper
13
13
  validate :scopes, error: :invalid_scope
14
14
 
15
15
  def initialize(server, request)
16
- @server, @request = server, request
16
+ @server, @request, @client = server, request, request.client
17
+
17
18
  validate
18
19
  end
19
20
 
20
21
  private
21
22
 
22
23
  def validate_client
23
- @request.client.present?
24
+ @client.present?
24
25
  end
25
26
 
26
27
  def validate_scopes
27
28
  return true unless @request.original_scopes.present?
28
- ScopeChecker.valid?(@request.original_scopes, @server.scopes)
29
+
30
+ application_scopes = if @client.present?
31
+ @client.application.scopes
32
+ else
33
+ ''
34
+ end
35
+
36
+ ScopeChecker.valid?(
37
+ @request.original_scopes,
38
+ @server.scopes,
39
+ application_scopes
40
+ )
29
41
  end
30
42
  end
31
43
  end
@@ -2,15 +2,16 @@ module Doorkeeper
2
2
  module OAuth
3
3
  module Helpers
4
4
  module ScopeChecker
5
- def self.matches?(current_scopes, scopes)
6
- return false if current_scopes.nil? || scopes.nil?
7
- current_scopes == scopes
8
- end
5
+ def self.valid?(scope_str, server_scopes, application_scopes = nil)
6
+ valid_scopes = if application_scopes.present?
7
+ server_scopes & application_scopes
8
+ else
9
+ server_scopes
10
+ end
9
11
 
10
- def self.valid?(scope, server_scopes)
11
- scope.present? &&
12
- scope !~ /[\n|\r|\t]/ &&
13
- server_scopes.has_scopes?(OAuth::Scopes.from_string(scope))
12
+ scope_str.present? &&
13
+ scope_str !~ /[\n|\r|\t]/ &&
14
+ valid_scopes.has_scopes?(OAuth::Scopes.from_string(scope_str))
14
15
  end
15
16
  end
16
17
  end
@@ -32,7 +32,7 @@ module Doorkeeper
32
32
 
33
33
  def validate_scopes
34
34
  return true unless @original_scopes.present?
35
- ScopeChecker.valid?(@original_scopes, @server.scopes)
35
+ ScopeChecker.valid? @original_scopes, server.scopes, client.try(:scopes)
36
36
  end
37
37
 
38
38
  def validate_resource_owner
@@ -48,11 +48,11 @@ module Doorkeeper
48
48
 
49
49
  def validate_scopes
50
50
  return true unless scope.present?
51
- if client.application.scopes.empty?
52
- Helpers::ScopeChecker.valid?(scope, server.scopes)
53
- else
54
- Helpers::ScopeChecker.valid?(scope, server.scopes & client.application.scopes)
55
- end
51
+ Helpers::ScopeChecker.valid?(
52
+ scope,
53
+ server.scopes,
54
+ client.application.scopes
55
+ )
56
56
  end
57
57
 
58
58
  # TODO: test uri should be matched against the client's one
@@ -5,7 +5,8 @@ module Doorkeeper
5
5
  include OAuth::RequestConcern
6
6
  include OAuth::Helpers
7
7
 
8
- validate :token, error: :invalid_request
8
+ validate :token_presence, error: :invalid_request
9
+ validate :token, error: :invalid_grant
9
10
  validate :client, error: :invalid_client
10
11
  validate :client_match, error: :invalid_grant
11
12
  validate :scope, error: :invalid_scope
@@ -18,6 +19,7 @@ module Doorkeeper
18
19
  @refresh_token = refresh_token
19
20
  @credentials = credentials
20
21
  @original_scopes = parameters[:scopes]
22
+ @refresh_token_parameter = parameters[:refresh_token]
21
23
 
22
24
  if credentials
23
25
  @client = Application.by_uid_and_secret credentials.uid,
@@ -27,6 +29,8 @@ module Doorkeeper
27
29
 
28
30
  private
29
31
 
32
+ attr_reader :refresh_token_parameter
33
+
30
34
  def before_successful_response
31
35
  refresh_token.revoke
32
36
  create_access_token
@@ -45,6 +49,10 @@ module Doorkeeper
45
49
  use_refresh_token: true)
46
50
  end
47
51
 
52
+ def validate_token_presence
53
+ refresh_token.present? || refresh_token_parameter.present?
54
+ end
55
+
48
56
  def validate_token
49
57
  refresh_token.present? && !refresh_token.revoked?
50
58
  end
@@ -13,7 +13,8 @@ module Doorkeeper
13
13
  'token_type' => token.token_type,
14
14
  'expires_in' => token.expires_in_seconds,
15
15
  'refresh_token' => token.refresh_token,
16
- 'scope' => token.scopes_string
16
+ 'scope' => token.scopes_string,
17
+ 'created_at' => token.created_at.to_i,
17
18
  }.reject { |_, value| value.blank? }
18
19
  end
19
20
 
@@ -1,3 +1,3 @@
1
1
  module Doorkeeper
2
- VERSION = '2.0.1'
2
+ VERSION = '2.1.0'
3
3
  end
@@ -78,9 +78,15 @@ Doorkeeper.configure do
78
78
  # "password" => Resource Owner Password Credentials Grant Flow
79
79
  # "client_credentials" => Client Credentials Grant Flow
80
80
  #
81
- # If not specified, Doorkeeper enables all the four grant flows.
81
+ # If not specified, Doorkeeper enables authorization_code and
82
+ # client_credentials.
82
83
  #
83
- # grant_flows %w(authorization_code implicit password client_credentials)
84
+ # implicit and password grant flows have risks that you should understand
85
+ # before enabling:
86
+ # http://tools.ietf.org/html/rfc6819#section-4.4.2
87
+ # http://tools.ietf.org/html/rfc6819#section-4.4.3
88
+ #
89
+ # grant_flows %w(authorization_code client_credentials)
84
90
 
85
91
  # Under some circumstances you might want to have applications auto-approved,
86
92
  # so that the user skips the authorization step.
@@ -16,6 +16,7 @@ describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do
16
16
  let(:user) { User.create!(name: 'Joe', password: 'sekret') }
17
17
 
18
18
  before do
19
+ allow(Doorkeeper.configuration).to receive(:grant_flows).and_return(["implicit"])
19
20
  allow(controller).to receive(:current_resource_owner).and_return(user)
20
21
  end
21
22
 
@@ -123,6 +124,8 @@ describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do
123
124
 
124
125
  describe 'GET #new code request with native url and skip_authorization true' do
125
126
  before do
127
+ allow(Doorkeeper.configuration).to receive(:grant_flows).
128
+ and_return(%w(authorization_code))
126
129
  allow(Doorkeeper.configuration).to receive(:skip_authorization).and_return(proc do
127
130
  true
128
131
  end)
@@ -8,7 +8,6 @@ Dummy::Application.configure do
8
8
  config.cache_classes = true
9
9
 
10
10
  # Configure static asset server for tests with Cache-Control for performance
11
- config.serve_static_assets = true
12
11
  config.static_cache_control = 'public, max-age=3600'
13
12
 
14
13
  if Rails.version.to_i < 4
@@ -1,34 +1,42 @@
1
1
  Doorkeeper.configure do
2
- # Change the ORM that doorkeeper will use
3
- # Currently supported options are :active_record, :mongoid2, :mongoid3, :mongo_mapper
2
+ # Change the ORM that doorkeeper will use.
3
+ # Currently supported options are :active_record, :mongoid2, :mongoid3,
4
+ # :mongoid4, :mongo_mapper
4
5
  orm DOORKEEPER_ORM
5
6
 
6
- # This block will be called to check whether the
7
- # resource owner is authenticated or not
7
+ # This block will be called to check whether the resource owner is authenticated or not.
8
8
  resource_owner_authenticator do
9
9
  # Put your resource owner authentication logic here.
10
- # e.g. User.find_by_id(session[:user_id]) || redirect_to(new_user_session_url)
11
10
  User.where(id: session[:user_id]).first || redirect_to(root_url, alert: 'Needs sign in.')
12
11
  end
13
12
 
14
- # If you want to restrict the access to the web interface for
15
- # adding oauth authorized applications you need to declare the
16
- # block below
13
+ # If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below.
17
14
  # admin_authenticator do
18
15
  # # Put your admin authentication logic here.
16
+ # # Example implementation:
19
17
  # Admin.find_by_id(session[:admin_id]) || redirect_to(new_admin_session_url)
20
18
  # end
21
19
 
22
20
  # Authorization Code expiration time (default 10 minutes).
23
- # access_token_expires_in 10.minutes
21
+ # authorization_code_expires_in 10.minutes
24
22
 
25
- # Access token expiration time (default 2 hours)
23
+ # Access token expiration time (default 2 hours).
26
24
  # If you want to disable expiration, set this to nil.
27
25
  # access_token_expires_in 2.hours
28
26
 
27
+ # Reuse access token for the same resource owner within an application (disabled by default)
28
+ # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383
29
+ # reuse_access_token
30
+
29
31
  # Issue access tokens with refresh token (disabled by default)
30
32
  use_refresh_token
31
33
 
34
+ # Provide support for an owner to be assigned to each registered application (disabled by default)
35
+ # Optional parameter :confirmation => true (default false) if you want to enforce ownership of
36
+ # a registered application
37
+ # Note: you must also run the rails g doorkeeper:application_owner generator to provide the necessary support
38
+ # enable_application_owner :confirmation => false
39
+
32
40
  # Define access token scopes for your provider
33
41
  # For more information go to
34
42
  # https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
@@ -36,15 +44,15 @@ Doorkeeper.configure do
36
44
  optional_scopes :write, :update
37
45
 
38
46
  # Change the way client credentials are retrieved from the request object.
39
- # By default it retrieves first from `HTTP_AUTHORIZATION` header and
40
- # fallsback to `:client_id` and `:client_secret` from `params` object
41
- # Check out the wiki for mor information on customization
47
+ # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
48
+ # falls back to the `:client_id` and `:client_secret` params from the `params` object.
49
+ # Check out the wiki for more information on customization
42
50
  # client_credentials :from_basic, :from_params
43
51
 
44
52
  # Change the way access token is authenticated from the request object.
45
- # By default it retrieves first from `HTTP_AUTHORIZATION` header and
46
- # fallsback to `:access_token` or `:bearer_token` from `params` object
47
- # Check out the wiki for mor information on customization
53
+ # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
54
+ # falls back to the `:access_token` or `:bearer_token` params from the `params` object.
55
+ # Check out the wiki for more information on customization
48
56
  # access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param
49
57
 
50
58
  # Change the native redirect uri for client apps
@@ -54,6 +62,42 @@ Doorkeeper.configure do
54
62
  #
55
63
  # native_redirect_uri 'urn:ietf:wg:oauth:2.0:oob'
56
64
 
57
- # WWW-Authenticate Realm (default 'Doorkeeper').
58
- realm 'Doorkeeper'
65
+ # Forces the usage of the HTTPS protocol in non-native redirect uris (enabled
66
+ # by default in non-development environments). OAuth2 delegates security in
67
+ # communication to the HTTPS protocol so it is wise to keep this enabled.
68
+ #
69
+ # force_ssl_in_redirect_uri !Rails.env.development?
70
+
71
+ # Specify what grant flows are enabled in array of Strings. The valid
72
+ # strings and the flows they enable are:
73
+ #
74
+ # "authorization_code" => Authorization Code Grant Flow
75
+ # "implicit" => Implicit Grant Flow
76
+ # "password" => Resource Owner Password Credentials Grant Flow
77
+ # "client_credentials" => Client Credentials Grant Flow
78
+ #
79
+ # If not specified, Doorkeeper enables authorization_code and
80
+ # client_credentials.
81
+ #
82
+ # implicit and password grant flows have risks that you should understand
83
+ # before enabling:
84
+ # http://tools.ietf.org/html/rfc6819#section-4.4.2
85
+ # http://tools.ietf.org/html/rfc6819#section-4.4.3
86
+ #
87
+ # grant_flows %w(authorization_code client_credentials)
88
+
89
+ # Under some circumstances you might want to have applications auto-approved,
90
+ # so that the user skips the authorization step.
91
+ # For example if dealing with trusted a application.
92
+ # skip_authorization do |resource_owner, client|
93
+ # client.superapp? or resource_owner.admin?
94
+ # end
95
+
96
+ # WWW-Authenticate Realm (default "Doorkeeper").
97
+ realm "Doorkeeper"
98
+
99
+ # Allow dynamic query parameters (disabled by default)
100
+ # Some applications require dynamic query parameters on their request_uri
101
+ # set to true if you want this to be allowed
102
+ # wildcard_redirect_uri false
59
103
  end
@@ -221,12 +221,8 @@ describe Doorkeeper, 'configuration' do
221
221
 
222
222
  describe "grant_flows" do
223
223
  it "is set to all grant flows by default" do
224
- expect(Doorkeeper.configuration.grant_flows).to eq [
225
- 'authorization_code',
226
- 'implicit',
227
- 'password',
228
- 'client_credentials'
229
- ]
224
+ expect(Doorkeeper.configuration.grant_flows).
225
+ to eq(%w(authorization_code client_credentials))
230
226
  end
231
227
 
232
228
  it "can change the value" do
@@ -4,8 +4,10 @@ require 'doorkeeper/oauth/client_credentials/validation'
4
4
 
5
5
  class Doorkeeper::OAuth::ClientCredentialsRequest
6
6
  describe Validation do
7
- let(:server) { double :server, scopes: nil }
8
- let(:request) { double :request, client: double, original_scopes: nil }
7
+ let(:server) { double :server, scopes: nil }
8
+ let(:application) { double scopes: nil }
9
+ let(:client) { double application: application }
10
+ let(:request) { double :request, client: client, original_scopes: nil }
9
11
 
10
12
  subject { Validation.new(server, request) }
11
13
 
@@ -20,10 +22,31 @@ class Doorkeeper::OAuth::ClientCredentialsRequest
20
22
 
21
23
  context 'with scopes' do
22
24
  it 'is invalid when scopes are not included in the server' do
23
- allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string('email'))
25
+ server_scopes = Doorkeeper::OAuth::Scopes.from_string 'email'
26
+ allow(server).to receive(:scopes).and_return(server_scopes)
24
27
  allow(request).to receive(:original_scopes).and_return('invalid')
25
28
  expect(subject).not_to be_valid
26
29
  end
30
+
31
+ context 'with application scopes' do
32
+ it 'is valid when scopes are included in the application' do
33
+ application_scopes = Doorkeeper::OAuth::Scopes.from_string 'app'
34
+ server_scopes = Doorkeeper::OAuth::Scopes.from_string 'email app'
35
+ allow(application).to receive(:scopes).and_return(application_scopes)
36
+ allow(server).to receive(:scopes).and_return(server_scopes)
37
+ allow(request).to receive(:original_scopes).and_return('app')
38
+ expect(subject).to be_valid
39
+ end
40
+
41
+ it 'is invalid when scopes are not included in the application' do
42
+ application_scopes = Doorkeeper::OAuth::Scopes.from_string 'app'
43
+ server_scopes = Doorkeeper::OAuth::Scopes.from_string 'email app'
44
+ allow(application).to receive(:scopes).and_return(application_scopes)
45
+ allow(server).to receive(:scopes).and_return(server_scopes)
46
+ allow(request).to receive(:original_scopes).and_return('email')
47
+ expect(subject).not_to be_valid
48
+ end
49
+ end
27
50
  end
28
51
  end
29
52
  end
@@ -4,41 +4,6 @@ require 'doorkeeper/oauth/helpers/scope_checker'
4
4
  require 'doorkeeper/oauth/scopes'
5
5
 
6
6
  module Doorkeeper::OAuth::Helpers
7
- describe ScopeChecker, '.matches?' do
8
- def new_scope(*args)
9
- Doorkeeper::OAuth::Scopes.from_array args
10
- end
11
-
12
- it 'true if scopes matches' do
13
- scopes = new_scope :public
14
- scopes_to_match = new_scope :public
15
- expect(ScopeChecker.matches?(scopes, scopes_to_match)).to be_truthy
16
- end
17
-
18
- it 'is false when scopes differs' do
19
- scopes = new_scope :public
20
- scopes_to_match = new_scope :write
21
- expect(ScopeChecker.matches?(scopes, scopes_to_match)).to be_falsey
22
- end
23
-
24
- it 'is false when scope in array is missing' do
25
- scopes = new_scope :public
26
- scopes_to_match = new_scope :public, :write
27
- expect(ScopeChecker.matches?(scopes, scopes_to_match)).to be_falsey
28
- end
29
-
30
- it 'is false when scope in string is missing' do
31
- scopes = new_scope :public, :write
32
- scopes_to_match = new_scope :public
33
- expect(ScopeChecker.matches?(scopes, scopes_to_match)).to be_falsey
34
- end
35
-
36
- it 'is false when any of attributes is nil' do
37
- expect(ScopeChecker.matches?(nil, double)).to be_falsey
38
- expect(ScopeChecker.matches?(double, nil)).to be_falsey
39
- end
40
- end
41
-
42
7
  describe ScopeChecker, '.valid?' do
43
8
  let(:server_scopes) { Doorkeeper::OAuth::Scopes.new }
44
9
 
@@ -70,5 +35,30 @@ module Doorkeeper::OAuth::Helpers
70
35
  it 'is invalid if any scope is not included in server scopes' do
71
36
  expect(ScopeChecker.valid?('scope another', server_scopes)).to be_falsey
72
37
  end
38
+
39
+ context 'with application_scopes' do
40
+ let(:server_scopes) do
41
+ Doorkeeper::OAuth::Scopes.from_string 'common svr'
42
+ end
43
+ let(:application_scopes) do
44
+ Doorkeeper::OAuth::Scopes.from_string 'common'
45
+ end
46
+
47
+ it 'is valid if scope is included in the server and the application' do
48
+ expect(ScopeChecker.valid?(
49
+ 'common',
50
+ server_scopes,
51
+ application_scopes
52
+ )).to be_truthy
53
+ end
54
+
55
+ it 'is invalid if any scope is not included in the application' do
56
+ expect(ScopeChecker.valid?(
57
+ 'svr',
58
+ server_scopes,
59
+ application_scopes
60
+ )).to be_falsey
61
+ end
62
+ end
73
63
  end
74
64
  end