doorkeeper 4.3.2 → 4.4.3
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/.rubocop.yml +4 -0
- data/NEWS.md +17 -0
- data/app/controllers/doorkeeper/applications_controller.rb +2 -1
- data/app/controllers/doorkeeper/tokens_controller.rb +9 -10
- data/app/views/doorkeeper/applications/_form.html.erb +11 -0
- data/app/views/doorkeeper/applications/index.html.erb +2 -0
- data/app/views/doorkeeper/applications/show.html.erb +3 -0
- data/config/locales/en.yml +6 -0
- data/doorkeeper.gemspec +2 -0
- data/lib/doorkeeper/config.rb +14 -0
- data/lib/doorkeeper/models/access_token_mixin.rb +1 -1
- data/lib/doorkeeper/models/application_mixin.rb +8 -1
- data/lib/doorkeeper/oauth/client/credentials.rb +3 -1
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +1 -0
- data/lib/doorkeeper/oauth/pre_authorization.rb +5 -3
- data/lib/doorkeeper/orm/active_record/application.rb +17 -0
- data/lib/doorkeeper/rails/routes.rb +5 -1
- data/lib/doorkeeper/request/password.rb +1 -11
- data/lib/doorkeeper/version.rb +23 -2
- data/lib/generators/doorkeeper/add_client_confidentiality_generator.rb +31 -0
- data/lib/generators/doorkeeper/templates/add_confidential_to_application_migration.rb.erb +11 -0
- data/lib/generators/doorkeeper/templates/migration.rb.erb +1 -0
- data/spec/controllers/authorizations_controller_spec.rb +34 -2
- data/spec/controllers/tokens_controller_spec.rb +59 -7
- data/spec/dummy/config/initializers/doorkeeper.rb +5 -0
- data/spec/dummy/db/migrate/20111122132257_create_users.rb +3 -1
- data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +3 -1
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +3 -1
- data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +3 -1
- data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +3 -1
- data/spec/dummy/db/migrate/20180210183654_add_confidential_to_application.rb +13 -0
- data/spec/dummy/db/schema.rb +2 -1
- data/spec/lib/config_spec.rb +25 -0
- data/spec/lib/oauth/authorization_code_request_spec.rb +15 -0
- data/spec/lib/oauth/client/credentials_spec.rb +4 -2
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +5 -0
- data/spec/lib/oauth/pre_authorization_spec.rb +12 -7
- data/spec/models/doorkeeper/application_spec.rb +96 -5
- data/spec/requests/flows/authorization_code_spec.rb +1 -1
- data/spec/requests/flows/password_spec.rb +64 -21
- metadata +25 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 8d8d3550d8406d4abb224c4960d1d6e8a0c4c706
         | 
| 4 | 
            +
              data.tar.gz: b12408cb8b0dc2b14ee69b57798943b5c1bfaa30
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 0674af950f6070d6457e09f73fc89736b092ae6595e484ca6e67e7f126912ea007509d9249fdc4eb01e66bf981c1e49da33712203d8428d10401a43faabd1cfd
         | 
| 7 | 
            +
              data.tar.gz: e447513c202dfde4c622b898da2a98dff64272193136fe399b890bb97488e7915156a2588caa6de3566db411f4c7dfa89e88be3a8b8d0a76511251f2f980c382
         | 
    
        data/.rubocop.yml
    CHANGED
    
    
    
        data/NEWS.md
    CHANGED
    
    | @@ -4,6 +4,23 @@ User-visible changes worth mentioning. | |
| 4 4 |  | 
| 5 5 | 
             
            ## master
         | 
| 6 6 |  | 
| 7 | 
            +
            ## 4.4.3
         | 
| 8 | 
            +
            - [#1143] Adds a config option opt_out_native_route_change to opt out of the
         | 
| 9 | 
            +
              breaking api changed introduced in
         | 
| 10 | 
            +
              https://github.com/doorkeeper-gem/doorkeeper/pull/1003
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            ## 4.4.2
         | 
| 13 | 
            +
            - [#1130] Backport fix for native redirect_uri from 5.x.
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            ## 4.4.1
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            - [#1127] Backport token type to comply with the RFC6750 specification.
         | 
| 18 | 
            +
            - [#1125] Backport Quote surround I18n yes/no keys
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            ## 4.4.0
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            - [#1120] Backport security fix from 5.x for token revocation when using public clients
         | 
| 23 | 
            +
             | 
| 7 24 | 
             
            ## 4.3.2
         | 
| 8 25 |  | 
| 9 26 | 
             
            - [#1053] Support authorizing with query params in the request `redirect_uri` if explicitly present in app's `Application#redirect_uri`
         | 
| @@ -58,16 +58,15 @@ module Doorkeeper | |
| 58 58 | 
             
                # https://tools.ietf.org/html/rfc6749#section-2.1
         | 
| 59 59 | 
             
                # https://tools.ietf.org/html/rfc7009
         | 
| 60 60 | 
             
                def authorized?
         | 
| 61 | 
            -
                   | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
                    end
         | 
| 61 | 
            +
                  return unless token.present?
         | 
| 62 | 
            +
                  # Client is confidential, therefore client authentication & authorization
         | 
| 63 | 
            +
                  # is required
         | 
| 64 | 
            +
                  if token.application_id? && token.application.confidential?
         | 
| 65 | 
            +
                    # We authorize client by checking token's application
         | 
| 66 | 
            +
                    server.client && server.client.application == token.application
         | 
| 67 | 
            +
                  else
         | 
| 68 | 
            +
                    # Client is public, authentication unnecessary
         | 
| 69 | 
            +
                    true
         | 
| 71 70 | 
             
                  end
         | 
| 72 71 | 
             
                end
         | 
| 73 72 |  | 
| @@ -27,6 +27,17 @@ | |
| 27 27 | 
             
                </div>
         | 
| 28 28 | 
             
              <% end %>
         | 
| 29 29 |  | 
| 30 | 
            +
              <%= content_tag :div, class: "form-group#{' has-error' if application.errors[:confidential].present?}" do %>
         | 
| 31 | 
            +
                <%= f.label :confidential, class: 'col-sm-2 control-label' %>
         | 
| 32 | 
            +
                <div class="col-sm-10">
         | 
| 33 | 
            +
                  <%= f.check_box :confidential, class: 'form-control', disabled: !Doorkeeper::Application.supports_confidentiality? %>
         | 
| 34 | 
            +
                  <%= doorkeeper_errors_for application, :confidential %>
         | 
| 35 | 
            +
                  <span class="help-block">
         | 
| 36 | 
            +
                    <%= t('doorkeeper.applications.help.confidential') %>
         | 
| 37 | 
            +
                  </span>
         | 
| 38 | 
            +
                </div>
         | 
| 39 | 
            +
              <% end %>
         | 
| 40 | 
            +
             | 
| 30 41 | 
             
              <%= content_tag :div, class: "form-group#{' has-error' if application.errors[:scopes].present?}" do %>
         | 
| 31 42 | 
             
                <%= f.label :scopes, class: 'col-sm-2 control-label' %>
         | 
| 32 43 | 
             
                <div class="col-sm-10">
         | 
| @@ -9,6 +9,7 @@ | |
| 9 9 | 
             
              <tr>
         | 
| 10 10 | 
             
                <th><%= t('.name') %></th>
         | 
| 11 11 | 
             
                <th><%= t('.callback_url') %></th>
         | 
| 12 | 
            +
                <th><%= t('.confidential') %></th>
         | 
| 12 13 | 
             
                <th></th>
         | 
| 13 14 | 
             
                <th></th>
         | 
| 14 15 | 
             
              </tr>
         | 
| @@ -18,6 +19,7 @@ | |
| 18 19 | 
             
                <tr id="application_<%= application.id %>">
         | 
| 19 20 | 
             
                  <td><%= link_to application.name, oauth_application_path(application) %></td>
         | 
| 20 21 | 
             
                  <td><%= application.redirect_uri %></td>
         | 
| 22 | 
            +
                  <td><%= application.confidential? ? t('doorkeeper.applications.index.confidentiality.yes') : t('doorkeeper.applications.index.confidentiality.no') %></td>
         | 
| 21 23 | 
             
                  <td><%= link_to t('doorkeeper.applications.buttons.edit'), edit_oauth_application_path(application), class: 'btn btn-link' %></td>
         | 
| 22 24 | 
             
                  <td><%= render 'delete_form', application: application %></td>
         | 
| 23 25 | 
             
                </tr>
         | 
| @@ -13,6 +13,9 @@ | |
| 13 13 | 
             
                <h4><%= t('.scopes') %>:</h4>
         | 
| 14 14 | 
             
                <p><code id="scopes"><%= @application.scopes %></code></p>
         | 
| 15 15 |  | 
| 16 | 
            +
                <h4><%= t('.confidential') %>:</h4>
         | 
| 17 | 
            +
                <p><code id="confidential"><%= @application.confidential? %></code></p>
         | 
| 18 | 
            +
             | 
| 16 19 | 
             
                <h4><%= t('.callback_urls') %>:</h4>
         | 
| 17 20 |  | 
| 18 21 | 
             
                <table>
         | 
    
        data/config/locales/en.yml
    CHANGED
    
    | @@ -28,6 +28,7 @@ en: | |
| 28 28 | 
             
                  form:
         | 
| 29 29 | 
             
                    error: 'Whoops! Check your form for possible errors'
         | 
| 30 30 | 
             
                  help:
         | 
| 31 | 
            +
                    confidential: 'Application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential.'
         | 
| 31 32 | 
             
                    redirect_uri: 'Use one line per URI'
         | 
| 32 33 | 
             
                    native_redirect_uri: 'Use %{native_redirect_uri} if you want to add localhost URIs for development purposes'
         | 
| 33 34 | 
             
                    scopes: 'Separate scopes with spaces. Leave blank to use the default scopes.'
         | 
| @@ -38,6 +39,10 @@ en: | |
| 38 39 | 
             
                    new: 'New Application'
         | 
| 39 40 | 
             
                    name: 'Name'
         | 
| 40 41 | 
             
                    callback_url: 'Callback URL'
         | 
| 42 | 
            +
                    confidential: 'Confidential?'
         | 
| 43 | 
            +
                    confidentiality:
         | 
| 44 | 
            +
                      'yes': 'Yes'
         | 
| 45 | 
            +
                      'no': 'No'
         | 
| 41 46 | 
             
                  new:
         | 
| 42 47 | 
             
                    title: 'New Application'
         | 
| 43 48 | 
             
                  show:
         | 
| @@ -45,6 +50,7 @@ en: | |
| 45 50 | 
             
                    application_id: 'Application Id'
         | 
| 46 51 | 
             
                    secret: 'Secret'
         | 
| 47 52 | 
             
                    scopes: 'Scopes'
         | 
| 53 | 
            +
                    confidential: 'Confidential'
         | 
| 48 54 | 
             
                    callback_urls: 'Callback urls'
         | 
| 49 55 | 
             
                    actions: 'Actions'
         | 
| 50 56 |  | 
    
        data/doorkeeper.gemspec
    CHANGED
    
    
    
        data/lib/doorkeeper/config.rb
    CHANGED
    
    | @@ -114,6 +114,15 @@ doorkeeper. | |
| 114 114 | 
             
                  def reuse_access_token
         | 
| 115 115 | 
             
                    @config.instance_variable_set(:@reuse_access_token, true)
         | 
| 116 116 | 
             
                  end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                  # Opt out of breaking api change to the native authorization code flow.
         | 
| 119 | 
            +
                  # Opting out sets the authorization code response route for native
         | 
| 120 | 
            +
                  # redirect uris to oauth/authorize/<code>. The default is
         | 
| 121 | 
            +
                  # oauth/authorize/native?code=<code>.
         | 
| 122 | 
            +
                  # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/1143
         | 
| 123 | 
            +
                  def opt_out_native_route_change
         | 
| 124 | 
            +
                    @config.instance_variable_set(:@opt_out_native_route_change, true)
         | 
| 125 | 
            +
                  end
         | 
| 117 126 | 
             
                end
         | 
| 118 127 |  | 
| 119 128 | 
             
                module Option
         | 
| @@ -295,6 +304,11 @@ doorkeeper. | |
| 295 304 | 
             
                  @token_grant_types ||= calculate_token_grant_types
         | 
| 296 305 | 
             
                end
         | 
| 297 306 |  | 
| 307 | 
            +
                def native_authorization_code_route
         | 
| 308 | 
            +
                  @opt_out_native_route_change ||= false
         | 
| 309 | 
            +
                  @opt_out_native_route_change ? '/:code' : '/native'
         | 
| 310 | 
            +
                end
         | 
| 311 | 
            +
             | 
| 298 312 | 
             
                private
         | 
| 299 313 |  | 
| 300 314 | 
             
                # Determines what values are acceptable for 'response_type' param in
         | 
| @@ -10,6 +10,9 @@ module Doorkeeper | |
| 10 10 | 
             
                  # Returns an instance of the Doorkeeper::Application with
         | 
| 11 11 | 
             
                  # specific UID and secret.
         | 
| 12 12 | 
             
                  #
         | 
| 13 | 
            +
                  # Public/Non-confidential applications will only find by uid if secret is
         | 
| 14 | 
            +
                  # blank.
         | 
| 15 | 
            +
                  #
         | 
| 13 16 | 
             
                  # @param uid [#to_s] UID (any object that responds to `#to_s`)
         | 
| 14 17 | 
             
                  # @param secret [#to_s] secret (any object that responds to `#to_s`)
         | 
| 15 18 | 
             
                  #
         | 
| @@ -17,7 +20,11 @@ module Doorkeeper | |
| 17 20 | 
             
                  #   if there is no record with such credentials
         | 
| 18 21 | 
             
                  #
         | 
| 19 22 | 
             
                  def by_uid_and_secret(uid, secret)
         | 
| 20 | 
            -
                     | 
| 23 | 
            +
                    app = by_uid(uid)
         | 
| 24 | 
            +
                    return unless app
         | 
| 25 | 
            +
                    return app if secret.blank? && !app.confidential?
         | 
| 26 | 
            +
                    return unless app.secret == secret
         | 
| 27 | 
            +
                    app
         | 
| 21 28 | 
             
                  end
         | 
| 22 29 |  | 
| 23 30 | 
             
                  # Returns an instance of the Doorkeeper::Application with specific UID.
         | 
| @@ -57,9 +57,11 @@ module Doorkeeper | |
| 57 57 |  | 
| 58 58 | 
             
                  # TODO: test uri should be matched against the client's one
         | 
| 59 59 | 
             
                  def validate_redirect_uri
         | 
| 60 | 
            -
                    return false  | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 60 | 
            +
                    return false if redirect_uri.blank?
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                    Helpers::URIChecker.valid_for_authorization?(
         | 
| 63 | 
            +
                      redirect_uri, client.redirect_uri
         | 
| 64 | 
            +
                    )
         | 
| 63 65 | 
             
                  end
         | 
| 64 66 | 
             
                end
         | 
| 65 67 | 
             
              end
         | 
| @@ -11,6 +11,7 @@ module Doorkeeper | |
| 11 11 | 
             
                validates :name, :secret, :uid, presence: true
         | 
| 12 12 | 
             
                validates :uid, uniqueness: true
         | 
| 13 13 | 
             
                validates :redirect_uri, redirect_uri: true
         | 
| 14 | 
            +
                validates :confidential, inclusion: { in: [true, false] }
         | 
| 14 15 |  | 
| 15 16 | 
             
                before_validation :generate_uid, :generate_secret, on: :create
         | 
| 16 17 |  | 
| @@ -31,6 +32,22 @@ module Doorkeeper | |
| 31 32 | 
             
                  where(id: resource_access_tokens.select(:application_id).distinct)
         | 
| 32 33 | 
             
                end
         | 
| 33 34 |  | 
| 35 | 
            +
                # Fallback to existing, default behaviour of assuming all apps to be
         | 
| 36 | 
            +
                # confidential if the migration hasn't been run
         | 
| 37 | 
            +
                def confidential
         | 
| 38 | 
            +
                  return super if self.class.supports_confidentiality?
         | 
| 39 | 
            +
                  ActiveSupport::Deprecation.warn 'You are susceptible to security bug ' \
         | 
| 40 | 
            +
                    'CVE-2018-1000211. Please follow instructions outlined in ' \
         | 
| 41 | 
            +
                    'Doorkeeper::CVE_2018_1000211_WARNING'
         | 
| 42 | 
            +
                  true
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                alias_method :confidential?, :confidential
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                def self.supports_confidentiality?
         | 
| 48 | 
            +
                  column_names.include?('confidential')
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 34 51 | 
             
                private
         | 
| 35 52 |  | 
| 36 53 | 
             
                def generate_uid
         | 
| @@ -47,7 +47,7 @@ module Doorkeeper | |
| 47 47 | 
             
                      as: mapping[:as],
         | 
| 48 48 | 
             
                      controller: mapping[:controllers]
         | 
| 49 49 | 
             
                    ) do
         | 
| 50 | 
            -
                      routes.get  | 
| 50 | 
            +
                      routes.get native_authorization_code_route, action: :show, on: :member
         | 
| 51 51 | 
             
                      routes.get '/', action: :new, on: :member
         | 
| 52 52 | 
             
                    end
         | 
| 53 53 | 
             
                  end
         | 
| @@ -85,6 +85,10 @@ module Doorkeeper | |
| 85 85 | 
             
                  def authorized_applications_routes(mapping)
         | 
| 86 86 | 
             
                    routes.resources :authorized_applications, only: %i[index destroy], controller: mapping[:controllers]
         | 
| 87 87 | 
             
                  end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                  def native_authorization_code_route
         | 
| 90 | 
            +
                    Doorkeeper.configuration.native_authorization_code_route
         | 
| 91 | 
            +
                  end
         | 
| 88 92 | 
             
                end
         | 
| 89 93 | 
             
              end
         | 
| 90 94 | 
             
            end
         | 
| @@ -3,7 +3,7 @@ require 'doorkeeper/request/strategy' | |
| 3 3 | 
             
            module Doorkeeper
         | 
| 4 4 | 
             
              module Request
         | 
| 5 5 | 
             
                class Password < Strategy
         | 
| 6 | 
            -
                  delegate :credentials, :resource_owner, :parameters, to: :server
         | 
| 6 | 
            +
                  delegate :credentials, :resource_owner, :parameters, :client, to: :server
         | 
| 7 7 |  | 
| 8 8 | 
             
                  def request
         | 
| 9 9 | 
             
                    @request ||= OAuth::PasswordAccessTokenRequest.new(
         | 
| @@ -13,16 +13,6 @@ module Doorkeeper | |
| 13 13 | 
             
                      parameters
         | 
| 14 14 | 
             
                    )
         | 
| 15 15 | 
             
                  end
         | 
| 16 | 
            -
             | 
| 17 | 
            -
                  private
         | 
| 18 | 
            -
             | 
| 19 | 
            -
                  def client
         | 
| 20 | 
            -
                    if credentials
         | 
| 21 | 
            -
                      server.client
         | 
| 22 | 
            -
                    elsif parameters[:client_id]
         | 
| 23 | 
            -
                      server.client_via_uid
         | 
| 24 | 
            -
                    end
         | 
| 25 | 
            -
                  end
         | 
| 26 16 | 
             
                end
         | 
| 27 17 | 
             
              end
         | 
| 28 18 | 
             
            end
         | 
    
        data/lib/doorkeeper/version.rb
    CHANGED
    
    | @@ -1,4 +1,25 @@ | |
| 1 1 | 
             
            module Doorkeeper
         | 
| 2 | 
            +
              CVE_2018_1000211_WARNING = <<-HEREDOC.freeze
         | 
| 3 | 
            +
             | 
| 4 | 
            +
             | 
| 5 | 
            +
              WARNING: This is a security release that addresses token revocation not working for public apps (CVE-2018-1000211)
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              There is no breaking change in this release, however to take advantage of the security fix you must:
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                1. Run `rails generate doorkeeper:add_client_confidentiality` for the migration
         | 
| 10 | 
            +
                2. Review your OAuth apps and determine which ones exclusively use public grant flows (eg implicit)
         | 
| 11 | 
            +
                3. Update their `confidential` column to `false` for those public apps
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              This is a backported security release.
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              For more information:
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                * https://github.com/doorkeeper-gem/doorkeeper/pull/1119
         | 
| 18 | 
            +
                * https://github.com/doorkeeper-gem/doorkeeper/issues/891
         | 
| 19 | 
            +
             | 
| 20 | 
            +
             | 
| 21 | 
            +
            HEREDOC
         | 
| 22 | 
            +
             | 
| 2 23 | 
             
              def self.gem_version
         | 
| 3 24 | 
             
                Gem::Version.new VERSION::STRING
         | 
| 4 25 | 
             
              end
         | 
| @@ -6,8 +27,8 @@ module Doorkeeper | |
| 6 27 | 
             
              module VERSION
         | 
| 7 28 | 
             
                # Semantic versioning
         | 
| 8 29 | 
             
                MAJOR = 4
         | 
| 9 | 
            -
                MINOR =  | 
| 10 | 
            -
                TINY =  | 
| 30 | 
            +
                MINOR = 4
         | 
| 31 | 
            +
                TINY = 3
         | 
| 11 32 |  | 
| 12 33 | 
             
                # Full version number
         | 
| 13 34 | 
             
                STRING = [MAJOR, MINOR, TINY].compact.join('.')
         | 
| @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'rails/generators/active_record'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Doorkeeper
         | 
| 6 | 
            +
              class AddClientConfidentialityGenerator < ::Rails::Generators::Base
         | 
| 7 | 
            +
                include ::Rails::Generators::Migration
         | 
| 8 | 
            +
                source_root File.expand_path('templates', __dir__)
         | 
| 9 | 
            +
                desc 'Adds a migration to fix CVE-2018-1000211.'
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def install
         | 
| 12 | 
            +
                  migration_template(
         | 
| 13 | 
            +
                    'add_confidential_to_application_migration.rb.erb',
         | 
| 14 | 
            +
                    'db/migrate/add_confidential_to_doorkeeper_application.rb',
         | 
| 15 | 
            +
                    migration_version: migration_version
         | 
| 16 | 
            +
                  )
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def self.next_migration_number(dirname)
         | 
| 20 | 
            +
                  ::ActiveRecord::Generators::Base.next_migration_number(dirname)
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                private
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def migration_version
         | 
| 26 | 
            +
                  if ::ActiveRecord::VERSION::MAJOR >= 5
         | 
| 27 | 
            +
                    "[#{::ActiveRecord::VERSION::MAJOR}.#{::ActiveRecord::VERSION::MINOR}]"
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
            end
         | 
| @@ -0,0 +1,11 @@ | |
| 1 | 
            +
            class AddConfidentialToDoorkeeperApplication < ActiveRecord::Migration<%= migration_version %>
         | 
| 2 | 
            +
              def change
         | 
| 3 | 
            +
                add_column(
         | 
| 4 | 
            +
                  :oauth_applications,
         | 
| 5 | 
            +
                  :confidential,
         | 
| 6 | 
            +
                  :boolean,
         | 
| 7 | 
            +
                  null: false,
         | 
| 8 | 
            +
                  default: true # maintaining backwards compatibility: require secrets
         | 
| 9 | 
            +
                )
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
            end
         | 
| @@ -6,6 +6,7 @@ class CreateDoorkeeperTables < ActiveRecord::Migration<%= migration_version %> | |
| 6 6 | 
             
                  t.string  :secret,       null: false
         | 
| 7 7 | 
             
                  t.text    :redirect_uri, null: false
         | 
| 8 8 | 
             
                  t.string  :scopes,       null: false, default: ''
         | 
| 9 | 
            +
                  t.boolean :confidential, null: false, default: true
         | 
| 9 10 | 
             
                  t.timestamps             null: false
         | 
| 10 11 | 
             
                end
         | 
| 11 12 |  | 
| @@ -54,7 +54,7 @@ describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do | |
| 54 54 | 
             
                end
         | 
| 55 55 |  | 
| 56 56 | 
             
                it 'includes token type in fragment' do
         | 
| 57 | 
            -
                  expect(response.query_params['token_type']).to eq(' | 
| 57 | 
            +
                  expect(response.query_params['token_type']).to eq('Bearer')
         | 
| 58 58 | 
             
                end
         | 
| 59 59 |  | 
| 60 60 | 
             
                it 'includes token expiration in fragment' do
         | 
| @@ -164,6 +164,38 @@ describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do | |
| 164 164 | 
             
                it 'should not issue a token' do
         | 
| 165 165 | 
             
                  expect(Doorkeeper::AccessToken.count).to be 0
         | 
| 166 166 | 
             
                end
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                context 'with opt_out_native_route_change' do
         | 
| 169 | 
            +
                  around(:each) do |example|
         | 
| 170 | 
            +
                    Doorkeeper.configure do
         | 
| 171 | 
            +
                      orm DOORKEEPER_ORM
         | 
| 172 | 
            +
                      opt_out_native_route_change
         | 
| 173 | 
            +
                    end
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                    Rails.application.reload_routes!
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                    example.run
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                    Doorkeeper.configure do
         | 
| 180 | 
            +
                      orm DOORKEEPER_ORM
         | 
| 181 | 
            +
                    end
         | 
| 182 | 
            +
             | 
| 183 | 
            +
                    Rails.application.reload_routes!
         | 
| 184 | 
            +
                  end
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                  it 'should redirect immediately' do
         | 
| 187 | 
            +
                    expect(response).to be_redirect
         | 
| 188 | 
            +
                    expect(response.location).to match(/oauth\/authorize\/#{Doorkeeper::AccessGrant.first.token}/)
         | 
| 189 | 
            +
                  end
         | 
| 190 | 
            +
             | 
| 191 | 
            +
                  it 'should issue a grant' do
         | 
| 192 | 
            +
                    expect(Doorkeeper::AccessGrant.count).to be 1
         | 
| 193 | 
            +
                  end
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                  it 'should not issue a token' do
         | 
| 196 | 
            +
                    expect(Doorkeeper::AccessToken.count).to be 0
         | 
| 197 | 
            +
                  end
         | 
| 198 | 
            +
                end
         | 
| 167 199 | 
             
              end
         | 
| 168 200 |  | 
| 169 201 | 
             
              describe 'GET #new with skip_authorization true' do
         | 
| @@ -184,7 +216,7 @@ describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do | |
| 184 216 | 
             
                end
         | 
| 185 217 |  | 
| 186 218 | 
             
                it 'includes token type in fragment' do
         | 
| 187 | 
            -
                  expect(response.query_params['token_type']).to eq(' | 
| 219 | 
            +
                  expect(response.query_params['token_type']).to eq('Bearer')
         | 
| 188 220 | 
             
                end
         | 
| 189 221 |  | 
| 190 222 | 
             
                it 'includes token expiration in fragment' do
         | 
| @@ -59,15 +59,67 @@ describe Doorkeeper::TokensController do | |
| 59 59 | 
             
                end
         | 
| 60 60 | 
             
              end
         | 
| 61 61 |  | 
| 62 | 
            -
               | 
| 63 | 
            -
             | 
| 64 | 
            -
                 | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 62 | 
            +
              # http://tools.ietf.org/html/rfc7009#section-2.2
         | 
| 63 | 
            +
              describe 'revoking tokens' do
         | 
| 64 | 
            +
                let(:client) { FactoryBot.create(:application) }
         | 
| 65 | 
            +
                let(:access_token) { FactoryBot.create(:access_token, application: client) }
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                before(:each) do
         | 
| 68 | 
            +
                  allow(controller).to receive(:token) { access_token }
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                context 'when associated app is public' do
         | 
| 72 | 
            +
                  let(:client) { FactoryBot.create(:application, confidential: false) }
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  it 'returns 200' do
         | 
| 75 | 
            +
                    post :revoke
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                    expect(response.status).to eq 200
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  it 'revokes the access token' do
         | 
| 81 | 
            +
                    post :revoke
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    expect(access_token.reload).to have_attributes(revoked?: true)
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                context 'when associated app is confidential' do
         | 
| 88 | 
            +
                  let(:client) { FactoryBot.create(:application, confidential: true) }
         | 
| 89 | 
            +
                  let(:oauth_client) { Doorkeeper::OAuth::Client.new(client) }
         | 
| 67 90 |  | 
| 68 | 
            -
                   | 
| 91 | 
            +
                  before(:each) do
         | 
| 92 | 
            +
                    allow_any_instance_of(Doorkeeper::Server).to receive(:client) { oauth_client }
         | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  it 'returns 200' do
         | 
| 96 | 
            +
                    post :revoke
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                    expect(response.status).to eq 200
         | 
| 99 | 
            +
                  end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                  it 'revokes the access token' do
         | 
| 102 | 
            +
                    post :revoke
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                    expect(access_token.reload).to have_attributes(revoked?: true)
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                  context 'when authorization fails' do
         | 
| 108 | 
            +
                    let(:some_other_client) { FactoryBot.create(:application, confidential: true) }
         | 
| 109 | 
            +
                    let(:oauth_client) { Doorkeeper::OAuth::Client.new(some_other_client) }
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                    it 'returns 200' do
         | 
| 112 | 
            +
                      post :revoke
         | 
| 69 113 |  | 
| 70 | 
            -
             | 
| 114 | 
            +
                      expect(response.status).to eq 200
         | 
| 115 | 
            +
                    end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                    it 'does not revoke the access token' do
         | 
| 118 | 
            +
                      post :revoke
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                      expect(access_token.reload).to have_attributes(revoked?: false)
         | 
| 121 | 
            +
                    end
         | 
| 122 | 
            +
                  end
         | 
| 71 123 | 
             
                end
         | 
| 72 124 | 
             
              end
         | 
| 73 125 |  | 
| @@ -29,6 +29,11 @@ Doorkeeper.configure do | |
| 29 29 | 
             
              # Issue access tokens with refresh token (disabled by default)
         | 
| 30 30 | 
             
              use_refresh_token
         | 
| 31 31 |  | 
| 32 | 
            +
              # Opt out of breaking api change to the native authorization code flow. Opting out sets the authorization
         | 
| 33 | 
            +
              # code response route for native redirect uris to oauth/authorize/<code>. The default is oauth/authorize/native?code=<code>.
         | 
| 34 | 
            +
              # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/1143
         | 
| 35 | 
            +
              # opt_out_native_route_change
         | 
| 36 | 
            +
             | 
| 32 37 | 
             
              # Provide support for an owner to be assigned to each registered application (disabled by default)
         | 
| 33 38 | 
             
              # Optional parameter confirmation: true (default false) if you want to enforce ownership of
         | 
| 34 39 | 
             
              # a registered application
         | 
| @@ -1,4 +1,6 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class AddOwnerToApplication < ActiveRecord::Migration[4.2]
         | 
| 2 4 | 
             
              def change
         | 
| 3 5 | 
             
                add_column :oauth_applications, :owner_id, :integer, null: true
         | 
| 4 6 | 
             
                add_column :oauth_applications, :owner_type, :string, null: true
         | 
| @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class AddConfidentialToApplication < ActiveRecord::Migration[5.1]
         | 
| 4 | 
            +
              def change
         | 
| 5 | 
            +
                add_column(
         | 
| 6 | 
            +
                  :oauth_applications,
         | 
| 7 | 
            +
                  :confidential,
         | 
| 8 | 
            +
                  :boolean,
         | 
| 9 | 
            +
                  null: false,
         | 
| 10 | 
            +
                  default: true # maintaining backwards compatibility: require secrets
         | 
| 11 | 
            +
                )
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
            end
         | 
    
        data/spec/dummy/db/schema.rb
    CHANGED
    
    | @@ -11,7 +11,7 @@ | |
| 11 11 | 
             
            #
         | 
| 12 12 | 
             
            # It's strongly recommended that you check this file into your version control system.
         | 
| 13 13 |  | 
| 14 | 
            -
            ActiveRecord::Schema.define(version:  | 
| 14 | 
            +
            ActiveRecord::Schema.define(version: 20180210183654) do
         | 
| 15 15 |  | 
| 16 16 | 
             
              create_table "oauth_access_grants", force: :cascade do |t|
         | 
| 17 17 | 
             
                t.integer  "resource_owner_id", null: false
         | 
| @@ -52,6 +52,7 @@ ActiveRecord::Schema.define(version: 20160320211015) do | |
| 52 52 | 
             
                t.datetime "updated_at"
         | 
| 53 53 | 
             
                t.integer  "owner_id"
         | 
| 54 54 | 
             
                t.string   "owner_type"
         | 
| 55 | 
            +
                t.boolean "confidential", default: true, null: false
         | 
| 55 56 | 
             
              end
         | 
| 56 57 |  | 
| 57 58 | 
             
              add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type"
         | 
    
        data/spec/lib/config_spec.rb
    CHANGED
    
    | @@ -162,6 +162,31 @@ describe Doorkeeper, 'configuration' do | |
| 162 162 | 
             
                end
         | 
| 163 163 | 
             
              end
         | 
| 164 164 |  | 
| 165 | 
            +
              describe 'opt_out_native_route_change' do
         | 
| 166 | 
            +
                around(:each) do |example|
         | 
| 167 | 
            +
                  Doorkeeper.configure do
         | 
| 168 | 
            +
                    orm DOORKEEPER_ORM
         | 
| 169 | 
            +
                    opt_out_native_route_change
         | 
| 170 | 
            +
                  end
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                  Rails.application.reload_routes!
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                  subject { Doorkeeper.configuration }
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                  example.run
         | 
| 177 | 
            +
             | 
| 178 | 
            +
                  Doorkeeper.configure do
         | 
| 179 | 
            +
                    orm DOORKEEPER_ORM
         | 
| 180 | 
            +
                  end
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                  Rails.application.reload_routes!
         | 
| 183 | 
            +
                end
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                it 'sets the native authorization code route /:code' do
         | 
| 186 | 
            +
                  expect(subject.native_authorization_code_route).to eq('/:code')
         | 
| 187 | 
            +
                end
         | 
| 188 | 
            +
              end
         | 
| 189 | 
            +
             | 
| 165 190 | 
             
              describe 'client_credentials' do
         | 
| 166 191 | 
             
                it 'has defaults order' do
         | 
| 167 192 | 
             
                  expect(subject.client_credentials_methods).to eq([:from_basic, :from_params])
         | 
| @@ -104,5 +104,20 @@ module Doorkeeper::OAuth | |
| 104 104 | 
             
                    expect(subject.error).to eq(:invalid_grant)
         | 
| 105 105 | 
             
                  end
         | 
| 106 106 | 
             
                end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                context "when redirect_uri is the native one" do
         | 
| 109 | 
            +
                  let(:redirect_uri) { 'urn:ietf:wg:oauth:2.0:oob' }
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                  it "invalidates when redirect_uri of the grant is not native" do
         | 
| 112 | 
            +
                    subject.validate
         | 
| 113 | 
            +
                    expect(subject.error).to eq(:invalid_grant)
         | 
| 114 | 
            +
                  end
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                  it "validates when redirect_uri of the grant is also native" do
         | 
| 117 | 
            +
                    allow(grant).to receive(:redirect_uri) { redirect_uri }
         | 
| 118 | 
            +
                    subject.validate
         | 
| 119 | 
            +
                    expect(subject.error).to eq(nil)
         | 
| 120 | 
            +
                  end
         | 
| 121 | 
            +
                end
         | 
| 107 122 | 
             
              end
         | 
| 108 123 | 
             
            end
         | 
| @@ -7,9 +7,11 @@ class Doorkeeper::OAuth::Client | |
| 7 7 | 
             
                let(:client_id) { 'some-uid' }
         | 
| 8 8 | 
             
                let(:client_secret) { 'some-secret' }
         | 
| 9 9 |  | 
| 10 | 
            -
                it 'is blank when  | 
| 10 | 
            +
                it 'is blank when the uid in credentials is blank' do
         | 
| 11 | 
            +
                  expect(Credentials.new(nil, nil)).to be_blank
         | 
| 11 12 | 
             
                  expect(Credentials.new(nil, 'something')).to be_blank
         | 
| 12 | 
            -
                  expect(Credentials.new('something', nil)).to  | 
| 13 | 
            +
                  expect(Credentials.new('something', nil)).to be_present
         | 
| 14 | 
            +
                  expect(Credentials.new('something', 'something')).to be_present
         | 
| 13 15 | 
             
                end
         | 
| 14 16 |  | 
| 15 17 | 
             
                describe :from_request do
         | 
| @@ -5,6 +5,11 @@ require 'doorkeeper/oauth/helpers/uri_checker' | |
| 5 5 | 
             
            module Doorkeeper::OAuth::Helpers
         | 
| 6 6 | 
             
              describe URIChecker do
         | 
| 7 7 | 
             
                describe '.valid?' do
         | 
| 8 | 
            +
                  it 'is valid for native uris' do
         | 
| 9 | 
            +
                    uri = 'urn:ietf:wg:oauth:2.0:oob'
         | 
| 10 | 
            +
                    expect(URIChecker.valid?(uri)).to be_truthy
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 8 13 | 
             
                  it 'is valid for valid uris' do
         | 
| 9 14 | 
             
                    uri = 'http://app.co'
         | 
| 10 15 | 
             
                    expect(URIChecker.valid?(uri)).to be_truthy
         | 
| @@ -123,14 +123,19 @@ module Doorkeeper::OAuth | |
| 123 123 | 
             
                  expect(subject.scopes).to eq(Scopes.from_string('default'))
         | 
| 124 124 | 
             
                end
         | 
| 125 125 |  | 
| 126 | 
            -
                 | 
| 127 | 
            -
                   | 
| 128 | 
            -
                  expect(subject).to be_authorizable
         | 
| 129 | 
            -
                end
         | 
| 126 | 
            +
                context 'with native redirect uri' do
         | 
| 127 | 
            +
                  let(:native_redirect_uri) { 'urn:ietf:wg:oauth:2.0:oob' }
         | 
| 130 128 |  | 
| 131 | 
            -
             | 
| 132 | 
            -
             | 
| 133 | 
            -
             | 
| 129 | 
            +
                  it 'accepts redirect_uri when it matches with the client' do
         | 
| 130 | 
            +
                    subject.redirect_uri = native_redirect_uri
         | 
| 131 | 
            +
                    allow(subject.client).to receive(:redirect_uri) { native_redirect_uri }
         | 
| 132 | 
            +
                    expect(subject).to be_authorizable
         | 
| 133 | 
            +
                  end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                  it 'invalidates redirect_uri when it does\'n match with the client' do
         | 
| 136 | 
            +
                    subject.redirect_uri = native_redirect_uri
         | 
| 137 | 
            +
                    expect(subject).not_to be_authorizable
         | 
| 138 | 
            +
                  end
         | 
| 134 139 | 
             
                end
         | 
| 135 140 |  | 
| 136 141 | 
             
                it 'stores the state' do
         | 
| @@ -49,6 +49,11 @@ module Doorkeeper | |
| 49 49 | 
             
                  expect(new_application).not_to be_valid
         | 
| 50 50 | 
             
                end
         | 
| 51 51 |  | 
| 52 | 
            +
                it 'is invalid without determining confidentiality' do
         | 
| 53 | 
            +
                  new_application.confidential = nil
         | 
| 54 | 
            +
                  expect(new_application).not_to be_valid
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 52 57 | 
             
                it 'generates uid on create' do
         | 
| 53 58 | 
             
                  expect(new_application.uid).to be_nil
         | 
| 54 59 | 
             
                  new_application.save
         | 
| @@ -201,11 +206,97 @@ module Doorkeeper | |
| 201 206 | 
             
                  end
         | 
| 202 207 | 
             
                end
         | 
| 203 208 |  | 
| 204 | 
            -
                describe : | 
| 205 | 
            -
                   | 
| 206 | 
            -
                     | 
| 207 | 
            -
             | 
| 208 | 
            -
             | 
| 209 | 
            +
                describe :by_uid_and_secret do
         | 
| 210 | 
            +
                  context "when application is private/confidential" do
         | 
| 211 | 
            +
                    it "finds the application via uid/secret" do
         | 
| 212 | 
            +
                      app = FactoryBot.create :application
         | 
| 213 | 
            +
                      authenticated = Application.by_uid_and_secret(app.uid, app.secret)
         | 
| 214 | 
            +
                      expect(authenticated).to eq(app)
         | 
| 215 | 
            +
                    end
         | 
| 216 | 
            +
                    context "when secret is wrong" do
         | 
| 217 | 
            +
                      it "should not find the application" do
         | 
| 218 | 
            +
                        app = FactoryBot.create :application
         | 
| 219 | 
            +
                        authenticated = Application.by_uid_and_secret(app.uid, 'bad')
         | 
| 220 | 
            +
                        expect(authenticated).to eq(nil)
         | 
| 221 | 
            +
                      end
         | 
| 222 | 
            +
                    end
         | 
| 223 | 
            +
                  end
         | 
| 224 | 
            +
             | 
| 225 | 
            +
                  context "when application is public/non-confidential" do
         | 
| 226 | 
            +
                    context "when secret is blank" do
         | 
| 227 | 
            +
                      it "should find the application" do
         | 
| 228 | 
            +
                        app = FactoryBot.create :application, confidential: false
         | 
| 229 | 
            +
                        authenticated = Application.by_uid_and_secret(app.uid, nil)
         | 
| 230 | 
            +
                        expect(authenticated).to eq(app)
         | 
| 231 | 
            +
                      end
         | 
| 232 | 
            +
                    end
         | 
| 233 | 
            +
                    context "when secret is wrong" do
         | 
| 234 | 
            +
                      it "should not find the application" do
         | 
| 235 | 
            +
                        app = FactoryBot.create :application, confidential: false
         | 
| 236 | 
            +
                        authenticated = Application.by_uid_and_secret(app.uid, 'bad')
         | 
| 237 | 
            +
                        expect(authenticated).to eq(nil)
         | 
| 238 | 
            +
                      end
         | 
| 239 | 
            +
                    end
         | 
| 240 | 
            +
                  end
         | 
| 241 | 
            +
                end
         | 
| 242 | 
            +
             | 
| 243 | 
            +
                describe :confidential? do
         | 
| 244 | 
            +
                  subject { FactoryBot.create(:application, confidential: confidential).confidential? }
         | 
| 245 | 
            +
             | 
| 246 | 
            +
                  context 'when application is private/confidential' do
         | 
| 247 | 
            +
                    let(:confidential) { true }
         | 
| 248 | 
            +
                    it { expect(subject).to eq(true) }
         | 
| 249 | 
            +
                  end
         | 
| 250 | 
            +
             | 
| 251 | 
            +
                  context 'when application is public/non-confidential' do
         | 
| 252 | 
            +
                    let(:confidential) { false }
         | 
| 253 | 
            +
                    it { expect(subject).to eq(false) }
         | 
| 254 | 
            +
                  end
         | 
| 255 | 
            +
                end
         | 
| 256 | 
            +
             | 
| 257 | 
            +
                describe :confidential do
         | 
| 258 | 
            +
                  subject { FactoryBot.create(:application, confidential: confidential).confidential }
         | 
| 259 | 
            +
             | 
| 260 | 
            +
                  context 'when application is private/confidential' do
         | 
| 261 | 
            +
                    let(:confidential) { true }
         | 
| 262 | 
            +
                    it { expect(subject).to eq(true) }
         | 
| 263 | 
            +
                  end
         | 
| 264 | 
            +
             | 
| 265 | 
            +
                  context 'when application is public/non-confidential' do
         | 
| 266 | 
            +
                    let(:confidential) { false }
         | 
| 267 | 
            +
                    it { expect(subject).to eq(false) }
         | 
| 268 | 
            +
                  end
         | 
| 269 | 
            +
             | 
| 270 | 
            +
                  context 'when the application does not support confidentiality' do
         | 
| 271 | 
            +
                    let(:confidential) { false }
         | 
| 272 | 
            +
             | 
| 273 | 
            +
                    before { allow(Application).to receive(:supports_confidentiality?).and_return(false) }
         | 
| 274 | 
            +
             | 
| 275 | 
            +
                    it 'warns of the CVE' do
         | 
| 276 | 
            +
                      expect(ActiveSupport::Deprecation).to receive(:warn).with(
         | 
| 277 | 
            +
                        'You are susceptible to security bug ' \
         | 
| 278 | 
            +
                        'CVE-2018-1000211. Please follow instructions outlined in ' \
         | 
| 279 | 
            +
                        'Doorkeeper::CVE_2018_1000211_WARNING'
         | 
| 280 | 
            +
                      )
         | 
| 281 | 
            +
                      Application.new.confidential
         | 
| 282 | 
            +
                    end
         | 
| 283 | 
            +
             | 
| 284 | 
            +
                    it { expect(subject).to eq(true) }
         | 
| 285 | 
            +
                  end
         | 
| 286 | 
            +
                end
         | 
| 287 | 
            +
             | 
| 288 | 
            +
                describe :supports_confidentiality? do
         | 
| 289 | 
            +
                  context 'when no column' do
         | 
| 290 | 
            +
                    it 'returns false' do
         | 
| 291 | 
            +
                      expect(Application).to receive(:column_names).and_return(%w[foo bar])
         | 
| 292 | 
            +
                      expect(Application.supports_confidentiality?).to eq(false)
         | 
| 293 | 
            +
                    end
         | 
| 294 | 
            +
                  end
         | 
| 295 | 
            +
                  context 'when column' do
         | 
| 296 | 
            +
                    it 'returns true' do
         | 
| 297 | 
            +
                      expect(Application).to receive(:column_names).and_return(%w[foo bar confidential])
         | 
| 298 | 
            +
                      expect(Application.supports_confidentiality?).to eq(true)
         | 
| 299 | 
            +
                    end
         | 
| 209 300 | 
             
                  end
         | 
| 210 301 | 
             
                end
         | 
| 211 302 | 
             
              end
         | 
| @@ -53,7 +53,7 @@ feature 'Authorization Code Flow' do | |
| 53 53 | 
             
                should_not_have_json 'error'
         | 
| 54 54 |  | 
| 55 55 | 
             
                should_have_json 'access_token', Doorkeeper::AccessToken.first.token
         | 
| 56 | 
            -
                should_have_json 'token_type', ' | 
| 56 | 
            +
                should_have_json 'token_type', 'Bearer'
         | 
| 57 57 | 
             
                should_have_json_within 'expires_in', Doorkeeper::AccessToken.first.expires_in, 1
         | 
| 58 58 | 
             
              end
         | 
| 59 59 |  | 
| @@ -10,46 +10,89 @@ describe 'Resource Owner Password Credentials Flow not set up' do | |
| 10 10 | 
             
                it 'doesn\'t issue new token' do
         | 
| 11 11 | 
             
                  expect do
         | 
| 12 12 | 
             
                    post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)
         | 
| 13 | 
            -
                  end.to_not | 
| 13 | 
            +
                  end.to_not(change { Doorkeeper::AccessToken.count })
         | 
| 14 14 | 
             
                end
         | 
| 15 15 | 
             
              end
         | 
| 16 16 | 
             
            end
         | 
| 17 17 |  | 
| 18 18 | 
             
            describe 'Resource Owner Password Credentials Flow' do
         | 
| 19 | 
            +
              let(:client_attributes) { {} }
         | 
| 20 | 
            +
             | 
| 19 21 | 
             
              before do
         | 
| 20 22 | 
             
                config_is_set(:grant_flows, ["password"])
         | 
| 21 23 | 
             
                config_is_set(:resource_owner_from_credentials) { User.authenticate! params[:username], params[:password] }
         | 
| 22 | 
            -
                client_exists
         | 
| 24 | 
            +
                client_exists(client_attributes)
         | 
| 23 25 | 
             
                create_resource_owner
         | 
| 24 26 | 
             
              end
         | 
| 25 27 |  | 
| 26 28 | 
             
              context 'with valid user credentials' do
         | 
| 27 | 
            -
                 | 
| 28 | 
            -
                   | 
| 29 | 
            -
                    post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)
         | 
| 30 | 
            -
                  end.to change { Doorkeeper::AccessToken.count }.by(1)
         | 
| 29 | 
            +
                context "with non-confidential/public client" do
         | 
| 30 | 
            +
                  let(:client_attributes) { { confidential: false } }
         | 
| 31 31 |  | 
| 32 | 
            -
                   | 
| 32 | 
            +
                  context "when client_secret absent" do
         | 
| 33 | 
            +
                    it "should issue new token" do
         | 
| 34 | 
            +
                      expect do
         | 
| 35 | 
            +
                        post password_token_endpoint_url(client_id: @client.uid, resource_owner: @resource_owner)
         | 
| 36 | 
            +
                      end.to change { Doorkeeper::AccessToken.count }.by(1)
         | 
| 33 37 |  | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 38 | 
            +
                      token = Doorkeeper::AccessToken.first
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                      expect(token.application_id).to eq @client.id
         | 
| 41 | 
            +
                      should_have_json 'access_token', token.token
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  context "when client_secret present" do
         | 
| 46 | 
            +
                    it "should issue new token" do
         | 
| 47 | 
            +
                      expect do
         | 
| 48 | 
            +
                        post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)
         | 
| 49 | 
            +
                      end.to change { Doorkeeper::AccessToken.count }.by(1)
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                      token = Doorkeeper::AccessToken.first
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                      expect(token.application_id).to eq @client.id
         | 
| 54 | 
            +
                      should_have_json 'access_token', token.token
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    context "when client_secret incorrect" do
         | 
| 58 | 
            +
                      it "should not issue new token" do
         | 
| 59 | 
            +
                        expect do
         | 
| 60 | 
            +
                          post password_token_endpoint_url(client_id: @client.uid, client_secret: 'foobar', resource_owner: @resource_owner)
         | 
| 61 | 
            +
                        end.not_to(change { Doorkeeper::AccessToken.count })
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                        expect(response).not_to be_ok
         | 
| 64 | 
            +
                      end
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
                  end
         | 
| 36 67 | 
             
                end
         | 
| 37 68 |  | 
| 38 | 
            -
                 | 
| 39 | 
            -
                   | 
| 40 | 
            -
                     | 
| 41 | 
            -
             | 
| 69 | 
            +
                context "with confidential/private client" do
         | 
| 70 | 
            +
                  it "should issue new token" do
         | 
| 71 | 
            +
                    expect do
         | 
| 72 | 
            +
                      post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)
         | 
| 73 | 
            +
                    end.to change { Doorkeeper::AccessToken.count }.by(1)
         | 
| 42 74 |  | 
| 43 | 
            -
             | 
| 75 | 
            +
                    token = Doorkeeper::AccessToken.first
         | 
| 44 76 |  | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 77 | 
            +
                    expect(token.application_id).to eq @client.id
         | 
| 78 | 
            +
                    should_have_json 'access_token', token.token
         | 
| 79 | 
            +
                  end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  context "when client_secret absent" do
         | 
| 82 | 
            +
                    it "should not issue new token" do
         | 
| 83 | 
            +
                      expect do
         | 
| 84 | 
            +
                        post password_token_endpoint_url(client_id: @client.uid, resource_owner: @resource_owner)
         | 
| 85 | 
            +
                      end.not_to(change { Doorkeeper::AccessToken.count })
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                      expect(response).not_to be_ok
         | 
| 88 | 
            +
                    end
         | 
| 89 | 
            +
                  end
         | 
| 47 90 | 
             
                end
         | 
| 48 91 |  | 
| 49 92 | 
             
                it 'should issue new token without client credentials' do
         | 
| 50 93 | 
             
                  expect do
         | 
| 51 94 | 
             
                    post password_token_endpoint_url(resource_owner: @resource_owner)
         | 
| 52 | 
            -
                  end.to | 
| 95 | 
            +
                  end.to(change { Doorkeeper::AccessToken.count }.by(1))
         | 
| 53 96 |  | 
| 54 97 | 
             
                  token = Doorkeeper::AccessToken.first
         | 
| 55 98 |  | 
| @@ -124,13 +167,13 @@ describe 'Resource Owner Password Credentials Flow' do | |
| 124 167 | 
             
                    post password_token_endpoint_url(client: @client,
         | 
| 125 168 | 
             
                                                     resource_owner_username: @resource_owner.name,
         | 
| 126 169 | 
             
                                                     resource_owner_password: 'wrongpassword')
         | 
| 127 | 
            -
                  end.to_not | 
| 170 | 
            +
                  end.to_not(change { Doorkeeper::AccessToken.count })
         | 
| 128 171 | 
             
                end
         | 
| 129 172 |  | 
| 130 173 | 
             
                it 'should not issue new token without credentials' do
         | 
| 131 174 | 
             
                  expect do
         | 
| 132 175 | 
             
                    post password_token_endpoint_url(client: @client)
         | 
| 133 | 
            -
                  end.to_not | 
| 176 | 
            +
                  end.to_not(change { Doorkeeper::AccessToken.count })
         | 
| 134 177 | 
             
                end
         | 
| 135 178 | 
             
              end
         | 
| 136 179 |  | 
| @@ -140,7 +183,7 @@ describe 'Resource Owner Password Credentials Flow' do | |
| 140 183 | 
             
                    post password_token_endpoint_url(client_id: @client.uid,
         | 
| 141 184 | 
             
                                                     client_secret: 'bad_secret',
         | 
| 142 185 | 
             
                                                     resource_owner: @resource_owner)
         | 
| 143 | 
            -
                  end.to_not | 
| 186 | 
            +
                  end.to_not(change { Doorkeeper::AccessToken.count })
         | 
| 144 187 | 
             
                end
         | 
| 145 188 | 
             
              end
         | 
| 146 189 |  | 
| @@ -148,7 +191,7 @@ describe 'Resource Owner Password Credentials Flow' do | |
| 148 191 | 
             
                it 'should not issue new token with bad client id' do
         | 
| 149 192 | 
             
                  expect do
         | 
| 150 193 | 
             
                    post password_token_endpoint_url(client_id: 'bad_id', resource_owner: @resource_owner)
         | 
| 151 | 
            -
                  end.to_not | 
| 194 | 
            +
                  end.to_not(change { Doorkeeper::AccessToken.count })
         | 
| 152 195 | 
             
                end
         | 
| 153 196 | 
             
              end
         | 
| 154 197 | 
             
            end
         | 
    
        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: 4.3 | 
| 4 | 
            +
              version: 4.4.3
         | 
| 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: 2018- | 
| 14 | 
            +
            date: 2018-09-19 00:00:00.000000000 Z
         | 
| 15 15 | 
             
            dependencies:
         | 
| 16 16 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 17 17 | 
             
              name: railties
         | 
| @@ -259,11 +259,13 @@ files: | |
| 259 259 | 
             
            - lib/doorkeeper/server.rb
         | 
| 260 260 | 
             
            - lib/doorkeeper/validations.rb
         | 
| 261 261 | 
             
            - lib/doorkeeper/version.rb
         | 
| 262 | 
            +
            - lib/generators/doorkeeper/add_client_confidentiality_generator.rb
         | 
| 262 263 | 
             
            - lib/generators/doorkeeper/application_owner_generator.rb
         | 
| 263 264 | 
             
            - lib/generators/doorkeeper/install_generator.rb
         | 
| 264 265 | 
             
            - lib/generators/doorkeeper/migration_generator.rb
         | 
| 265 266 | 
             
            - lib/generators/doorkeeper/previous_refresh_token_generator.rb
         | 
| 266 267 | 
             
            - lib/generators/doorkeeper/templates/README
         | 
| 268 | 
            +
            - lib/generators/doorkeeper/templates/add_confidential_to_application_migration.rb.erb
         | 
| 267 269 | 
             
            - lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb
         | 
| 268 270 | 
             
            - lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb
         | 
| 269 271 | 
             
            - lib/generators/doorkeeper/templates/initializer.rb
         | 
| @@ -307,6 +309,7 @@ files: | |
| 307 309 | 
             
            - spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb
         | 
| 308 310 | 
             
            - spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb
         | 
| 309 311 | 
             
            - spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb
         | 
| 312 | 
            +
            - spec/dummy/db/migrate/20180210183654_add_confidential_to_application.rb
         | 
| 310 313 | 
             
            - spec/dummy/db/schema.rb
         | 
| 311 314 | 
             
            - spec/dummy/public/404.html
         | 
| 312 315 | 
             
            - spec/dummy/public/422.html
         | 
| @@ -397,7 +400,25 @@ homepage: https://github.com/doorkeeper-gem/doorkeeper | |
| 397 400 | 
             
            licenses:
         | 
| 398 401 | 
             
            - MIT
         | 
| 399 402 | 
             
            metadata: {}
         | 
| 400 | 
            -
            post_install_message: 
         | 
| 403 | 
            +
            post_install_message: |2+
         | 
| 404 | 
            +
             | 
| 405 | 
            +
             | 
| 406 | 
            +
                WARNING: This is a security release that addresses token revocation not working for public apps (CVE-2018-1000211)
         | 
| 407 | 
            +
             | 
| 408 | 
            +
                There is no breaking change in this release, however to take advantage of the security fix you must:
         | 
| 409 | 
            +
             | 
| 410 | 
            +
                  1. Run `rails generate doorkeeper:add_client_confidentiality` for the migration
         | 
| 411 | 
            +
                  2. Review your OAuth apps and determine which ones exclusively use public grant flows (eg implicit)
         | 
| 412 | 
            +
                  3. Update their `confidential` column to `false` for those public apps
         | 
| 413 | 
            +
             | 
| 414 | 
            +
                This is a backported security release.
         | 
| 415 | 
            +
             | 
| 416 | 
            +
                For more information:
         | 
| 417 | 
            +
             | 
| 418 | 
            +
                  * https://github.com/doorkeeper-gem/doorkeeper/pull/1119
         | 
| 419 | 
            +
                  * https://github.com/doorkeeper-gem/doorkeeper/issues/891
         | 
| 420 | 
            +
             | 
| 421 | 
            +
             | 
| 401 422 | 
             
            rdoc_options: []
         | 
| 402 423 | 
             
            require_paths:
         | 
| 403 424 | 
             
            - lib
         | 
| @@ -456,6 +477,7 @@ test_files: | |
| 456 477 | 
             
            - spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb
         | 
| 457 478 | 
             
            - spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb
         | 
| 458 479 | 
             
            - spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb
         | 
| 480 | 
            +
            - spec/dummy/db/migrate/20180210183654_add_confidential_to_application.rb
         | 
| 459 481 | 
             
            - spec/dummy/db/schema.rb
         | 
| 460 482 | 
             
            - spec/dummy/public/404.html
         | 
| 461 483 | 
             
            - spec/dummy/public/422.html
         |