doorkeeper 5.2.0.rc1 → 5.2.0.rc2
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.
Potentially problematic release.
This version of doorkeeper might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -1
- data/CONTRIBUTING.md +7 -0
- data/Dangerfile +1 -1
- data/Dockerfile +29 -0
- data/app/controllers/doorkeeper/authorizations_controller.rb +6 -1
- data/app/controllers/doorkeeper/tokens_controller.rb +14 -1
- data/lib/doorkeeper/config.rb +9 -6
- data/lib/doorkeeper/models/access_token_mixin.rb +43 -2
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +14 -0
- data/lib/doorkeeper/oauth/token_introspection.rb +12 -6
- data/lib/doorkeeper/version.rb +1 -1
- data/lib/generators/doorkeeper/templates/initializer.rb +35 -22
- data/spec/controllers/authorizations_controller_spec.rb +13 -0
- data/spec/controllers/tokens_controller_spec.rb +140 -40
- data/spec/dummy/config/initializers/doorkeeper.rb +47 -20
- data/spec/lib/oauth/client_credentials/creator_spec.rb +3 -0
- metadata +3 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 30363aebde1b255fb64b8df11e5d9b1c80f0d896ebb970908f764a8a5fd17fdf
         | 
| 4 | 
            +
              data.tar.gz: 129cddd18b7ea164da1387d8a3fef5c40b4be96eca9f141ebb0167a318f28a07
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 48f707ac0ee598c6987cf396cc168f57975abe8fbf78ce0479733e1b7fdf35ef6abcd73474f0141c72eee1b08ce8a9f0cf46d866f974c0feea8ae9609cb36d07
         | 
| 7 | 
            +
              data.tar.gz: 79c4aa3fdae50be283f3ef78c544554ad5d50d8395d6f6970a41c73a0d46d427b859dd07e38685120d0df9b1016567a3a34b79d882b9b23d6bb470a19c95ecd8
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -7,6 +7,19 @@ User-visible changes worth mentioning. | |
| 7 7 |  | 
| 8 8 | 
             
            ## master
         | 
| 9 9 |  | 
| 10 | 
            +
            - [#PR ID] Add your description here
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            ## 5.2.0.rc2
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            - [#1270] Find matching tokens in batches for `reuse_access_token` option (fix #1193).
         | 
| 15 | 
            +
            - [#1271] Reintroduce existing token revocation for client credentials.
         | 
| 16 | 
            +
            - [#1269] Update initializer template documentation.
         | 
| 17 | 
            +
            - [#1266] Use strong parameters within pre-authorization.
         | 
| 18 | 
            +
            - [#1264] Add :before_successful_authorization and :after_successful_authorization hooks in TokensController
         | 
| 19 | 
            +
            - [#1263] Response properly when introspection fails and fix configurations's user guide.
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            ## 5.2.0.rc1
         | 
| 22 | 
            +
             | 
| 10 23 | 
             
            - [#1260], [#1261] Improve Token Introspection configuration option (access to tokens, client).
         | 
| 11 24 | 
             
            - [#1257] Add constraint configuration when using client authentication on introspection endpoint.
         | 
| 12 25 | 
             
            - [#1252] Returning `unauthorized` when the revocation of the token should not be performed due to wrong permissions.
         | 
| @@ -31,7 +44,7 @@ User-visible changes worth mentioning. | |
| 31 44 |  | 
| 32 45 | 
             
            - [#1208] Unify hashing implementation into secret storing strategies
         | 
| 33 46 |  | 
| 34 | 
            -
              **[IMPORTANT] | 
| 47 | 
            +
              **[IMPORTANT]** If you have been using the master branch of doorkeeper with bcrypt in your Gemfile.lock,
         | 
| 35 48 | 
             
              your application secrets have been hashed using BCrypt. To restore this behavior, use the initializer option
         | 
| 36 49 | 
             
              `use_application_hashing using: 'Doorkeeper::SecretStoring::BCrypt`.
         | 
| 37 50 |  | 
    
        data/CONTRIBUTING.md
    CHANGED
    
    | @@ -7,6 +7,13 @@ Fork, then clone the repo: | |
| 7 7 |  | 
| 8 8 | 
             
                git clone git@github.com:your-username/doorkeeper.git
         | 
| 9 9 |  | 
| 10 | 
            +
            ### Docker Setup
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            Build the container image with: `docker build --pull -t doorkeeper:test .`
         | 
| 13 | 
            +
            Run the tests with: `docker run -it --rm doorkeeper:test`
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            ### Local Setup
         | 
| 16 | 
            +
             | 
| 10 17 | 
             
            Set up Ruby dependencies via Bundler
         | 
| 11 18 |  | 
| 12 19 | 
             
                bundle install
         | 
    
        data/Dangerfile
    CHANGED
    
    | @@ -11,7 +11,7 @@ def changelog_entry_example | |
| 11 11 | 
             
                               .sub(/[?.!,;]?$/, '')
         | 
| 12 12 | 
             
                               .capitalize
         | 
| 13 13 |  | 
| 14 | 
            -
              "- [##{pr_number}] | 
| 14 | 
            +
              "- [##{pr_number}] #{pr_title}."
         | 
| 15 15 | 
             
            end
         | 
| 16 16 |  | 
| 17 17 | 
             
            # --------------------------------------------------------------------------------------------------------------------
         | 
    
        data/Dockerfile
    ADDED
    
    | @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            FROM ruby:2.6.3-alpine3.9
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            RUN apk add --no-cache \
         | 
| 4 | 
            +
              ca-certificates \
         | 
| 5 | 
            +
              wget \
         | 
| 6 | 
            +
              openssl \ 
         | 
| 7 | 
            +
              bash \
         | 
| 8 | 
            +
              build-base \
         | 
| 9 | 
            +
              git \
         | 
| 10 | 
            +
              sqlite-dev \
         | 
| 11 | 
            +
              tzdata
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            ENV LANG en_US.UTF-8
         | 
| 14 | 
            +
            ENV LANGUAGE en_US:en
         | 
| 15 | 
            +
            ENV LC_ALL en_US.UTF-8
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            ENV BUNDLER_VERSION 2.0.1
         | 
| 18 | 
            +
            RUN gem install bundler -v ${BUNDLER_VERSION} -i /usr/local/lib/ruby/gems/$(ls /usr/local/lib/ruby/gems) --force
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            WORKDIR /srv
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            COPY Gemfile doorkeeper.gemspec /srv/
         | 
| 23 | 
            +
            COPY lib/doorkeeper/version.rb /srv/lib/doorkeeper/version.rb
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            RUN bundle install
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            COPY . /srv/
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            CMD ["rake"]
         | 
| @@ -68,7 +68,12 @@ module Doorkeeper | |
| 68 68 | 
             
                def pre_auth
         | 
| 69 69 | 
             
                  @pre_auth ||= OAuth::PreAuthorization.new(Doorkeeper.configuration,
         | 
| 70 70 | 
             
                                                            server.client_via_uid,
         | 
| 71 | 
            -
                                                             | 
| 71 | 
            +
                                                            pre_auth_params)
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                def pre_auth_params
         | 
| 75 | 
            +
                  params.permit(:response_type, :redirect_uri, :scope, :state,
         | 
| 76 | 
            +
                                :code_challenge, :code_challenge_method)
         | 
| 72 77 | 
             
                end
         | 
| 73 78 |  | 
| 74 79 | 
             
                def authorization
         | 
| @@ -88,7 +88,20 @@ module Doorkeeper | |
| 88 88 | 
             
                end
         | 
| 89 89 |  | 
| 90 90 | 
             
                def authorize_response
         | 
| 91 | 
            -
                  @authorize_response ||=  | 
| 91 | 
            +
                  @authorize_response ||= begin
         | 
| 92 | 
            +
                    before_successful_authorization
         | 
| 93 | 
            +
                    auth = strategy.authorize
         | 
| 94 | 
            +
                    after_successful_authorization unless auth.is_a?(Doorkeeper::OAuth::ErrorResponse)
         | 
| 95 | 
            +
                    auth
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                def after_successful_authorization
         | 
| 100 | 
            +
                  Doorkeeper.configuration.after_successful_authorization.call(self)
         | 
| 101 | 
            +
                end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                def before_successful_authorization
         | 
| 104 | 
            +
                  Doorkeeper.configuration.before_successful_authorization.call(self)
         | 
| 92 105 | 
             
                end
         | 
| 93 106 |  | 
| 94 107 | 
             
                def revocation_error_response
         | 
    
        data/lib/doorkeeper/config.rb
    CHANGED
    
    | @@ -322,9 +322,10 @@ module Doorkeeper | |
| 322 322 | 
             
                       end)
         | 
| 323 323 |  | 
| 324 324 | 
             
                # Configure protection of token introspection request.
         | 
| 325 | 
            -
                # By default  | 
| 326 | 
            -
                #  | 
| 327 | 
            -
                #  | 
| 325 | 
            +
                # By default this configuration allows to introspect a token by
         | 
| 326 | 
            +
                # another token of the same application, or to introspect the token
         | 
| 327 | 
            +
                # that belongs to authorized client, or access token has been introspected
         | 
| 328 | 
            +
                # is a public one (doesn't belong to any client)
         | 
| 328 329 | 
             
                #
         | 
| 329 330 | 
             
                # You can define any custom rule you need or just disable token
         | 
| 330 331 | 
             
                # introspection at all.
         | 
| @@ -340,9 +341,11 @@ module Doorkeeper | |
| 340 341 | 
             
                #   Bearer token used to authorize the request
         | 
| 341 342 | 
             
                #
         | 
| 342 343 | 
             
                option :allow_token_introspection,
         | 
| 343 | 
            -
                       default: (lambda do |token, authorized_client,  | 
| 344 | 
            -
                         if  | 
| 345 | 
            -
                            | 
| 344 | 
            +
                       default: (lambda do |token, authorized_client, authorized_token|
         | 
| 345 | 
            +
                         if authorized_token
         | 
| 346 | 
            +
                           authorized_token.application == token&.application
         | 
| 347 | 
            +
                         elsif token.application
         | 
| 348 | 
            +
                           authorized_client == token.application
         | 
| 346 349 | 
             
                         else
         | 
| 347 350 | 
             
                           true
         | 
| 348 351 | 
             
                         end
         | 
| @@ -76,9 +76,50 @@ module Doorkeeper | |
| 76 76 | 
             
                                        end
         | 
| 77 77 |  | 
| 78 78 | 
             
                    tokens = authorized_tokens_for(application.try(:id), resource_owner_id)
         | 
| 79 | 
            -
                    tokens | 
| 80 | 
            -
             | 
| 79 | 
            +
                    find_matching_token(tokens, application, scopes)
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  # Interface to enumerate access token records in batches in order not
         | 
| 83 | 
            +
                  # to bloat the memory. Could be overloaded in any ORM extension.
         | 
| 84 | 
            +
                  #
         | 
| 85 | 
            +
                  def find_access_token_in_batches(relation, *args, &block)
         | 
| 86 | 
            +
                    relation.find_in_batches(*args, &block)
         | 
| 87 | 
            +
                  end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                  # Enumerates AccessToken records in batches to find a matching token.
         | 
| 90 | 
            +
                  # Batching is required in order not to pollute the memory if Application
         | 
| 91 | 
            +
                  # has huge amount of associated records.
         | 
| 92 | 
            +
                  #
         | 
| 93 | 
            +
                  # ActiveRecord 5.x - 6.x ignores custom ordering so we can't perform a
         | 
| 94 | 
            +
                  # database sort by created_at, so we need to load all the matching records,
         | 
| 95 | 
            +
                  # sort them and find latest one. Probably it would be better to rewrite this
         | 
| 96 | 
            +
                  # query using Time math if possible, but we n eed to consider ORM and
         | 
| 97 | 
            +
                  # different databases support.
         | 
| 98 | 
            +
                  #
         | 
| 99 | 
            +
                  # @param relation [ActiveRecord::Relation]
         | 
| 100 | 
            +
                  #   Access tokens relation
         | 
| 101 | 
            +
                  # @param application [Doorkeeper::Application]
         | 
| 102 | 
            +
                  #   Application instance
         | 
| 103 | 
            +
                  # @param scopes [String, Doorkeeper::OAuth::Scopes]
         | 
| 104 | 
            +
                  #   set of scopes
         | 
| 105 | 
            +
                  #
         | 
| 106 | 
            +
                  # @return [Doorkeeper::AccessToken, nil] Access Token instance or
         | 
| 107 | 
            +
                  #   nil if matching record was not found
         | 
| 108 | 
            +
                  #
         | 
| 109 | 
            +
                  def find_matching_token(relation, application, scopes)
         | 
| 110 | 
            +
                    return nil unless relation
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                    matching_tokens = []
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                    find_access_token_in_batches(relation) do |batch|
         | 
| 115 | 
            +
                      tokens = batch.select do |token|
         | 
| 116 | 
            +
                        scopes_match?(token.scopes, scopes, application.try(:scopes))
         | 
| 117 | 
            +
                      end
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                      matching_tokens.concat(tokens)
         | 
| 81 120 | 
             
                    end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                    matching_tokens.max_by(&:created_at)
         | 
| 82 123 | 
             
                  end
         | 
| 83 124 |  | 
| 84 125 | 
             
                  # Checks whether the token scopes match the scopes from the parameters
         | 
| @@ -5,11 +5,25 @@ module Doorkeeper | |
| 5 5 | 
             
                class ClientCredentialsRequest < BaseRequest
         | 
| 6 6 | 
             
                  class Creator
         | 
| 7 7 | 
             
                    def call(client, scopes, attributes = {})
         | 
| 8 | 
            +
                      existing_token = existing_token_for(client, scopes)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                      if Doorkeeper.configuration.reuse_access_token && existing_token&.reusable?
         | 
| 11 | 
            +
                        return existing_token
         | 
| 12 | 
            +
                      end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                      existing_token&.revoke
         | 
| 15 | 
            +
             | 
| 8 16 | 
             
                      AccessToken.find_or_create_for(
         | 
| 9 17 | 
             
                        client, nil, scopes, attributes[:expires_in],
         | 
| 10 18 | 
             
                        attributes[:use_refresh_token]
         | 
| 11 19 | 
             
                      )
         | 
| 12 20 | 
             
                    end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    private
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    def existing_token_for(client, scopes)
         | 
| 25 | 
            +
                      Doorkeeper::AccessToken.matching_token_for client, nil, scopes
         | 
| 26 | 
            +
                    end
         | 
| 13 27 | 
             
                  end
         | 
| 14 28 | 
             
                end
         | 
| 15 29 | 
             
              end
         | 
| @@ -67,7 +67,7 @@ module Doorkeeper | |
| 67 67 | 
             
                      #  HTTP 401 code as described in Section 3 of OAuth 2.0 Bearer Token
         | 
| 68 68 | 
             
                      #  Usage [RFC6750].
         | 
| 69 69 | 
             
                      #
         | 
| 70 | 
            -
                      @error = :invalid_token  | 
| 70 | 
            +
                      @error = :invalid_token unless valid_authorized_token?
         | 
| 71 71 | 
             
                    else
         | 
| 72 72 | 
             
                      @error = :invalid_request
         | 
| 73 73 | 
             
                    end
         | 
| @@ -149,9 +149,9 @@ module Doorkeeper | |
| 149 149 | 
             
                  #
         | 
| 150 150 | 
             
                  def active?
         | 
| 151 151 | 
             
                    if authorized_client
         | 
| 152 | 
            -
                      valid_token? && token_introspection_allowed?(authorized_client.application)
         | 
| 152 | 
            +
                      valid_token? && token_introspection_allowed?(auth_client: authorized_client.application)
         | 
| 153 153 | 
             
                    else
         | 
| 154 | 
            -
                      valid_token? | 
| 154 | 
            +
                      valid_token?
         | 
| 155 155 | 
             
                    end
         | 
| 156 156 | 
             
                  end
         | 
| 157 157 |  | 
| @@ -160,20 +160,26 @@ module Doorkeeper | |
| 160 160 | 
             
                    @token&.accessible?
         | 
| 161 161 | 
             
                  end
         | 
| 162 162 |  | 
| 163 | 
            +
                  def valid_authorized_token?
         | 
| 164 | 
            +
                    !authorized_token_matches_introspected? &&
         | 
| 165 | 
            +
                      authorized_token.accessible? &&
         | 
| 166 | 
            +
                      token_introspection_allowed?(auth_token: authorized_token)
         | 
| 167 | 
            +
                  end
         | 
| 168 | 
            +
             | 
| 163 169 | 
             
                  # RFC7662 Section 2.1
         | 
| 164 170 | 
             
                  def authorized_token_matches_introspected?
         | 
| 165 171 | 
             
                    authorized_token.token == @token&.token
         | 
| 166 172 | 
             
                  end
         | 
| 167 173 |  | 
| 168 174 | 
             
                  # config constraints for introspection in Doorkeeper.configuration.allow_token_introspection
         | 
| 169 | 
            -
                  def token_introspection_allowed?( | 
| 175 | 
            +
                  def token_introspection_allowed?(auth_client: nil, auth_token: nil)
         | 
| 170 176 | 
             
                    allow_introspection = Doorkeeper.configuration.allow_token_introspection
         | 
| 171 177 | 
             
                    return allow_introspection unless allow_introspection.respond_to?(:call)
         | 
| 172 178 |  | 
| 173 179 | 
             
                    allow_introspection.call(
         | 
| 174 180 | 
             
                      @token,
         | 
| 175 | 
            -
                       | 
| 176 | 
            -
                       | 
| 181 | 
            +
                      auth_client,
         | 
| 182 | 
            +
                      auth_token
         | 
| 177 183 | 
             
                    )
         | 
| 178 184 | 
             
                  end
         | 
| 179 185 |  | 
    
        data/lib/doorkeeper/version.rb
    CHANGED
    
    
| @@ -65,7 +65,7 @@ Doorkeeper.configure do | |
| 65 65 | 
             
              # end
         | 
| 66 66 |  | 
| 67 67 | 
             
              # Use a custom class for generating the access token.
         | 
| 68 | 
            -
              # See https:// | 
| 68 | 
            +
              # See https://doorkeeper.gitbook.io/guides/configuration/other-configurations#custom-access-token-generator
         | 
| 69 69 | 
             
              #
         | 
| 70 70 | 
             
              # access_token_generator '::Doorkeeper::JWT'
         | 
| 71 71 |  | 
| @@ -160,7 +160,7 @@ Doorkeeper.configure do | |
| 160 160 |  | 
| 161 161 | 
             
              # Define access token scopes for your provider
         | 
| 162 162 | 
             
              # For more information go to
         | 
| 163 | 
            -
              # https:// | 
| 163 | 
            +
              # https://doorkeeper.gitbook.io/guides/ruby-on-rails/scopes
         | 
| 164 164 | 
             
              #
         | 
| 165 165 | 
             
              # default_scopes  :public
         | 
| 166 166 | 
             
              # optional_scopes :write, :update
         | 
| @@ -296,7 +296,7 @@ Doorkeeper.configure do | |
| 296 296 | 
             
              # or add any other functionality.
         | 
| 297 297 | 
             
              #
         | 
| 298 298 | 
             
              # before_successful_authorization do |controller|
         | 
| 299 | 
            -
              #   Rails.logger.info(params.inspect)
         | 
| 299 | 
            +
              #   Rails.logger.info(controller.request.params.inspect)
         | 
| 300 300 | 
             
              # end
         | 
| 301 301 | 
             
              #
         | 
| 302 302 | 
             
              # after_successful_authorization do |controller|
         | 
| @@ -314,23 +314,13 @@ Doorkeeper.configure do | |
| 314 314 | 
             
              #   client.superapp? or resource_owner.admin?
         | 
| 315 315 | 
             
              # end
         | 
| 316 316 |  | 
| 317 | 
            -
              # Configure custom constraints for the Token Introspection request. | 
| 318 | 
            -
              # this configuration option allows to introspect  | 
| 319 | 
            -
              #  | 
| 317 | 
            +
              # Configure custom constraints for the Token Introspection request.
         | 
| 318 | 
            +
              # By default this configuration option allows to introspect a token by another
         | 
| 319 | 
            +
              # token of the same application, OR to introspect the token that belongs to
         | 
| 320 | 
            +
              # authorized client (from authenticated client) OR when token doesn't
         | 
| 320 321 | 
             
              # belong to any client (public token). Otherwise requester has no access to the
         | 
| 321 322 | 
             
              # introspection and it will return response as stated in the RFC.
         | 
| 322 323 | 
             
              #
         | 
| 323 | 
            -
              # You can define your custom check:
         | 
| 324 | 
            -
              #
         | 
| 325 | 
            -
              # allow_token_introspection do |token, authorized_client, _authorized_token|
         | 
| 326 | 
            -
              #   if token.application
         | 
| 327 | 
            -
              #     # `protected_resource` is a new database boolean column, for example
         | 
| 328 | 
            -
              #     token.application == authorized_client || authorized_client.protected_resource?
         | 
| 329 | 
            -
              #   else
         | 
| 330 | 
            -
              #     true
         | 
| 331 | 
            -
              #   end
         | 
| 332 | 
            -
              # end
         | 
| 333 | 
            -
              #
         | 
| 334 324 | 
             
              # Block arguments:
         | 
| 335 325 | 
             
              #
         | 
| 336 326 | 
             
              # @param token [Doorkeeper::AccessToken]
         | 
| @@ -343,15 +333,38 @@ Doorkeeper.configure do | |
| 343 333 | 
             
              # @param authorized_token [Doorkeeper::AccessToken]
         | 
| 344 334 | 
             
              #   Bearer token used to authorize the request
         | 
| 345 335 | 
             
              #
         | 
| 346 | 
            -
              #  | 
| 347 | 
            -
              #  | 
| 348 | 
            -
              #  | 
| 336 | 
            +
              # In case the block returns `nil` or `false` introspection responses with 401 status code
         | 
| 337 | 
            +
              # when using authorized token to introspect, or you'll get 200 with { "active": false } body
         | 
| 338 | 
            +
              # when using authorized client to introspect as stated in the
         | 
| 339 | 
            +
              # RFC 7662 section 2.2. Introspection Response.
         | 
| 340 | 
            +
              #
         | 
| 341 | 
            +
              # Using with caution:
         | 
| 342 | 
            +
              # Keep in mind that these three parameters pass to block can be nil as following case:
         | 
| 343 | 
            +
              #  `authorized_client` is nil if and only if `authorized_token` is present, and vice versa.
         | 
| 344 | 
            +
              #  `token` will be nil if and only if `authorized_token` is present.
         | 
| 345 | 
            +
              # So remember to use `&` or check if it is present before calling method on
         | 
| 346 | 
            +
              # them to make sure you doesn't get NoMethodError exception.
         | 
| 347 | 
            +
              #
         | 
| 348 | 
            +
              # You can define your custom check:
         | 
| 349 | 
            +
              #
         | 
| 350 | 
            +
              # allow_token_introspection do |token, authorized_client, authorized_token|
         | 
| 351 | 
            +
              #   if authorized_token
         | 
| 352 | 
            +
              #     # customize: require `introspection` scope
         | 
| 353 | 
            +
              #     authorized_token.application == token&.application ||
         | 
| 354 | 
            +
              #       authorized_token.scopes.include?("introspection")
         | 
| 355 | 
            +
              #   elsif token.application
         | 
| 356 | 
            +
              #     # `protected_resource` is a new database boolean column, for example
         | 
| 357 | 
            +
              #     authorized_client == token.application || authorized_client.protected_resource?
         | 
| 358 | 
            +
              #   else
         | 
| 359 | 
            +
              #     # public token (when token.application is nil, token doesn't belong to any application)
         | 
| 360 | 
            +
              #     true
         | 
| 361 | 
            +
              #   end
         | 
| 362 | 
            +
              # end
         | 
| 349 363 | 
             
              #
         | 
| 350 | 
            -
              #  | 
| 364 | 
            +
              # Or you can completely disable any token introspection:
         | 
| 351 365 | 
             
              #
         | 
| 352 366 | 
             
              # allow_token_introspection false
         | 
| 353 367 | 
             
              #
         | 
| 354 | 
            -
              # In such case every request for token introspection will get { "active": false } response.
         | 
| 355 368 | 
             
              # If you need to block the request at all, then configure your routes.rb or web-server
         | 
| 356 369 | 
             
              # like nginx to forbid the request.
         | 
| 357 370 |  | 
| @@ -524,4 +524,17 @@ describe Doorkeeper::AuthorizationsController, "implicit grant flow" do | |
| 524 524 | 
             
                  post :create
         | 
| 525 525 | 
             
                end
         | 
| 526 526 | 
             
              end
         | 
| 527 | 
            +
             | 
| 528 | 
            +
              describe "strong parameters" do
         | 
| 529 | 
            +
                it "ignores non-scalar scope parameter" do
         | 
| 530 | 
            +
                  get :new, params: {
         | 
| 531 | 
            +
                    client_id: client.uid,
         | 
| 532 | 
            +
                    response_type: "token",
         | 
| 533 | 
            +
                    redirect_uri: client.redirect_uri,
         | 
| 534 | 
            +
                    scope: { "0" => "profile" },
         | 
| 535 | 
            +
                  }
         | 
| 536 | 
            +
             | 
| 537 | 
            +
                  expect(response).to be_successful
         | 
| 538 | 
            +
                end
         | 
| 539 | 
            +
              end
         | 
| 527 540 | 
             
            end
         | 
| @@ -3,31 +3,139 @@ | |
| 3 3 | 
             
            require "spec_helper"
         | 
| 4 4 |  | 
| 5 5 | 
             
            describe Doorkeeper::TokensController do
         | 
| 6 | 
            -
               | 
| 7 | 
            -
             | 
| 6 | 
            +
              let(:client) { FactoryBot.create :application }
         | 
| 7 | 
            +
              let!(:user)  { User.create!(name: "Joe", password: "sekret") }
         | 
| 8 8 |  | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 9 | 
            +
              before do
         | 
| 10 | 
            +
                Doorkeeper.configure do
         | 
| 11 | 
            +
                  resource_owner_from_credentials do
         | 
| 12 | 
            +
                    User.first
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
                end
         | 
| 11 15 |  | 
| 12 | 
            -
             | 
| 16 | 
            +
                allow(Doorkeeper.configuration).to receive(:grant_flows).and_return(["password"])
         | 
| 17 | 
            +
              end
         | 
| 13 18 |  | 
| 14 | 
            -
             | 
| 19 | 
            +
              subject { JSON.parse(response.body) }
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              describe "POST #create" do
         | 
| 22 | 
            +
                before do
         | 
| 23 | 
            +
                  post :create, params: {
         | 
| 24 | 
            +
                    client_id: client.uid,
         | 
| 25 | 
            +
                    client_secret: client.secret,
         | 
| 26 | 
            +
                    grant_type: "password",
         | 
| 27 | 
            +
                  }
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                it "responds after authorization" do
         | 
| 31 | 
            +
                  expect(response).to be_successful
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                it "includes access token in response" do
         | 
| 35 | 
            +
                  expect(subject["access_token"]).to eq(Doorkeeper::AccessToken.first.token)
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                it "includes token type in response" do
         | 
| 39 | 
            +
                  expect(subject["token_type"]).to eq("Bearer")
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                it "includes token expiration in response" do
         | 
| 43 | 
            +
                  expect(subject["expires_in"].to_i).to eq(Doorkeeper.configuration.access_token_expires_in)
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                it "issues the token for the current client" do
         | 
| 47 | 
            +
                  expect(Doorkeeper::AccessToken.first.application_id).to eq(client.id)
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                it "issues the token for the current resource owner" do
         | 
| 51 | 
            +
                  expect(Doorkeeper::AccessToken.first.resource_owner_id).to eq(user.id)
         | 
| 15 52 | 
             
                end
         | 
| 16 53 | 
             
              end
         | 
| 17 54 |  | 
| 18 | 
            -
              describe " | 
| 19 | 
            -
                 | 
| 20 | 
            -
                   | 
| 21 | 
            -
             | 
| 55 | 
            +
              describe "POST #create with errors" do
         | 
| 56 | 
            +
                before do
         | 
| 57 | 
            +
                  post :create, params: {
         | 
| 58 | 
            +
                    client_id: client.uid,
         | 
| 59 | 
            +
                    client_secret: "invalid",
         | 
| 60 | 
            +
                    grant_type: "password",
         | 
| 61 | 
            +
                  }
         | 
| 62 | 
            +
                end
         | 
| 22 63 |  | 
| 23 | 
            -
             | 
| 64 | 
            +
                it "responds after authorization" do
         | 
| 65 | 
            +
                  expect(response).to be_unauthorized
         | 
| 66 | 
            +
                end
         | 
| 24 67 |  | 
| 25 | 
            -
             | 
| 26 | 
            -
                  expect( | 
| 68 | 
            +
                it "include error in response" do
         | 
| 69 | 
            +
                  expect(subject["error"]).to eq("invalid_client")
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                it "include error_description in response" do
         | 
| 73 | 
            +
                  expect(subject["error_description"]).to be
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                it "does not include access token in response" do
         | 
| 77 | 
            +
                  expect(subject["access_token"]).to be_nil
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                it "does not include token type in response" do
         | 
| 81 | 
            +
                  expect(subject["token_type"]).to be_nil
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                it "does not include token expiration in response" do
         | 
| 85 | 
            +
                  expect(subject["expires_in"]).to be_nil
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                it "does not issue any access token" do
         | 
| 89 | 
            +
                  expect(Doorkeeper::AccessToken.all).to be_empty
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
              end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
              describe "POST #create with callbacks" do
         | 
| 94 | 
            +
                after do
         | 
| 95 | 
            +
                  client.update_attribute :redirect_uri, "urn:ietf:wg:oauth:2.0:oob"
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                describe "when successful" do
         | 
| 99 | 
            +
                  after do
         | 
| 100 | 
            +
                    post :create, params: {
         | 
| 101 | 
            +
                      client_id: client.uid,
         | 
| 102 | 
            +
                      client_secret: client.secret,
         | 
| 103 | 
            +
                      grant_type: "password",
         | 
| 104 | 
            +
                    }
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                  it "should call :before_successful_authorization callback" do
         | 
| 108 | 
            +
                    expect(Doorkeeper.configuration)
         | 
| 109 | 
            +
                      .to receive_message_chain(:before_successful_authorization, :call).with(instance_of(described_class))
         | 
| 110 | 
            +
                  end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                  it "should call :after_successful_authorization callback" do
         | 
| 113 | 
            +
                    expect(Doorkeeper.configuration)
         | 
| 114 | 
            +
                      .to receive_message_chain(:after_successful_authorization, :call).with(instance_of(described_class))
         | 
| 115 | 
            +
                  end
         | 
| 116 | 
            +
                end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                describe "with errors" do
         | 
| 119 | 
            +
                  after do
         | 
| 120 | 
            +
                    post :create, params: {
         | 
| 121 | 
            +
                      client_id: client.uid,
         | 
| 122 | 
            +
                      client_secret: "invalid",
         | 
| 123 | 
            +
                      grant_type: "password",
         | 
| 124 | 
            +
                    }
         | 
| 125 | 
            +
                  end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                  it "should call :before_successful_authorization callback" do
         | 
| 128 | 
            +
                    expect(Doorkeeper.configuration)
         | 
| 129 | 
            +
                      .to receive_message_chain(:before_successful_authorization, :call).with(instance_of(described_class))
         | 
| 130 | 
            +
                  end
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                  it "should not call :after_successful_authorization callback" do
         | 
| 133 | 
            +
                    expect(Doorkeeper.configuration).not_to receive(:after_successful_authorization)
         | 
| 134 | 
            +
                  end
         | 
| 27 135 | 
             
                end
         | 
| 28 136 | 
             
              end
         | 
| 29 137 |  | 
| 30 | 
            -
              describe " | 
| 138 | 
            +
              describe "POST #create with custom error" do
         | 
| 31 139 | 
             
                it "returns the error response with a custom message" do
         | 
| 32 140 | 
             
                  # I18n looks for `doorkeeper.errors.messages.custom_message` in locale files
         | 
| 33 141 | 
             
                  custom_message = "my_message"
         | 
| @@ -58,7 +166,7 @@ describe Doorkeeper::TokensController do | |
| 58 166 | 
             
              end
         | 
| 59 167 |  | 
| 60 168 | 
             
              # http://tools.ietf.org/html/rfc7009#section-2.2
         | 
| 61 | 
            -
              describe " | 
| 169 | 
            +
              describe "POST #revoke" do
         | 
| 62 170 | 
             
                let(:client) { FactoryBot.create(:application) }
         | 
| 63 171 | 
             
                let(:access_token) { FactoryBot.create(:access_token, application: client) }
         | 
| 64 172 |  | 
| @@ -117,21 +225,7 @@ describe Doorkeeper::TokensController do | |
| 117 225 | 
             
                end
         | 
| 118 226 | 
             
              end
         | 
| 119 227 |  | 
| 120 | 
            -
              describe " | 
| 121 | 
            -
                it "memoizes the result of the authorization" do
         | 
| 122 | 
            -
                  strategy = double(:strategy, authorize: true)
         | 
| 123 | 
            -
                  expect(strategy).to receive(:authorize).once
         | 
| 124 | 
            -
                  allow(controller).to receive(:strategy) { strategy }
         | 
| 125 | 
            -
                  allow(controller).to receive(:create) do
         | 
| 126 | 
            -
                    2.times { controller.send :authorize_response }
         | 
| 127 | 
            -
                    controller.render json: {}, status: :ok
         | 
| 128 | 
            -
                  end
         | 
| 129 | 
            -
             | 
| 130 | 
            -
                  post :create
         | 
| 131 | 
            -
                end
         | 
| 132 | 
            -
              end
         | 
| 133 | 
            -
             | 
| 134 | 
            -
              describe "when requested token introspection" do
         | 
| 228 | 
            +
              describe "POST #introspect" do
         | 
| 135 229 | 
             
                let(:client) { FactoryBot.create(:application) }
         | 
| 136 230 | 
             
                let(:access_token) { FactoryBot.create(:access_token, application: client) }
         | 
| 137 231 | 
             
                let(:token_for_introspection) { FactoryBot.create(:access_token, application: client) }
         | 
| @@ -167,13 +261,15 @@ describe Doorkeeper::TokensController do | |
| 167 261 | 
             
                    end
         | 
| 168 262 | 
             
                  end
         | 
| 169 263 |  | 
| 170 | 
            -
                  it "responds with  | 
| 264 | 
            +
                  it "responds with invalid_token error" do
         | 
| 171 265 | 
             
                    request.headers["Authorization"] = "Bearer #{access_token.token}"
         | 
| 172 266 |  | 
| 173 267 | 
             
                    post :introspect, params: { token: token_for_introspection.token }
         | 
| 174 268 |  | 
| 175 | 
            -
                     | 
| 176 | 
            -
             | 
| 269 | 
            +
                    response_status_should_be 401
         | 
| 270 | 
            +
             | 
| 271 | 
            +
                    should_not_have_json "active"
         | 
| 272 | 
            +
                    should_have_json "error", "invalid_token"
         | 
| 177 273 | 
             
                  end
         | 
| 178 274 | 
             
                end
         | 
| 179 275 |  | 
| @@ -268,15 +364,17 @@ describe Doorkeeper::TokensController do | |
| 268 364 | 
             
                    expect(json_response).to include("client_id", "token_type", "exp", "iat")
         | 
| 269 365 | 
             
                  end
         | 
| 270 366 |  | 
| 271 | 
            -
                  it "responds with  | 
| 367 | 
            +
                  it "responds with invalid_token error if authorized token doesn't have introspection scope" do
         | 
| 272 368 | 
             
                    access_token.update(scopes: "read write")
         | 
| 273 369 |  | 
| 274 370 | 
             
                    request.headers["Authorization"] = "Bearer #{access_token.token}"
         | 
| 275 371 |  | 
| 276 372 | 
             
                    post :introspect, params: { token: token_for_introspection.token }
         | 
| 277 373 |  | 
| 278 | 
            -
                     | 
| 279 | 
            -
             | 
| 374 | 
            +
                    response_status_should_be 401
         | 
| 375 | 
            +
             | 
| 376 | 
            +
                    should_not_have_json "active"
         | 
| 377 | 
            +
                    should_have_json "error", "invalid_token"
         | 
| 280 378 | 
             
                  end
         | 
| 281 379 | 
             
                end
         | 
| 282 380 |  | 
| @@ -285,7 +383,7 @@ describe Doorkeeper::TokensController do | |
| 285 383 | 
             
                    FactoryBot.create(:access_token, application: client, revoked_at: 1.day.ago)
         | 
| 286 384 | 
             
                  end
         | 
| 287 385 |  | 
| 288 | 
            -
                  it "responds with  | 
| 386 | 
            +
                  it "responds with invalid_token error" do
         | 
| 289 387 | 
             
                    request.headers["Authorization"] = "Bearer #{access_token.token}"
         | 
| 290 388 |  | 
| 291 389 | 
             
                    post :introspect, params: { token: token_for_introspection.token }
         | 
| @@ -340,13 +438,15 @@ describe Doorkeeper::TokensController do | |
| 340 438 | 
             
                  end
         | 
| 341 439 |  | 
| 342 440 | 
             
                  context "authorized using valid Bearer token" do
         | 
| 343 | 
            -
                    it "responds with  | 
| 441 | 
            +
                    it "responds with invalid_token error" do
         | 
| 344 442 | 
             
                      request.headers["Authorization"] = "Bearer #{access_token.token}"
         | 
| 345 443 |  | 
| 346 444 | 
             
                      post :introspect, params: { token: SecureRandom.hex(16) }
         | 
| 347 445 |  | 
| 348 | 
            -
                       | 
| 349 | 
            -
             | 
| 446 | 
            +
                      response_status_should_be 401
         | 
| 447 | 
            +
             | 
| 448 | 
            +
                      should_not_have_json "active"
         | 
| 449 | 
            +
                      should_have_json "error", "invalid_token"
         | 
| 350 450 | 
             
                    end
         | 
| 351 451 | 
             
                  end
         | 
| 352 452 | 
             
                end
         | 
| @@ -107,32 +107,59 @@ Doorkeeper.configure do | |
| 107 107 | 
             
              #   client.superapp? or resource_owner.admin?
         | 
| 108 108 | 
             
              # end
         | 
| 109 109 |  | 
| 110 | 
            -
              #  | 
| 111 | 
            -
              #  | 
| 112 | 
            -
              #  | 
| 113 | 
            -
              #  | 
| 114 | 
            -
              #
         | 
| 115 | 
            -
              #  | 
| 116 | 
            -
              # | 
| 117 | 
            -
              #  | 
| 118 | 
            -
              #
         | 
| 119 | 
            -
              #  | 
| 120 | 
            -
              #  | 
| 121 | 
            -
              # | 
| 122 | 
            -
              #  | 
| 110 | 
            +
              # Configure custom constraints for the Token Introspection request.
         | 
| 111 | 
            +
              # By default this configuration option allows to introspect a token by another
         | 
| 112 | 
            +
              # token of the same application, OR to introspect the token that belongs to
         | 
| 113 | 
            +
              # authorized client (from authenticated client) OR when token doesn't
         | 
| 114 | 
            +
              # belong to any client (public token). Otherwise requester has no access to the
         | 
| 115 | 
            +
              # introspection and it will return response as stated in the RFC.
         | 
| 116 | 
            +
              #
         | 
| 117 | 
            +
              # Block arguments:
         | 
| 118 | 
            +
              #
         | 
| 119 | 
            +
              # @param token [Doorkeeper::AccessToken]
         | 
| 120 | 
            +
              #   token to be introspected
         | 
| 121 | 
            +
              #
         | 
| 122 | 
            +
              # @param authorized_client [Doorkeeper::Application]
         | 
| 123 | 
            +
              #   authorized client (if request is authorized using Basic auth with
         | 
| 124 | 
            +
              #   Client Credentials for example)
         | 
| 125 | 
            +
              #
         | 
| 126 | 
            +
              # @param authorized_token [Doorkeeper::AccessToken]
         | 
| 127 | 
            +
              #   Bearer token used to authorize the request
         | 
| 123 128 | 
             
              #
         | 
| 124 | 
            -
              #  | 
| 125 | 
            -
              # | 
| 126 | 
            -
              # | 
| 127 | 
            -
              # | 
| 129 | 
            +
              # In case the block returns `nil` or `false` introspection responses with 401 status code
         | 
| 130 | 
            +
              # when using authorized token to introspect, or you'll get 200 with { "active": false } body
         | 
| 131 | 
            +
              # when using authorized client to introspect as stated in the
         | 
| 132 | 
            +
              # RFC 7662 section 2.2. Introspection Response.
         | 
| 128 133 | 
             
              #
         | 
| 129 | 
            -
              #  | 
| 130 | 
            -
              # | 
| 131 | 
            -
              # | 
| 134 | 
            +
              # Using with caution:
         | 
| 135 | 
            +
              # Keep in mind that these three parameters pass to block can be nil as following case:
         | 
| 136 | 
            +
              #  `authorized_client` is nil if and only if `authorized_token` is present, and vice versa.
         | 
| 137 | 
            +
              #  `token` will be nil if and only if `authorized_token` is present.
         | 
| 138 | 
            +
              # So remember to use `&` or check if it is present before calling method on
         | 
| 139 | 
            +
              # them to make sure you doesn't get NoMethodError exception.
         | 
| 140 | 
            +
              #
         | 
| 141 | 
            +
              # You can define your custom check:
         | 
| 142 | 
            +
              #
         | 
| 143 | 
            +
              # allow_token_introspection do |token, authorized_client, authorized_token|
         | 
| 144 | 
            +
              #   if authorized_token
         | 
| 145 | 
            +
              #     # customize: require `introspection` scope
         | 
| 146 | 
            +
              #     authorized_token.application == token&.application ||
         | 
| 147 | 
            +
              #       authorized_token.scopes.include?("introspection")
         | 
| 148 | 
            +
              #   elsif token.application
         | 
| 149 | 
            +
              #     # `protected_resource` is a new database boolean column, for example
         | 
| 150 | 
            +
              #     authorized_client == token.application || authorized_client.protected_resource?
         | 
| 132 151 | 
             
              #   else
         | 
| 152 | 
            +
              #     # public token (when token.application is nil, token doesn't belong to any application)
         | 
| 133 153 | 
             
              #     true
         | 
| 134 154 | 
             
              #   end
         | 
| 135 155 | 
             
              # end
         | 
| 156 | 
            +
              #
         | 
| 157 | 
            +
              # Or you can completely disable any token introspection:
         | 
| 158 | 
            +
              #
         | 
| 159 | 
            +
              # allow_token_introspection false
         | 
| 160 | 
            +
              #
         | 
| 161 | 
            +
              # If you need to block the request at all, then configure your routes.rb or web-server
         | 
| 162 | 
            +
              # like nginx to forbid the request.
         | 
| 136 163 |  | 
| 137 164 | 
             
              # WWW-Authenticate Realm (default "Doorkeeper").
         | 
| 138 165 | 
             
              realm "Doorkeeper"
         | 
| @@ -55,6 +55,7 @@ class Doorkeeper::OAuth::ClientCredentialsRequest | |
| 55 55 |  | 
| 56 56 | 
             
                      expect(Doorkeeper::AccessToken.count).to eq(2)
         | 
| 57 57 | 
             
                      expect(result).not_to eq(existing_token)
         | 
| 58 | 
            +
                      expect(existing_token.reload).to be_revoked
         | 
| 58 59 | 
             
                    end
         | 
| 59 60 | 
             
                  end
         | 
| 60 61 |  | 
| @@ -69,6 +70,7 @@ class Doorkeeper::OAuth::ClientCredentialsRequest | |
| 69 70 |  | 
| 70 71 | 
             
                      expect(Doorkeeper::AccessToken.count).to eq(2)
         | 
| 71 72 | 
             
                      expect(result).not_to eq(existing_token)
         | 
| 73 | 
            +
                      expect(existing_token.reload).to be_revoked
         | 
| 72 74 | 
             
                    end
         | 
| 73 75 | 
             
                  end
         | 
| 74 76 | 
             
                end
         | 
| @@ -82,6 +84,7 @@ class Doorkeeper::OAuth::ClientCredentialsRequest | |
| 82 84 |  | 
| 83 85 | 
             
                    expect(Doorkeeper::AccessToken.count).to eq(2)
         | 
| 84 86 | 
             
                    expect(result).not_to eq(existing_token)
         | 
| 87 | 
            +
                    expect(existing_token.reload).to be_revoked
         | 
| 85 88 | 
             
                  end
         | 
| 86 89 | 
             
                end
         | 
| 87 90 |  | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: doorkeeper
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 5.2.0. | 
| 4 | 
            +
              version: 5.2.0.rc2
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Felipe Elias Philipp
         | 
| @@ -11,7 +11,7 @@ authors: | |
| 11 11 | 
             
            autorequire: 
         | 
| 12 12 | 
             
            bindir: bin
         | 
| 13 13 | 
             
            cert_chain: []
         | 
| 14 | 
            -
            date: 2019- | 
| 14 | 
            +
            date: 2019-06-17 00:00:00.000000000 Z
         | 
| 15 15 | 
             
            dependencies:
         | 
| 16 16 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 17 17 | 
             
              name: railties
         | 
| @@ -179,6 +179,7 @@ files: | |
| 179 179 | 
             
            - CODE_OF_CONDUCT.md
         | 
| 180 180 | 
             
            - CONTRIBUTING.md
         | 
| 181 181 | 
             
            - Dangerfile
         | 
| 182 | 
            +
            - Dockerfile
         | 
| 182 183 | 
             
            - Gemfile
         | 
| 183 184 | 
             
            - MIT-LICENSE
         | 
| 184 185 | 
             
            - NEWS.md
         |