doorkeeper 4.2.6 → 4.3.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/.github/ISSUE_TEMPLATE.md +19 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +17 -0
- data/.gitignore +1 -1
- data/.hound.yml +2 -13
- data/.rubocop.yml +13 -0
- data/.travis.yml +13 -5
- data/Appraisals +6 -2
- data/CODE_OF_CONDUCT.md +46 -0
- data/Gemfile +1 -1
- data/NEWS.md +24 -0
- data/README.md +39 -9
- data/SECURITY.md +13 -0
- data/app/controllers/doorkeeper/application_controller.rb +1 -5
- data/app/controllers/doorkeeper/applications_controller.rb +14 -1
- data/app/controllers/doorkeeper/tokens_controller.rb +13 -1
- data/app/helpers/doorkeeper/dashboard_helper.rb +4 -2
- data/app/validators/redirect_uri_validator.rb +12 -2
- data/app/views/doorkeeper/applications/_form.html.erb +1 -1
- data/app/views/doorkeeper/authorized_applications/index.html.erb +0 -1
- data/config/locales/en.yml +3 -5
- data/doorkeeper.gemspec +4 -3
- data/gemfiles/rails_4_2.gemfile +6 -4
- data/gemfiles/rails_5_0.gemfile +4 -4
- data/gemfiles/rails_5_1.gemfile +6 -7
- data/gemfiles/rails_5_2.gemfile +12 -0
- data/gemfiles/rails_master.gemfile +14 -0
- data/lib/doorkeeper.rb +1 -0
- data/lib/doorkeeper/config.rb +55 -55
- data/lib/doorkeeper/engine.rb +3 -3
- data/lib/doorkeeper/grape/helpers.rb +13 -8
- data/lib/doorkeeper/helpers/controller.rb +8 -4
- data/lib/doorkeeper/models/access_token_mixin.rb +14 -7
- data/lib/doorkeeper/models/application_mixin.rb +11 -6
- data/lib/doorkeeper/models/concerns/expirable.rb +7 -5
- data/lib/doorkeeper/oauth/authorization/token.rb +22 -18
- data/lib/doorkeeper/oauth/authorization_code_request.rb +6 -1
- data/lib/doorkeeper/oauth/base_request.rb +5 -5
- data/lib/doorkeeper/oauth/client.rb +2 -2
- data/lib/doorkeeper/oauth/client/credentials.rb +2 -2
- data/lib/doorkeeper/oauth/error.rb +2 -2
- data/lib/doorkeeper/oauth/error_response.rb +1 -2
- data/lib/doorkeeper/oauth/forbidden_token_response.rb +1 -1
- data/lib/doorkeeper/oauth/invalid_token_response.rb +2 -3
- data/lib/doorkeeper/oauth/password_access_token_request.rb +1 -0
- data/lib/doorkeeper/oauth/refresh_token_request.rb +1 -0
- data/lib/doorkeeper/oauth/scopes.rb +18 -8
- data/lib/doorkeeper/oauth/token.rb +1 -1
- data/lib/doorkeeper/oauth/token_introspection.rb +128 -0
- data/lib/doorkeeper/orm/active_record.rb +20 -8
- data/lib/doorkeeper/orm/active_record/access_grant.rb +1 -1
- data/lib/doorkeeper/orm/active_record/access_token.rb +1 -23
- data/lib/doorkeeper/orm/active_record/application.rb +1 -1
- data/lib/doorkeeper/orm/active_record/base_record.rb +11 -0
- data/lib/doorkeeper/rails/helpers.rb +5 -6
- data/lib/doorkeeper/rails/routes.rb +9 -7
- data/lib/doorkeeper/request.rb +7 -1
- data/lib/doorkeeper/validations.rb +3 -2
- data/lib/doorkeeper/version.rb +13 -1
- data/lib/generators/doorkeeper/application_owner_generator.rb +11 -2
- data/lib/generators/doorkeeper/migration_generator.rb +13 -1
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +7 -1
- data/lib/generators/doorkeeper/templates/{add_owner_to_application_migration.rb → add_owner_to_application_migration.rb.erb} +1 -1
- data/lib/generators/doorkeeper/templates/{add_previous_refresh_token_to_access_tokens.rb → add_previous_refresh_token_to_access_tokens.rb.erb} +1 -1
- data/lib/generators/doorkeeper/templates/initializer.rb +19 -3
- data/lib/generators/doorkeeper/templates/{migration.rb → migration.rb.erb} +1 -1
- data/spec/controllers/applications_controller_spec.rb +15 -4
- data/spec/controllers/authorizations_controller_spec.rb +5 -5
- data/spec/controllers/protected_resources_controller_spec.rb +28 -19
- data/spec/controllers/token_info_controller_spec.rb +17 -13
- data/spec/controllers/tokens_controller_spec.rb +138 -4
- data/spec/dummy/config/initializers/doorkeeper.rb +1 -1
- data/spec/dummy/config/initializers/{active_record_belongs_to_required_by_default.rb → new_framework_defaults.rb} +1 -1
- data/spec/dummy/config/initializers/secret_token.rb +0 -1
- data/spec/factories.rb +1 -1
- data/spec/generators/application_owner_generator_spec.rb +24 -5
- data/spec/generators/migration_generator_spec.rb +24 -3
- data/spec/generators/previous_refresh_token_generator_spec.rb +57 -0
- data/spec/grape/grape_integration_spec.rb +135 -0
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
- data/spec/lib/config_spec.rb +115 -12
- data/spec/lib/models/revocable_spec.rb +2 -2
- data/spec/lib/oauth/authorization_code_request_spec.rb +39 -11
- data/spec/lib/oauth/base_request_spec.rb +2 -7
- data/spec/lib/oauth/client_credentials/creator_spec.rb +1 -1
- data/spec/lib/oauth/client_credentials_integration_spec.rb +1 -1
- data/spec/lib/oauth/client_credentials_request_spec.rb +1 -0
- data/spec/lib/oauth/code_request_spec.rb +1 -3
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +5 -0
- data/spec/lib/oauth/invalid_token_response_spec.rb +1 -1
- data/spec/lib/oauth/password_access_token_request_spec.rb +9 -3
- data/spec/lib/oauth/refresh_token_request_spec.rb +19 -7
- data/spec/lib/oauth/scopes_spec.rb +28 -1
- data/spec/lib/oauth/token_request_spec.rb +6 -8
- data/spec/lib/server_spec.rb +10 -0
- data/spec/models/doorkeeper/access_grant_spec.rb +1 -1
- data/spec/models/doorkeeper/access_token_spec.rb +72 -48
- data/spec/models/doorkeeper/application_spec.rb +51 -18
- data/spec/requests/applications/applications_request_spec.rb +5 -5
- data/spec/requests/endpoints/token_spec.rb +8 -1
- data/spec/requests/flows/authorization_code_spec.rb +1 -0
- data/spec/requests/flows/client_credentials_spec.rb +1 -1
- data/spec/requests/flows/implicit_grant_errors_spec.rb +2 -2
- data/spec/requests/flows/refresh_token_spec.rb +4 -4
- data/spec/requests/flows/revoke_token_spec.rb +15 -15
- 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 +4 -0
- data/spec/routing/default_routes_spec.rb +5 -1
- data/spec/spec_helper_integration.rb +15 -4
- data/spec/support/dependencies/factory_girl.rb +2 -2
- data/spec/support/helpers/access_token_request_helper.rb +1 -1
- data/spec/support/helpers/model_helper.rb +9 -4
- data/spec/support/helpers/request_spec_helper.rb +7 -3
- data/spec/support/helpers/url_helper.rb +8 -8
- data/spec/support/shared/controllers_shared_context.rb +2 -6
- data/spec/support/shared/models_shared_examples.rb +4 -4
- data/spec/validators/redirect_uri_validator_spec.rb +51 -6
- data/spec/version/version_spec.rb +15 -0
- metadata +42 -13
@@ -5,11 +5,13 @@ module Doorkeeper
|
|
5
5
|
module Controller
|
6
6
|
private
|
7
7
|
|
8
|
-
|
8
|
+
# :doc:
|
9
|
+
def authenticate_resource_owner!
|
9
10
|
current_resource_owner
|
10
11
|
end
|
11
12
|
|
12
|
-
|
13
|
+
# :doc:
|
14
|
+
def current_resource_owner
|
13
15
|
instance_eval(&Doorkeeper.configuration.authenticate_resource_owner)
|
14
16
|
end
|
15
17
|
|
@@ -17,7 +19,8 @@ module Doorkeeper
|
|
17
19
|
instance_eval(&Doorkeeper.configuration.resource_owner_from_credentials)
|
18
20
|
end
|
19
21
|
|
20
|
-
|
22
|
+
# :doc:
|
23
|
+
def authenticate_admin!
|
21
24
|
instance_eval(&Doorkeeper.configuration.authenticate_admin)
|
22
25
|
end
|
23
26
|
|
@@ -25,7 +28,8 @@ module Doorkeeper
|
|
25
28
|
@server ||= Server.new(self)
|
26
29
|
end
|
27
30
|
|
28
|
-
|
31
|
+
# :doc:
|
32
|
+
def doorkeeper_token
|
29
33
|
@token ||= OAuth::Token.authenticate request, *config_methods
|
30
34
|
end
|
31
35
|
|
@@ -68,11 +68,11 @@ module Doorkeeper
|
|
68
68
|
# @param resource_owner [ActiveRecord::Base]
|
69
69
|
# instance of the Resource Owner model
|
70
70
|
#
|
71
|
-
def revoke_all_for(application_id, resource_owner)
|
71
|
+
def revoke_all_for(application_id, resource_owner, clock = Time)
|
72
72
|
where(application_id: application_id,
|
73
73
|
resource_owner_id: resource_owner.id,
|
74
74
|
revoked_at: nil).
|
75
|
-
|
75
|
+
update_all(revoked_at: clock.now.utc)
|
76
76
|
end
|
77
77
|
|
78
78
|
# Looking for not expired Access Token with a matching set of scopes
|
@@ -168,7 +168,7 @@ module Doorkeeper
|
|
168
168
|
# nil if nothing was found
|
169
169
|
#
|
170
170
|
def last_authorized_token_for(application_id, resource_owner_id)
|
171
|
-
|
171
|
+
ordered_by(:created_at, :desc).
|
172
172
|
find_by(application_id: application_id,
|
173
173
|
resource_owner_id: resource_owner_id,
|
174
174
|
revoked_at: nil)
|
@@ -247,7 +247,11 @@ module Doorkeeper
|
|
247
247
|
def generate_token
|
248
248
|
self.created_at ||= Time.now.utc
|
249
249
|
|
250
|
-
generator =
|
250
|
+
generator = token_generator
|
251
|
+
unless generator.respond_to?(:generate)
|
252
|
+
raise Errors::UnableToGenerateToken, "#{generator} does not respond to `.generate`."
|
253
|
+
end
|
254
|
+
|
251
255
|
self.token = generator.generate(
|
252
256
|
resource_owner_id: resource_owner_id,
|
253
257
|
scopes: scopes,
|
@@ -255,10 +259,13 @@ module Doorkeeper
|
|
255
259
|
expires_in: expires_in,
|
256
260
|
created_at: created_at
|
257
261
|
)
|
258
|
-
|
259
|
-
|
262
|
+
end
|
263
|
+
|
264
|
+
def token_generator
|
265
|
+
generator_name = Doorkeeper.configuration.access_token_generator
|
266
|
+
generator_name.constantize
|
260
267
|
rescue NameError
|
261
|
-
raise Errors::TokenGeneratorNotFound, "#{
|
268
|
+
raise Errors::TokenGeneratorNotFound, "#{generator_name} not found"
|
262
269
|
end
|
263
270
|
end
|
264
271
|
end
|
@@ -43,6 +43,15 @@ module Doorkeeper
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
+
# Set an application's valid redirect URIs.
|
47
|
+
#
|
48
|
+
# @param uris [String, Array] Newline-separated string or array the URI(s)
|
49
|
+
#
|
50
|
+
# @return [String] The redirect URI(s) seperated by newlines.
|
51
|
+
def redirect_uri=(uris)
|
52
|
+
super(uris.is_a?(Array) ? uris.join("\n") : uris)
|
53
|
+
end
|
54
|
+
|
46
55
|
private
|
47
56
|
|
48
57
|
def has_scopes?
|
@@ -51,15 +60,11 @@ module Doorkeeper
|
|
51
60
|
end
|
52
61
|
|
53
62
|
def generate_uid
|
54
|
-
if uid.blank?
|
55
|
-
self.uid = UniqueToken.generate
|
56
|
-
end
|
63
|
+
self.uid = UniqueToken.generate if uid.blank?
|
57
64
|
end
|
58
65
|
|
59
66
|
def generate_secret
|
60
|
-
if secret.blank?
|
61
|
-
self.secret = UniqueToken.generate
|
62
|
-
end
|
67
|
+
self.secret = UniqueToken.generate if secret.blank?
|
63
68
|
end
|
64
69
|
end
|
65
70
|
end
|
@@ -6,7 +6,7 @@ module Doorkeeper
|
|
6
6
|
#
|
7
7
|
# @return [Boolean] true if object expired and false in other case
|
8
8
|
def expired?
|
9
|
-
expires_in && Time.now.utc >
|
9
|
+
expires_in && Time.now.utc > expires_at
|
10
10
|
end
|
11
11
|
|
12
12
|
# Calculates expiration time in seconds.
|
@@ -15,14 +15,16 @@ module Doorkeeper
|
|
15
15
|
# or nil if object never expires.
|
16
16
|
def expires_in_seconds
|
17
17
|
return nil if expires_in.nil?
|
18
|
-
expires =
|
18
|
+
expires = expires_at - Time.now.utc
|
19
19
|
expires_sec = expires.seconds.round(0)
|
20
20
|
expires_sec > 0 ? expires_sec : 0
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
# Expiration time (date time of creation + TTL).
|
24
|
+
#
|
25
|
+
# @return [Time] expiration time in UTC
|
26
|
+
#
|
27
|
+
def expires_at
|
26
28
|
created_at + expires_in.seconds
|
27
29
|
end
|
28
30
|
end
|
@@ -4,19 +4,33 @@ module Doorkeeper
|
|
4
4
|
class Token
|
5
5
|
attr_accessor :pre_auth, :resource_owner, :token
|
6
6
|
|
7
|
+
class << self
|
8
|
+
def access_token_expires_in(server, pre_auth_or_oauth_client)
|
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)
|
19
|
+
oauth_client = if pre_auth_or_oauth_client.respond_to?(:client)
|
20
|
+
pre_auth_or_oauth_client.client
|
21
|
+
else
|
22
|
+
pre_auth_or_oauth_client
|
23
|
+
end
|
24
|
+
|
25
|
+
server.custom_access_token_expires_in.call(oauth_client)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
7
29
|
def initialize(pre_auth, resource_owner)
|
8
30
|
@pre_auth = pre_auth
|
9
31
|
@resource_owner = resource_owner
|
10
32
|
end
|
11
33
|
|
12
|
-
def self.access_token_expires_in(server, pre_auth_or_oauth_client)
|
13
|
-
if expiration = custom_expiration(server, pre_auth_or_oauth_client)
|
14
|
-
expiration
|
15
|
-
else
|
16
|
-
server.access_token_expires_in
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
34
|
def issue_token
|
21
35
|
@token ||= AccessToken.find_or_create_for(
|
22
36
|
pre_auth.client,
|
@@ -37,16 +51,6 @@ module Doorkeeper
|
|
37
51
|
|
38
52
|
private
|
39
53
|
|
40
|
-
def self.custom_expiration(server, pre_auth_or_oauth_client)
|
41
|
-
oauth_client = if pre_auth_or_oauth_client.respond_to?(:client)
|
42
|
-
pre_auth_or_oauth_client.client
|
43
|
-
else
|
44
|
-
pre_auth_or_oauth_client
|
45
|
-
end
|
46
|
-
|
47
|
-
server.custom_access_token_expires_in.call(oauth_client)
|
48
|
-
end
|
49
|
-
|
50
54
|
def configuration
|
51
55
|
Doorkeeper.configuration
|
52
56
|
end
|
@@ -4,6 +4,7 @@ module Doorkeeper
|
|
4
4
|
validate :attributes, error: :invalid_request
|
5
5
|
validate :client, error: :invalid_client
|
6
6
|
validate :grant, error: :invalid_grant
|
7
|
+
# @see https://tools.ietf.org/html/rfc6749#section-5.2
|
7
8
|
validate :redirect_uri, error: :invalid_grant
|
8
9
|
|
9
10
|
attr_accessor :server, :grant, :client, :redirect_uri, :access_token
|
@@ -28,6 +29,7 @@ module Doorkeeper
|
|
28
29
|
grant.scopes,
|
29
30
|
server)
|
30
31
|
end
|
32
|
+
super
|
31
33
|
end
|
32
34
|
|
33
35
|
def validate_attributes
|
@@ -44,7 +46,10 @@ module Doorkeeper
|
|
44
46
|
end
|
45
47
|
|
46
48
|
def validate_redirect_uri
|
47
|
-
|
49
|
+
Helpers::URIChecker.valid_for_authorization?(
|
50
|
+
redirect_uri,
|
51
|
+
grant.redirect_uri
|
52
|
+
)
|
48
53
|
end
|
49
54
|
end
|
50
55
|
end
|
@@ -5,6 +5,7 @@ module Doorkeeper
|
|
5
5
|
|
6
6
|
def authorize
|
7
7
|
validate
|
8
|
+
|
8
9
|
if valid?
|
9
10
|
before_successful_response
|
10
11
|
@response = TokenResponse.new(access_token)
|
@@ -37,14 +38,13 @@ module Doorkeeper
|
|
37
38
|
resource_owner_id,
|
38
39
|
scopes,
|
39
40
|
Authorization::Token.access_token_expires_in(server, client),
|
40
|
-
server.refresh_token_enabled?
|
41
|
+
server.refresh_token_enabled?
|
42
|
+
)
|
41
43
|
end
|
42
44
|
|
43
|
-
def before_successful_response
|
44
|
-
end
|
45
|
+
def before_successful_response; end
|
45
46
|
|
46
|
-
def after_successful_response
|
47
|
-
end
|
47
|
+
def after_successful_response; end
|
48
48
|
end
|
49
49
|
end
|
50
50
|
end
|
@@ -12,7 +12,7 @@ module Doorkeeper
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def self.find(uid, method = Application.method(:by_uid))
|
15
|
-
if application = method.call(uid)
|
15
|
+
if (application = method.call(uid))
|
16
16
|
new(application)
|
17
17
|
end
|
18
18
|
end
|
@@ -20,7 +20,7 @@ module Doorkeeper
|
|
20
20
|
def self.authenticate(credentials, method = Application.method(:by_uid_and_secret))
|
21
21
|
return false if credentials.blank?
|
22
22
|
|
23
|
-
if application = method.call(credentials.uid, credentials.secret)
|
23
|
+
if (application = method.call(credentials.uid, credentials.secret))
|
24
24
|
new(application)
|
25
25
|
end
|
26
26
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Doorkeeper
|
2
2
|
module OAuth
|
3
3
|
class Client
|
4
|
-
|
4
|
+
Credentials = Struct.new(:uid, :secret) do
|
5
5
|
class << self
|
6
6
|
def from_request(request, *credentials_methods)
|
7
7
|
credentials_methods.inject(nil) do |credentials, method|
|
@@ -18,7 +18,7 @@ module Doorkeeper
|
|
18
18
|
def from_basic(request)
|
19
19
|
authorization = request.authorization
|
20
20
|
if authorization.present? && authorization =~ /^Basic (.*)/m
|
21
|
-
Base64.decode64(
|
21
|
+
Base64.decode64(Regexp.last_match(1)).split(/:/, 2)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module Doorkeeper
|
2
2
|
module OAuth
|
3
|
-
|
3
|
+
Error = Struct.new(:name, :state) do
|
4
4
|
def description
|
5
5
|
I18n.translate(
|
6
6
|
name,
|
7
|
-
scope: [
|
7
|
+
scope: %i[doorkeeper errors messages],
|
8
8
|
default: :server_error
|
9
9
|
)
|
10
10
|
end
|
@@ -4,8 +4,7 @@ module Doorkeeper
|
|
4
4
|
include OAuth::Helpers
|
5
5
|
|
6
6
|
def self.from_request(request, attributes = {})
|
7
|
-
|
8
|
-
new(attributes.merge(name: request.error, state: state))
|
7
|
+
new(attributes.merge(name: request.error, state: request.try(:state)))
|
9
8
|
end
|
10
9
|
|
11
10
|
delegate :name, :description, :state, to: :@error
|
@@ -4,10 +4,9 @@ module Doorkeeper
|
|
4
4
|
attr_reader :reason
|
5
5
|
|
6
6
|
def self.from_access_token(access_token, attributes = {})
|
7
|
-
reason =
|
8
|
-
when access_token.try(:revoked?)
|
7
|
+
reason = if access_token.try(:revoked?)
|
9
8
|
:revoked
|
10
|
-
|
9
|
+
elsif access_token.try(:expired?)
|
11
10
|
:expired
|
12
11
|
else
|
13
12
|
:unknown
|
@@ -45,20 +45,30 @@ module Doorkeeper
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def +(other)
|
48
|
-
|
49
|
-
self.class.from_array(all + other.all)
|
50
|
-
else
|
51
|
-
super(other)
|
52
|
-
end
|
48
|
+
self.class.from_array(all + to_array(other))
|
53
49
|
end
|
54
50
|
|
55
51
|
def <=>(other)
|
56
|
-
|
52
|
+
if other.respond_to?(:map)
|
53
|
+
map(&:to_s).sort <=> other.map(&:to_s).sort
|
54
|
+
else
|
55
|
+
super
|
56
|
+
end
|
57
57
|
end
|
58
58
|
|
59
59
|
def &(other)
|
60
|
-
|
61
|
-
|
60
|
+
self.class.from_array(all & to_array(other))
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def to_array(other)
|
66
|
+
case other
|
67
|
+
when Scopes
|
68
|
+
other.all
|
69
|
+
else
|
70
|
+
other.to_a
|
71
|
+
end
|
62
72
|
end
|
63
73
|
end
|
64
74
|
end
|
@@ -11,7 +11,7 @@ module Doorkeeper
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def authenticate(request, *methods)
|
14
|
-
if token = from_request(request, *methods)
|
14
|
+
if (token = from_request(request, *methods))
|
15
15
|
access_token = AccessToken.by_token(token)
|
16
16
|
access_token.revoke_previous_refresh_token! if access_token
|
17
17
|
access_token
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module Doorkeeper
|
2
|
+
module OAuth
|
3
|
+
# RFC7662 OAuth 2.0 Token Introspection
|
4
|
+
#
|
5
|
+
# @see https://tools.ietf.org/html/rfc7662
|
6
|
+
class TokenIntrospection
|
7
|
+
attr_reader :server, :token
|
8
|
+
attr_reader :error
|
9
|
+
|
10
|
+
def initialize(server, token)
|
11
|
+
@server = server
|
12
|
+
@token = token
|
13
|
+
|
14
|
+
authorize!
|
15
|
+
end
|
16
|
+
|
17
|
+
def authorized?
|
18
|
+
@error.blank?
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_json
|
22
|
+
active? ? success_response : failure_response
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# If the protected resource uses OAuth 2.0 client credentials to
|
28
|
+
# authenticate to the introspection endpoint and its credentials are
|
29
|
+
# invalid, the authorization server responds with an HTTP 401
|
30
|
+
# (Unauthorized) as described in Section 5.2 of OAuth 2.0 [RFC6749].
|
31
|
+
#
|
32
|
+
# Endpoint must first validate the authentication.
|
33
|
+
# If the authentication is invalid, the endpoint should respond with
|
34
|
+
# an HTTP 401 status code and an invalid_client response.
|
35
|
+
#
|
36
|
+
# @see https://www.oauth.com/oauth2-servers/token-introspection-endpoint/
|
37
|
+
#
|
38
|
+
def authorize!
|
39
|
+
# Requested client authorization
|
40
|
+
if server.credentials
|
41
|
+
@error = :invalid_client unless authorized_client
|
42
|
+
else
|
43
|
+
# Requested bearer token authorization
|
44
|
+
@error = :invalid_request unless authorized_token
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Client Authentication
|
49
|
+
def authorized_client
|
50
|
+
@_authorized_client ||= server.credentials && server.client
|
51
|
+
end
|
52
|
+
|
53
|
+
# Bearer Token Authentication
|
54
|
+
def authorized_token
|
55
|
+
@_authorized_token ||=
|
56
|
+
OAuth::Token.authenticate(server.context.request, :from_bearer_authorization)
|
57
|
+
end
|
58
|
+
|
59
|
+
# 2.2. Introspection Response
|
60
|
+
def success_response
|
61
|
+
{
|
62
|
+
active: true,
|
63
|
+
scope: @token.scopes_string,
|
64
|
+
client_id: @token.try(:application).try(:uid),
|
65
|
+
token_type: @token.token_type,
|
66
|
+
exp: @token.expires_at.to_i,
|
67
|
+
iat: @token.created_at.to_i
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
# If the introspection call is properly authorized but the token is not
|
72
|
+
# active, does not exist on this server, or the protected resource is
|
73
|
+
# not allowed to introspect this particular token, then the
|
74
|
+
# authorization server MUST return an introspection response with the
|
75
|
+
# "active" field set to "false". Note that to avoid disclosing too
|
76
|
+
# much of the authorization server's state to a third party, the
|
77
|
+
# authorization server SHOULD NOT include any additional information
|
78
|
+
# about an inactive token, including why the token is inactive.
|
79
|
+
#
|
80
|
+
# @see https://tools.ietf.org/html/rfc7662 2.2. Introspection Response
|
81
|
+
#
|
82
|
+
def failure_response
|
83
|
+
{
|
84
|
+
active: false
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
# Boolean indicator of whether or not the presented token
|
89
|
+
# is currently active. The specifics of a token's "active" state
|
90
|
+
# will vary depending on the implementation of the authorization
|
91
|
+
# server and the information it keeps about its tokens, but a "true"
|
92
|
+
# value return for the "active" property will generally indicate
|
93
|
+
# that a given token has been issued by this authorization server,
|
94
|
+
# has not been revoked by the resource owner, and is within its
|
95
|
+
# given time window of validity (e.g., after its issuance time and
|
96
|
+
# before its expiration time).
|
97
|
+
#
|
98
|
+
# Any other error is considered an "inactive" token.
|
99
|
+
#
|
100
|
+
# * The token requested does not exist or is invalid
|
101
|
+
# * The token expired
|
102
|
+
# * The token was issued to a different client than is making this request
|
103
|
+
#
|
104
|
+
def active?
|
105
|
+
if authorized_client
|
106
|
+
valid_token? && authorized_for_client?
|
107
|
+
else
|
108
|
+
valid_token?
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Token can be valid only if it is not expired or revoked.
|
113
|
+
def valid_token?
|
114
|
+
@token.present? && @token.accessible?
|
115
|
+
end
|
116
|
+
|
117
|
+
# If token doesn't belong to some client, then it is public.
|
118
|
+
# Otherwise in it required for token to be connected to the same client.
|
119
|
+
def authorized_for_client?
|
120
|
+
if @token.application.present?
|
121
|
+
@token.application == authorized_client.application
|
122
|
+
else
|
123
|
+
true
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|