doorkeeper 4.2.6 → 5.0.0.rc1
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.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE.md +25 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +17 -0
- data/.gitignore +2 -1
- data/.hound.yml +2 -13
- data/.rubocop.yml +17 -0
- data/.travis.yml +19 -5
- data/Appraisals +8 -4
- data/CODE_OF_CONDUCT.md +46 -0
- data/Gemfile +1 -1
- data/NEWS.md +77 -0
- data/README.md +169 -34
- data/Rakefile +6 -0
- data/SECURITY.md +15 -0
- data/app/assets/stylesheets/doorkeeper/admin/application.css +2 -2
- data/app/controllers/doorkeeper/application_controller.rb +2 -5
- data/app/controllers/doorkeeper/application_metal_controller.rb +4 -0
- data/app/controllers/doorkeeper/applications_controller.rb +47 -13
- data/app/controllers/doorkeeper/authorizations_controller.rb +55 -12
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +15 -1
- data/app/controllers/doorkeeper/tokens_controller.rb +15 -7
- data/app/helpers/doorkeeper/dashboard_helper.rb +8 -6
- data/app/validators/redirect_uri_validator.rb +13 -2
- data/app/views/doorkeeper/applications/_delete_form.html.erb +3 -1
- data/app/views/doorkeeper/applications/_form.html.erb +31 -19
- data/app/views/doorkeeper/applications/edit.html.erb +1 -1
- data/app/views/doorkeeper/applications/index.html.erb +18 -6
- data/app/views/doorkeeper/applications/new.html.erb +1 -1
- data/app/views/doorkeeper/applications/show.html.erb +8 -5
- data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
- data/app/views/doorkeeper/authorizations/new.html.erb +4 -0
- data/app/views/doorkeeper/authorized_applications/index.html.erb +0 -1
- data/app/views/layouts/doorkeeper/admin.html.erb +15 -15
- data/config/locales/en.yml +18 -6
- data/doorkeeper.gemspec +6 -5
- data/gemfiles/rails_4_2.gemfile +6 -4
- data/gemfiles/rails_5_0.gemfile +4 -4
- data/gemfiles/rails_5_1.gemfile +6 -7
- data/gemfiles/rails_5_2.gemfile +12 -0
- data/gemfiles/rails_master.gemfile +14 -0
- data/lib/doorkeeper/config.rb +107 -68
- data/lib/doorkeeper/engine.rb +7 -3
- data/lib/doorkeeper/errors.rb +2 -5
- data/lib/doorkeeper/grape/helpers.rb +14 -9
- data/lib/doorkeeper/helpers/controller.rb +15 -6
- data/lib/doorkeeper/models/access_grant_mixin.rb +52 -23
- data/lib/doorkeeper/models/access_token_mixin.rb +51 -52
- data/lib/doorkeeper/models/application_mixin.rb +16 -30
- data/lib/doorkeeper/models/concerns/expirable.rb +7 -5
- data/lib/doorkeeper/models/concerns/orderable.rb +13 -0
- data/lib/doorkeeper/models/concerns/scopes.rb +1 -1
- data/lib/doorkeeper/oauth/authorization/code.rb +31 -8
- data/lib/doorkeeper/oauth/authorization/context.rb +15 -0
- data/lib/doorkeeper/oauth/authorization/token.rb +41 -20
- data/lib/doorkeeper/oauth/authorization_code_request.rb +33 -3
- data/lib/doorkeeper/oauth/base_request.rb +22 -7
- data/lib/doorkeeper/oauth/client/credentials.rb +6 -4
- data/lib/doorkeeper/oauth/client.rb +2 -2
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +6 -1
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +4 -2
- data/lib/doorkeeper/oauth/error.rb +2 -2
- data/lib/doorkeeper/oauth/error_response.rb +11 -4
- data/lib/doorkeeper/oauth/forbidden_token_response.rb +1 -1
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +0 -8
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +15 -0
- data/lib/doorkeeper/oauth/invalid_token_response.rb +3 -4
- data/lib/doorkeeper/oauth/password_access_token_request.rb +8 -4
- data/lib/doorkeeper/oauth/pre_authorization.rb +45 -13
- data/lib/doorkeeper/oauth/refresh_token_request.rb +7 -1
- data/lib/doorkeeper/oauth/scopes.rb +19 -9
- data/lib/doorkeeper/oauth/token.rb +6 -3
- data/lib/doorkeeper/oauth/token_introspection.rb +128 -0
- data/lib/doorkeeper/oauth/token_response.rb +4 -2
- data/lib/doorkeeper/oauth.rb +13 -0
- data/lib/doorkeeper/orm/active_record/access_grant.rb +27 -0
- data/lib/doorkeeper/orm/active_record/access_token.rb +21 -20
- data/lib/doorkeeper/orm/active_record/application.rb +34 -0
- data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +26 -0
- data/lib/doorkeeper/orm/active_record.rb +21 -8
- data/lib/doorkeeper/rails/helpers.rb +7 -10
- data/lib/doorkeeper/rails/routes.rb +21 -7
- data/lib/doorkeeper/rake/db.rake +40 -0
- data/lib/doorkeeper/rake/setup.rake +6 -0
- data/lib/doorkeeper/rake.rb +14 -0
- data/lib/doorkeeper/request/password.rb +1 -11
- data/lib/doorkeeper/request.rb +29 -23
- data/lib/doorkeeper/validations.rb +3 -2
- data/lib/doorkeeper/version.rb +14 -1
- data/lib/doorkeeper.rb +6 -17
- data/lib/generators/doorkeeper/application_owner_generator.rb +26 -12
- data/lib/generators/doorkeeper/confidential_applications_generator.rb +32 -0
- data/lib/generators/doorkeeper/install_generator.rb +17 -9
- data/lib/generators/doorkeeper/migration_generator.rb +26 -9
- data/lib/generators/doorkeeper/pkce_generator.rb +32 -0
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +31 -20
- data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
- data/lib/generators/doorkeeper/templates/{add_owner_to_application_migration.rb → add_owner_to_application_migration.rb.erb} +1 -1
- data/lib/generators/doorkeeper/templates/{add_previous_refresh_token_to_access_tokens.rb → add_previous_refresh_token_to_access_tokens.rb.erb} +1 -1
- data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +6 -0
- data/lib/generators/doorkeeper/templates/initializer.rb +88 -10
- data/lib/generators/doorkeeper/templates/{migration.rb → migration.rb.erb} +2 -1
- data/lib/generators/doorkeeper/views_generator.rb +3 -1
- data/spec/controllers/application_metal_controller_spec.rb +50 -0
- data/spec/controllers/applications_controller_spec.rb +141 -17
- data/spec/controllers/authorizations_controller_spec.rb +255 -20
- data/spec/controllers/protected_resources_controller_spec.rb +44 -35
- data/spec/controllers/token_info_controller_spec.rb +17 -21
- data/spec/controllers/tokens_controller_spec.rb +142 -10
- data/spec/dummy/app/assets/config/manifest.js +2 -0
- data/spec/dummy/config/environments/test.rb +4 -5
- data/spec/dummy/config/initializers/doorkeeper.rb +18 -1
- data/spec/dummy/config/initializers/{active_record_belongs_to_required_by_default.rb → new_framework_defaults.rb} +5 -1
- data/spec/dummy/config/initializers/secret_token.rb +0 -1
- data/spec/dummy/config/routes.rb +3 -42
- data/spec/dummy/db/migrate/20111122132257_create_users.rb +3 -1
- data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +3 -1
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +3 -1
- data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +3 -1
- data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +3 -1
- data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +6 -0
- data/spec/dummy/db/migrate/20180210183654_add_confidential_to_applications.rb +13 -0
- data/spec/dummy/db/schema.rb +38 -37
- data/spec/factories.rb +1 -1
- data/spec/generators/application_owner_generator_spec.rb +25 -6
- data/spec/generators/confidential_applications_generator_spec.rb +45 -0
- data/spec/generators/install_generator_spec.rb +1 -1
- data/spec/generators/migration_generator_spec.rb +25 -4
- data/spec/generators/pkce_generator_spec.rb +43 -0
- data/spec/generators/previous_refresh_token_generator_spec.rb +57 -0
- data/spec/generators/views_generator_spec.rb +1 -1
- data/spec/grape/grape_integration_spec.rb +135 -0
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +2 -2
- data/spec/lib/config_spec.rb +170 -22
- data/spec/lib/doorkeeper_spec.rb +1 -126
- data/spec/lib/models/expirable_spec.rb +0 -3
- data/spec/lib/models/revocable_spec.rb +2 -4
- data/spec/lib/models/scopes_spec.rb +0 -4
- data/spec/lib/oauth/authorization/uri_builder_spec.rb +0 -4
- data/spec/lib/oauth/authorization_code_request_spec.rb +63 -13
- data/spec/lib/oauth/base_request_spec.rb +19 -10
- data/spec/lib/oauth/base_response_spec.rb +1 -1
- data/spec/lib/oauth/client/credentials_spec.rb +5 -5
- data/spec/lib/oauth/client_credentials/creator_spec.rb +6 -2
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +26 -7
- data/spec/lib/oauth/client_credentials/validation_spec.rb +2 -3
- data/spec/lib/oauth/client_credentials_integration_spec.rb +2 -2
- data/spec/lib/oauth/client_credentials_request_spec.rb +4 -5
- data/spec/lib/oauth/client_spec.rb +0 -3
- data/spec/lib/oauth/code_request_spec.rb +5 -5
- data/spec/lib/oauth/error_response_spec.rb +0 -3
- data/spec/lib/oauth/error_spec.rb +1 -3
- data/spec/lib/oauth/forbidden_token_response_spec.rb +1 -4
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +0 -3
- data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -1
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +115 -3
- data/spec/lib/oauth/invalid_token_response_spec.rb +2 -5
- data/spec/lib/oauth/password_access_token_request_spec.rb +46 -5
- data/spec/lib/oauth/pre_authorization_spec.rb +40 -6
- data/spec/lib/oauth/refresh_token_request_spec.rb +30 -14
- data/spec/lib/oauth/scopes_spec.rb +28 -4
- data/spec/lib/oauth/token_request_spec.rb +10 -13
- data/spec/lib/oauth/token_response_spec.rb +0 -1
- data/spec/lib/oauth/token_spec.rb +37 -14
- data/spec/lib/orm/active_record/stale_records_cleaner_spec.rb +79 -0
- data/spec/lib/request/strategy_spec.rb +0 -1
- data/spec/lib/server_spec.rb +10 -0
- data/spec/models/doorkeeper/access_grant_spec.rb +2 -2
- data/spec/models/doorkeeper/access_token_spec.rb +118 -60
- data/spec/models/doorkeeper/application_spec.rb +101 -23
- data/spec/requests/applications/applications_request_spec.rb +94 -6
- data/spec/requests/applications/authorized_applications_spec.rb +1 -1
- data/spec/requests/endpoints/authorization_spec.rb +1 -1
- data/spec/requests/endpoints/token_spec.rb +15 -6
- data/spec/requests/flows/authorization_code_errors_spec.rb +1 -1
- data/spec/requests/flows/authorization_code_spec.rb +198 -1
- data/spec/requests/flows/client_credentials_spec.rb +73 -5
- data/spec/requests/flows/implicit_grant_errors_spec.rb +3 -3
- data/spec/requests/flows/implicit_grant_spec.rb +38 -11
- data/spec/requests/flows/password_spec.rb +160 -24
- data/spec/requests/flows/refresh_token_spec.rb +6 -6
- data/spec/requests/flows/revoke_token_spec.rb +26 -26
- data/spec/requests/flows/skip_authorization_spec.rb +16 -11
- data/spec/requests/protected_resources/metal_spec.rb +2 -2
- data/spec/requests/protected_resources/private_api_spec.rb +2 -2
- data/spec/routing/custom_controller_routes_spec.rb +63 -7
- data/spec/routing/default_routes_spec.rb +6 -2
- data/spec/routing/scoped_routes_spec.rb +16 -2
- data/spec/spec_helper.rb +54 -3
- data/spec/spec_helper_integration.rb +2 -63
- data/spec/support/dependencies/factory_bot.rb +2 -0
- data/spec/support/doorkeeper_rspec.rb +19 -0
- data/spec/support/helpers/access_token_request_helper.rb +1 -1
- data/spec/support/helpers/authorization_request_helper.rb +4 -4
- data/spec/support/helpers/model_helper.rb +9 -4
- data/spec/support/helpers/request_spec_helper.rb +10 -6
- data/spec/support/helpers/url_helper.rb +15 -10
- data/spec/support/http_method_shim.rb +12 -16
- data/spec/support/shared/controllers_shared_context.rb +2 -6
- data/spec/support/shared/models_shared_examples.rb +4 -4
- data/spec/validators/redirect_uri_validator_spec.rb +58 -7
- data/spec/version/version_spec.rb +15 -0
- data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
- metadata +73 -19
- data/spec/controllers/application_metal_controller.rb +0 -10
- data/spec/support/dependencies/factory_girl.rb +0 -2
|
@@ -6,32 +6,8 @@ module Doorkeeper
|
|
|
6
6
|
include Models::Expirable
|
|
7
7
|
include Models::Revocable
|
|
8
8
|
include Models::Accessible
|
|
9
|
+
include Models::Orderable
|
|
9
10
|
include Models::Scopes
|
|
10
|
-
include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
|
|
11
|
-
|
|
12
|
-
included do
|
|
13
|
-
belongs_to_options = {
|
|
14
|
-
class_name: 'Doorkeeper::Application',
|
|
15
|
-
inverse_of: :access_tokens
|
|
16
|
-
}
|
|
17
|
-
if defined?(ActiveRecord::Base) && ActiveRecord::VERSION::MAJOR >= 5
|
|
18
|
-
belongs_to_options[:optional] = true
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
belongs_to :application, belongs_to_options
|
|
22
|
-
|
|
23
|
-
validates :token, presence: true, uniqueness: true
|
|
24
|
-
validates :refresh_token, uniqueness: true, if: :use_refresh_token?
|
|
25
|
-
|
|
26
|
-
# @attr_writer [Boolean, nil] use_refresh_token
|
|
27
|
-
# indicates the possibility of using refresh token
|
|
28
|
-
attr_writer :use_refresh_token
|
|
29
|
-
|
|
30
|
-
before_validation :generate_token, on: :create
|
|
31
|
-
before_validation :generate_refresh_token,
|
|
32
|
-
on: :create,
|
|
33
|
-
if: :use_refresh_token?
|
|
34
|
-
end
|
|
35
11
|
|
|
36
12
|
module ClassMethods
|
|
37
13
|
# Returns an instance of the Doorkeeper::AccessToken with
|
|
@@ -68,11 +44,11 @@ module Doorkeeper
|
|
|
68
44
|
# @param resource_owner [ActiveRecord::Base]
|
|
69
45
|
# instance of the Resource Owner model
|
|
70
46
|
#
|
|
71
|
-
def revoke_all_for(application_id, resource_owner)
|
|
47
|
+
def revoke_all_for(application_id, resource_owner, clock = Time)
|
|
72
48
|
where(application_id: application_id,
|
|
73
49
|
resource_owner_id: resource_owner.id,
|
|
74
50
|
revoked_at: nil).
|
|
75
|
-
|
|
51
|
+
update_all(revoked_at: clock.now.utc)
|
|
76
52
|
end
|
|
77
53
|
|
|
78
54
|
# Looking for not expired Access Token with a matching set of scopes
|
|
@@ -94,30 +70,34 @@ module Doorkeeper
|
|
|
94
70
|
else
|
|
95
71
|
resource_owner_or_id
|
|
96
72
|
end
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
73
|
+
|
|
74
|
+
tokens = authorized_tokens_for(application.try(:id), resource_owner_id)
|
|
75
|
+
tokens.detect do |token|
|
|
76
|
+
scopes_match?(token.scopes, scopes, application.try(:scopes))
|
|
100
77
|
end
|
|
101
78
|
end
|
|
102
79
|
|
|
103
|
-
# Checks whether the token scopes match the scopes from the parameters
|
|
104
|
-
# Application scopes (if present).
|
|
80
|
+
# Checks whether the token scopes match the scopes from the parameters
|
|
105
81
|
#
|
|
106
82
|
# @param token_scopes [#to_s]
|
|
107
83
|
# set of scopes (any object that responds to `#to_s`)
|
|
108
|
-
# @param param_scopes [
|
|
84
|
+
# @param param_scopes [Doorkeeper::OAuth::Scopes]
|
|
109
85
|
# scopes from params
|
|
110
|
-
# @param app_scopes [
|
|
86
|
+
# @param app_scopes [Doorkeeper::OAuth::Scopes]
|
|
111
87
|
# Application scopes
|
|
112
88
|
#
|
|
113
|
-
# @return [Boolean] true if
|
|
89
|
+
# @return [Boolean] true if the param scopes match the token scopes,
|
|
90
|
+
# and all the param scopes are defined in the application (or in the
|
|
91
|
+
# server configuration if the application doesn't define any scopes),
|
|
114
92
|
# and false in other cases
|
|
115
93
|
#
|
|
116
94
|
def scopes_match?(token_scopes, param_scopes, app_scopes)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
95
|
+
return true if token_scopes.empty? && param_scopes.empty?
|
|
96
|
+
|
|
97
|
+
(token_scopes.sort == param_scopes.sort) &&
|
|
98
|
+
Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
|
|
99
|
+
param_scopes.to_s,
|
|
100
|
+
Doorkeeper.configuration.scopes,
|
|
121
101
|
app_scopes
|
|
122
102
|
)
|
|
123
103
|
end
|
|
@@ -142,9 +122,8 @@ module Doorkeeper
|
|
|
142
122
|
def find_or_create_for(application, resource_owner_id, scopes, expires_in, use_refresh_token)
|
|
143
123
|
if Doorkeeper.configuration.reuse_access_token
|
|
144
124
|
access_token = matching_token_for(application, resource_owner_id, scopes)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
end
|
|
125
|
+
|
|
126
|
+
return access_token if access_token && !access_token.expired?
|
|
148
127
|
end
|
|
149
128
|
|
|
150
129
|
create!(
|
|
@@ -156,7 +135,7 @@ module Doorkeeper
|
|
|
156
135
|
)
|
|
157
136
|
end
|
|
158
137
|
|
|
159
|
-
# Looking for not revoked Access Token
|
|
138
|
+
# Looking for not revoked Access Token records that belongs to specific
|
|
160
139
|
# Application and Resource Owner.
|
|
161
140
|
#
|
|
162
141
|
# @param application_id [Integer]
|
|
@@ -164,14 +143,28 @@ module Doorkeeper
|
|
|
164
143
|
# @param resource_owner_id [Integer]
|
|
165
144
|
# ID of the Resource Owner model instance
|
|
166
145
|
#
|
|
146
|
+
# @return [Doorkeeper::AccessToken] array of matching AccessToken objects
|
|
147
|
+
#
|
|
148
|
+
def authorized_tokens_for(application_id, resource_owner_id)
|
|
149
|
+
ordered_by(:created_at, :desc)
|
|
150
|
+
.where(application_id: application_id,
|
|
151
|
+
resource_owner_id: resource_owner_id,
|
|
152
|
+
revoked_at: nil)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Convenience method for backwards-compatibility, return the last
|
|
156
|
+
# matching token for the given Application and Resource Owner.
|
|
157
|
+
#
|
|
158
|
+
# @param application_id [Integer]
|
|
159
|
+
# ID of the Application model instance
|
|
160
|
+
# @param resource_owner_id [Integer]
|
|
161
|
+
# ID of the Resource Owner model instance
|
|
162
|
+
#
|
|
167
163
|
# @return [Doorkeeper::AccessToken, nil] matching AccessToken object or
|
|
168
164
|
# nil if nothing was found
|
|
169
165
|
#
|
|
170
166
|
def last_authorized_token_for(application_id, resource_owner_id)
|
|
171
|
-
|
|
172
|
-
find_by(application_id: application_id,
|
|
173
|
-
resource_owner_id: resource_owner_id,
|
|
174
|
-
revoked_at: nil)
|
|
167
|
+
authorized_tokens_for(application_id, resource_owner_id).first
|
|
175
168
|
end
|
|
176
169
|
end
|
|
177
170
|
|
|
@@ -231,7 +224,7 @@ module Doorkeeper
|
|
|
231
224
|
# @return [String] refresh token value
|
|
232
225
|
#
|
|
233
226
|
def generate_refresh_token
|
|
234
|
-
|
|
227
|
+
self.refresh_token = UniqueToken.generate
|
|
235
228
|
end
|
|
236
229
|
|
|
237
230
|
# Generates and sets the token value with the
|
|
@@ -247,18 +240,24 @@ module Doorkeeper
|
|
|
247
240
|
def generate_token
|
|
248
241
|
self.created_at ||= Time.now.utc
|
|
249
242
|
|
|
250
|
-
|
|
251
|
-
self.token = generator.generate(
|
|
243
|
+
self.token = token_generator.generate(
|
|
252
244
|
resource_owner_id: resource_owner_id,
|
|
253
245
|
scopes: scopes,
|
|
254
246
|
application: application,
|
|
255
247
|
expires_in: expires_in,
|
|
256
248
|
created_at: created_at
|
|
257
249
|
)
|
|
258
|
-
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def token_generator
|
|
253
|
+
generator_name = Doorkeeper.configuration.access_token_generator
|
|
254
|
+
generator = generator_name.constantize
|
|
255
|
+
|
|
256
|
+
return generator if generator.respond_to?(:generate)
|
|
257
|
+
|
|
259
258
|
raise Errors::UnableToGenerateToken, "#{generator} does not respond to `.generate`."
|
|
260
259
|
rescue NameError
|
|
261
|
-
raise Errors::TokenGeneratorNotFound, "#{
|
|
260
|
+
raise Errors::TokenGeneratorNotFound, "#{generator_name} not found"
|
|
262
261
|
end
|
|
263
262
|
end
|
|
264
263
|
end
|
|
@@ -3,24 +3,16 @@ module Doorkeeper
|
|
|
3
3
|
extend ActiveSupport::Concern
|
|
4
4
|
|
|
5
5
|
include OAuth::Helpers
|
|
6
|
+
include Models::Orderable
|
|
6
7
|
include Models::Scopes
|
|
7
|
-
include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
|
|
8
|
-
|
|
9
|
-
included do
|
|
10
|
-
has_many :access_grants, dependent: :delete_all, class_name: 'Doorkeeper::AccessGrant'
|
|
11
|
-
has_many :access_tokens, dependent: :delete_all, class_name: 'Doorkeeper::AccessToken'
|
|
12
|
-
|
|
13
|
-
validates :name, :secret, :uid, presence: true
|
|
14
|
-
validates :uid, uniqueness: true
|
|
15
|
-
validates :redirect_uri, redirect_uri: true
|
|
16
|
-
|
|
17
|
-
before_validation :generate_uid, :generate_secret, on: :create
|
|
18
|
-
end
|
|
19
8
|
|
|
20
9
|
module ClassMethods
|
|
21
10
|
# Returns an instance of the Doorkeeper::Application with
|
|
22
11
|
# specific UID and secret.
|
|
23
12
|
#
|
|
13
|
+
# Public/Non-confidential applications will only find by uid if secret is
|
|
14
|
+
# blank.
|
|
15
|
+
#
|
|
24
16
|
# @param uid [#to_s] UID (any object that responds to `#to_s`)
|
|
25
17
|
# @param secret [#to_s] secret (any object that responds to `#to_s`)
|
|
26
18
|
#
|
|
@@ -28,7 +20,11 @@ module Doorkeeper
|
|
|
28
20
|
# if there is no record with such credentials
|
|
29
21
|
#
|
|
30
22
|
def by_uid_and_secret(uid, secret)
|
|
31
|
-
|
|
23
|
+
app = by_uid(uid)
|
|
24
|
+
return unless app
|
|
25
|
+
return app if secret.blank? && !app.confidential?
|
|
26
|
+
return unless app.secret == secret
|
|
27
|
+
app
|
|
32
28
|
end
|
|
33
29
|
|
|
34
30
|
# Returns an instance of the Doorkeeper::Application with specific UID.
|
|
@@ -43,23 +39,13 @@ module Doorkeeper
|
|
|
43
39
|
end
|
|
44
40
|
end
|
|
45
41
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def generate_uid
|
|
54
|
-
if uid.blank?
|
|
55
|
-
self.uid = UniqueToken.generate
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def generate_secret
|
|
60
|
-
if secret.blank?
|
|
61
|
-
self.secret = UniqueToken.generate
|
|
62
|
-
end
|
|
42
|
+
# Set an application's valid redirect URIs.
|
|
43
|
+
#
|
|
44
|
+
# @param uris [String, Array] Newline-separated string or array the URI(s)
|
|
45
|
+
#
|
|
46
|
+
# @return [String] The redirect URI(s) seperated by newlines.
|
|
47
|
+
def redirect_uri=(uris)
|
|
48
|
+
super(uris.is_a?(Array) ? uris.join("\n") : uris)
|
|
63
49
|
end
|
|
64
50
|
end
|
|
65
51
|
end
|
|
@@ -6,7 +6,7 @@ module Doorkeeper
|
|
|
6
6
|
#
|
|
7
7
|
# @return [Boolean] true if object expired and false in other case
|
|
8
8
|
def expired?
|
|
9
|
-
expires_in && Time.now.utc >
|
|
9
|
+
expires_in && Time.now.utc > expires_at
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
# Calculates expiration time in seconds.
|
|
@@ -15,14 +15,16 @@ module Doorkeeper
|
|
|
15
15
|
# or nil if object never expires.
|
|
16
16
|
def expires_in_seconds
|
|
17
17
|
return nil if expires_in.nil?
|
|
18
|
-
expires =
|
|
18
|
+
expires = expires_at - Time.now.utc
|
|
19
19
|
expires_sec = expires.seconds.round(0)
|
|
20
20
|
expires_sec > 0 ? expires_sec : 0
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
# Expiration time (date time of creation + TTL).
|
|
24
|
+
#
|
|
25
|
+
# @return [Time] expiration time in UTC
|
|
26
|
+
#
|
|
27
|
+
def expires_at
|
|
26
28
|
created_at + expires_in.seconds
|
|
27
29
|
end
|
|
28
30
|
end
|
|
@@ -5,18 +5,12 @@ module Doorkeeper
|
|
|
5
5
|
attr_accessor :pre_auth, :resource_owner, :token
|
|
6
6
|
|
|
7
7
|
def initialize(pre_auth, resource_owner)
|
|
8
|
-
@pre_auth
|
|
8
|
+
@pre_auth = pre_auth
|
|
9
9
|
@resource_owner = resource_owner
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def issue_token
|
|
13
|
-
@token ||= AccessGrant.create!
|
|
14
|
-
application_id: pre_auth.client.id,
|
|
15
|
-
resource_owner_id: resource_owner.id,
|
|
16
|
-
expires_in: configuration.authorization_code_expires_in,
|
|
17
|
-
redirect_uri: pre_auth.redirect_uri,
|
|
18
|
-
scopes: pre_auth.scopes.to_s
|
|
19
|
-
)
|
|
13
|
+
@token ||= AccessGrant.create! access_grant_attributes
|
|
20
14
|
end
|
|
21
15
|
|
|
22
16
|
def native_redirect
|
|
@@ -26,6 +20,35 @@ module Doorkeeper
|
|
|
26
20
|
def configuration
|
|
27
21
|
Doorkeeper.configuration
|
|
28
22
|
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def authorization_code_expires_in
|
|
27
|
+
configuration.authorization_code_expires_in
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def access_grant_attributes
|
|
31
|
+
pkce_attributes.merge application_id: pre_auth.client.id,
|
|
32
|
+
resource_owner_id: resource_owner.id,
|
|
33
|
+
expires_in: authorization_code_expires_in,
|
|
34
|
+
redirect_uri: pre_auth.redirect_uri,
|
|
35
|
+
scopes: pre_auth.scopes.to_s
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def pkce_attributes
|
|
39
|
+
return {} unless pkce_supported?
|
|
40
|
+
|
|
41
|
+
{
|
|
42
|
+
code_challenge: pre_auth.code_challenge,
|
|
43
|
+
code_challenge_method: pre_auth.code_challenge_method
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# ensures firstly, if migration with additional pcke columns was
|
|
48
|
+
# generated and migrated
|
|
49
|
+
def pkce_supported?
|
|
50
|
+
Doorkeeper::AccessGrant.pkce_supported?
|
|
51
|
+
end
|
|
29
52
|
end
|
|
30
53
|
end
|
|
31
54
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Doorkeeper
|
|
2
|
+
module OAuth
|
|
3
|
+
module Authorization
|
|
4
|
+
class Context
|
|
5
|
+
attr_reader :client, :grant_type, :scopes
|
|
6
|
+
|
|
7
|
+
def initialize(client, grant_type, scopes)
|
|
8
|
+
@client = client
|
|
9
|
+
@grant_type = grant_type
|
|
10
|
+
@scopes = scopes
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -4,32 +4,56 @@ module Doorkeeper
|
|
|
4
4
|
class Token
|
|
5
5
|
attr_accessor :pre_auth, :resource_owner, :token
|
|
6
6
|
|
|
7
|
+
class << self
|
|
8
|
+
def access_token_expires_in(server, pre_auth_or_oauth_client, grant_type, scopes)
|
|
9
|
+
if (expiration = custom_expiration(server, pre_auth_or_oauth_client, grant_type, scopes))
|
|
10
|
+
expiration
|
|
11
|
+
else
|
|
12
|
+
server.access_token_expires_in
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def custom_expiration(server, pre_auth_or_oauth_client, grant_type, scopes)
|
|
19
|
+
oauth_client = if pre_auth_or_oauth_client.respond_to?(:client)
|
|
20
|
+
pre_auth_or_oauth_client.client
|
|
21
|
+
else
|
|
22
|
+
pre_auth_or_oauth_client
|
|
23
|
+
end
|
|
24
|
+
context = Doorkeeper::OAuth::Authorization::Context.new(
|
|
25
|
+
oauth_client,
|
|
26
|
+
grant_type,
|
|
27
|
+
scopes
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
server.custom_access_token_expires_in.call(context)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
7
34
|
def initialize(pre_auth, resource_owner)
|
|
8
35
|
@pre_auth = pre_auth
|
|
9
36
|
@resource_owner = resource_owner
|
|
10
37
|
end
|
|
11
38
|
|
|
12
|
-
def self.access_token_expires_in(server, pre_auth_or_oauth_client)
|
|
13
|
-
if expiration = custom_expiration(server, pre_auth_or_oauth_client)
|
|
14
|
-
expiration
|
|
15
|
-
else
|
|
16
|
-
server.access_token_expires_in
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
39
|
def issue_token
|
|
21
40
|
@token ||= AccessToken.find_or_create_for(
|
|
22
41
|
pre_auth.client,
|
|
23
42
|
resource_owner.id,
|
|
24
43
|
pre_auth.scopes,
|
|
25
|
-
self.class.access_token_expires_in(
|
|
44
|
+
self.class.access_token_expires_in(
|
|
45
|
+
configuration,
|
|
46
|
+
pre_auth,
|
|
47
|
+
Doorkeeper::OAuth::IMPLICIT,
|
|
48
|
+
pre_auth.scopes
|
|
49
|
+
),
|
|
26
50
|
false
|
|
27
51
|
)
|
|
28
52
|
end
|
|
29
53
|
|
|
30
54
|
def native_redirect
|
|
31
55
|
{
|
|
32
|
-
controller:
|
|
56
|
+
controller: controller,
|
|
33
57
|
action: :show,
|
|
34
58
|
access_token: token.token
|
|
35
59
|
}
|
|
@@ -37,19 +61,16 @@ module Doorkeeper
|
|
|
37
61
|
|
|
38
62
|
private
|
|
39
63
|
|
|
40
|
-
def self.custom_expiration(server, pre_auth_or_oauth_client)
|
|
41
|
-
oauth_client = if pre_auth_or_oauth_client.respond_to?(:client)
|
|
42
|
-
pre_auth_or_oauth_client.client
|
|
43
|
-
else
|
|
44
|
-
pre_auth_or_oauth_client
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
server.custom_access_token_expires_in.call(oauth_client)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
64
|
def configuration
|
|
51
65
|
Doorkeeper.configuration
|
|
52
66
|
end
|
|
67
|
+
|
|
68
|
+
def controller
|
|
69
|
+
@controller ||= begin
|
|
70
|
+
mapping = Doorkeeper::Rails::Routes.mapping[:token_info] || {}
|
|
71
|
+
mapping[:controllers] || 'doorkeeper/token_info'
|
|
72
|
+
end
|
|
73
|
+
end
|
|
53
74
|
end
|
|
54
75
|
end
|
|
55
76
|
end
|
|
@@ -4,19 +4,28 @@ module Doorkeeper
|
|
|
4
4
|
validate :attributes, error: :invalid_request
|
|
5
5
|
validate :client, error: :invalid_client
|
|
6
6
|
validate :grant, error: :invalid_grant
|
|
7
|
+
# @see https://tools.ietf.org/html/rfc6749#section-5.2
|
|
7
8
|
validate :redirect_uri, error: :invalid_grant
|
|
9
|
+
validate :code_verifier, error: :invalid_grant
|
|
8
10
|
|
|
9
|
-
attr_accessor :server, :grant, :client, :redirect_uri, :access_token
|
|
11
|
+
attr_accessor :server, :grant, :client, :redirect_uri, :access_token,
|
|
12
|
+
:code_verifier
|
|
10
13
|
|
|
11
14
|
def initialize(server, grant, client, parameters = {})
|
|
12
15
|
@server = server
|
|
13
16
|
@client = client
|
|
14
17
|
@grant = grant
|
|
18
|
+
@grant_type = Doorkeeper::OAuth::AUTHORIZATION_CODE
|
|
15
19
|
@redirect_uri = parameters[:redirect_uri]
|
|
20
|
+
@code_verifier = parameters[:code_verifier]
|
|
16
21
|
end
|
|
17
22
|
|
|
18
23
|
private
|
|
19
24
|
|
|
25
|
+
def client_by_uid(parameters)
|
|
26
|
+
Doorkeeper::Application.by_uid(parameters[:client_id])
|
|
27
|
+
end
|
|
28
|
+
|
|
20
29
|
def before_successful_response
|
|
21
30
|
grant.transaction do
|
|
22
31
|
grant.lock!
|
|
@@ -28,14 +37,17 @@ module Doorkeeper
|
|
|
28
37
|
grant.scopes,
|
|
29
38
|
server)
|
|
30
39
|
end
|
|
40
|
+
super
|
|
31
41
|
end
|
|
32
42
|
|
|
33
43
|
def validate_attributes
|
|
44
|
+
return false if grant && grant.uses_pkce? && code_verifier.blank?
|
|
45
|
+
return false if grant && !grant.pkce_supported? && !code_verifier.blank?
|
|
34
46
|
redirect_uri.present?
|
|
35
47
|
end
|
|
36
48
|
|
|
37
49
|
def validate_client
|
|
38
|
-
|
|
50
|
+
!client.nil?
|
|
39
51
|
end
|
|
40
52
|
|
|
41
53
|
def validate_grant
|
|
@@ -44,7 +56,25 @@ module Doorkeeper
|
|
|
44
56
|
end
|
|
45
57
|
|
|
46
58
|
def validate_redirect_uri
|
|
47
|
-
|
|
59
|
+
Helpers::URIChecker.valid_for_authorization?(
|
|
60
|
+
redirect_uri,
|
|
61
|
+
grant.redirect_uri
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# if either side (server or client) request pkce, check the verifier
|
|
66
|
+
# against the DB - if pkce is supported
|
|
67
|
+
def validate_code_verifier
|
|
68
|
+
return true unless grant.uses_pkce? || code_verifier
|
|
69
|
+
return false unless grant.pkce_supported?
|
|
70
|
+
|
|
71
|
+
if grant.code_challenge_method == 'S256'
|
|
72
|
+
grant.code_challenge == AccessGrant.generate_code_challenge(code_verifier)
|
|
73
|
+
elsif grant.code_challenge_method == 'plain'
|
|
74
|
+
grant.code_challenge == code_verifier
|
|
75
|
+
else
|
|
76
|
+
false
|
|
77
|
+
end
|
|
48
78
|
end
|
|
49
79
|
end
|
|
50
80
|
end
|
|
@@ -3,8 +3,11 @@ module Doorkeeper
|
|
|
3
3
|
class BaseRequest
|
|
4
4
|
include Validations
|
|
5
5
|
|
|
6
|
+
attr_reader :grant_type
|
|
7
|
+
|
|
6
8
|
def authorize
|
|
7
9
|
validate
|
|
10
|
+
|
|
8
11
|
if valid?
|
|
9
12
|
before_successful_response
|
|
10
13
|
@response = TokenResponse.new(access_token)
|
|
@@ -16,11 +19,7 @@ module Doorkeeper
|
|
|
16
19
|
end
|
|
17
20
|
|
|
18
21
|
def scopes
|
|
19
|
-
@scopes ||=
|
|
20
|
-
OAuth::Scopes.from_string(@original_scopes)
|
|
21
|
-
else
|
|
22
|
-
default_scopes
|
|
23
|
-
end
|
|
22
|
+
@scopes ||= build_scopes
|
|
24
23
|
end
|
|
25
24
|
|
|
26
25
|
def default_scopes
|
|
@@ -36,14 +35,30 @@ module Doorkeeper
|
|
|
36
35
|
client,
|
|
37
36
|
resource_owner_id,
|
|
38
37
|
scopes,
|
|
39
|
-
Authorization::Token.access_token_expires_in(server, client),
|
|
40
|
-
server.refresh_token_enabled?
|
|
38
|
+
Authorization::Token.access_token_expires_in(server, client, grant_type, scopes),
|
|
39
|
+
server.refresh_token_enabled?
|
|
40
|
+
)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def before_successful_response
|
|
44
|
+
Doorkeeper.configuration.before_successful_strategy_response.call(self)
|
|
44
45
|
end
|
|
45
46
|
|
|
46
47
|
def after_successful_response
|
|
48
|
+
Doorkeeper.configuration.after_successful_strategy_response.call(self, @response)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def build_scopes
|
|
54
|
+
if @original_scopes.present?
|
|
55
|
+
OAuth::Scopes.from_string(@original_scopes)
|
|
56
|
+
else
|
|
57
|
+
client_scopes = @client.try(:scopes)
|
|
58
|
+
return default_scopes if client_scopes.blank?
|
|
59
|
+
|
|
60
|
+
default_scopes & @client.scopes
|
|
61
|
+
end
|
|
47
62
|
end
|
|
48
63
|
end
|
|
49
64
|
end
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
module Doorkeeper
|
|
2
2
|
module OAuth
|
|
3
3
|
class Client
|
|
4
|
-
|
|
4
|
+
Credentials = Struct.new(:uid, :secret) do
|
|
5
5
|
class << self
|
|
6
6
|
def from_request(request, *credentials_methods)
|
|
7
|
-
credentials_methods.inject(nil) do |
|
|
7
|
+
credentials_methods.inject(nil) do |_, method|
|
|
8
8
|
method = self.method(method) if method.is_a?(Symbol)
|
|
9
9
|
credentials = Credentials.new(*method.call(request))
|
|
10
10
|
break credentials unless credentials.blank?
|
|
@@ -18,13 +18,15 @@ module Doorkeeper
|
|
|
18
18
|
def from_basic(request)
|
|
19
19
|
authorization = request.authorization
|
|
20
20
|
if authorization.present? && authorization =~ /^Basic (.*)/m
|
|
21
|
-
Base64.decode64(
|
|
21
|
+
Base64.decode64(Regexp.last_match(1)).split(/:/, 2)
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
# Public clients may have their secret blank, but "credentials" are
|
|
27
|
+
# still present
|
|
26
28
|
def blank?
|
|
27
|
-
uid.blank?
|
|
29
|
+
uid.blank?
|
|
28
30
|
end
|
|
29
31
|
end
|
|
30
32
|
end
|
|
@@ -12,7 +12,7 @@ module Doorkeeper
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def self.find(uid, method = Application.method(:by_uid))
|
|
15
|
-
if application = method.call(uid)
|
|
15
|
+
if (application = method.call(uid))
|
|
16
16
|
new(application)
|
|
17
17
|
end
|
|
18
18
|
end
|
|
@@ -20,7 +20,7 @@ module Doorkeeper
|
|
|
20
20
|
def self.authenticate(credentials, method = Application.method(:by_uid_and_secret))
|
|
21
21
|
return false if credentials.blank?
|
|
22
22
|
|
|
23
|
-
if application = method.call(credentials.uid, credentials.secret)
|
|
23
|
+
if (application = method.call(credentials.uid, credentials.secret))
|
|
24
24
|
new(application)
|
|
25
25
|
end
|
|
26
26
|
end
|
|
@@ -25,7 +25,12 @@ module Doorkeeper
|
|
|
25
25
|
private
|
|
26
26
|
|
|
27
27
|
def create_token(client, scopes, creator)
|
|
28
|
-
ttl = Authorization::Token.access_token_expires_in(
|
|
28
|
+
ttl = Authorization::Token.access_token_expires_in(
|
|
29
|
+
@server,
|
|
30
|
+
client,
|
|
31
|
+
Doorkeeper::OAuth::CLIENT_CREDENTIALS,
|
|
32
|
+
scopes
|
|
33
|
+
)
|
|
29
34
|
|
|
30
35
|
creator.call(
|
|
31
36
|
client,
|