doorkeeper 4.4.3 → 5.0.0.rc1
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +2 -0
- data/Appraisals +2 -2
- data/Gemfile +1 -1
- data/NEWS.md +36 -17
- 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 +42 -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 +12 -15
- 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 +25 -24
- data/app/views/doorkeeper/applications/edit.html.erb +1 -1
- data/app/views/doorkeeper/applications/index.html.erb +17 -7
- data/app/views/doorkeeper/applications/new.html.erb +1 -1
- data/app/views/doorkeeper/applications/show.html.erb +6 -6
- 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 +9 -1
- data/doorkeeper.gemspec +0 -2
- data/gemfiles/rails_5_2.gemfile +1 -1
- data/lib/doorkeeper/config.rb +58 -35
- 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 +38 -21
- 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 +1 -1
- 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/password_access_token_request.rb +7 -4
- data/lib/doorkeeper/oauth/pre_authorization.rb +41 -11
- 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 +13 -16
- 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 +14 -6
- 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.rb +28 -28
- data/lib/doorkeeper/version.rb +5 -25
- 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/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 +252 -49
- 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 +19 -73
- 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 +5 -4
- data/spec/dummy/config/initializers/new_framework_defaults.rb +4 -0
- data/spec/dummy/config/routes.rb +3 -42
- data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +6 -0
- data/spec/dummy/db/migrate/{20180210183654_add_confidential_to_application.rb → 20180210183654_add_confidential_to_applications.rb} +1 -1
- data/spec/dummy/db/schema.rb +36 -36
- 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 +51 -31
- 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 +9 -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 +1 -3
- 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 -7
- 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 +33 -4
- 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 +1 -47
- 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 +198 -2
- 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 +56 -2
- 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/dependencies/{factory_girl.rb → factory_bot.rb} +0 -0
- 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 +33 -31
- data/lib/generators/doorkeeper/add_client_confidentiality_generator.rb +0 -31
- data/lib/generators/doorkeeper/templates/add_confidential_to_application_migration.rb.erb +0 -11
- data/spec/controllers/application_metal_controller.rb +0 -10
@@ -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
|
|
@@ -156,7 +173,7 @@ module Doorkeeper
|
|
156
173
|
# The OAuth 2.0 Authorization Framework: Bearer Token Usage
|
157
174
|
#
|
158
175
|
def token_type
|
159
|
-
'
|
176
|
+
'bearer'
|
160
177
|
end
|
161
178
|
|
162
179
|
def use_refresh_token?
|
@@ -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?
|
@@ -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,14 +81,18 @@ 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
85
|
return false if redirect_uri.blank?
|
61
86
|
|
62
87
|
Helpers::URIChecker.valid_for_authorization?(
|
63
|
-
redirect_uri,
|
88
|
+
redirect_uri,
|
89
|
+
client.redirect_uri
|
64
90
|
)
|
65
91
|
end
|
92
|
+
|
93
|
+
def validate_code_challenge_method
|
94
|
+
!code_challenge.present? || (code_challenge_method.present? && code_challenge_method =~ /^plain$|^S256$/)
|
95
|
+
end
|
66
96
|
end
|
67
97
|
end
|
68
98
|
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
|
@@ -3,7 +3,7 @@ module Doorkeeper
|
|
3
3
|
class Token
|
4
4
|
class << self
|
5
5
|
def from_request(request, *methods)
|
6
|
-
methods.inject(nil) do |
|
6
|
+
methods.inject(nil) do |_, method|
|
7
7
|
method = self.method(method) if method.is_a?(Symbol)
|
8
8
|
credentials = method.call(request)
|
9
9
|
break credentials unless credentials.blank?
|
@@ -13,7 +13,10 @@ module Doorkeeper
|
|
13
13
|
def authenticate(request, *methods)
|
14
14
|
if (token = from_request(request, *methods))
|
15
15
|
access_token = AccessToken.by_token(token)
|
16
|
-
|
16
|
+
refresh_token_enabled = Doorkeeper.configuration.refresh_token_enabled?
|
17
|
+
if access_token.present? && refresh_token_enabled
|
18
|
+
access_token.revoke_previous_refresh_token!
|
19
|
+
end
|
17
20
|
access_token
|
18
21
|
end
|
19
22
|
end
|
@@ -47,12 +47,12 @@ module Doorkeeper
|
|
47
47
|
|
48
48
|
# Client Authentication
|
49
49
|
def authorized_client
|
50
|
-
@
|
50
|
+
@authorized_client ||= server.credentials && server.client
|
51
51
|
end
|
52
52
|
|
53
53
|
# Bearer Token Authentication
|
54
54
|
def authorized_token
|
55
|
-
@
|
55
|
+
@authorized_token ||=
|
56
56
|
OAuth::Token.authenticate(server.context.request, :from_bearer_authorization)
|
57
57
|
end
|
58
58
|
|
@@ -23,9 +23,11 @@ module Doorkeeper
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def headers
|
26
|
-
{
|
26
|
+
{
|
27
|
+
'Cache-Control' => 'no-store',
|
27
28
|
'Pragma' => 'no-cache',
|
28
|
-
'Content-Type' => 'application/json; charset=utf-8'
|
29
|
+
'Content-Type' => 'application/json; charset=utf-8'
|
30
|
+
}
|
29
31
|
end
|
30
32
|
end
|
31
33
|
end
|