diff --git a/CHANGELOG.md b/CHANGELOG.md index edc7c81b5..e0454a863 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Add your entry here. - [#1715] Fix token introspection invalid request reason - [#1714] Fix `Doorkeeper::AccessToken.find_or_create_for` with empty scopes which raises NoMethodError - [#1712] Add `Pragma: no-cache` to token response +- [#1726] Refactor token introspection class. ## 5.7.1 diff --git a/lib/doorkeeper/config.rb b/lib/doorkeeper/config.rb index 277d427d0..d8d8ce88a 100644 --- a/lib/doorkeeper/config.rb +++ b/lib/doorkeeper/config.rb @@ -418,7 +418,7 @@ def configure_secrets_for(type, using:, fallback:) default: (lambda do |token, authorized_client, authorized_token| if authorized_token authorized_token.application == token&.application - elsif token.application + elsif token&.application authorized_client == token.application else true diff --git a/lib/doorkeeper/oauth/token_introspection.rb b/lib/doorkeeper/oauth/token_introspection.rb index 002c73cb0..4a88118fc 100644 --- a/lib/doorkeeper/oauth/token_introspection.rb +++ b/lib/doorkeeper/oauth/token_introspection.rb @@ -6,16 +6,15 @@ module OAuth # # @see https://datatracker.ietf.org/doc/html/rfc7662 class TokenIntrospection - attr_reader :error, :invalid_request_reason + attr_reader :token, :error, :invalid_request_reason def initialize(server, token) @server = server @token = token - - authorize! end def authorized? + authorize! @error.blank? end @@ -37,7 +36,7 @@ def to_json(*) private - attr_reader :server, :token + attr_reader :server # If the protected resource uses OAuth 2.0 client credentials to # authenticate to the introspection endpoint and its credentials are @@ -59,24 +58,38 @@ def to_json(*) def authorize! # Requested client authorization if server.credentials - @error = Errors::InvalidClient unless authorized_client + authorize_using_basic_auth! elsif authorized_token - # Requested bearer token authorization - # - # If the protected resource uses an OAuth 2.0 bearer token to authorize - # its call to the introspection endpoint and the token used for - # authorization does not contain sufficient privileges or is otherwise - # invalid for this request, the authorization server responds with an - # HTTP 401 code as described in Section 3 of OAuth 2.0 Bearer Token - # Usage [RFC6750]. - # - @error = Errors::InvalidToken unless valid_authorized_token? + authorize_using_bearer_token! else @error = Errors::InvalidRequest @invalid_request_reason = :request_not_authorized end end + def authorize_using_basic_auth! + # Note that a properly formed and authorized query for an inactive or + # otherwise invalid token (or a token the protected resource is not + # allowed to know about) is not considered an error response by this + # specification. In these cases, the authorization server MUST instead + # respond with an introspection response with the "active" field set to + # "false" as described in Section 2.2. + @error = Errors::InvalidClient unless authorized_client + end + + def authorize_using_bearer_token! + # Requested bearer token authorization + # + # If the protected resource uses an OAuth 2.0 bearer token to authorize + # its call to the introspection endpoint and the token used for + # authorization does not contain sufficient privileges or is otherwise + # invalid for this request, the authorization server responds with an + # HTTP 401 code as described in Section 3 of OAuth 2.0 Bearer Token + # Usage [RFC6750]. + # + @error = Errors::InvalidToken unless valid_authorized_token? + end + # Client Authentication def authorized_client @authorized_client ||= server.credentials && server.client diff --git a/spec/controllers/tokens_controller_spec.rb b/spec/controllers/tokens_controller_spec.rb index 9d029314a..08de1c189 100644 --- a/spec/controllers/tokens_controller_spec.rb +++ b/spec/controllers/tokens_controller_spec.rb @@ -272,7 +272,7 @@ end end - it "responds with invalid_token error" do + it "responds with invalid_token error for bearer auth" do request.headers["Authorization"] = "Bearer #{access_token.token}" post :introspect, params: { token: token_for_introspection.token } @@ -282,6 +282,16 @@ expect(json_response).not_to include("active") expect(json_response).to include("error" => "invalid_token") end + + it "responds with access_denied error for basic auth" do + request.headers["Authorization"] = basic_auth_header_for_client(client) + + post :introspect, params: { token: token_for_introspection.token } + + response_status_should_be 200 + + expect(json_response).to include("active" => false) + end end context "when custom introspection response configured" do