doorkeeper 4.3.2 → 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/.gitignore +1 -0
- data/.rubocop.yml +4 -0
- data/.travis.yml +2 -0
- data/Appraisals +2 -2
- data/Gemfile +1 -1
- data/NEWS.md +36 -0
- data/README.md +85 -3
- data/Rakefile +6 -0
- data/app/assets/stylesheets/doorkeeper/admin/application.css +2 -2
- data/app/controllers/doorkeeper/application_controller.rb +4 -3
- data/app/controllers/doorkeeper/application_metal_controller.rb +4 -0
- data/app/controllers/doorkeeper/applications_controller.rb +43 -22
- 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 +2 -6
- data/app/helpers/doorkeeper/dashboard_helper.rb +7 -7
- data/app/validators/redirect_uri_validator.rb +3 -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/layouts/doorkeeper/admin.html.erb +15 -15
- data/config/locales/en.yml +15 -1
- data/gemfiles/rails_5_2.gemfile +1 -1
- data/lib/doorkeeper/config.rb +60 -23
- data/lib/doorkeeper/engine.rb +4 -0
- data/lib/doorkeeper/errors.rb +2 -5
- data/lib/doorkeeper/grape/helpers.rb +1 -1
- data/lib/doorkeeper/helpers/controller.rb +7 -2
- data/lib/doorkeeper/models/access_grant_mixin.rb +56 -0
- data/lib/doorkeeper/models/access_token_mixin.rb +37 -20
- data/lib/doorkeeper/models/application_mixin.rb +8 -1
- 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 +23 -6
- data/lib/doorkeeper/oauth/authorization_code_request.rb +27 -2
- data/lib/doorkeeper/oauth/base_request.rb +18 -8
- data/lib/doorkeeper/oauth/client/credentials.rb +4 -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_response.rb +11 -3
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +0 -8
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +1 -0
- data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -4
- data/lib/doorkeeper/oauth/pre_authorization.rb +45 -13
- data/lib/doorkeeper/oauth/refresh_token_request.rb +6 -1
- data/lib/doorkeeper/oauth/scopes.rb +1 -1
- data/lib/doorkeeper/oauth/token.rb +5 -2
- data/lib/doorkeeper/oauth/token_introspection.rb +2 -2
- data/lib/doorkeeper/oauth/token_response.rb +4 -2
- data/lib/doorkeeper/oauth.rb +13 -0
- data/lib/doorkeeper/orm/active_record/application.rb +14 -0
- data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +26 -0
- data/lib/doorkeeper/orm/active_record.rb +2 -0
- data/lib/doorkeeper/rails/helpers.rb +2 -4
- data/lib/doorkeeper/rails/routes.rb +13 -1
- 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 +28 -28
- data/lib/doorkeeper/version.rb +5 -4
- data/lib/doorkeeper.rb +4 -17
- data/lib/generators/doorkeeper/application_owner_generator.rb +23 -18
- 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 +23 -18
- data/lib/generators/doorkeeper/pkce_generator.rb +32 -0
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +29 -24
- data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
- data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +6 -0
- data/lib/generators/doorkeeper/templates/initializer.rb +60 -9
- data/lib/generators/doorkeeper/templates/migration.rb.erb +1 -0
- 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 +126 -13
- data/spec/controllers/authorizations_controller_spec.rb +250 -15
- data/spec/controllers/protected_resources_controller_spec.rb +16 -16
- data/spec/controllers/token_info_controller_spec.rb +4 -12
- data/spec/controllers/tokens_controller_spec.rb +12 -14
- 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 +6 -0
- data/spec/dummy/config/initializers/new_framework_defaults.rb +4 -0
- 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/generators/application_owner_generator_spec.rb +1 -1
- 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 +1 -1
- data/spec/generators/pkce_generator_spec.rb +43 -0
- data/spec/generators/previous_refresh_token_generator_spec.rb +1 -1
- data/spec/generators/views_generator_spec.rb +1 -1
- data/spec/grape/grape_integration_spec.rb +1 -1
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
- data/spec/lib/config_spec.rb +59 -14
- data/spec/lib/doorkeeper_spec.rb +1 -126
- data/spec/lib/models/expirable_spec.rb +0 -3
- data/spec/lib/models/revocable_spec.rb +0 -2
- 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 +24 -2
- data/spec/lib/oauth/base_request_spec.rb +16 -2
- 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 +5 -1
- 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 +1 -1
- data/spec/lib/oauth/client_credentials_request_spec.rb +3 -5
- data/spec/lib/oauth/client_spec.rb +0 -3
- data/spec/lib/oauth/code_request_spec.rb +4 -2
- data/spec/lib/oauth/error_response_spec.rb +0 -3
- data/spec/lib/oauth/error_spec.rb +0 -2
- 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 +5 -2
- data/spec/lib/oauth/invalid_token_response_spec.rb +1 -4
- data/spec/lib/oauth/password_access_token_request_spec.rb +37 -2
- data/spec/lib/oauth/pre_authorization_spec.rb +40 -6
- data/spec/lib/oauth/refresh_token_request_spec.rb +11 -7
- data/spec/lib/oauth/scopes_spec.rb +0 -3
- data/spec/lib/oauth/token_request_spec.rb +4 -5
- 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 +1 -1
- data/spec/models/doorkeeper/access_grant_spec.rb +1 -1
- data/spec/models/doorkeeper/access_token_spec.rb +50 -16
- data/spec/models/doorkeeper/application_spec.rb +51 -6
- data/spec/requests/applications/applications_request_spec.rb +89 -1
- 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 +7 -5
- data/spec/requests/flows/authorization_code_errors_spec.rb +1 -1
- data/spec/requests/flows/authorization_code_spec.rb +197 -1
- data/spec/requests/flows/client_credentials_spec.rb +46 -6
- data/spec/requests/flows/implicit_grant_errors_spec.rb +1 -1
- data/spec/requests/flows/implicit_grant_spec.rb +38 -11
- data/spec/requests/flows/password_spec.rb +120 -23
- data/spec/requests/flows/refresh_token_spec.rb +2 -2
- data/spec/requests/flows/revoke_token_spec.rb +11 -11
- data/spec/requests/flows/skip_authorization_spec.rb +16 -11
- data/spec/requests/protected_resources/metal_spec.rb +1 -1
- data/spec/requests/protected_resources/private_api_spec.rb +1 -1
- data/spec/routing/custom_controller_routes_spec.rb +59 -7
- data/spec/routing/default_routes_spec.rb +2 -2
- data/spec/routing/scoped_routes_spec.rb +16 -2
- data/spec/spec_helper.rb +54 -3
- data/spec/spec_helper_integration.rb +2 -74
- data/spec/support/doorkeeper_rspec.rb +19 -0
- data/spec/support/helpers/authorization_request_helper.rb +4 -4
- data/spec/support/helpers/request_spec_helper.rb +2 -2
- data/spec/support/helpers/url_helper.rb +7 -3
- data/spec/support/http_method_shim.rb +12 -16
- data/spec/validators/redirect_uri_validator_spec.rb +7 -1
- data/spec/version/version_spec.rb +3 -3
- data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
- metadata +32 -8
- data/spec/controllers/application_metal_controller.rb +0 -10
- /data/spec/support/dependencies/{factory_girl.rb → factory_bot.rb} +0 -0
|
@@ -70,30 +70,34 @@ module Doorkeeper
|
|
|
70
70
|
else
|
|
71
71
|
resource_owner_or_id
|
|
72
72
|
end
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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))
|
|
76
77
|
end
|
|
77
78
|
end
|
|
78
79
|
|
|
79
|
-
# Checks whether the token scopes match the scopes from the parameters
|
|
80
|
-
# Application scopes (if present).
|
|
80
|
+
# Checks whether the token scopes match the scopes from the parameters
|
|
81
81
|
#
|
|
82
82
|
# @param token_scopes [#to_s]
|
|
83
83
|
# set of scopes (any object that responds to `#to_s`)
|
|
84
|
-
# @param param_scopes [
|
|
84
|
+
# @param param_scopes [Doorkeeper::OAuth::Scopes]
|
|
85
85
|
# scopes from params
|
|
86
|
-
# @param app_scopes [
|
|
86
|
+
# @param app_scopes [Doorkeeper::OAuth::Scopes]
|
|
87
87
|
# Application scopes
|
|
88
88
|
#
|
|
89
|
-
# @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),
|
|
90
92
|
# and false in other cases
|
|
91
93
|
#
|
|
92
94
|
def scopes_match?(token_scopes, param_scopes, app_scopes)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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,
|
|
97
101
|
app_scopes
|
|
98
102
|
)
|
|
99
103
|
end
|
|
@@ -118,9 +122,8 @@ module Doorkeeper
|
|
|
118
122
|
def find_or_create_for(application, resource_owner_id, scopes, expires_in, use_refresh_token)
|
|
119
123
|
if Doorkeeper.configuration.reuse_access_token
|
|
120
124
|
access_token = matching_token_for(application, resource_owner_id, scopes)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
end
|
|
125
|
+
|
|
126
|
+
return access_token if access_token && !access_token.expired?
|
|
124
127
|
end
|
|
125
128
|
|
|
126
129
|
create!(
|
|
@@ -132,7 +135,7 @@ module Doorkeeper
|
|
|
132
135
|
)
|
|
133
136
|
end
|
|
134
137
|
|
|
135
|
-
# Looking for not revoked Access Token
|
|
138
|
+
# Looking for not revoked Access Token records that belongs to specific
|
|
136
139
|
# Application and Resource Owner.
|
|
137
140
|
#
|
|
138
141
|
# @param application_id [Integer]
|
|
@@ -140,14 +143,28 @@ module Doorkeeper
|
|
|
140
143
|
# @param resource_owner_id [Integer]
|
|
141
144
|
# ID of the Resource Owner model instance
|
|
142
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
|
+
#
|
|
143
163
|
# @return [Doorkeeper::AccessToken, nil] matching AccessToken object or
|
|
144
164
|
# nil if nothing was found
|
|
145
165
|
#
|
|
146
166
|
def last_authorized_token_for(application_id, resource_owner_id)
|
|
147
|
-
|
|
148
|
-
find_by(application_id: application_id,
|
|
149
|
-
resource_owner_id: resource_owner_id,
|
|
150
|
-
revoked_at: nil)
|
|
167
|
+
authorized_tokens_for(application_id, resource_owner_id).first
|
|
151
168
|
end
|
|
152
169
|
end
|
|
153
170
|
|
|
@@ -10,6 +10,9 @@ module Doorkeeper
|
|
|
10
10
|
# Returns an instance of the Doorkeeper::Application with
|
|
11
11
|
# specific UID and secret.
|
|
12
12
|
#
|
|
13
|
+
# Public/Non-confidential applications will only find by uid if secret is
|
|
14
|
+
# blank.
|
|
15
|
+
#
|
|
13
16
|
# @param uid [#to_s] UID (any object that responds to `#to_s`)
|
|
14
17
|
# @param secret [#to_s] secret (any object that responds to `#to_s`)
|
|
15
18
|
#
|
|
@@ -17,7 +20,11 @@ module Doorkeeper
|
|
|
17
20
|
# if there is no record with such credentials
|
|
18
21
|
#
|
|
19
22
|
def by_uid_and_secret(uid, secret)
|
|
20
|
-
|
|
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
|
|
21
28
|
end
|
|
22
29
|
|
|
23
30
|
# Returns an instance of the Doorkeeper::Application with specific UID.
|
|
@@ -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
|
|
@@ -5,8 +5,8 @@ module Doorkeeper
|
|
|
5
5
|
attr_accessor :pre_auth, :resource_owner, :token
|
|
6
6
|
|
|
7
7
|
class << self
|
|
8
|
-
def access_token_expires_in(server, pre_auth_or_oauth_client)
|
|
9
|
-
if (expiration = custom_expiration(server, pre_auth_or_oauth_client))
|
|
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
10
|
expiration
|
|
11
11
|
else
|
|
12
12
|
server.access_token_expires_in
|
|
@@ -15,14 +15,19 @@ module Doorkeeper
|
|
|
15
15
|
|
|
16
16
|
private
|
|
17
17
|
|
|
18
|
-
def custom_expiration(server, pre_auth_or_oauth_client)
|
|
18
|
+
def custom_expiration(server, pre_auth_or_oauth_client, grant_type, scopes)
|
|
19
19
|
oauth_client = if pre_auth_or_oauth_client.respond_to?(:client)
|
|
20
20
|
pre_auth_or_oauth_client.client
|
|
21
21
|
else
|
|
22
22
|
pre_auth_or_oauth_client
|
|
23
23
|
end
|
|
24
|
+
context = Doorkeeper::OAuth::Authorization::Context.new(
|
|
25
|
+
oauth_client,
|
|
26
|
+
grant_type,
|
|
27
|
+
scopes
|
|
28
|
+
)
|
|
24
29
|
|
|
25
|
-
server.custom_access_token_expires_in.call(
|
|
30
|
+
server.custom_access_token_expires_in.call(context)
|
|
26
31
|
end
|
|
27
32
|
end
|
|
28
33
|
|
|
@@ -36,14 +41,19 @@ module Doorkeeper
|
|
|
36
41
|
pre_auth.client,
|
|
37
42
|
resource_owner.id,
|
|
38
43
|
pre_auth.scopes,
|
|
39
|
-
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
|
+
),
|
|
40
50
|
false
|
|
41
51
|
)
|
|
42
52
|
end
|
|
43
53
|
|
|
44
54
|
def native_redirect
|
|
45
55
|
{
|
|
46
|
-
controller:
|
|
56
|
+
controller: controller,
|
|
47
57
|
action: :show,
|
|
48
58
|
access_token: token.token
|
|
49
59
|
}
|
|
@@ -54,6 +64,13 @@ module Doorkeeper
|
|
|
54
64
|
def configuration
|
|
55
65
|
Doorkeeper.configuration
|
|
56
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
|
|
57
74
|
end
|
|
58
75
|
end
|
|
59
76
|
end
|
|
@@ -6,18 +6,26 @@ module Doorkeeper
|
|
|
6
6
|
validate :grant, error: :invalid_grant
|
|
7
7
|
# @see https://tools.ietf.org/html/rfc6749#section-5.2
|
|
8
8
|
validate :redirect_uri, error: :invalid_grant
|
|
9
|
+
validate :code_verifier, error: :invalid_grant
|
|
9
10
|
|
|
10
|
-
attr_accessor :server, :grant, :client, :redirect_uri, :access_token
|
|
11
|
+
attr_accessor :server, :grant, :client, :redirect_uri, :access_token,
|
|
12
|
+
:code_verifier
|
|
11
13
|
|
|
12
14
|
def initialize(server, grant, client, parameters = {})
|
|
13
15
|
@server = server
|
|
14
16
|
@client = client
|
|
15
17
|
@grant = grant
|
|
18
|
+
@grant_type = Doorkeeper::OAuth::AUTHORIZATION_CODE
|
|
16
19
|
@redirect_uri = parameters[:redirect_uri]
|
|
20
|
+
@code_verifier = parameters[:code_verifier]
|
|
17
21
|
end
|
|
18
22
|
|
|
19
23
|
private
|
|
20
24
|
|
|
25
|
+
def client_by_uid(parameters)
|
|
26
|
+
Doorkeeper::Application.by_uid(parameters[:client_id])
|
|
27
|
+
end
|
|
28
|
+
|
|
21
29
|
def before_successful_response
|
|
22
30
|
grant.transaction do
|
|
23
31
|
grant.lock!
|
|
@@ -33,11 +41,13 @@ module Doorkeeper
|
|
|
33
41
|
end
|
|
34
42
|
|
|
35
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?
|
|
36
46
|
redirect_uri.present?
|
|
37
47
|
end
|
|
38
48
|
|
|
39
49
|
def validate_client
|
|
40
|
-
|
|
50
|
+
!client.nil?
|
|
41
51
|
end
|
|
42
52
|
|
|
43
53
|
def validate_grant
|
|
@@ -51,6 +61,21 @@ module Doorkeeper
|
|
|
51
61
|
grant.redirect_uri
|
|
52
62
|
)
|
|
53
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
|
|
78
|
+
end
|
|
54
79
|
end
|
|
55
80
|
end
|
|
56
81
|
end
|
|
@@ -3,6 +3,8 @@ 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
|
|
8
10
|
|
|
@@ -17,11 +19,7 @@ module Doorkeeper
|
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
def scopes
|
|
20
|
-
@scopes ||=
|
|
21
|
-
OAuth::Scopes.from_string(@original_scopes)
|
|
22
|
-
else
|
|
23
|
-
default_scopes
|
|
24
|
-
end
|
|
22
|
+
@scopes ||= build_scopes
|
|
25
23
|
end
|
|
26
24
|
|
|
27
25
|
def default_scopes
|
|
@@ -37,7 +35,7 @@ module Doorkeeper
|
|
|
37
35
|
client,
|
|
38
36
|
resource_owner_id,
|
|
39
37
|
scopes,
|
|
40
|
-
Authorization::Token.access_token_expires_in(server, client),
|
|
38
|
+
Authorization::Token.access_token_expires_in(server, client, grant_type, scopes),
|
|
41
39
|
server.refresh_token_enabled?
|
|
42
40
|
)
|
|
43
41
|
end
|
|
@@ -47,8 +45,20 @@ module Doorkeeper
|
|
|
47
45
|
end
|
|
48
46
|
|
|
49
47
|
def after_successful_response
|
|
50
|
-
Doorkeeper.configuration.after_successful_strategy_response.
|
|
51
|
-
|
|
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
|
|
52
62
|
end
|
|
53
63
|
end
|
|
54
64
|
end
|
|
@@ -4,7 +4,7 @@ module Doorkeeper
|
|
|
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?
|
|
@@ -23,8 +23,10 @@ module Doorkeeper
|
|
|
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
|
|
@@ -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,
|
|
@@ -13,7 +13,9 @@ module Doorkeeper
|
|
|
13
13
|
validate :scopes, error: :invalid_scope
|
|
14
14
|
|
|
15
15
|
def initialize(server, request)
|
|
16
|
-
@server
|
|
16
|
+
@server = server
|
|
17
|
+
@request = request
|
|
18
|
+
@client = request.client
|
|
17
19
|
|
|
18
20
|
validate
|
|
19
21
|
end
|
|
@@ -25,7 +27,7 @@ module Doorkeeper
|
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
def validate_scopes
|
|
28
|
-
return true
|
|
30
|
+
return true if @request.scopes.blank?
|
|
29
31
|
|
|
30
32
|
application_scopes = if @client.present?
|
|
31
33
|
@client.application.scopes
|
|
@@ -4,7 +4,13 @@ module Doorkeeper
|
|
|
4
4
|
include OAuth::Helpers
|
|
5
5
|
|
|
6
6
|
def self.from_request(request, attributes = {})
|
|
7
|
-
new(
|
|
7
|
+
new(
|
|
8
|
+
attributes.merge(
|
|
9
|
+
name: request.error,
|
|
10
|
+
state: request.try(:state),
|
|
11
|
+
redirect_uri: request.try(:redirect_uri)
|
|
12
|
+
)
|
|
13
|
+
)
|
|
8
14
|
end
|
|
9
15
|
|
|
10
16
|
delegate :name, :description, :state, to: :@error
|
|
@@ -41,10 +47,12 @@ module Doorkeeper
|
|
|
41
47
|
end
|
|
42
48
|
|
|
43
49
|
def headers
|
|
44
|
-
{
|
|
50
|
+
{
|
|
51
|
+
'Cache-Control' => 'no-store',
|
|
45
52
|
'Pragma' => 'no-cache',
|
|
46
53
|
'Content-Type' => 'application/json; charset=utf-8',
|
|
47
|
-
'WWW-Authenticate' => authenticate_info
|
|
54
|
+
'WWW-Authenticate' => authenticate_info
|
|
55
|
+
}
|
|
48
56
|
end
|
|
49
57
|
|
|
50
58
|
protected
|
|
@@ -17,10 +17,6 @@ module Doorkeeper
|
|
|
17
17
|
@valid_scopes.has_scopes?(parsed_scopes)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def match?
|
|
21
|
-
valid? && parsed_scopes.has_scopes?(@valid_scopes)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
20
|
private
|
|
25
21
|
|
|
26
22
|
def valid_scopes(server_scopes, application_scopes)
|
|
@@ -35,10 +31,6 @@ module Doorkeeper
|
|
|
35
31
|
def self.valid?(scope_str, server_scopes, application_scopes = nil)
|
|
36
32
|
Validator.new(scope_str, server_scopes, application_scopes).valid?
|
|
37
33
|
end
|
|
38
|
-
|
|
39
|
-
def self.match?(scope_str, server_scopes, application_scopes = nil)
|
|
40
|
-
Validator.new(scope_str, server_scopes, application_scopes).match?
|
|
41
|
-
end
|
|
42
34
|
end
|
|
43
35
|
end
|
|
44
36
|
end
|
|
@@ -16,6 +16,7 @@ module Doorkeeper
|
|
|
16
16
|
@client = client
|
|
17
17
|
@parameters = parameters
|
|
18
18
|
@original_scopes = parameters[:scope]
|
|
19
|
+
@grant_type = Doorkeeper::OAuth::PASSWORD
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
private
|
|
@@ -26,16 +27,18 @@ module Doorkeeper
|
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
def validate_scopes
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
client_scopes = client.try(:scopes)
|
|
31
|
+
return true if scopes.blank?
|
|
32
|
+
|
|
33
|
+
ScopeChecker.valid?(scopes.to_s, server.scopes, client_scopes)
|
|
31
34
|
end
|
|
32
35
|
|
|
33
36
|
def validate_resource_owner
|
|
34
|
-
|
|
37
|
+
!resource_owner.nil?
|
|
35
38
|
end
|
|
36
39
|
|
|
37
40
|
def validate_client
|
|
38
|
-
!parameters[:client_id] ||
|
|
41
|
+
!parameters[:client_id] || !client.nil?
|
|
39
42
|
end
|
|
40
43
|
end
|
|
41
44
|
end
|
|
@@ -7,17 +7,21 @@ module Doorkeeper
|
|
|
7
7
|
validate :client, error: :invalid_client
|
|
8
8
|
validate :scopes, error: :invalid_scope
|
|
9
9
|
validate :redirect_uri, error: :invalid_redirect_uri
|
|
10
|
+
validate :code_challenge_method, error: :invalid_code_challenge_method
|
|
10
11
|
|
|
11
|
-
attr_accessor :server, :client, :response_type, :redirect_uri, :state
|
|
12
|
+
attr_accessor :server, :client, :response_type, :redirect_uri, :state,
|
|
13
|
+
:code_challenge, :code_challenge_method
|
|
12
14
|
attr_writer :scope
|
|
13
15
|
|
|
14
16
|
def initialize(server, client, attrs = {})
|
|
15
|
-
@server
|
|
16
|
-
@client
|
|
17
|
-
@response_type
|
|
18
|
-
@redirect_uri
|
|
19
|
-
@scope
|
|
20
|
-
@state
|
|
17
|
+
@server = server
|
|
18
|
+
@client = client
|
|
19
|
+
@response_type = attrs[:response_type]
|
|
20
|
+
@redirect_uri = attrs[:redirect_uri]
|
|
21
|
+
@scope = attrs[:scope]
|
|
22
|
+
@state = attrs[:state]
|
|
23
|
+
@code_challenge = attrs[:code_challenge]
|
|
24
|
+
@code_challenge_method = attrs[:code_challenge_method]
|
|
21
25
|
end
|
|
22
26
|
|
|
23
27
|
def authorizable?
|
|
@@ -29,15 +33,36 @@ module Doorkeeper
|
|
|
29
33
|
end
|
|
30
34
|
|
|
31
35
|
def scope
|
|
32
|
-
@scope.presence ||
|
|
36
|
+
@scope.presence || build_scopes
|
|
33
37
|
end
|
|
34
38
|
|
|
35
39
|
def error_response
|
|
36
40
|
OAuth::ErrorResponse.from_request(self)
|
|
37
41
|
end
|
|
38
42
|
|
|
43
|
+
def as_json(_options)
|
|
44
|
+
{
|
|
45
|
+
client_id: client.uid,
|
|
46
|
+
redirect_uri: redirect_uri,
|
|
47
|
+
state: state,
|
|
48
|
+
response_type: response_type,
|
|
49
|
+
scope: scope,
|
|
50
|
+
client_name: client.name,
|
|
51
|
+
status: I18n.t('doorkeeper.pre_authorization.status')
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
|
|
39
55
|
private
|
|
40
56
|
|
|
57
|
+
def build_scopes
|
|
58
|
+
client_scopes = client.application.scopes
|
|
59
|
+
if client_scopes.blank?
|
|
60
|
+
server.default_scopes.to_s
|
|
61
|
+
else
|
|
62
|
+
(server.default_scopes & client_scopes).to_s
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
41
66
|
def validate_response_type
|
|
42
67
|
server.authorization_response_types.include? response_type
|
|
43
68
|
end
|
|
@@ -47,7 +72,8 @@ module Doorkeeper
|
|
|
47
72
|
end
|
|
48
73
|
|
|
49
74
|
def validate_scopes
|
|
50
|
-
return true
|
|
75
|
+
return true if scope.blank?
|
|
76
|
+
|
|
51
77
|
Helpers::ScopeChecker.valid?(
|
|
52
78
|
scope,
|
|
53
79
|
server.scopes,
|
|
@@ -55,11 +81,17 @@ module Doorkeeper
|
|
|
55
81
|
)
|
|
56
82
|
end
|
|
57
83
|
|
|
58
|
-
# TODO: test uri should be matched against the client's one
|
|
59
84
|
def validate_redirect_uri
|
|
60
|
-
return false
|
|
61
|
-
|
|
62
|
-
|
|
85
|
+
return false if redirect_uri.blank?
|
|
86
|
+
|
|
87
|
+
Helpers::URIChecker.valid_for_authorization?(
|
|
88
|
+
redirect_uri,
|
|
89
|
+
client.redirect_uri
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def validate_code_challenge_method
|
|
94
|
+
!code_challenge.present? || (code_challenge_method.present? && code_challenge_method =~ /^plain$|^S256$/)
|
|
63
95
|
end
|
|
64
96
|
end
|
|
65
97
|
end
|
|
@@ -65,7 +65,12 @@ module Doorkeeper
|
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def access_token_expires_in
|
|
68
|
-
Authorization::Token.access_token_expires_in(
|
|
68
|
+
Authorization::Token.access_token_expires_in(
|
|
69
|
+
server,
|
|
70
|
+
client,
|
|
71
|
+
Doorkeeper::OAuth::REFRESH_TOKEN,
|
|
72
|
+
scopes
|
|
73
|
+
)
|
|
69
74
|
end
|
|
70
75
|
|
|
71
76
|
def validate_token_presence
|