doorkeeper 4.4.3 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of doorkeeper might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.gitlab-ci.yml +16 -0
- data/.travis.yml +2 -0
- data/Appraisals +2 -2
- data/Gemfile +1 -1
- data/NEWS.md +61 -8
- data/README.md +92 -9
- data/Rakefile +6 -0
- data/UPGRADE.md +2 -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 +19 -2
- 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 +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 +10 -1
- data/doorkeeper.gemspec +18 -20
- data/gemfiles/rails_5_2.gemfile +1 -1
- data/gemfiles/rails_master.gemfile +4 -1
- data/lib/doorkeeper/config.rb +75 -39
- 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 +71 -0
- data/lib/doorkeeper/models/access_token_mixin.rb +39 -22
- 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 +36 -14
- data/lib/doorkeeper/oauth/authorization_code_request.rb +27 -2
- data/lib/doorkeeper/oauth/base_request.rb +20 -9
- data/lib/doorkeeper/oauth/client/credentials.rb +1 -1
- data/lib/doorkeeper/oauth/client.rb +0 -2
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +2 -1
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +6 -3
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +4 -6
- data/lib/doorkeeper/oauth/client_credentials_request.rb +0 -4
- 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 +22 -14
- 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/authorization_code.rb +0 -2
- data/lib/doorkeeper/request/client_credentials.rb +0 -2
- data/lib/doorkeeper/request/code.rb +0 -2
- data/lib/doorkeeper/request/password.rb +0 -2
- data/lib/doorkeeper/request/refresh_token.rb +0 -2
- data/lib/doorkeeper/request/token.rb +0 -2
- data/lib/doorkeeper/request.rb +28 -35
- data/lib/doorkeeper/version.rb +5 -25
- data/lib/doorkeeper.rb +19 -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 +76 -11
- 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 +277 -47
- 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 +13 -15
- 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 +10 -5
- 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 +80 -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 +40 -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 +44 -1
- data/spec/models/doorkeeper/access_token_spec.rb +66 -22
- data/spec/models/doorkeeper/application_spec.rb +14 -47
- data/spec/requests/applications/applications_request_spec.rb +134 -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 +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 +10 -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 +37 -33
- 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
data/lib/doorkeeper/errors.rb
CHANGED
@@ -36,10 +36,7 @@ module Doorkeeper
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
class TokenGeneratorNotFound < DoorkeeperError
|
43
|
-
end
|
39
|
+
UnableToGenerateToken = Class.new(DoorkeeperError)
|
40
|
+
TokenGeneratorNotFound = Class.new(DoorkeeperError)
|
44
41
|
end
|
45
42
|
end
|
@@ -30,11 +30,11 @@ module Doorkeeper
|
|
30
30
|
|
31
31
|
# :doc:
|
32
32
|
def doorkeeper_token
|
33
|
-
@
|
33
|
+
@doorkeeper_token ||= OAuth::Token.authenticate request, *config_methods
|
34
34
|
end
|
35
35
|
|
36
36
|
def config_methods
|
37
|
-
@
|
37
|
+
@config_methods ||= Doorkeeper.configuration.access_token_methods
|
38
38
|
end
|
39
39
|
|
40
40
|
def get_error_response_from_exception(exception)
|
@@ -51,6 +51,11 @@ module Doorkeeper
|
|
51
51
|
def skip_authorization?
|
52
52
|
!!instance_exec([@server.current_resource_owner, @pre_auth.client], &Doorkeeper.configuration.skip_authorization)
|
53
53
|
end
|
54
|
+
|
55
|
+
def enforce_content_type
|
56
|
+
return if request.content_type == 'application/x-www-form-urlencoded'
|
57
|
+
render json: {}, status: :unsupported_media_type
|
58
|
+
end
|
54
59
|
end
|
55
60
|
end
|
56
61
|
end
|
@@ -9,6 +9,15 @@ module Doorkeeper
|
|
9
9
|
include Models::Orderable
|
10
10
|
include Models::Scopes
|
11
11
|
|
12
|
+
# never uses pkce, if pkce migrations were not generated
|
13
|
+
def uses_pkce?
|
14
|
+
pkce_supported? && code_challenge.present?
|
15
|
+
end
|
16
|
+
|
17
|
+
def pkce_supported?
|
18
|
+
respond_to? :code_challenge
|
19
|
+
end
|
20
|
+
|
12
21
|
module ClassMethods
|
13
22
|
# Searches for Doorkeeper::AccessGrant record with the
|
14
23
|
# specific token value.
|
@@ -21,6 +30,68 @@ module Doorkeeper
|
|
21
30
|
def by_token(token)
|
22
31
|
find_by(token: token.to_s)
|
23
32
|
end
|
33
|
+
|
34
|
+
# Revokes AccessGrant records that have not been revoked and associated
|
35
|
+
# with the specific Application and Resource Owner.
|
36
|
+
#
|
37
|
+
# @param application_id [Integer]
|
38
|
+
# ID of the Application
|
39
|
+
# @param resource_owner [ActiveRecord::Base]
|
40
|
+
# instance of the Resource Owner model
|
41
|
+
#
|
42
|
+
def revoke_all_for(application_id, resource_owner, clock = Time)
|
43
|
+
where(application_id: application_id,
|
44
|
+
resource_owner_id: resource_owner.id,
|
45
|
+
revoked_at: nil).
|
46
|
+
update_all(revoked_at: clock.now.utc)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Implements PKCE code_challenge encoding without base64 padding as described in the spec.
|
50
|
+
# https://tools.ietf.org/html/rfc7636#appendix-A
|
51
|
+
# Appendix A. Notes on Implementing Base64url Encoding without Padding
|
52
|
+
#
|
53
|
+
# This appendix describes how to implement a base64url-encoding
|
54
|
+
# function without padding, based upon the standard base64-encoding
|
55
|
+
# function that uses padding.
|
56
|
+
#
|
57
|
+
# To be concrete, example C# code implementing these functions is shown
|
58
|
+
# below. Similar code could be used in other languages.
|
59
|
+
#
|
60
|
+
# static string base64urlencode(byte [] arg)
|
61
|
+
# {
|
62
|
+
# string s = Convert.ToBase64String(arg); // Regular base64 encoder
|
63
|
+
# s = s.Split('=')[0]; // Remove any trailing '='s
|
64
|
+
# s = s.Replace('+', '-'); // 62nd char of encoding
|
65
|
+
# s = s.Replace('/', '_'); // 63rd char of encoding
|
66
|
+
# return s;
|
67
|
+
# }
|
68
|
+
#
|
69
|
+
# An example correspondence between unencoded and encoded values
|
70
|
+
# follows. The octet sequence below encodes into the string below,
|
71
|
+
# which when decoded, reproduces the octet sequence.
|
72
|
+
#
|
73
|
+
# 3 236 255 224 193
|
74
|
+
#
|
75
|
+
# A-z_4ME
|
76
|
+
#
|
77
|
+
# https://ruby-doc.org/stdlib-2.1.3/libdoc/base64/rdoc/Base64.html#method-i-urlsafe_encode64
|
78
|
+
#
|
79
|
+
# urlsafe_encode64(bin)
|
80
|
+
# Returns the Base64-encoded version of bin. This method complies with
|
81
|
+
# “Base 64 Encoding with URL and Filename Safe Alphabet” in RFC 4648.
|
82
|
+
# The alphabet uses '-' instead of '+' and '_' instead of '/'.
|
83
|
+
|
84
|
+
# @param code_verifier [#to_s] a one time use value (any object that responds to `#to_s`)
|
85
|
+
#
|
86
|
+
# @return [#to_s] An encoded code challenge based on the provided verifier suitable for PKCE validation
|
87
|
+
def generate_code_challenge(code_verifier)
|
88
|
+
padded_result = Base64.urlsafe_encode64(Digest::SHA256.digest(code_verifier))
|
89
|
+
padded_result.split('=')[0] # Remove any trailing '='
|
90
|
+
end
|
91
|
+
|
92
|
+
def pkce_supported?
|
93
|
+
new.pkce_supported?
|
94
|
+
end
|
24
95
|
end
|
25
96
|
end
|
26
97
|
end
|
@@ -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
|
|
@@ -170,8 +187,8 @@ module Doorkeeper
|
|
170
187
|
def as_json(_options = {})
|
171
188
|
{
|
172
189
|
resource_owner_id: resource_owner_id,
|
173
|
-
|
174
|
-
|
190
|
+
scope: scopes,
|
191
|
+
expires_in: expires_in_seconds,
|
175
192
|
application: { uid: application.try(:uid) },
|
176
193
|
created_at: created_at.to_i
|
177
194
|
}
|
@@ -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,24 +5,34 @@ module Doorkeeper
|
|
5
5
|
attr_accessor :pre_auth, :resource_owner, :token
|
6
6
|
|
7
7
|
class << self
|
8
|
-
def
|
9
|
-
if (expiration = custom_expiration(server, pre_auth_or_oauth_client))
|
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)
|
8
|
+
def build_context(pre_auth_or_oauth_client, grant_type, scopes)
|
19
9
|
oauth_client = if pre_auth_or_oauth_client.respond_to?(:client)
|
20
10
|
pre_auth_or_oauth_client.client
|
21
11
|
else
|
22
12
|
pre_auth_or_oauth_client
|
23
13
|
end
|
24
14
|
|
25
|
-
|
15
|
+
Doorkeeper::OAuth::Authorization::Context.new(
|
16
|
+
oauth_client,
|
17
|
+
grant_type,
|
18
|
+
scopes
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def access_token_expires_in(server, context)
|
23
|
+
if (expiration = server.custom_access_token_expires_in.call(context))
|
24
|
+
expiration
|
25
|
+
else
|
26
|
+
server.access_token_expires_in
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def refresh_token_enabled?(server, context)
|
31
|
+
if server.refresh_token_enabled?.respond_to? :call
|
32
|
+
server.refresh_token_enabled?.call(context)
|
33
|
+
else
|
34
|
+
!!server.refresh_token_enabled?
|
35
|
+
end
|
26
36
|
end
|
27
37
|
end
|
28
38
|
|
@@ -32,18 +42,23 @@ module Doorkeeper
|
|
32
42
|
end
|
33
43
|
|
34
44
|
def issue_token
|
45
|
+
context = self.class.build_context(
|
46
|
+
pre_auth.client,
|
47
|
+
Doorkeeper::OAuth::IMPLICIT,
|
48
|
+
pre_auth.scopes
|
49
|
+
)
|
35
50
|
@token ||= AccessToken.find_or_create_for(
|
36
51
|
pre_auth.client,
|
37
52
|
resource_owner.id,
|
38
53
|
pre_auth.scopes,
|
39
|
-
self.class.access_token_expires_in(configuration,
|
54
|
+
self.class.access_token_expires_in(configuration, context),
|
40
55
|
false
|
41
56
|
)
|
42
57
|
end
|
43
58
|
|
44
59
|
def native_redirect
|
45
60
|
{
|
46
|
-
controller:
|
61
|
+
controller: controller,
|
47
62
|
action: :show,
|
48
63
|
access_token: token.token
|
49
64
|
}
|
@@ -54,6 +69,13 @@ module Doorkeeper
|
|
54
69
|
def configuration
|
55
70
|
Doorkeeper.configuration
|
56
71
|
end
|
72
|
+
|
73
|
+
def controller
|
74
|
+
@controller ||= begin
|
75
|
+
mapping = Doorkeeper::Rails::Routes.mapping[:token_info] || {}
|
76
|
+
mapping[:controllers] || 'doorkeeper/token_info'
|
77
|
+
end
|
78
|
+
end
|
57
79
|
end
|
58
80
|
end
|
59
81
|
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
|
@@ -33,12 +31,13 @@ module Doorkeeper
|
|
33
31
|
end
|
34
32
|
|
35
33
|
def find_or_create_access_token(client, resource_owner_id, scopes, server)
|
34
|
+
context = Authorization::Token.build_context(client, grant_type, scopes)
|
36
35
|
@access_token = AccessToken.find_or_create_for(
|
37
36
|
client,
|
38
37
|
resource_owner_id,
|
39
38
|
scopes,
|
40
|
-
Authorization::Token.access_token_expires_in(server,
|
41
|
-
|
39
|
+
Authorization::Token.access_token_expires_in(server, context),
|
40
|
+
Authorization::Token.refresh_token_enabled?(server, context)
|
42
41
|
)
|
43
42
|
end
|
44
43
|
|
@@ -47,8 +46,20 @@ module Doorkeeper
|
|
47
46
|
end
|
48
47
|
|
49
48
|
def after_successful_response
|
50
|
-
Doorkeeper.configuration.after_successful_strategy_response.
|
51
|
-
|
49
|
+
Doorkeeper.configuration.after_successful_strategy_response.call(self, @response)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def build_scopes
|
55
|
+
if @original_scopes.present?
|
56
|
+
OAuth::Scopes.from_string(@original_scopes)
|
57
|
+
else
|
58
|
+
client_scopes = @client.try(:scopes)
|
59
|
+
return default_scopes if client_scopes.blank?
|
60
|
+
|
61
|
+
default_scopes & @client.scopes
|
62
|
+
end
|
52
63
|
end
|
53
64
|
end
|
54
65
|
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?
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'doorkeeper/oauth/client_credentials/validation'
|
2
|
-
|
3
1
|
module Doorkeeper
|
4
2
|
module OAuth
|
5
3
|
class ClientCredentialsRequest < BaseRequest
|
@@ -25,7 +23,12 @@ module Doorkeeper
|
|
25
23
|
private
|
26
24
|
|
27
25
|
def create_token(client, scopes, creator)
|
28
|
-
|
26
|
+
context = Authorization::Token.build_context(
|
27
|
+
client,
|
28
|
+
Doorkeeper::OAuth::CLIENT_CREDENTIALS,
|
29
|
+
scopes
|
30
|
+
)
|
31
|
+
ttl = Authorization::Token.access_token_expires_in(@server, context)
|
29
32
|
|
30
33
|
creator.call(
|
31
34
|
client,
|
@@ -1,7 +1,3 @@
|
|
1
|
-
require 'doorkeeper/validations'
|
2
|
-
require 'doorkeeper/oauth/scopes'
|
3
|
-
require 'doorkeeper/oauth/helpers/scope_checker'
|
4
|
-
|
5
1
|
module Doorkeeper
|
6
2
|
module OAuth
|
7
3
|
class ClientCredentialsRequest < BaseRequest
|
@@ -13,7 +9,9 @@ module Doorkeeper
|
|
13
9
|
validate :scopes, error: :invalid_scope
|
14
10
|
|
15
11
|
def initialize(server, request)
|
16
|
-
@server
|
12
|
+
@server = server
|
13
|
+
@request = request
|
14
|
+
@client = request.client
|
17
15
|
|
18
16
|
validate
|
19
17
|
end
|
@@ -25,7 +23,7 @@ module Doorkeeper
|
|
25
23
|
end
|
26
24
|
|
27
25
|
def validate_scopes
|
28
|
-
return true
|
26
|
+
return true if @request.scopes.blank?
|
29
27
|
|
30
28
|
application_scopes = if @client.present?
|
31
29
|
@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
|