[
  {
    "path": ".codeclimate.yml",
    "content": "exclude_patterns:\n- \"lib/doorkeeper/config.rb\"\n- \"spec/\"\n"
  },
  {
    "path": ".coveralls.yml",
    "content": "service_name: travis-ci\n"
  },
  {
    "path": ".dockerignore",
    "content": "Gemfile.lock\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*.{rb,json}]\nindent_style = space\nindent_size = 2\ninsert_final_newline = true\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\nopen_collective: doorkeeper-gem\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "### Steps to reproduce\nWhat we need to do to see your problem or bug?\n\nThe more detailed the issue, the more likely that we will fix it ASAP.\n\nDon't use GitHub issues for questions like \"How can I do that?\" —\nuse [StackOverflow](https://stackoverflow.com/questions/tagged/doorkeeper)\ninstead with the corresponding tag.\n\n### Expected behavior\nTell us what should happen\n\n### Actual behavior\nTell us what happens instead\n\n### System configuration\nYou can help us to understand your problem if you will share some very\nuseful information about your project environment (don't forget to\nremove any confidential data if it exists).\n\n**Doorkeeper initializer**:\n\n```ruby\n# config/initializers/doorkeeper.rb\nDoorkeeper.configure do\n  # ...\nend\n```\n\n**Ruby version**: ``\n\n**Gemfile.lock**:\n\n<details>\n  <summary>Gemfile.lock content</summary>\n  \n```\nPlace your Gemfile.lock content here\n```\n</details>\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "### Summary\n\nProvide a general description of the code changes in your pull\nrequest... were there any bugs you had fixed? If so, mention them. If\nthese bugs have open GitHub issues, be sure to tag them here as well,\nto keep the conversation linked together.\n\n### Other Information\n\nIf there's anything else that's important and relevant to your pull\nrequest, mention that information here. This could include\nbenchmarks, or other information.\n\nIf you are updating CHANGELOG.md file or are asked to update it by reviewers,\nplease add the changelog entry at the top of the file.\n\nThanks for contributing to Doorkeeper project!\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n- package-ecosystem: bundler\n  directory: \"/\"\n  schedule:\n    interval: daily\n  open-pull-requests-limit: 10\n- package-ecosystem: github-actions\n  directory: \"/\"\n  schedule:\n    interval: daily\n  open-pull-requests-limit: 10\n"
  },
  {
    "path": ".github/workflows/changelog.yml",
    "content": "name: \"Changelog verifier\"\non:\n  pull_request:\n    # The specific activity types are listed here to include \"labeled\" and \"unlabeled\"\n    # (which are not included by default for the \"pull_request\" trigger).\n    # This is needed to allow skipping enforcement of the changelog in PRs with specific labels,\n    # as defined in the (optional) \"skipLabels\" property.\n    types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled]\n\njobs:\n  # Enforces the update of a changelog file on every pull request\n  changelog:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: dangoslen/changelog-enforcer@v3\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non: [push, pull_request]\n\npermissions:\n  contents: read\n\njobs:\n  build:\n    name: >-\n      Ruby ${{ matrix.ruby }} (${{ matrix.gemfile }})\n    env:\n      CI: true\n      BUNDLE_GEMFILE: ${{ matrix.gemfile }}\n    runs-on: ${{ matrix.os }}\n    if: |\n      !(   contains(github.event.pull_request.title,  '[ci skip]')\n        || contains(github.event.pull_request.title,  '[skip ci]'))\n    strategy:\n      fail-fast: true\n      matrix:\n        os: [ubuntu-latest]\n        ruby:\n          - \"3.1\"\n          - \"3.2\"\n          - \"3.3\"\n          - \"3.4\"\n        gemfile:\n          - gemfiles/rails_7_0.gemfile\n          - gemfiles/rails_7_1.gemfile\n          - gemfiles/rails_7_2.gemfile\n          - gemfiles/rails_8_0.gemfile\n        exclude:\n          - ruby: 3.1\n            gemfile: gemfiles/rails_8_0.gemfile\n    steps:\n      - name: Repo checkout\n        uses: actions/checkout@v6.0.2\n\n      - name: Setup Ruby\n        uses: ruby/setup-ruby@v1\n        with:\n          ruby-version: ${{ matrix.ruby }}\n          bundler-cache: true\n          rubygems: latest\n\n      - name: Run tests\n        timeout-minutes: 10\n        run: bundle exec rake spec\n\n  rails_edge:\n    runs-on: ubuntu-latest\n    env:\n      BUNDLE_GEMFILE: gemfiles/rails_edge.gemfile\n    steps:\n      - uses: actions/checkout@v6.0.2\n      - name: Setup Ruby\n        uses: ruby/setup-ruby@v1\n        with:\n          ruby-version: \"3.4\"\n          bundler-cache: true\n      - run: bundle exec rake spec || echo \"Rails edge test is done.\"\n\n  ruby_edge:\n    strategy:\n      matrix:\n        gemfile:\n          - gemfiles/rails_7_0.gemfile\n          - gemfiles/rails_7_1.gemfile\n          - gemfiles/rails_7_2.gemfile\n          - gemfiles/rails_8_0.gemfile\n    runs-on: ubuntu-latest\n    env:\n      BUNDLE_GEMFILE: ${{ matrix.gemfile }}\n    steps:\n      - uses: actions/checkout@v6.0.2\n      - name: Setup Ruby\n        uses: ruby/setup-ruby@v1\n        with:\n          ruby-version: \"ruby-head\"\n          bundler-cache: true\n      - run: bundle exec rake spec || echo \"Ruby edge test is done.\"\n"
  },
  {
    "path": ".github/workflows/rubocop.yml",
    "content": "name: rubocop\non:\n  pull_request:\npermissions:\n  contents: read\n  pull-requests: write\njobs:\n  rubocop:\n    name: runner / rubocop\n    runs-on: ubuntu-latest\n    env:\n      BUNDLE_ONLY: rubocop\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - uses: ruby/setup-ruby@1a615958ad9d422dd932dc1d5823942ee002799f # v1.227.0\n        with:\n          ruby-version: '3.1'\n          bundler-cache: true\n      - uses: reviewdog/action-rubocop@b6d5e953a5fc0bf3ab65254e77730ea2174d6d6d # v2.22.0\n        with:\n          reporter: github-pr-review # Default is github-pr-check\n          skip_install: true\n          use_bundler: true"
  },
  {
    "path": ".gitignore",
    "content": ".bundle/\nvendor/bundle/\n.rbx\n*.rbc\nlog/*.log\npkg/\nspec/dummy/db/*.sqlite3\nspec/dummy/log/*.log\nspec/dummy/tmp/\nspec/generators/tmp\nGemfile.lock\ngemfiles/*.lock\n.rvmrc\n*.swp\n.idea\n/.yardoc/\n/_yardoc/\n/doc/\n/rdoc/\ncoverage\n*.gem\ngemfiles/vendor\nvendor/bundle/\n"
  },
  {
    "path": ".hound.yml",
    "content": "rubocop:\n  config_file: .rubocop.yml\n  version: 1.5.2\n"
  },
  {
    "path": ".rspec",
    "content": "--colour\n"
  },
  {
    "path": ".rubocop.yml",
    "content": "inherit_from: .rubocop_todo.yml\nplugins:\n  - rubocop-capybara\n  - rubocop-factory_bot\n  - rubocop-performance\n  - rubocop-rails\n  - rubocop-rspec\n  - rubocop-rspec_rails\nAllCops:\n  TargetRubyVersion: 3.1\n  Exclude:\n    - \"spec/generators/tmp/**/*\"\n    - \"spec/dummy/db/*\"\n    - \"spec/dummy/config/*\"\n    - \"Dangerfile\"\n    - \"gemfiles/*.gemfile\"\nLayout/MultilineMethodCallIndentation:\n  EnforcedStyle: indented\nLayout/TrailingEmptyLines:\n  Enabled: true\nLayout/DotPosition:\n  EnforcedStyle: leading\nLayout/LineLength:\n  Exclude:\n    - spec/**/*\nMetrics/BlockLength:\n  Exclude:\n    - spec/**/*\n    - lib/doorkeeper/rake/*\n    - doorkeeper.gemspec\nMetrics/MethodLength:\n  Exclude:\n    - spec/dummy/db/**/*\nStyle/CaseEquality:\n  Exclude:\n    - lib/doorkeeper/grant_flow/flow.rb\nStyle/StringLiterals:\n  EnforcedStyle: double_quotes\nStyle/StringLiteralsInInterpolation:\n  EnforcedStyle: double_quotes\nStyle/FrozenStringLiteralComment:\n  Enabled: true\nStyle/TrailingCommaInHashLiteral:\n  EnforcedStyleForMultiline: consistent_comma\nStyle/TrailingCommaInArrayLiteral:\n  EnforcedStyleForMultiline: consistent_comma\nStyle/TrailingCommaInArguments:\n  EnforcedStyleForMultiline: consistent_comma\nStyle/SymbolArray:\n  MinSize: 3\nStyle/WordArray:\n  MinSize: 3\nStyle/ClassAndModuleChildren:\n  Enabled: false\nStyle/NumericPredicate:\n  Enabled: false\nStyle/DoubleNegation:\n  Enabled: false\nStyle/HashEachMethods:\n  Enabled: true\nStyle/HashTransformKeys:\n  Enabled: true\nStyle/HashTransformValues:\n  Enabled: true\n\nRails/DynamicFindBy:\n  Whitelist:\n    - find_by_sql\n    - find_by_plaintext_token\n    - find_by_fallback_token\nRails/HttpPositionalArguments:\n  Exclude:\n    - spec/grape/*\nRails/HttpStatus:\n  Enabled: false\nRails/RakeEnvironment:\n  Exclude:\n    - Rakefile\nRails/ReflectionClassName:\n  Exclude:\n    - \"lib/doorkeeper/orm/active_record/mixins/access_grant.rb\"\n    - \"lib/doorkeeper/orm/active_record/mixins/access_token.rb\"\n    - \"lib/doorkeeper/orm/active_record/mixins/application.rb\"\nRails/SkipsModelValidations:\n  Enabled: false\n\nRSpec/BeforeAfterAll:\n  Exclude:\n    - \"spec/routing/scoped_routes_spec.rb\"\n    - \"spec/routing/custom_controller_routes_spec.rb\"\nRSpec/ContextWording:\n  Exclude:\n    - \"spec/support/shared/controllers_shared_context.rb\"\nRSpec/DescribeClass:\n  Enabled: false\nRSpec/ExampleLength:\n  Enabled: false\nRSpec/SpecFilePathFormat:\n  Enabled: false\nRSpec/MultipleExpectations:\n  Enabled: false\nRSpec/NestedGroups:\n  Enabled: false\nRSpec/NoExpectationExample:\n  Enabled: true\n  Exclude:\n    - \"spec/requests/**/*\""
  },
  {
    "path": ".rubocop_todo.yml",
    "content": "# This configuration was generated by\n# `rubocop --auto-gen-config`\n# on 2020-06-04 00:15:49 +0300 using RuboCop version 0.84.0.\n# The point is for the user to remove these configuration records\n# one by one as the offenses are removed from the code base.\n# Note that changes in the inspected code, or installation of new\n# versions of RuboCop, may require this file to be generated again.\n\n# Offense count: 13\n# Configuration parameters: IgnoredMethods.\nMetrics/AbcSize:\n  Max: 33\n\n# Offense count: 2\n# Configuration parameters: CountComments, ExcludedMethods.\n# ExcludedMethods: refine\nMetrics/BlockLength:\n  Max: 85\n\n# Offense count: 2\n# Configuration parameters: CountComments.\nMetrics/ClassLength:\n  Max: 242\n\n# Offense count: 2\n# Configuration parameters: IgnoredMethods.\nMetrics/CyclomaticComplexity:\n  Max: 10\n\n# Offense count: 24\n# Configuration parameters: CountComments, ExcludedMethods.\nMetrics/MethodLength:\n  Max: 29\n\n# Offense count: 1\n# Configuration parameters: CountComments.\nMetrics/ModuleLength:\n  Max: 209\n\n# Offense count: 2\n# Configuration parameters: IgnoredMethods.\nMetrics/PerceivedComplexity:\n  Max: 11\n\n# Offense count: 1\n# Configuration parameters: EnforcedStyleForLeadingUnderscores.\n# SupportedStylesForLeadingUnderscores: disallowed, required, optional\nNaming/MemoizedInstanceVariableName:\n  Exclude:\n    - 'lib/doorkeeper/config.rb'\n\n# Offense count: 10\nRSpec/AnyInstance:\n  Exclude:\n    - 'spec/generators/previous_refresh_token_generator_spec.rb'\n    - 'spec/lib/oauth/authorization_code_request_spec.rb'\n    - 'spec/lib/oauth/client_credentials/creator_spec.rb'\n    - 'spec/lib/oauth/password_access_token_request_spec.rb'\n    - 'spec/lib/oauth/token_request_spec.rb'\n    - 'spec/requests/flows/authorization_code_spec.rb'\n    - 'spec/requests/flows/refresh_token_spec.rb'\n\n# Offense count: 2\nRSpec/ExpectInHook:\n  Exclude:\n    - 'spec/controllers/protected_resources_controller_spec.rb'\n\n# Offense count: 300\n# Configuration parameters: AssignmentOnly.\nRSpec/InstanceVariable:\n  Enabled: false\n\n# Offense count: 22\nRSpec/LeakyConstantDeclaration:\n  Exclude:\n    - 'spec/controllers/authorizations_controller_spec.rb'\n    - 'spec/controllers/protected_resources_controller_spec.rb'\n    - 'spec/lib/config_spec.rb'\n    - 'spec/lib/option_spec.rb'\n    - 'spec/models/doorkeeper/access_token_spec.rb'\n\n# Offense count: 7\nRSpec/MessageChain:\n  Exclude:\n    - 'spec/controllers/authorizations_controller_spec.rb'\n    - 'spec/controllers/tokens_controller_spec.rb'\n\n# Offense count: 98\n# Configuration parameters: .\n# SupportedStyles: have_received, receive\nRSpec/MessageSpies:\n  EnforcedStyle: receive\n\n# Offense count: 39\nRSpec/SubjectStub:\n  Exclude:\n    - 'spec/lib/models/expirable_spec.rb'\n    - 'spec/lib/models/reusable_spec.rb'\n    - 'spec/lib/models/revocable_spec.rb'\n    - 'spec/lib/oauth/base_request_spec.rb'\n    - 'spec/lib/oauth/client_credentials_request_spec.rb'\n    - 'spec/support/shared/models_shared_examples.rb'\n\n# Offense count: 73\n# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames.\nRSpec/VerifiedDoubles:\n  Enabled: false\n\n# Offense count: 4\n# Configuration parameters: MinBodyLength.\nStyle/GuardClause:\n  Exclude:\n    - 'lib/doorkeeper/config.rb'\n    - 'lib/doorkeeper/helpers/controller.rb'\n    - 'lib/doorkeeper/oauth/client/credentials.rb'\n    - 'lib/doorkeeper/oauth/token.rb'\n"
  },
  {
    "path": "AGENTS.md",
    "content": "# Doorkeeper Codebase Guide for AI Coding Agents\n\nThis is the code base of the OAuth 2 provider for Ruby web applications.\n\n## Architecture Overview\n\nDoorkeeper is a Ruby gem which is a Rails engine. It provides a set of models, controllers, and views that can be\nmounted into a Rails application to handle OAuth 2 authorization flows.\n\n**Key principle**: all the changes should conform OAuth 2 published specifications such as RFC 6749, RFC 6819 and etc.\n\n## Testing Commands\n \nFrom within the root directory (preferred method):\n\n```bash\nbundle exec rake spec\n```\n\n## Code Conventions\n\n### Changelog Updates\n\nWhen fixing bugs or adding features:\n\n- Add an entry to the top of `CHANGELOG.md`\n- Format: `- [PR number] Brief description`\n- See existing entries for style\n\n### Code Style\n\n- Run RuboCop: `bundle exec rubocop` (there's a project-wide `.rubocop.yml`)\n\n## Documentation\n\n- API docs use YARD/RDoc format\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\nSee https://github.com/doorkeeper-gem/doorkeeper/wiki/Migration-from-old-versions for\nupgrade guides.\n\nUser-visible changes worth mentioning.\n\n## main\n\n- [#1781] Honor `handle_auth_errors :raise` in `AuthorizationsController#authorize_response`\n- [#1795] Fix: detailed error 'insufficient_scope' in protected resources 403s\n- [#1797] Fix `doorkeeper:db:cleanup` rake task failure on PostgreSQL\n- [#1800] Set `@grant_type` in `ClientCredentialsRequest` and `RefreshTokenRequest` constructors so `request.grant_type` returns\n  the correct value in hooks like `before_successful_strategy_response`.\n- [#1802] Fix `filter_parameters` not applied when `Doorkeeper.configure` is called inside to_prepare.\n- [#1804] Use `ActiveSupport.on_load(:active_record)` in ORM hooks to prevent loading ActiveRecord models too early\n- [#1806] Fix token revocation bypass for public clients (RFC 7009)\n- [#1815] Expose `current_resource_owner` as a view helper in `Doorkeeper::ApplicationController`.\n- [#1818] Fix token introspection returning `exp: 0` for non-expiring tokens.\n- [#1784] Remove hardcoded colons from view templates, move punctuation to i18n translation strings.\n\n  **[IMPORTANT]**: if you have customized Doorkeeper views (`authorizations/new`, `authorizations/show`,\n  `applications/show`) or overridden the default `en.yml` translations, you may need to update them.\n  Colons are no longer hardcoded in the views — they are now part of the translation strings.\n  Update the [doorkeeper-i18n](https://github.com/doorkeeper-gem/doorkeeper-i18n) gem to get the\n  updated translations for all locales.\n\n## 5.9.0\n\n- [#1791] Add support for Rails read replicas with automatic role switching via `enable_multiple_database_roles` configuration option\n- [#1792] Consider expires_in when clear expired tokens with StaleRecordsCleaner.\n- [#1790] Fix race condition in refresh token revocation check by moving InvalidGrantReuse check inside the lock block\n- [#1788] Fix regex for basic auth to be case-insensitive\n- [#1775] Fix Applications Secret Not Null Constraint generator\n- [#1779] Only lock previous access token model when creating a new token from its refresh token if revoke_previous_refresh_token_on_use is false\n- [#1778] Ensure that token revocation is idempotent by checking that that token has not already been revoked before revoking.\n\n\n## 5.8.2\n\n- [#1755] Fix the error message for force_pkce\n- [#1761] Memoize authentication failure\n- [#1762] Allow missing client to trigger invalid client error when force_pkce is enabled\n- [#1767] Make sure error handling happens on a controller level opposed to action level to account for the controller being extended\n\n## 5.8.1\n\n- [#1752] Bump the range of supported Ruby and Rails versions\n- [#1747] Fix unknown pkce method error when configured\n- [#1744] Allow for expired refresh tokens to be revoked\n- [#1754] Fix refresh tokens with dynamic scopes\n\n## 5.8.0\n\n- [#1739] Add support for dynamic scopes\n- [#1715] Fix token introspection invalid request reason\n- [#1714] Fix `Doorkeeper::AccessToken.find_or_create_for` with empty scopes which raises NoMethodError\n- [#1712] Add `Pragma: no-cache` to token response\n- [#1726] Refactor token introspection class.\n- [#1727] Allow to set null secret value for Applications if they are public.\n- [#1735] Add `pkce_code_challenge_methods` config option.\n\n## 5.7.1\n\n- [#1705] Add `force_pkce` option that requires non-confidential clients to use PKCE when requesting an access_token using an authorization code\n\n## 5.7.0\n\n- [#1696] Add missing `#issued_token` method to `OAuth::TokenResponse`\n- [#1697] Allow a TokenResponse body to be customized (memoize response body).\n- [#1702] Fix bugs for error response in the form_post and error view\n- [#1660] Custom access token attributes are now considered when finding matching tokens (fixes #1665).\n  Introduce `revoke_previous_client_credentials_token` configuration option.\n\n## 5.6.9\n\n- [#1691] Make new Doorkeeper errors backward compatible with older extensions.\n\n## 5.6.8\n\n- [#1680] Fix handle_auth_errors :raise NotImplementedError\n\n## 5.6.7\n\n- [#1662] Specify uri_redirect validation class explicitly.\n- [#1652] Add custom attributes support to token generator.\n- [#1667] Pass `client` instead of `grant.application` to `find_or_create_access_token`.\n- [#1673] Honor `custom_access_token_attributes` in client credentials grant flow.\n- [#1676] Improve AuthorizationsController error response handling\n- [#1677] Fix URIHelper.valid_for_authorization? breaking for non url URIs.\n\n## 5.6.6\n\n- [#1644] Update HTTP headers.\n- [#1646] Block public clients automatic authorization skip.\n- [#1648] Add custom token attributes to Refresh Token Request.\n- [#1649] Fixed custom_access_token_attributes related errors.\n\n## 5.6.5\n\n- [#1602] Allow custom data to be stored inside access grants/tokens.\n- [#1634] Code refactoring for custom token attributes.\n- [#1639] Add grant type validation to avoid Internal Server Error for DELETE /oauth/authorize endpoint.\n\n## 5.6.4\n\n- [#1633] Apply ORM configuration in #to_prepare block to avoid autoloading errors.\n\n## 5.6.3\n\n- [#1622] Drop support for Rubies 2.5 and 2.6\n- [#1605] Fix URI validation for Ruby 3.2+.\n- [#1625] Exclude endless access tokens from `StaleRecordsCleaner`.\n- [#1626] Remove deprecated `active_record_options` config option.\n- [#1631] Fix regression with redirect behavior after token lookup optimizations (redirect to app URI when found).\n- [#1630] Special case unique index creation for refresh_token on SQL Server.\n- [#1627] Lazy evaluate Doorkeeper config when loading files and executing initializers.\n\n## 5.6.2\n\n- [#1604] Fix fetching of the application when custom application_class defined.\n\n## 5.6.1\n\n- [#1593] Add support for Trilogy ActiveRecord adapter.\n- [#1597] Add optional support to use the url path for the native authorization code flow. Ports forward [#1143] from 4.4.3\n- [#1599] Remove unnecessarily re-fetch of application object when creating an access token.\n\n## 5.6.0\n\n- [#1581] Consider `token_type_hint` when searching for access token in TokensController to avoid extra database calls.\n\n## 5.6.0.rc2\n\n- [#1558] Fixed bug: able to obtain a token with default scopes even if they are not present in the\n  application scopes when using client credentials.\n- [#1567] Only filter `code` parameter if authorization_code grant flow is enabled.\n\n## 5.6.0.rc1\n\n- [#1551] Change lazy loading for ORM to be Ruby standard autoload.\n- [#1552] Remove duplicate IDs on Auth form to improve accessibility.\n- [#1542] Improve performance of `Doorkeeper::AccessToken#matching_token_for` using database specific SQL time math.\n\n  **[IMPORTANT]**: API of the `Doorkeeper::AccessToken#matching_token_for` method has changed and now it returns\n  only **active** access tokens (previously they were just not revoked). Please remember that the idea of the\n  `reuse_access_token` option is to check for existing _active_ token (see configuration option description).\n\n## 5.5.4\n\n- [#1535] Revert changes introduced in #1528 to allow query params in `redirect_uri` as per the spec.\n\n## 5.5.3\n\n- [#1528] Don't allow extra query params in redirect_uri.\n- [#1525] I18n source for forbidden token error is now `doorkeeper.errors.messages.forbidden_token.missing_scope`.\n- [#1531] Disable `strict-loading` for Doorkeeper models by default.\n- [#1532] Add support for Rails 7.\n\n## 5.5.2\n\n- [#1502] Drop support for Ruby 2.4 because of EOL.\n- [#1504] Updated the url fragment in the comment for code documentation.\n- [#1512] Fix form behavior when response mode is form_post.\n- [#1511] Fix that authorization code is returned by fragment if response_mode is fragment.\n\n## 5.5.1\n\n- [#1496] Revoke `old_refresh_token` if `previous_refresh_token` is present.\n- [#1495] Fix `respond_to` undefined in API-only mode\n- [#1488] Verify client authentication for Resource Owner Password Grant when\n  `config.skip_client_authentication_for_password_grant` is set and the client credentials\n  are sent in a HTTP Basic auth header.\n\n## 5.5.0\n\n- [#1482] Simplify `TokenInfoController` to be overridable (extract response rendering).\n- [#1478] Fix ownership association and Rake tasks when custom models configured.\n- [#1477] Respect `ActiveRecord::Base.pluralize_table_names` for Doorkeeper table names.\n\n## 5.5.0.rc2\n\n- [#1473] Enable `Applications` and `AuthorizedApplications` controllers in API mode.\n\n  **[IMPORTANT]** you can still skip these controllers using `skip_controllers` in\n    `use_doorkeeper` inside `routes.rb`. Please do it in case you don't need them.\n\n- [#1472] Fix `establish_connection` configuration for custom defined models.\n- [#1471] Add support for Ruby 3.0.\n- [#1469] Check if `redirect_uri` exists.\n- [#1465] Memoize nil doorkeeper_token.\n- [#1459] Use built-in Ruby option to remove padding in PKCE code challenge value.\n- [#1457] Make owner_id a bigint for newly-generated owner migrations\n- [#1452] Empty previous_refresh_token only if present.\n- [#1440] Validate empty host in redirect_uri.\n- [#1438] Add form post response mode.\n- [#1458] Make `config.skip_client_authentication_for_password_grant` a long term configuration option.\n\n## 5.5.0.rc1\n\n- [#1435] Make error response not redirectable when client is unauthorized\n- [#1426] Ensure ActiveRecord callbacks are executed on token revocation.\n- [#1407] Remove redundant and complex to support helpers froms tests (`should_have_json`, etc).\n- [#1416] Don't add introspection route if token introspection completely disabled.\n- [#1410] Properly memoize `current_resource_owner` value (consider `nil` and `false` values).\n- [#1415] Ignore PKCE params for non-PKCE grants.\n- [#1418] Add ability to register custom OAuth Grant Flows.\n- [#1420] Require client authentication for Resource Owner Password Grant as stated in OAuth RFC.\n\n  **[IMPORTANT]** you need to create a new OAuth client (`Doorkeeper::Application`) if you didn't\n    have it before and use client credentials in HTTP Basic auth if you previously used this grant\n    flow without client authentication. To opt out of this you could set the\n    `skip_client_authentication_for_password_grant` configuration option to `true`, but note that\n    this is in violation of the OAuth spec and represents a security risk.\n    All the users of your provider application now need to include client credentials when they use\n    this grant flow.\n\n- [#1421] Add Resource Owner instance to authorization hook context for `custom_access_token_expires_in`\n  configuration option to allow resource owner based Access Tokens TTL.\n\n## 5.4.0\n\n- [#1404] Make `Doorkeeper::Application#read_attribute_for_serialization` public.\n\n## 5.4.0.rc2\n\n- [#1371] Add `#as_json` method and attributes serialization restriction for Application model.\n  Fixes information disclosure vulnerability (CVE-2020-10187).\n\n  **[IMPORTANT]** you need to re-implement `#as_json` method for Doorkeeper Application model\n  if you previously used `#to_json` serialization with custom options or attributes or rely on\n  JSON response from /oauth/applications.json or /oauth/authorized_applications.json. This change\n  is a breaking change which restricts serialized attributes to a very small set of columns.\n\n- [#1395] Fix `NameError: uninitialized constant Doorkeeper::AccessToken` for Rake tasks.\n- [#1397] Add `as: :doorkeeper_application` on Doorkeeper application form in order to support\n  custom configured application model.\n- [#1400] Correctly yield the application instance to `allow_grant_flow_for_client?` config\n  option (fixes #1398).\n- [#1402] Handle trying authorization with client credentials.\n\n## 5.4.0.rc1\n- [#1366] Sets expiry of token generated using `refresh_token` to that of original token. (Fixes #1364)\n- [#1354] Add `authorize_resource_owner_for_client` option to authorize the calling user to access an application.\n- [#1355] Allow to enable polymorphic Resource Owner association for Access Token & Grant\n  models (`use_polymorphic_resource_owner` configuration option).\n\n  **[IMPORTANT]** Review your custom patches or extensions for Doorkeeper internals if you\n  have such - since now Doorkeeper passes Resource Owner instance to every objects and not\n  just it's ID. See PR description for details.\n\n- [#1356] Remove duplicated scopes from Access Tokens and Grants on attribute assignment.\n- [#1357] Fix `Doorkeeper::OAuth::PreAuthorization#as_json` method causing\n  `Stack level too deep` error with AMS (fix #1312).\n- [#1358] Deprecate `active_record_options` configuration option.\n- [#1359] Refactor Doorkeeper configuration options DSL to make it easy to reuse it\n  in external extensions.\n- [#1360] Increase `matching_token_for` lookup size to 10 000 and make it configurable.\n- [#1371] Fix controllers to use valid classes in case Doorkeeper has custom models configured.\n- [#1370] Fix revocation response for invalid token and unauthorized requests to conform with RFC 7009 (fixes #1362).\n\n  **[IMPORTANT]** now fully according to RFC 7009 nobody can do a revocation request without `client_id`\n  (for public clients) and `client_secret` (for private clients). Please update your apps to include that\n  info in the revocation request payload.\n\n- [#1373] Make Doorkeeper routes mapper reusable in extensions.\n- [#1374] Revoke and issue client credentials token in a transaction with a row lock.\n- [#1384] Add context object with auth/pre_auth and issued_token for authorization hooks.\n- [#1387] Add `AccessToken#create_for` and use in `RefreshTokenRequest`.\n- [#1392] Fix `enable_polymorphic_resource_owner` migration template to have proper index name.\n- [#1393] Improve Applications #show page with more informative data on client secret and scopes.\n- [#1394] Use Ruby `autoload` feature to load Doorkeeper files.\n\n## 5.3.3\n\n- [#1404] Backport: Make `Doorkeeper::Application#read_attribute_for_serialization` public.\n\n## 5.3.2\n\n- [#1371] Backport: add `#as_json` method and attributes serialization restriction for Application model.\n  Fixes information disclosure vulnerability (CVE-2020-10187).\n\n## 5.3.1\n\n- [#1360] Backport: Increase `matching_token_for` batch lookup size to 10 000 and make it configurable.\n\n## 5.3.0\n\n- [#1339] Validate Resource Owner in `PasswordAccessTokenRequest` against `nil` and `false` values.\n- [#1341] Fix `refresh_token_revoked_on_use` with `hash_token_secrets` enabled.\n- [#1343] Fix ruby 2.7 kwargs warning in InvalidTokenResponse.\n- [#1345] Allow to set custom classes for Doorkeeper models, extract reusable AR mixins.\n- [#1346] Refactor `Doorkeeper::Application#to_json` into convenient `#as_json` (fix #1344).\n- [#1349] Fix `Doorkeeper::Application` AR associations using an incorrect foreign key name when using a custom class.\n- [#1318] Make existing token revocation for client credentials optional and disable it by default.\n\n  **[IMPORTANT]** This is a change compared to the behaviour of version 5.2.\n  If you were relying on access tokens being revoked once the same client\n  requested a new access token, reenable it with `revoke_previous_client_credentials_token` in Doorkeeper\n  initialization file.\n\n## 5.2.6\n\n- [#1404] Backport: Make `Doorkeeper::Application#read_attribute_for_serialization` public.\n\n## 5.2.5\n\n- [#1371] Backport: add `#as_json` method and attributes serialization restriction for Application model.\n  Fixes information disclosure vulnerability (CVE-2020-10187).\n\n## 5.2.4\n\n- [#1360] Backport: Increase `matching_token_for` batch lookup size to 10 000 and make it configurable.\n\n## 5.2.3\n\n- [#1334] Remove `application_secret` flash helper and `redirect_to` keyword.\n- [#1331] Move redirect_uri_validator to where it is used (`Application` model).\n- [#1326] Move response_type check in pre_authorization to a method to be easily to override.\n- [#1329] Fix `find_in_batches` order warning.\n\n## 5.2.2\n\n- [#1320] Call configured `authenticate_resource_owner` method once per request.\n- [#1315] Allow generation of new secret with `Doorkeeper::Application#renew_secret`.\n- [#1309] Allow `Doorkeeper::Application#to_json` to work without arguments.\n\n## 5.2.1\n\n- [#1308] Fix flash types for `api_only` mode (no flashes for `ActionController::API`).\n- [#1306] Fix interpolation of `missing_param` I18n.\n\n## 5.2.0\n\n- [#1305] Make `Doorkeeper::ApplicationController` to inherit from `ActionController::API` in cases\n  when `api_mode` enabled (fixes #1302).\n\n## 5.2.0.rc3\n\n- [#1298] Slice strong params so doesn't error with Rails forms.\n- [#1300] Limiting access to attributes of pre_authorization.\n- [#1296] Adding client_id to strong parameters.\n\n  **[IMPORTANT]** `Doorkeeper::Server#client_via_uid` was removed.\n\n- [#1293] Move ar specific redirect uri validator to ar orm directory.\n- [#1288] Allow to pass attributes to the `Doorkeeper::OAuth::PreAuthorization#as_json` method to customize\n  the PreAuthorization response.\n- [#1286] Add ability to customize grant flows per application (OAuth client) (#1245 , #1207)\n- [#1283] Allow to customize base class for `Doorkeeper::ApplicationMetalController` (new configuration\n  option called `base_metal_controller` (fix #1273).\n- [#1277] Prevent requested scope be empty on authorization request, handle and add description for invalid request.\n\n## 5.2.0.rc2\n\n- [#1270] Find matching tokens in batches for `reuse_access_token` option (fix #1193).\n- [#1271] Reintroduce existing token revocation for client credentials.\n\n  **[IMPORTANT]** If you rely on being able to fetch multiple access tokens from the same\n  client using client credentials flow, you should skip to version 5.3, where this behaviour\n  is deactivated by default.\n\n- [#1269] Update initializer template documentation.\n- [#1266] Use strong parameters within pre-authorization.\n- [#1264] Add :before_successful_authorization and :after_successful_authorization hooks in TokensController\n- [#1263] Response properly when introspection fails and fix configurations's user guide.\n\n## 5.2.0.rc1\n\n- [#1260], [#1262] Improve Token Introspection configuration option (access to tokens, client).\n- [#1257] Add constraint configuration when using client authentication on introspection endpoint.\n- [#1252] Returning `unauthorized` when the revocation of the token should not be performed due to wrong permissions.\n- [#1249] Specify case sensitive uniqueness to remove Rails 6 deprecation message\n- [#1248] Display the Application Secret in HTML after creating a new application even when `hash_application_secrets` is used.\n- [#1248] Return the unhashed Application Secret in the JSON response after creating new application even when `hash_application_secrets` is used.\n- [#1238] Better support for native app with support for custom scheme and localhost redirection.\n\n## 5.1.2\n\n- [#1404] Backport: Make `Doorkeeper::Application#read_attribute_for_serialization` public.\n\n## 5.1.1\n\n- [#1371] Backport: add `#as_json` method and attributes serialization restriction for Application model.\n  Fixes information disclosure vulnerability (CVE-2020-10187).\n\n## 5.1.0\n\n- [#1243] Add nil check operator in token checking at token introspection.\n- [#1241] Explaining foreign key options for resource owner in a single place\n- [#1237] Allow to set blank redirect URI if Doorkeeper configured to use redirect URI-less grant flows.\n- [#1234] Fix `StaleRecordsCleaner` to properly work with big amount of records.\n- [#1228] Allow to explicitly set non-expiring tokens in `custom_access_token_expires_in` configuration\n  option using `Float::INFINITY` return value.\n- [#1224] Do not try to store token if not found by fallback hashing strategy.\n- [#1223] Update Hound/Rubocop rules, correct Doorkeeper codebase to follow style-guides.\n- [#1220] Drop Rails 4.2 & Ruby < 2.4 support.\n\n## 5.1.0.rc2\n\n- [#1208] Unify hashing implementation into secret storing strategies\n\n  **[IMPORTANT]** If you have been using the master branch of doorkeeper with bcrypt in your Gemfile.lock,\n  your application secrets have been hashed using BCrypt. To restore this behavior, use the initializer option\n  `hash_application_secrets using: 'Doorkeeper::SecretStoring::BCrypt`.\n\n- [#1216] Add nil check to `expires_at` method.\n- [#1215] Fix deprecates for Rails 6.\n- [#1214] Scopes field accepts array.\n- [#1209] Fix tokens validation for Token Introspection request.\n- [#1202] Use correct HTTP status codes for error responses.\n\n  **[IMPORTANT]**: this change might break your application if you were relying on the previous\n  401 status codes, this is now a 400 by default, or a 401 for `invalid_client` and `invalid_token` errors.\n\n- [#1201] Fix custom TTL block `client` parameter to always be an `Doorkeeper::Application` instance.\n\n  **[IMPORTANT]**: those who defined `custom_access_token_expires_in` configuration option need to check\n  their block implementation: if you are using `oauth_client.application` to get `Doorkeeper::Application`\n  instance, then you need to replace it with just `oauth_client`.\n\n- [#1200] Increase default Doorkeeper access token value complexity (`urlsafe_base64` instead of just `hex`)\n  matching RFC6749/RFC6750.\n\n  **[IMPORTANT]**: this change have possible side-effects in case you have custom database constraints for\n  access token value, application secrets, refresh tokens or you patched Doorkeeper models and introduced\n  token value validations, or you are using database with case-insensitive WHERE clause like MySQL\n  (you can face some collisions). Before this change access token value matched `[a-f0-9]` regex, and now\n  it matches `[a-zA-Z0-9\\-_]`. In case you have such restrictions and your don't use custom token generator\n  please change configuration option `default_generator_method` to `:hex`.\n\n- [#1195] Allow to customize Token Introspection response (fixes #1194).\n- [#1189] Option to set `token_reuse_limit`.\n- [#1191] Try to load bcrypt for hashing of application secrets, but add fallback.\n\n## 5.1.0.rc1\n\n- [#1188] Use `params` instead of `request.POST` in tokens controller (fixes #1183).\n- [#1182] Fix loopback IP redirect URIs to conform with RFC8252, p. 7.3 (fixes #1170).\n- [#1179] Authorization Code Grant Flow without client id returns invalid_client error.\n- [#1177] Allow to limit `scopes` for certain `grant_types`\n- [#1176] Fix test factory support for `factory_bot_rails`\n- [#1175] Internal refactor: use `scopes_string` inside `scopes`.\n- [#1168] Allow optional hashing of tokens and secrets.\n- [#1164] Fix error when `root_path` is not defined.\n- [#1162] Fix `enforce_content_type` for requests without body.\n\n## 5.0.3\n\n- [#1371] Backport: add `#as_json` method and attributes serialization restriction for Application model.\n  Fixes information disclosure vulnerability (CVE-2020-10187).\n\n## 5.0.2\n\n- [#1158] Fix initializer template: change `handle_auth_errors` option\n- [#1157] Remove redundant index from migration template.\n\n## 5.0.1\n\n- [#1154] Refactor `StaleRecordsCleaner` to be ORM agnostic.\n- [#1152] Fix migration template: change resource owner data type from integer to Rails generic `references`\n- [#1151] Fix Refresh Token strategy: add proper validation of client credentials both for Public & Private clients.\n- [#1149] Fix for `URIChecker#valid_for_authorization?` false negative when query is blank, but `?` present.\n- [#1140] Allow rendering custom errors from exceptions (issue #844). Originally opened as [#944].\n- [#1138] Revert regression bug (check for token expiration in Authorizations controller so authorization\n  triggers every time)\n\n## 5.0.0\n\n- [#1127] Change the token_type initials of the Banner Token to uppercase to comply with the RFC6750 specification.\n\n## 5.0.0.rc2\n\n- [#1122] Fix AuthorizationsController#new error response to be in JSON format\n- [#1119] Fix token revocation for OAuth apps using \"implicit\" grant flow\n- [#1116] `AccessGrant`s will now be revoked along with `AccessToken`s when\n  hitting the `AuthorizedApplicationController#destroy` route.\n- [#1114] Make token info endpoint's attributes consistent with token creation\n- [#1108] Simple formatting of callback URLs when listing oauth applications\n- [#1106] Restrict access to AdminController with 'Forbidden 403' if admin_authenticator is not\n  configured by developers.\n\n## 5.0.0.rc1\n\n- [#1103] Allow customizing use_refresh_token\n- [#1089] Removed enable_pkce_without_secret configuration option\n- [#1102] Expiration time based on scopes\n- [#1099] All the configuration variables in `Doorkeeper.configuration` now\n  always return a non-nil value (`true` or `false`)\n- [#1099] ORM / Query optimization: Do not revoke the refresh token if it is not enabled\n  in `doorkeeper.rb`\n- [#996] Expiration Time Base On Grant Type\n- [#997] Allow PKCE authorization_code flow as specified in RFC7636\n- [#907] Fix lookup for matching tokens in certain edge-cases\n- [#992] Add API option to use Doorkeeper without management views for API only\n  Rails applications (`api_only`)\n- [#1045] Validate redirect_uri as the native URI when making authorization code requests\n- [#1048] Remove deprecated `Doorkeeper#configured?`, `Doorkeeper#database_installed?`, and\n  `Doorkeeper#installed?` method\n- [#1031] Allow public clients to authenticate without `client_secret`. Define an app as\n  either public or private/confidential\n\n  **[IMPORTANT]**: all the applications (clients) now are considered as private by default.\n  You need to manually change `confidential` column to `false` if you are using public clients,\n  in other case your mobile (or other) applications will not be able to authorize.\n  See [#1142](https://github.com/doorkeeper-gem/doorkeeper/issues/1142) for more details.\n\n- [#1010] Add configuration to enforce configured scopes (`default_scopes` and\n  `optional_scopes`) for applications\n- [#1060] Ensure that the native redirect_uri parameter matches with redirect_uri of the client\n- [#1064] Add :before_successful_authorization and :after_successful_authorization hooks\n- [#1069] Upgrade Bootstrap to 4 for Admin\n- [#1068] Add rake task to cleanup databases that can become large over time\n- [#1072] AuthorizationsController: Memoize strategy.authorize_response result to enable\n  subclasses to use the response object.\n- [#1075] Call `before_successful_authorization` and `after_successful_authorization` hooks\n  on `create` action as well as `new`\n- [#1082] Fix #916: remember routes mapping and use it required places (fix error with\n  customized Token Info route).\n- [#1086, #1088] Fix bug with receiving default scopes in the token even if they are\n  not present in the application scopes (use scopes intersection).\n- [#1076] Add config to enforce content type to application/x-www-form-urlencoded\n- Fix bug with `force_ssl_in_redirect_uri` when it breaks existing applications with an\n  SSL redirect_uri.\n\n## 4.4.3\n\n- [#1143] Adds a config option `opt_out_native_route_change` to opt out of the breaking api\n  changed introduced in https://github.com/doorkeeper-gem/doorkeeper/pull/1003\n\n## 4.4.2\n\n- [#1130] Backport fix for native redirect_uri from 5.x.\n\n## 4.4.1\n\n- [#1127] Backport token type to comply with the RFC6750 specification.\n- [#1125] Backport Quote surround I18n yes/no keys\n\n## 4.4.0\n\n- [#1120] Backport security fix from 5.x for token revocation when using public clients\n\n  **[IMPORTANT]**: all the applications (clients) now are considered as private by default.\n  You need to manually change `confidential` column to `false` if you are using public clients,\n  in other case your mobile (or other) applications will not be able to authorize.\n  See [#1142](https://github.com/doorkeeper-gem/doorkeeper/issues/1142) for more details.\n\n## 4.3.2\n\n- [#1053] Support authorizing with query params in the request `redirect_uri` if explicitly present in app's `Application#redirect_uri`\n\n## 4.3.1\n\n- Remove `BaseRecord` and introduce additional concern for ordering methods to fix\n  braking changes for Doorkeeper models.\n- [#1032] Refactor BaseRequest callbacks into configurable lambdas\n- [#1040] Clear mixins from ActiveRecord DSL and save only overridable API. It\n  allows to use this mixins in Doorkeeper ORM extensions with minimum code boilerplate.\n\n## 4.3.0\n\n- [#976] Fix to invalidate the second redirect URI when the first URI is the native URI\n- [#1035] Allow `Application#redirect_uri=` to handle array of URIs.\n- [#1036] Allow to forbid Application redirect URI's with specific rules.\n- [#1029] Deprecate `order_method` and introduce `ordered_by`. Sort applications\n  by `created_at` in index action.\n- [#1033] Allow Doorkeeper configuration option #force_ssl_in_redirect_uri to be a callable object.\n- Fix Grape integration & add specs for it\n- [#913] Deferred ORM (ActiveRecord) models loading\n- [#943] Fix Access Token token generation when certain errors occur in custom token generators\n- [#1026] Implement RFC7662 - OAuth 2.0 Token Introspection\n- [#985] Generate valid migration files for Rails >= 5\n- [#972] Replace Struct subclassing with block-form initialization\n- [#1003] Use URL query param to pass through native redirect auth code so automated apps can find it.\n\n  **[IMPORTANT]**: Previously authorization code response route was `/oauth/authorize/<code>`,\n  now it is `oauth/authorize/native?code=<code>` (in order to help applications to automatically find the code value).\n\n- [#868] `Scopes#&` and `Scopes#+` now take an array or any other enumerable\n  object.\n- [#1019] Remove translation not in use: `invalid_resource_owner`.\n- Use Ruby 2 hash style syntax (min required Ruby version = 2.1)\n- [#948] Make Scopes.<=> work with any \"other\" value.\n- [#974] Redirect URI is checked without query params within AuthorizationCodeRequest.\n- [#1004] More explicit help text for `native_redirect_uri`.\n- [#1023] Update Ruby versions and test against 2.5.0 on Travis CI.\n- [#1024] Migrate from FactoryGirl to FactoryBot.\n- [#1025] Improve documentation for adding foreign keys\n- [#1028] Make it possible to have composite strategy names.\n\n## 4.2.6\n\n- [#970] Escape certain attributes in authorization forms.\n\n## 4.2.5\n\n- [#936] Deprecate `Doorkeeper#configured?`, `Doorkeeper#database_installed?`, and\n  `Doorkeeper#installed?`\n- [#909] Add `InvalidTokenResponse#reason` reader method to allow read the kind\n  of invalid token error.\n- [#928] Test against more recent Ruby versions\n- Small refactorings within the codebase\n- [#921] Switch to Appraisal, and test against Rails master\n- [#892] Add minimum Ruby version requirement\n\n## 4.2.0\n\n- Security fix: Address CVE-2016-6582, implement token revocation according to\n  spec (tokens might not be revoked if client follows the spec).\n- [#873] Add hooks to Doorkeeper::ApplicationMetalController\n- [#871] Allow downstream users to better utilize doorkeeper spec factories by\n  eliminating name conflict on `:user` factory.\n\n## 4.1.0\n\n- [#845] Allow customising the `Doorkeeper::ApplicationController` base\n  controller\n\n## 4.0.0\n\n- [#834] Fix AssetNotPrecompiled error with Sprockets 4\n- [#843] Revert \"Fix validation error messages\"\n- [#847] Specify Null option to timestamps\n\n## 4.0.0.rc4\n\n- [#777] Add support for public client in password grant flow\n- [#823] Make configuration and specs ORM independent\n- [#745] Add created_at timestamp to token generation options\n- [#838] Drop `Application#scopes` generator and warning, introduced for\n  upgrading doorkeeper from v2 to v3.\n- [#801] Fix Rails 5 warning messages\n- Test against Rails 5 RC1\n\n## 4.0.0.rc3\n\n- [#769] Revoke refresh token on access token use. To make use of the new config\n  add `previous_refresh_token` column to `oauth_access_tokens`:\n\n  ```\n  rails generate doorkeeper:previous_refresh_token\n  ```\n\n- [#811] Toughen parameters filter with exact match\n- [#813] Applications admin bugfix\n- [#799] Fix Ruby Warnings\n- Drop `attr_accessible` from models\n\n### Backward incompatible changes\n\n- [#730] Force all timezones to use UTC to prevent comparison issues.\n- [#802] Remove `config.i18n.fallbacks` from engine\n\n## 4.0.0.rc2\n\n- Fix optional belongs_to for Rails 5\n- Fix Ruby warnings\n\n## 4.0.0.rc1\n\n### Backward incompatible changes\n\n- Drops support for Rails 4.1 and earlier\n- Drops support for Ruby 2.0\n- [#778] Bug fix: use the remaining time that a token is still valid when\n  building the redirect URI for the implicit grant flow\n\n### Other changes\n\n- [#771] Validation error messages fixes\n- Adds foreign key constraints in generated migrations between tokens and\n  grants, and applications\n- Support Rails 5\n\n## 3.1.0\n\n- [#736] Existing valid tokens are now reused in client_credentials flow\n- [#749] Allow user to raise authorization error with custom messages.\n  Under `resource_owner_authenticator` block a user can\n  `raise Doorkeeper::Errors::DoorkeeperError.new('custom_message')`\n- [#762] Check doesn’t abort the actual migration, so it runs\n- [#722] `doorkeeper_forbidden_render_options` now supports returning a 404 by\n  specifying `respond_not_found_when_forbidden: true` in the\n  `doorkeeper_forbidden_render_options` method.\n- [#734] Simplify and remove duplication in request strategy classes\n\n## 3.0.1\n\n- [#712] Wrap exchange of grant token for access token and access token refresh\n  in transactions\n- [#704] Allow applications scopes to be mass assigned\n- [#707] Fixed order of Mixin inclusion and table_name configuration in models\n- [#712] Wrap access token and refresh grants in transactions\n- Adds JRuby support\n- Specs, views and documentation adjustments\n\n## 3.0.0\n\n### Other changes\n\n- [#693] Updates `en.yml`.\n\n## 3.0.0 (rc2)\n\n### Backward incompatible changes\n\n- [#678] Change application-specific scopes to take precedence over server-wide\n  scopes. This removes the previous behavior where the intersection between\n  application and server scopes was used.\n\n### Other changes\n\n- [#671] Fixes `NoMethodError - undefined method 'getlocal'` when calling\n  the /oauth/token path. Switch from using a DateTime object to update\n  AR to using a Time object. (Issue #668)\n- [#677] Support editing application-specific scopes via the standard forms\n- [#682] Pass error hash to Grape `error!`\n- [#683] Generate application secret/UID if fields are blank strings\n\n## 3.0.0 (rc1)\n\n### Backward incompatible changes\n\n- [#648] Extracts mongodb ORMs to\n  https://github.com/doorkeeper-gem/doorkeeper-mongodb. If you use ActiveRecord\n  you don’t need to do any change, otherwise you will need to install the new\n  plugin.\n- [#665] `doorkeeper_unauthorized_render_options(error:)` and\n  `doorkeeper_forbidden_render_options(error:)` now accept `error` keyword\n  argument.\n\n### Removed deprecations\n\n- Removes `doorkeeper_for` deprecation notice.\n- Remove `applications.scopes` upgrade notice.\n\n## 2.2.2\n\n- [#541] Fixed `undefined method attr_accessible` problem on Rails 4\n  (happens only when ProtectedAttributes gem is used) in #599\n\n## 2.2.1\n\n- [#636] `custom_access_token_expires_in` bugfixes\n- [#641] syntax error fix (Issue #612)\n- [#633] Send extra details to Custom Token Generator\n- [#628] Refactor: improve orm adapters to ease extension\n- [#637] Upgrade to rspec to 3.2\n\n## 2.2.0 - 2015-04-19\n\n- [#611] Allow custom access token generators to be used\n- [#632] Properly fallback to `default_scopes` when no scope is specified\n- [#622] Clarify that there is a logical OR between scopes for authorizing\n- [#635] Upgrade to rspec 3\n- [#627] i18n fallbacks to english\n- Moved CHANGELOG to NEWS.md\n\n## 2.1.4 - 2015-03-27\n\n- [#595] HTTP spec: Add `scope` for refresh token scope param\n- [#596] Limit scopes in app scopes for client credentials\n- [#567] Add Grape helpers for easier integration with Grape framework\n- [#606] Add custom access token expiration support for Client Credentials flow\n\n## 2.1.3 - 2015-03-01\n\n- [#588] Fixes scopes_match? bug that skipped authorization form in some cases\n\n## 2.1.2 - 2015-02-25\n\n- [#574] Remove unused update authorization route.\n- [#576] Filter out sensitive parameters from logs.\n- [#582] The Authorization HTTP header fields are now case insensitive.\n- [#583] Database connection bugfix in certain scenarios.\n- Testing improvements\n\n## 2.1.1 - 2015-02-06\n\n- Remove `wildcard_redirect_url` option\n- [#481] Customize token flow OAuth expirations with a config lambda\n- [#568] TokensController: Memoize strategy.authorize_response result to enable\n  subclasses to use the response object.\n- [#571] Fix database initialization issues in some configurations.\n- Documentation improvements\n\n## 2.1.0 - 2015-01-13\n\n- [#540] Include `created_at` in response.\n- [#538] Check application-level scopes in client_credentials and password flow.\n- [5596227] Check application scopes in AccessToken when present. Fixes a bug in\n  doorkeeper 2.0.0 and 2.0.1 referring to application specific scopes.\n- [#534] Internationalizes doorkeeper views.\n- [#545] Ensure there is a connection to the database before checking for\n  missing columns\n- [#546] Use `Doorkeeper::` prefix when referencing `Application` to avoid\n  possible application model name conflict.\n- [#538] Test with Rails ~> 4.2.\n\n### Potentially backward incompatible changes\n\n- Enable by default `authorization_code` and `client_credentials` grant flows.\n  Disables implicit and password grant flows by default.\n- [#510, #544, 722113f] Revoked refresh token response bugfix.\n\n## 2.0.1 - 2014-12-17\n\n- [#525, #526, #527] Fix `ActiveRecord::NoDatabaseError` on gem load.\n\n## 2.0.0 - 2014-12-16\n\n### Backward incompatible changes\n\n- [#448] Removes `doorkeeper_for` helper. Now we use\n  `before_action :doorkeeper_authorize!`.\n- [#469] Allow client applications to restrict the set of allowable scopes.\n  Fixes #317. `oauth_applications` relation needs a new `scopes` string column,\n  non nullable, which defaults to an empty string. To add the column run:\n\n  ```\n  rails generate doorkeeper:application_scopes\n  ```\n\n  If you’d rather do it by hand, your ActiveRecord migration should contain:\n\n  ```ruby\n  add_column :oauth_applications, :scopes, :string, null: false, default: ‘’\n  ```\n\n### Removed deprecations\n\n- Removes `test_redirect_uri` option. It is now called `native_redirect_uri`.\n- [#446] Removes `mount Doorkeeper::Engine`. Now we use `use_doorkeeper`.\n\n### Others\n\n- [#484] Performance improvement - avoid performing order_by when not required.\n- [#450] When password is invalid in Password Credentials Grant, Doorkeeper\n  returned 'invalid_resource_owner' instead of 'invalid_grant', as the spec\n  declares. Fixes #444.\n- [#452] Allows `revoked_at` to be set in the future, for future expiry.\n  Rationale: https://github.com/doorkeeper-gem/doorkeeper/pull/452#issuecomment-51431459\n- [#480] For Implicit grant flow, access tokens can now be reused. Fixes #421.\n- [#491] Reworks of @jasl's #454 and #478. ORM refactor that allows doorkeeper\n  to be extended more easily with unsupported ORMs. It also marks the boundaries\n  between shared model code and ORM specifics inside of the gem.\n- [#496] Tests with Rails 4.2.\n- [#489] Adds `force_ssl_in_redirect_uri` to force the usage of the HTTPS\n  protocol in non-native redirect uris.\n- [#516] SECURITY: Adds `protect_from_forgery` to `Doorkeeper::ApplicationController`\n- [#518] Fix random failures in mongodb.\n\n---\n\n## 1.4.2 - 2015-03-02\n\n- [#576] Filter out sensitive parameters from logs\n\n## 1.4.1 - 2014-12-17\n\n- [#516] SECURITY: Adds `protect_from_forgery` to `Doorkeeper::ApplicationController`\n\n## 1.4.0 - 2014-07-31\n\n- internals\n  - [#427] Adds specs expectations.\n  - [#428] Error response refactor.\n  - [#417] Moves token validation into Access Token class.\n  - [#439] Removes redundant module includes.\n  - [#443] TokensController and TokenInfoController inherit from ActionController::Metal\n- bug\n  - [#418] fixes #243, requests with insufficient scope now respond 403 instead\n    of 401. (API change)\n  - [#438] fixes #398, native redirect for implicit token grant bug.\n  - [#440] namespace fixes\n- enhancements\n  - [#432] Keeps query parameters\n\n## 1.3.1 - 2014-07-06\n\n- enhancements\n  - [#405] Adds facade to more easily get the token from a request in a route\n    constraint.\n  - [#415] Extend Doorkeeper TokenResponse with an `after_successful_response`\n    callback that allows handling of `response` object.\n- internals\n  - [#409] Deprecates `test_redirect_uri` in favor of `native_redirect_uri`.\n    See discussion in: [#351].\n  - [#411] Clean rspec deprecations. General test improvements.\n  - [#412] rspec line width can go longer than 80 (hound CI config).\n- bug\n  - [#413] fixes #340, routing scope is now taken into account in redirect.\n  - [#401] and [#425] application is not required any longer for access_token.\n\n## 1.3.0 - 2014-05-23\n\n- enhancements\n  - [#387] Adds reuse_access_token configuration option.\n\n## 1.2.0 - 2014-05-02\n\n- enhancements\n  - [#376] Allow users to enable basic header authorization for access tokens.\n  - [#374] Token revocation implementation [RFC 7009]\n  - [#295] Only enable specific grant flows.\n- internals\n  - [#381] Locale source fix.\n  - [#380] Renames `errors_for` to `doorkeeper_errors_for`.\n  - [#390] Style adjustments in accordance with Ruby Style Guide form\n    Thoughtbot.\n\n## 1.1.0 - 2014-03-29\n\n- enhancements\n  - [#336] mongoid4 support.\n  - [#372] Allow users to set ActiveRecord table_name_prefix/suffix options\n- internals\n  - [#343] separate OAuth's admin and user end-point to different layouts, upgrade theme to Bootstrap 3.1.\n  - [#348] Move render_options in filter after `@error` has been set\n\n## 1.0.0 - 2014-01-13\n\n- bug (spec)\n  - [#228] token response `expires_in` value is now in seconds, relative to\n    request time\n  - [#296] client is optional for password grant type.\n  - [#319] If client credentials are present on password grant type they are validated\n  - [#326] If client credentials are present in refresh token they are validated\n  - [#326] If authenticated client does not match original client that\n    obtained a refresh token it responds `invalid_grant` instead of\n    `invalid_client`. Previous usage was invalid according to Section 5.2 of\n    the spec.\n  - [#329] access tokens' `scopes` string wa being compared against\n    `default_scopes` symbols, always unauthorizing.\n  - [#318] Include \"WWW-Authenticate\" header with Unauthorized responses\n- enhancements\n  - [#293] Adds ActionController::Instrumentation in TokensController\n  - [#298] Support for multiple redirect_uris added.\n  - [#313] `AccessToken.revoke_all_for` actually revokes all non-revoked\n    tokens for an application/owner instead of deleting them.\n  - [#333] Rails 4.1 support\n- internals\n  - Removes jQuery dependency [fixes #300][pr #312 is related]\n  - [#294] Client uid and secret will be generated only if not present.\n  - [#316] Test warnings addressed.\n  - [#338] Rspec 3 syntax.\n\n---\n\n## 0.7.4 - 2013-12-01\n\n- bug\n  - Symbols instead of strings for user input.\n\n## 0.7.3 - 2013-10-04\n\n- enhancements\n  - [#204] Allow to overwrite scope in routes\n- internals\n  - Returns only present keys in Token Response (may imply a backwards\n    incompatible change). https://github.com/doorkeeper-gem/doorkeeper/issues/220\n- bug\n  - [#290] Support for Rails 4 when 'protected_attributes' gem is present.\n\n## 0.7.2 - 2013-09-11\n\n- enhancements\n  - [#272] Allow issuing multiple access_tokens for one user/application for multiple devices\n  - [#170] Increase length of allowed redirect URIs\n  - [#239] Do not try to load unavailable Request class for the current phase.\n  - [#273] Relax jquery-rails gem dependency\n\n## 0.7.1 - 2013-08-30\n\n- bug\n  - [#269] Rails 3.2 raised `ActiveModel::MassAssignmentSecurity::Error`.\n\n## 0.7.0 - 2013-08-21\n\n- enhancements\n  - [#229] Rails 4!\n- internals\n  - [#203] Changing table name to be specific in column_names_with_table\n  - [#215] README update\n  - [#227] Use Rails.config.paths[\"config/routes\"] instead of assuming \"config/routes.rb\" exists\n  - [#262] Add jquery as gem dependency\n  - [#263] Add a configuration for ActiveRecord.establish_connection\n  - Deprecation and Ruby warnings (PRs merged outside of GitHub).\n\n## 0.6.7 - 2013-01-13\n\n- internals\n  - [#188] Add IDs to the show views for integration testing [@egtann](https://github.com/egtann)\n\n## 0.6.6 - 2013-01-04\n\n- enhancements\n  - [#187] Raise error if configuration is not set\n\n## 0.6.5 - 2012-12-26\n\n- enhancements\n  - [#184] Vendor the Bootstrap CSS [@tylerhunt](https://github.com/tylerhunt)\n\n## 0.6.4 - 2012-12-15\n\n- bug\n  - [#180] Add localization to authorized_applications destroy notice [@aalvarado](https://github.com/aalvarado)\n\n## 0.6.3 - 2012-12-07\n\n- bugfixes\n  - [#163] Error response content-type header should be application/json [@ggayan](https://github.com/ggayan)\n  - [#175] Make token.expires_in_seconds return nil when expires_in is nil [@miyagawa](https://github.com/miyagawa)\n- enhancements\n  - [#166, #172, #174] Behavior to automatically authorize based on a configured proc\n- internals\n  - [#168] Using expectation syntax for controller specs [@rdsoze](https://github.com/rdsoze)\n\n## 0.6.2 - 2012-11-10\n\n- bugfixes\n  - [#162] Remove ownership columns from base migration template [@rdsoze](https://github.com/rdsoze)\n\n## 0.6.1 - 2012-11-07\n\n- bugfixes\n  - [#160] Removed |routes| argument from initializer authenticator blocks\n- documentation\n  - [#160] Fixed description of context of authenticator blocks\n\n## 0.6.0 - 2012-11-05\n\n- enhancements\n  - Mongoid `orm` configuration accepts only :mongoid2 or :mongoid3\n  - Authorization endpoint does not redirect in #new action anymore. It wasn't specified by OAuth spec\n  - TokensController now inherits from ActionController::Metal. There might be performance upgrades\n  - Add link to authorization in Applications scaffold\n  - [#116] MongoMapper support [@carols10cents](https://github.com/carols10cents)\n  - [#122] Mongoid3 support [@petergoldstein](https://github.com/petergoldstein)\n  - [#150] Introduce test redirect uri for applications\n- bugfixes\n  - [#157] Response token status should be `:ok`, not `:success` [@theycallmeswift](https://github.com/theycallmeswift)\n  - [#159] Remove ActionView::Base.field_error_proc override (fixes #145)\n- internals\n  - Update development dependencies\n  - Several refactorings\n  - Rails/ORM are easily swichable with env vars (rails and orm)\n  - Travis now tests against Mongoid v2\n\n## 0.5.0 - 2012-10-20\n\nOfficial support for rubinius was removed.\n\n- enhancements\n  - Configure the way access token is retrieved from request (default to bearer header)\n  - Authorization Code expiration time is now configurable\n  - Add support for mongoid\n  - [#78, #128, #137, #138] Application Ownership\n  - [#92] Allow users to skip controllers\n  - [#99] Remove deprecated warnings for data-\\* attributes [@towerhe](https://github.com/towerhe)\n  - [#101] Return existing access_token for PasswordAccessTokenRequest [@benoist](https://github.com/benoist)\n  - [#104] Changed access token scopes example code to default_scopes and optional_scopes [@amkirwan](https://github.com/amkirwan)\n  - [#107] Fix typos in initializer\n  - [#123] i18n for validator, flash messages [@petergoldstein](https://github.com/petergoldstein)\n  - [#140] ActiveRecord is the default value for the ORM [@petergoldstein](https://github.com/petergoldstein)\n- internals\n  - [#112, #120] Replacing update_attribute with update_column to eliminate deprecation warnings [@rmoriz](https://github.com/rmoriz), [@petergoldstein](https://github.com/petergoldstein)\n  - [#121] Updating all development dependencies to recent versions. [@petergoldstein](https://github.com/petergoldstein)\n  - [#144] Adding MongoDB dependency to .travis.yml [@petergoldstein](https://github.com/petergoldstein)\n  - [#143] Displays errors for unconfigured error messages [@timgaleckas](https://github.com/timgaleckas)\n- bugfixes\n  - [#102] Not returning 401 when access token generation fails [@cslew](https://github.com/cslew)\n  - [#125] Doorkeeper is using ActiveRecord version of as_json in ORM agnostic code [@petergoldstein](https://github.com/petergoldstein)\n  - [#142] Prevent double submission of password based authentication [@bdurand](https://github.com/bdurand)\n- documentation\n  - [#141] Add rack-cors middleware to readme [@gottfrois](https://github.com/gottfrois)\n\n## 0.4.2 - 2012-06-05\n\n- bugfixes:\n  - [#94] Uninitialized Constant in Password Flow\n\n## 0.4.1 - 2012-06-02\n\n- enhancements:\n  - Backport: Move doorkeeper_for extension to Filter helper\n\n## 0.4.0 - 2012-05-26\n\n- deprecation\n  - Deprecate authorization_scopes\n- database changes\n  - AccessToken#resource_owner_id is not nullable\n- enhancements\n  - [#83] Add Resource Owner Password Credentials flow [@jaimeiniesta](https://github.com/jaimeiniesta)\n  - [#76] Allow token expiration to be disabled [@mattgreen](https://github.com/mattgreen)\n  - [#89] Configure the way client credentials are retrieved from request\n  - [#b6470a] Add Client Credentials flow\n- internals\n  - [#2ece8d, #f93778] Introduce Client and ErrorResponse classes\n\n## 0.3.4 - 2012-05-24\n\n- Fix attr_accessible for rails 3.2.x\n\n## 0.3.3 - 2012-05-07\n\n- [#86] shrink gem package size\n\n## 0.3.2 - 2012-04-29\n\n- enhancements\n  - [#54] Ignore Authorization: headers that are not Bearer [@miyagawa](https://github.com/miyagawa)\n  - [#58, #64] Add destroy action to applications endpoint [@jaimeiniesta](https://github.com/jaimeiniesta), [@davidfrey](https://github.com/davidfrey)\n  - [#63] TokensController responds with `401 unauthorized` [@jaimeiniesta](https://github.com/jaimeiniesta)\n  - [#67, #72] Fix for mass-assignment [@cicloid](https://github.com/cicloid)\n- internals\n  - [#49] Add Gemnasium status image to README [@laserlemon](https://github.com/laserlemon)\n  - [#50] Fix typos [@tomekw](https://github.com/tomekw)\n  - [#51] Updated the factory_girl_rails dependency, fix expires_in response which returned a float number instead of integer [@antekpiechnik](https://github.com/antekpiechnik)\n  - [#62] Typos, .gitignore [@jaimeiniesta](https://github.com/jaimeiniesta)\n  - [#65] Change \\_path redirections to \\_url redirections [@jaimeiniesta](https://github.com/jaimeiniesta)\n  - [#75] Fix unknown method #authenticate_admin! [@mattgreen](https://github.com/mattgreen)\n  - Remove application link in authorized app view\n\n## 0.3.1 - 2012-02-17\n\n- enhancements\n  - [#48] Add if, else options to doorkeeper_for\n  - Add views generator\n- internals\n  - Namespace models\n\n## 0.3.0 - 2012-02-11\n\n- enhancements\n  - [#17, #31] Add support for client credentials in basic auth header [@GoldsteinTechPartners](https://github.com/GoldsteinTechPartners)\n  - [#28] Add indices to migration [@GoldsteinTechPartners](https://github.com/GoldsteinTechPartners)\n  - [#29] Allow doorkeeper to run with rails 3.2 [@john-griffin](https://github.com/john-griffin)\n  - [#30] Improve client's redirect uri validation [@GoldsteinTechPartners](https://github.com/GoldsteinTechPartners)\n  - [#32] Add token (implicit grant) flow [@GoldsteinTechPartners](https://github.com/GoldsteinTechPartners)\n  - [#34] Add support for custom unathorized responses [@GoldsteinTechPartners](https://github.com/GoldsteinTechPartners)\n  - [#36] Remove repetitions from the Authorised Applications view [@carvil](https://github.com/carvil)\n  - When user revoke an application, all tokens for that application are revoked\n  - Error messages now can be translated\n  - Install generator copies the error messages localization file\n- internals\n  - Fix deprecation warnings in ActiveSupport::Base64\n  - Remove deprecation in doorkeeper_for that handles hash arguments\n  - Depends on railties instead of whole rails framework\n  - CI now integrates with rails 3.1 and 3.2\n\n## 0.2.0 - 2011-12-17\n\n- enhancements\n  - [#4] Add authorized applications endpoint\n  - [#5, #11] Add access token scopes\n  - [#10] Add access token expiration by default\n  - [#9, #12] Add refresh token flow\n- internals\n  - [#7] Improve configuration options with :default\n  - Improve configuration options with :builder\n  - Refactor config class\n  - Improve coverage of authorization request integration\n- bug fixes\n  - [#6, #20] Fix access token response headers\n  - Fix issue with state parameter\n- deprecation\n  - deprecate :only and :except options in doorkeeper_for\n\n## 0.1.1 - 2011-11-30\n\n- enhancements\n  - [#3] Authorization code must be short lived and single use\n  - [#2] Improve views provided by doorkeeper\n  - [#1] Skips authorization form if the client has been authorized by the resource owner\n  - Improve readme\n- bugfixes\n  - Fix issue when creating the access token (wrong client id)\n\n## 0.1.0 - 2011-11-25\n\n- Authorization Code flow\n- OAuth applications endpoint\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\r\n\r\n## Our Pledge\r\n\r\nIn the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.\r\n\r\n## Our Standards\r\n\r\nExamples of behavior that contributes to creating a positive environment include:\r\n\r\n* Using welcoming and inclusive language\r\n* Being respectful of differing viewpoints and experiences\r\n* Gracefully accepting constructive criticism\r\n* Focusing on what is best for the community\r\n* Showing empathy towards other community members\r\n\r\nExamples of unacceptable behavior by participants include:\r\n\r\n* The use of sexualized language or imagery and unwelcome sexual attention or advances\r\n* Trolling, insulting/derogatory comments, and personal or political attacks\r\n* Public or private harassment\r\n* Publishing others' private information, such as a physical or electronic address, without explicit permission\r\n* Other conduct which could reasonably be considered inappropriate in a professional setting\r\n\r\n## Our Responsibilities\r\n\r\nProject maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.\r\n\r\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.\r\n\r\n## Scope\r\n\r\nThis Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.\r\n\r\n## Enforcement\r\n\r\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team members or current maintainer email, specified in gemspec. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.\r\n\r\nProject maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.\r\n\r\n## Attribution\r\n\r\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]\r\n\r\n[homepage]: http://contributor-covenant.org\r\n[version]: http://contributor-covenant.org/version/1/4/\r\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nWe love pull requests from everyone. By participating in this project, you agree\nto abide by the [code of conduct](CODE_OF_CONDUCT.md).\n\nFork, then clone the repo:\n\n    git clone git@github.com:your-username/doorkeeper.git\n\n### Docker Setup\n\nBuild the container image with: `docker build --pull -t doorkeeper:test .`\nRun the tests with: `docker run -it --rm doorkeeper:test`\n\n### Local Setup\n\n* Set up Ruby dependencies via Bundler\n\n      bundle install\n\n* Make sure the tests pass:\n\n      rake spec\n\n* Make your changes.\n* Write tests.\n* Follow our [style guides](.rubocop.yml).\n* Make the tests pass:\n\n      rake spec\n\n* Add notes about your changes to the `CHANGELOG.md` file.\n\n* Write a [good commit message][commit].\n* Push to your fork.\n* [Submit a pull request][pr].\n\n[commit]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html\n[pr]: https://github.com/doorkeeper-gem/doorkeeper/compare/\n\n* If [Rubocop] catches style violations, fix them. If our bot suggested changes — please add them.\n\n* Wait for us. We try to at least comment on pull requests within one business day.\n* We may suggest changes.\n* Please, squash your commits to a single one if you introduced a new changes or pushed more than\none commit. Let's keep the history clean.\n\nThank you for your contribution! :handshake:\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM ruby:3.3.4-alpine\n\n# Linux UID (user id) for the doorkeeper user, change with [--build-arg UID=1234]\nARG UID=\"991\"\n# Linux GID (group id) for the doorkeeper user, change with [--build-arg GID=1234]\nARG GID=\"991\"\n# Timezone used by the Docker container and runtime, change with [--build-arg TZ=Europe/Berlin]\nARG TZ=\"Etc/UTC\"\n\n# Apply timezone\nENV TZ=${TZ}\n\nRUN addgroup -g \"${GID}\" doorkeeper; \\\n  adduser -u \"${UID}\" -G \"doorkeeper\" -h /srv doorkeeper; \\\n  echo \"${TZ}\" > /etc/localtime;\n\nRUN apk add --no-cache \\\n  ca-certificates \\\n  wget \\\n  openssl \\ \n  bash \\\n  build-base \\\n  git \\\n  sqlite-dev \\\n  tzdata\n\nENV LANG=en_US.UTF-8\nENV LANGUAGE=en_US:en\nENV LC_ALL=en_US.UTF-8\n\nENV BUNDLER_VERSION=2.5.11\nRUN gem install bundler -v ${BUNDLER_VERSION} -i /usr/local/lib/ruby/gems/$(ls /usr/local/lib/ruby/gems) --force\n\nWORKDIR /srv\n\nCOPY Gemfile doorkeeper.gemspec /srv/\nCOPY lib/doorkeeper/version.rb /srv/lib/doorkeeper/version.rb\n\n# This is a fix for sqlite alpine issues\nRUN bundle config force_ruby_platform true\nRUN bundle install\n\nCOPY . /srv/\n\nRUN chown -R doorkeeper:doorkeeper /srv/coverage /srv/spec/dummy/tmp /srv/spec/generators/tmp\n\n# Set the running user for resulting container\nUSER doorkeeper\n\nCMD [\"rake\"]\n"
  },
  {
    "path": "Gemfile",
    "content": "# frozen_string_literal: true\n\nsource \"https://rubygems.org\"\ngit_source(:github) { |repo| \"https://github.com/#{repo}.git\" }\n\ngemspec\n\ngem \"rails\", \">= 7.0\", \"< 8.1\"\n\ngem \"sprockets-rails\"\n\ngem \"rspec-core\"\ngem \"rspec-expectations\"\ngem \"rspec-mocks\"\ngem \"rspec-rails\", \"~> 8.0\"\ngem \"rspec-support\"\n\ngroup :development, :rubocop do\n  gem \"rubocop\", \"~> 1.72\"\n  gem \"rubocop-capybara\", \"~> 2.22\", require: false\n  gem \"rubocop-factory_bot\", \"~> 2.27\", require: false\n  gem \"rubocop-performance\", \"~> 1.24\", require: false\n  gem \"rubocop-rails\", \"~> 2.30\", require: false\n  gem \"rubocop-rspec\", \"~> 3.5\", require: false\n  gem \"rubocop-rspec_rails\", \"~> 2.31\", require: false\nend\ngem \"bcrypt\", \"~> 3.1\", require: false\n\ngem \"activerecord-jdbcsqlite3-adapter\", platform: :jruby\ngem \"sqlite3\", \"~> 2.3\", platform: [:ruby, :mswin, :mingw, :x64_mingw]\n\ngem \"tzinfo-data\", platforms: %i[mingw mswin x64_mingw]\ngem \"timecop\"\n\ngem 'irb', '~> 1.8'\n\n# Interactive Debugging tools\ngem 'debug', '~> 1.8'\n"
  },
  {
    "path": "MIT-LICENSE",
    "content": "Copyright 2011 Applicake. http://applicake.com\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "NEWS.md",
    "content": "Document moved [here](CHANGELOG.md)\n"
  },
  {
    "path": "README.md",
    "content": "# Doorkeeper — awesome OAuth 2 provider for your Rails / Grape app.\n\n[![Gem Version](https://badge.fury.io/rb/doorkeeper.svg)](https://rubygems.org/gems/doorkeeper)\n[![CI](https://github.com/doorkeeper-gem/doorkeeper/actions/workflows/ci.yml/badge.svg)](https://github.com/doorkeeper-gem/doorkeeper/actions/workflows/ci.yml)\n[![Maintainability](https://qlty.sh/gh/doorkeeper-gem/projects/doorkeeper/maintainability.svg)](https://qlty.sh/gh/doorkeeper-gem/projects/doorkeeper)\n[![Coverage Status](https://coveralls.io/repos/github/doorkeeper-gem/doorkeeper/badge.svg?branch=main)](https://coveralls.io/github/doorkeeper-gem/doorkeeper?branch=main)\n[![GuardRails badge](https://api.guardrails.io/v2/badges/21183?token=66768ce8f6995814df81f65a2cff40f739f688492704f973e62809e15599bb62)](https://dashboard.guardrails.io/gh/doorkeeper-gem/repos/21183)\n[![Dependabot](https://img.shields.io/badge/dependabot-enabled-success.svg)](https://dependabot.com)\n\nDoorkeeper is a gem (Rails engine) that makes it easy to introduce OAuth 2 provider\nfunctionality to your Ruby on Rails or Grape application.\n\nSupported features:\n\n- [The OAuth 2.0 Authorization Framework](https://datatracker.ietf.org/doc/html/rfc6749)\n  - [Authorization Code Flow](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1)\n  - [Access Token Scopes](https://datatracker.ietf.org/doc/html/rfc6749#section-3.3)\n  - [Refresh token](https://datatracker.ietf.org/doc/html/rfc6749#section-1.5)\n  - [Implicit grant](https://datatracker.ietf.org/doc/html/rfc6749#section-4.2)\n  - [Resource Owner Password Credentials](https://datatracker.ietf.org/doc/html/rfc6749#section-4.3)\n  - [Client Credentials](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4)\n- [OAuth 2.0 Token Revocation](https://datatracker.ietf.org/doc/html/rfc7009)\n- [OAuth 2.0 Token Introspection](https://datatracker.ietf.org/doc/html/rfc7662)\n- [OAuth 2.0 Threat Model and Security Considerations](https://datatracker.ietf.org/doc/html/rfc6819)\n- [OAuth 2.0 for Native Apps](https://datatracker.ietf.org/doc/html/rfc8252)\n- [Proof Key for Code Exchange by OAuth Public Clients](https://datatracker.ietf.org/doc/html/rfc7636)\n\n## Table of Contents\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n\n- [Documentation](#documentation)\n- [Installation](#installation)\n  - [Ruby on Rails](#ruby-on-rails)\n  - [Grape](#grape)\n- [ORMs](#orms)\n- [Extensions](#extensions)\n- [Example Applications](#example-applications)\n- [Sponsors](#sponsors)\n- [Development](#development)\n- [Contributing](#contributing)\n- [Contributors](#contributors)\n- [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Documentation\n\nThis documentation is valid for `main` branch. Please check the documentation for the version of doorkeeper you are using in:\nhttps://github.com/doorkeeper-gem/doorkeeper/releases.\n\nAdditionally, other resources can be found on:\n\n- [Guides](https://doorkeeper.gitbook.io/guides/) with how-to get started and configuration documentation\n- See the [Wiki](https://github.com/doorkeeper-gem/doorkeeper/wiki) for articles on how to integrate with other solutions\n- Screencast from [railscasts.com](http://railscasts.com/): [#353\nOAuth with\nDoorkeeper](http://railscasts.com/episodes/353-oauth-with-doorkeeper)\n- See [upgrade guides](https://github.com/doorkeeper-gem/doorkeeper/wiki/Migration-from-old-versions)\n- For general questions, please post on [Stack Overflow](http://stackoverflow.com/questions/tagged/doorkeeper)\n- See [SECURITY.md](SECURITY.md) for this project's security disclose\n  policy\n\n## Installation\n\nInstallation depends on the framework you're using. The first step is to add the following to your Gemfile:\n\n```ruby\ngem 'doorkeeper'\n```\n\nAnd run `bundle install`. After this, check out the guide related to the framework you're using.\n\n### Ruby on Rails\n\nDoorkeeper currently supports Ruby on Rails >= 5.0. See the guide [here](https://doorkeeper.gitbook.io/guides/ruby-on-rails/getting-started).\n\n### Grape\n\nGuide for integration with Grape framework can be found [here](https://doorkeeper.gitbook.io/guides/grape/grape).\n\n## ORMs\n\nDoorkeeper supports Active Record by default, but can be configured to work with the following ORMs:\n\n| ORM | Support via |\n| :--- | :--- |\n| Active Record | by default |\n| MongoDB | [doorkeeper-gem/doorkeeper-mongodb](https://github.com/doorkeeper-gem/doorkeeper-mongodb) |\n| Sequel | [nbulaj/doorkeeper-sequel](https://github.com/nbulaj/doorkeeper-sequel) |\n| Couchbase | [acaprojects/doorkeeper-couchbase](https://github.com/acaprojects/doorkeeper-couchbase) |\n| RethinkDB | [aca-labs/doorkeeper-rethinkdb](https://github.com/aca-labs/doorkeeper-rethinkdb) |\n\n## Extensions\n\nExtensions that are not included by default and can be installed separately.\n\n|  | Link |\n| :--- | :--- |\n| OpenID Connect extension | [doorkeeper-gem/doorkeeper-openid\\_connect](https://github.com/doorkeeper-gem/doorkeeper-openid_connect) |\n| JWT Token support | [doorkeeper-gem/doorkeeper-jwt](https://github.com/doorkeeper-gem/doorkeeper-jwt) |\n| Assertion grant extension | [doorkeeper-gem/doorkeeper-grants\\_assertion](https://github.com/doorkeeper-gem/doorkeeper-grants_assertion) |\n| I18n translations | [doorkeeper-gem/doorkeeper-i18n](https://github.com/doorkeeper-gem/doorkeeper-i18n) |\n| CIBA - Client Initiated Backchannel Authentication Flow extension | [doorkeeper-ciba](https://github.com/autoseg/doorkeeper-ciba) |\n| Device Authorization Grant | [doorkeeper-device_authorization_grant](https://github.com/exop-group/doorkeeper-device_authorization_grant) |\n\n## Example Applications\n\nThese applications show how Doorkeeper works and how to integrate with it. Start with the oAuth2 server and use the clients to connect with the server.\n\n| Application | Link |\n| :--- | :--- |\n| OAuth2 Server with Doorkeeper | [doorkeeper-gem/doorkeeper-provider-app](https://github.com/doorkeeper-gem/doorkeeper-provider-app) |\n| Sinatra Client connected to Provider App | [doorkeeper-gem/doorkeeper-sinatra-client](https://github.com/doorkeeper-gem/doorkeeper-sinatra-client) |\n| Devise + Omniauth Client | [doorkeeper-gem/doorkeeper-devise-client](https://github.com/doorkeeper-gem/doorkeeper-devise-client) |\n\nYou may want to create a client application to\ntest the integration. Check out these [client\nexamples](https://github.com/doorkeeper-gem/doorkeeper/wiki/Example-Applications)\nin our wiki or follow this [tutorial\nhere](https://github.com/doorkeeper-gem/doorkeeper/wiki/Testing-your-provider-with-OAuth2-gem).\n\n## Sponsors\n\n[![OpenCollective](https://opencollective.com/doorkeeper-gem/backers/badge.svg)](#backers) \n[![OpenCollective](https://opencollective.com/doorkeeper-gem/sponsors/badge.svg)](#sponsors)\n\nSupport this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/doorkeeper-gem#sponsor)]\n\n<a href=\"https://codecademy.com/about/careers?utm_source=doorkeeper-gem\" target=\"_blank\"><img src=\"https://static-assets.codecademy.com/marketing/codecademy_logo_padded.png\"/></a>\n\n> Codecademy supports open source as part of its mission to democratize tech. Come help us build the education the world deserves: [https://codecademy.com/about/careers](https://codecademy.com/about/careers?utm_source=doorkeeper-gem)\n\n<br>\n\n<a href=\"https://oauth.io/?utm_source=doorkeeper-gem\" target=\"_blank\"><img src=\"https://oauth.io/img/logo_text.png\"/></a>\n\n> If you prefer not to deal with the gory details of OAuth 2, need dedicated customer support & consulting, try the cloud-based SaaS version: [https://oauth.io](https://oauth.io/?utm_source=doorkeeper-gem)\n\n<br>\n\n<a href=\"https://www.wealthsimple.com/?utm_source=doorkeeper-gem\" target=\"_blank\"><img src=\"https://wealthsimple.s3.amazonaws.com/branding/medium-black.svg\"/></a>\n\n> Wealthsimple is a financial company on a mission to help everyone achieve financial freedom by providing products and advice that are accessible and affordable. Using smart technology, Wealthsimple takes financial services that are often confusing, opaque and expensive and makes them simple, transparent, and low-cost. See what Investing on Autopilot is all about: [https://www.wealthsimple.com](https://www.wealthsimple.com/?utm_source=doorkeeper-gem)\n\n## Development\n\nTo run the local engine server:\n\n```\nbundle install\nbundle exec rake doorkeeper:server\n````\n\nBy default, it uses the latest Rails version with ActiveRecord. To run the\ntests with a specific Rails version:\n\n```\nBUNDLE_GEMFILE=gemfiles/rails_6_0.gemfile bundle exec rake\n```\n\nYou can also experiment with the changes using `bin/console`. It uses in-memory SQLite database and default\nDoorkeeper config, but you can reestablish connection or reconfigure the gem if you need.\n\n## Contributing\n\nWant to contribute and don't know where to start? Check out [features we're\nmissing](https://github.com/doorkeeper-gem/doorkeeper/wiki/Supported-Features),\ncreate [example\napps](https://github.com/doorkeeper-gem/doorkeeper/wiki/Example-Applications),\nintegrate the gem with your app and let us know!\n\nAlso, check out our [contributing guidelines page](CONTRIBUTING.md).\n\n## Contributors\n\nThanks to all our [awesome\ncontributors](https://github.com/doorkeeper-gem/doorkeeper/graphs/contributors)!\n\n<a href=\"https://github.com/doorkeeper-gem/doorkeeper/graphs/contributors\"><img src=\"https://opencollective.com/doorkeeper-gem/contributors.svg?width=890&button=false\" /></a>\n\n## License\n\nMIT License. Created in Applicake. Maintained by the community.\n"
  },
  {
    "path": "RELEASING.md",
    "content": "# Releasing Doorkeeper\n\nHow to release Doorkeeper in five easy steps!\n\n1. Update `lib/doorkeeper/version.rb` file accordingly.\n2. Update `CHANGELOG.md` to reflect the changes since last release.\n3. Commit changes: `git commit -am 'Bump to vVERSION'`.\n4. Build and publish the gem.\n4. Create GitHub release.\n5. Announce the new release, making sure to say “thank you” to the contributors\n   who helped shape this version!\n"
  },
  {
    "path": "Rakefile",
    "content": "# frozen_string_literal: true\n\nrequire \"bundler/setup\"\nrequire \"rspec/core/rake_task\"\n\ndesc \"Default: run specs.\"\ntask default: :spec\n\ndesc \"Run all specs\"\nRSpec::Core::RakeTask.new(:spec) do |config|\n  config.verbose = false\nend\n\nnamespace :doorkeeper do\n  desc \"Install doorkeeper in dummy app\"\n  task :install do\n    cd \"spec/dummy\"\n    system \"bundle exec rails g doorkeeper:install --force\"\n  end\n\n  desc \"Runs local test server\"\n  task :server do\n    cd \"spec/dummy\"\n    system \"bundle exec rails server\"\n  end\nend\n\nBundler::GemHelper.install_tasks\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Reporting security issues in Doorkeeper\n\nHello! Thank you for wanting to disclose a possible security\nvulnerability within the Doorkeeper gem! Please follow our disclosure\npolicy as outlined below:\n\n1. Do NOT open up a GitHub issue with your report. Security reports\n   should be kept private until a possible fix is determined.\n2. Send an email to Nikita Bulai at bulaj.nikita AT gmail.com or one of\n   the others Doorkeeper maintainers listed in gemspec. You should receive\n   a prompt response.\n3. Be patient. Since Doorkeeper is in a stable maintenance phase, we want to\n   do as little as possible to rock the boat of the project.\n\nThank you very much for adhering for these policies!\n"
  },
  {
    "path": "UPGRADE.md",
    "content": "See [Upgrade Guides](https://github.com/doorkeeper-gem/doorkeeper/wiki/Migration-from-old-versions)\nin the project Wiki.\n"
  },
  {
    "path": "app/assets/stylesheets/doorkeeper/admin/application.css",
    "content": "/*\n *= require doorkeeper/bootstrap.min\n *\n *= require_self\n *= require_tree .\n*/\n\n.doorkeeper-admin .form-group > .field_with_errors {\n  width: 16.66667%;\n}\n"
  },
  {
    "path": "app/assets/stylesheets/doorkeeper/application.css",
    "content": "/*\n *= require doorkeeper/bootstrap.min\n *\n *= require_self\n *= require_tree .\n*/\n\nbody {\n    background-color: #eee;\n    font-size: 14px;\n}\n\n#container {\n    background-color: #fff;\n    border: 1px solid #999;\n    border: 1px solid rgba(0, 0, 0, 0.2);\n    border-radius: 6px;\n    -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n    box-shadow: 0 3px 20px rgba(0, 0, 0, 0.3);\n    margin: 2em auto;\n    max-width: 600px;\n    outline: 0;\n    padding: 1em;\n    width: 80%;\n}\n\n.page-header {\n    margin-top: 20px;\n}\n\n#oauth-permissions {\n    width: 260px;\n}\n\n.actions {\n    border-top: 1px solid #eee;\n    margin-top: 1em;\n    padding-top: 9px;\n}\n\n.actions > form > .btn {\n    margin-top: 5px;\n}\n\n.separator {\n    color: #eee;\n    padding: 0 .5em;\n}\n\n.inline_block {\n    display: inline-block;\n}\n\n#oauth {\n    margin-bottom: 1em;\n}\n\n#oauth > .btn {\n    width: 7em;\n}\n\ntd {\n    vertical-align: middle !important;\n}\n"
  },
  {
    "path": "app/controllers/doorkeeper/application_controller.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  class ApplicationController <\n    Doorkeeper.config.resolve_controller(:base)\n    include Helpers::Controller\n    include ActionController::MimeResponds if Doorkeeper.config.api_only\n\n    unless Doorkeeper.config.api_only\n      protect_from_forgery with: :exception\n      helper \"doorkeeper/dashboard\"\n    end\n  end\nend\n"
  },
  {
    "path": "app/controllers/doorkeeper/application_metal_controller.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  class ApplicationMetalController <\n    Doorkeeper.config.resolve_controller(:base_metal)\n    include Helpers::Controller\n\n    before_action :enforce_content_type,\n                  if: -> { Doorkeeper.config.enforce_content_type }\n\n    ActiveSupport.run_load_hooks(:doorkeeper_metal_controller, self)\n  end\nend\n"
  },
  {
    "path": "app/controllers/doorkeeper/applications_controller.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  class ApplicationsController < Doorkeeper::ApplicationController\n    layout \"doorkeeper/admin\" unless Doorkeeper.configuration.api_only\n\n    before_action :authenticate_admin!\n    before_action :set_application, only: %i[show edit update destroy]\n\n    def index\n      @applications = Doorkeeper.config.application_model.ordered_by(:created_at)\n\n      respond_to do |format|\n        format.html\n        format.json { head :no_content }\n      end\n    end\n\n    def show\n      respond_to do |format|\n        format.html\n        format.json { render json: @application, as_owner: true }\n      end\n    end\n\n    def new\n      @application = Doorkeeper.config.application_model.new\n    end\n\n    def create\n      @application = Doorkeeper.config.application_model.new(application_params)\n\n      if @application.save\n        flash[:notice] = I18n.t(:notice, scope: %i[doorkeeper flash applications create])\n        flash[:application_secret] = @application.plaintext_secret\n\n        respond_to do |format|\n          format.html { redirect_to oauth_application_url(@application) }\n          format.json { render json: @application, as_owner: true }\n        end\n      else\n        respond_to do |format|\n          format.html { render :new }\n          format.json do\n            errors = @application.errors.full_messages\n\n            render json: { errors: errors }, status: :unprocessable_entity\n          end\n        end\n      end\n    end\n\n    def edit; end\n\n    def update\n      if @application.update(application_params)\n        flash[:notice] = I18n.t(:notice, scope: i18n_scope(:update))\n\n        respond_to do |format|\n          format.html { redirect_to oauth_application_url(@application) }\n          format.json { render json: @application, as_owner: true }\n        end\n      else\n        respond_to do |format|\n          format.html { render :edit }\n          format.json do\n            errors = @application.errors.full_messages\n\n            render json: { errors: errors }, status: :unprocessable_entity\n          end\n        end\n      end\n    end\n\n    def destroy\n      flash[:notice] = I18n.t(:notice, scope: i18n_scope(:destroy)) if @application.destroy\n\n      respond_to do |format|\n        format.html { redirect_to oauth_applications_url }\n        format.json { head :no_content }\n      end\n    end\n\n    private\n\n    def set_application\n      @application = Doorkeeper.config.application_model.find(params[:id])\n    end\n\n    def application_params\n      params.require(:doorkeeper_application)\n        .permit(:name, :redirect_uri, :scopes, :confidential)\n    end\n\n    def i18n_scope(action)\n      %i[doorkeeper flash applications] << action\n    end\n  end\nend\n"
  },
  {
    "path": "app/controllers/doorkeeper/authorizations_controller.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  class AuthorizationsController < Doorkeeper::ApplicationController\n    before_action :authenticate_resource_owner!\n\n    def new\n      if pre_auth.authorizable?\n        render_success\n      else\n        render_error\n      end\n    end\n\n    def create\n      redirect_or_render(authorize_response)\n    end\n\n    def destroy\n      redirect_or_render(authorization.deny)\n    rescue Doorkeeper::Errors::InvalidTokenStrategy => e\n      error_response = get_error_response_from_exception(e)\n\n      if Doorkeeper.configuration.api_only\n        render json: error_response.body, status: :bad_request\n      else\n        render :error, locals: { error_response: error_response }\n      end\n    end\n\n    private\n\n    def render_success\n      if skip_authorization? || can_authorize_response?\n        redirect_or_render(authorize_response)\n      elsif Doorkeeper.configuration.api_only\n        render json: pre_auth\n      else\n        render :new\n      end\n    end\n\n    def render_error\n      pre_auth.error_response.raise_exception! if Doorkeeper.config.raise_on_errors?\n\n      if Doorkeeper.configuration.redirect_on_errors? && pre_auth.error_response.redirectable?\n        redirect_or_render(pre_auth.error_response)\n      elsif Doorkeeper.configuration.api_only\n        render json: pre_auth.error_response.body, status: pre_auth.error_response.status\n      else\n        render :error, locals: { error_response: pre_auth.error_response }, status: pre_auth.error_response.status\n      end\n    end\n\n    def can_authorize_response?\n      Doorkeeper.config.custom_access_token_attributes.empty? && pre_auth.client.application.confidential? && matching_token?\n    end\n\n    # Active access token issued for the same client and resource owner with\n    # the same set of the scopes exists?\n    def matching_token?\n      # We don't match tokens on the custom attributes here - we're in the pre-auth here,\n      # so they haven't been supplied yet (there are no custom attributes to match on yet)\n      @matching_token ||= Doorkeeper.config.access_token_model.matching_token_for(\n        pre_auth.client,\n        current_resource_owner,\n        pre_auth.scopes,\n      )\n    end\n\n    def redirect_or_render(auth)\n      if auth.redirectable?\n        if Doorkeeper.configuration.api_only\n          if pre_auth.form_post_response?\n            render(\n              json: { status: :post, redirect_uri: pre_auth.redirect_uri, body: auth.body },\n              status: auth.status,\n            )\n          else\n            render(\n              json: { status: :redirect, redirect_uri: auth.redirect_uri },\n              status: auth.status,\n            )\n          end\n        elsif pre_auth.form_post_response?\n          render :form_post, locals: { auth: auth }\n        else\n          redirect_to auth.redirect_uri, allow_other_host: true\n        end\n      else\n        render json: auth.body, status: auth.status\n      end\n    end\n\n    def pre_auth\n      @pre_auth ||= OAuth::PreAuthorization.new(\n        Doorkeeper.configuration,\n        pre_auth_params,\n        current_resource_owner,\n      )\n    end\n\n    def pre_auth_params\n      params.slice(*pre_auth_param_fields).permit(*pre_auth_param_fields)\n    end\n\n    def pre_auth_param_fields\n      custom_access_token_attributes + %i[\n        client_id\n        code_challenge\n        code_challenge_method\n        response_type\n        response_mode\n        redirect_uri\n        scope\n        state\n      ]\n    end\n\n    def custom_access_token_attributes\n      Doorkeeper.config.custom_access_token_attributes.map(&:to_sym)\n    end\n\n    def authorization\n      @authorization ||= strategy.request\n    end\n\n    def strategy\n      @strategy ||= server.authorization_request(pre_auth.response_type)\n    end\n\n    def authorize_response\n      @authorize_response ||= begin\n        unless pre_auth.authorizable?\n          response = pre_auth.error_response\n          response.raise_exception! if Doorkeeper.config.raise_on_errors?\n          return response\n        end\n\n        context = build_context(pre_auth: pre_auth)\n        before_successful_authorization(context)\n\n        auth = strategy.authorize\n\n        context = build_context(auth: auth)\n        after_successful_authorization(context)\n\n        auth\n      end\n    end\n\n    def build_context(**attributes)\n      Doorkeeper::OAuth::Hooks::Context.new(**attributes)\n    end\n\n    def before_successful_authorization(context = nil)\n      Doorkeeper.config.before_successful_authorization.call(self, context)\n    end\n\n    def after_successful_authorization(context)\n      Doorkeeper.config.after_successful_authorization.call(self, context)\n    end\n  end\nend\n"
  },
  {
    "path": "app/controllers/doorkeeper/authorized_applications_controller.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  class AuthorizedApplicationsController < Doorkeeper::ApplicationController\n    before_action :authenticate_resource_owner!\n\n    def index\n      @applications = Doorkeeper.config.application_model.authorized_for(current_resource_owner)\n\n      respond_to do |format|\n        format.html\n        format.json { render json: @applications, current_resource_owner: current_resource_owner }\n      end\n    end\n\n    def destroy\n      Doorkeeper.config.application_model.revoke_tokens_and_grants_for(\n        params[:id],\n        current_resource_owner,\n      )\n\n      respond_to do |format|\n        format.html do\n          redirect_to oauth_authorized_applications_url, notice: I18n.t(\n            :notice, scope: %i[doorkeeper flash authorized_applications destroy],\n          )\n        end\n\n        format.json { head :no_content }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/controllers/doorkeeper/token_info_controller.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  class TokenInfoController < Doorkeeper::ApplicationMetalController\n    def show\n      if doorkeeper_token&.accessible?\n        render json: doorkeeper_token_to_json, status: :ok\n      else\n        error = OAuth::InvalidTokenResponse.new\n        response.headers.merge!(error.headers)\n        render json: error_to_json(error), status: error.status\n      end\n    end\n\n    protected\n\n    def doorkeeper_token_to_json\n      doorkeeper_token\n    end\n\n    def error_to_json(error)\n      error.body\n    end\n  end\nend\n"
  },
  {
    "path": "app/controllers/doorkeeper/tokens_controller.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  class TokensController < Doorkeeper::ApplicationMetalController\n    before_action :validate_presence_of_client, only: [:revoke]\n\n    rescue_from Errors::DoorkeeperError do |e|\n      handle_token_exception(e)\n    end\n\n    def create\n      headers.merge!(authorize_response.headers)\n      render json: authorize_response.body,\n             status: authorize_response.status\n    end\n\n    # OAuth 2.0 Token Revocation - https://datatracker.ietf.org/doc/html/rfc7009\n    def revoke\n      # The authorization server responds with HTTP status code 200 if the client\n      # submitted an invalid token or the token has been revoked successfully.\n      if token.blank?\n        render json: {}, status: 200\n      # The authorization server validates [...] and whether the token\n      # was issued to the client making the revocation request. If this\n      # validation fails, the request is refused and the client is informed\n      # of the error by the authorization server as described below.\n      elsif authorized?\n        revoke_token\n        render json: {}, status: 200\n      else\n        render json: revocation_error_response, status: :forbidden\n      end\n    end\n\n    # OAuth 2.0 Token Introspection - https://datatracker.ietf.org/doc/html/rfc7662\n    def introspect\n      introspection = OAuth::TokenIntrospection.new(server, token)\n\n      if introspection.authorized?\n        render json: introspection.to_json, status: 200\n      else\n        error = introspection.error_response\n        headers.merge!(error.headers)\n        render json: error.body, status: error.status\n      end\n    end\n\n    private\n\n    def validate_presence_of_client\n      return if Doorkeeper.config.skip_client_authentication_for_password_grant\n\n      # @see 2.1.  Revocation Request\n      #\n      #  The client constructs the request by including the following\n      #  parameters using the \"application/x-www-form-urlencoded\" format in\n      #  the HTTP request entity-body:\n      #     token   REQUIRED.\n      #     token_type_hint  OPTIONAL.\n      #\n      #  The client also includes its authentication credentials as described\n      #  in Section 2.3. of [RFC6749].\n      #\n      #  The authorization server first validates the client credentials (in\n      #  case of a confidential client) and then verifies whether the token\n      #  was issued to the client making the revocation request.\n      return if server.client\n\n      # If this validation [client credentials / token ownership] fails, the request is\n      # refused and the  client is informed of the error by the authorization server as\n      # described below.\n      #\n      # @see 2.2.1.  Error Response\n      #\n      # The error presentation conforms to the definition in Section 5.2 of [RFC6749].\n      render json: revocation_error_response, status: :forbidden\n    end\n\n    # OAuth 2.0 Section 2.1 defines two client types, \"public\" & \"confidential\".\n    #\n    # RFC7009\n    # Section 5. Security Considerations\n    # A malicious client may attempt to guess valid tokens on this endpoint\n    # by making revocation requests against potential token strings.\n    # According to this specification, a client's request must contain a\n    # valid client_id, in the case of a public client, or valid client\n    # credentials, in the case of a confidential client. The token being\n    # revoked must also belong to the requesting client.\n    #\n    # Once a client is authenticated, it must be authorized to\n    # revoke the provided access or refresh token. This ensures one client\n    # cannot revoke another's tokens.\n    #\n    # Doorkeeper checks token ownership for any token that has an\n    # application_id set. Tokens issued without a client (application_id\n    # is null) can be revoked without client authorization.\n    #\n    # https://datatracker.ietf.org/doc/html/rfc6749#section-2.1\n    # https://datatracker.ietf.org/doc/html/rfc7009\n    def authorized?\n      # Token belongs to specific client, so we need to check if\n      # authenticated client could access it.\n      if token.application_id?\n        # We authorize client by comparing client and token application IDs\n        server.client && server.client.id == token.application_id\n      else\n        # Token was issued without client, authorization unnecessary\n        true\n      end\n    end\n\n    def revoke_token\n      # The authorization server responds with HTTP status code 200 if the token\n      # has been revoked successfully or if the client submitted an invalid\n      # token\n      revocable_token.revoke if revocable_token.revocable?\n    end\n\n    def token\n      revocable_token&.token\n    end\n\n    def revocable_token\n      return @revocable_token if defined? @revocable_token\n\n      @revocable_token =\n        if params[:token_type_hint] == \"refresh_token\"\n          refresh_token\n        else\n          access_token || refresh_token\n        end\n    end\n\n    def refresh_token\n      token = Doorkeeper.config.access_token_model.by_refresh_token(params[\"token\"])\n      return unless token\n\n      RevocableTokens::RevocableRefreshToken.new(token)\n    end\n\n    def access_token\n      token = Doorkeeper.config.access_token_model.by_token(params[\"token\"])\n      return unless token\n\n      RevocableTokens::RevocableAccessToken.new(token)\n    end\n\n    def strategy\n      @strategy ||= server.token_request(params[:grant_type])\n    end\n\n    def authorize_response\n      @authorize_response ||= begin\n        before_successful_authorization\n        auth = strategy.authorize\n        context = build_context(auth: auth)\n        after_successful_authorization(context) unless auth.is_a?(Doorkeeper::OAuth::ErrorResponse)\n        auth\n      end\n    end\n\n    def build_context(**attributes)\n      Doorkeeper::OAuth::Hooks::Context.new(**attributes)\n    end\n\n    def before_successful_authorization(context = nil)\n      Doorkeeper.config.before_successful_authorization.call(self, context)\n    end\n\n    def after_successful_authorization(context)\n      Doorkeeper.config.after_successful_authorization.call(self, context)\n    end\n\n    def revocation_error_response\n      error_description = I18n.t(:unauthorized, scope: %i[doorkeeper errors messages revoke])\n\n      { error: :unauthorized_client, error_description: error_description }\n    end\n  end\nend\n"
  },
  {
    "path": "app/helpers/doorkeeper/dashboard_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module DashboardHelper\n    def doorkeeper_errors_for(object, method)\n      return if object.errors[method].blank?\n\n      output = object.errors[method].map do |msg|\n        content_tag(:span, class: \"invalid-feedback\") do\n          msg.capitalize\n        end\n      end\n\n      safe_join(output)\n    end\n\n    def doorkeeper_submit_path(application)\n      application.persisted? ? oauth_application_path(application) : oauth_applications_path\n    end\n  end\nend\n"
  },
  {
    "path": "app/views/doorkeeper/applications/_delete_form.html.erb",
    "content": "<%- submit_btn_css ||= 'btn btn-link' %>\n<%= form_tag oauth_application_path(application), method: :delete do %>\n  <%= submit_tag t('doorkeeper.applications.buttons.destroy'),\n                 onclick: \"return confirm('#{ t('doorkeeper.applications.confirmations.destroy') }')\",\n                 class: submit_btn_css %>\n<% end %>\n"
  },
  {
    "path": "app/views/doorkeeper/applications/_form.html.erb",
    "content": "<%= form_for application, url: doorkeeper_submit_path(application), as: :doorkeeper_application, html: { role: 'form' } do |f| %>\n  <% if application.errors.any? %>\n    <div class=\"alert alert-danger\" data-alert><p><%= t('doorkeeper.applications.form.error') %></p></div>\n  <% end %>\n\n  <div class=\"form-group row\">\n    <%= f.label :name, class: 'col-sm-2 col-form-label font-weight-bold' %>\n    <div class=\"col-sm-10\">\n      <%= f.text_field :name, class: \"form-control #{ 'is-invalid' if application.errors[:name].present? }\", required: true %>\n      <%= doorkeeper_errors_for application, :name %>\n    </div>\n  </div>\n\n  <div class=\"form-group row\">\n    <%= f.label :redirect_uri, class: 'col-sm-2 col-form-label font-weight-bold' %>\n    <div class=\"col-sm-10\">\n      <%= f.text_area :redirect_uri, class: \"form-control #{ 'is-invalid' if application.errors[:redirect_uri].present? }\" %>\n      <%= doorkeeper_errors_for application, :redirect_uri %>\n      <span class=\"form-text text-secondary\">\n        <%= t('doorkeeper.applications.help.redirect_uri') %>\n      </span>\n\n      <% if Doorkeeper.configuration.allow_blank_redirect_uri?(application) %>\n        <span class=\"form-text text-secondary\">\n          <%= t('doorkeeper.applications.help.blank_redirect_uri') %>\n        </span>\n      <% end %>\n    </div>\n  </div>\n\n  <div class=\"form-group row\">\n    <%= f.label :confidential, class: 'col-sm-2 form-check-label font-weight-bold' %>\n    <div class=\"col-sm-10\">\n      <%= f.check_box :confidential, class: \"checkbox #{ 'is-invalid' if application.errors[:confidential].present? }\" %>\n      <%= doorkeeper_errors_for application, :confidential %>\n      <span class=\"form-text text-secondary\">\n        <%= t('doorkeeper.applications.help.confidential') %>\n      </span>\n    </div>\n  </div>\n\n  <div class=\"form-group row\">\n    <%= f.label :scopes, class: 'col-sm-2 col-form-label font-weight-bold' %>\n    <div class=\"col-sm-10\">\n      <%= f.text_field :scopes, class: \"form-control #{ 'has-error' if application.errors[:scopes].present? }\" %>\n      <%= doorkeeper_errors_for application, :scopes %>\n      <span class=\"form-text text-secondary\">\n        <%= t('doorkeeper.applications.help.scopes') %>\n      </span>\n    </div>\n  </div>\n\n  <div class=\"form-group\">\n    <div class=\"col-sm-offset-2 col-sm-10\">\n      <%= f.submit t('doorkeeper.applications.buttons.submit'), class: 'btn btn-primary' %>\n      <%= link_to t('doorkeeper.applications.buttons.cancel'), oauth_applications_path, class: 'btn btn-secondary' %>\n    </div>\n  </div>\n<% end %>\n"
  },
  {
    "path": "app/views/doorkeeper/applications/edit.html.erb",
    "content": "<div class=\"border-bottom mb-4\">\n  <h1><%= t('.title') %></h1>\n</div>\n\n<%= render 'form', application: @application %>\n"
  },
  {
    "path": "app/views/doorkeeper/applications/index.html.erb",
    "content": "<div class=\"border-bottom mb-4\">\n  <h1><%= t('.title') %></h1>\n</div>\n\n<p><%= link_to t('.new'), new_oauth_application_path, class: 'btn btn-success' %></p>\n\n<table class=\"table table-striped\">\n  <thead>\n  <tr>\n    <th><%= t('.name') %></th>\n    <th><%= t('.callback_url') %></th>\n    <th><%= t('.confidential') %></th>\n    <th><%= t('.actions') %></th>\n    <th></th>\n  </tr>\n  </thead>\n  <tbody>\n  <% @applications.each do |application| %>\n    <tr id=\"application_<%= application.id %>\">\n      <td class=\"align-middle\">\n        <%= link_to application.name, oauth_application_path(application) %>\n      </td>\n      <td class=\"align-middle\">\n        <%= simple_format(application.redirect_uri) %>\n      </td>\n      <td class=\"align-middle\">\n        <%= application.confidential? ? t('doorkeeper.applications.index.confidentiality.yes') : t('doorkeeper.applications.index.confidentiality.no') %>\n      </td>\n      <td class=\"align-middle\">\n        <%= link_to t('doorkeeper.applications.buttons.edit'), edit_oauth_application_path(application), class: 'btn btn-link' %>\n      </td>\n      <td class=\"align-middle\">\n        <%= render 'delete_form', application: application %>\n      </td>\n    </tr>\n  <% end %>\n  </tbody>\n</table>\n"
  },
  {
    "path": "app/views/doorkeeper/applications/new.html.erb",
    "content": "<div class=\"border-bottom mb-4\">\n  <h1><%= t('.title') %></h1>\n</div>\n\n<%= render 'form', application: @application %>\n"
  },
  {
    "path": "app/views/doorkeeper/applications/show.html.erb",
    "content": "<div class=\"border-bottom mb-4\">\n  <h1><%= t('.title', name: @application.name) %></h1>\n</div>\n\n<div class=\"row\">\n  <div class=\"col-md-8\">\n    <h4><%= t('.application_id') %></h4>\n    <p><code class=\"bg-light\" id=\"application_id\"><%= @application.uid %></code></p>\n\n    <h4><%= t('.secret') %></h4>\n    <p>\n      <code class=\"bg-light\" id=\"secret\">\n        <% secret = flash[:application_secret].presence || @application.plaintext_secret %>\n        <% if secret.blank? && Doorkeeper.config.application_secret_hashed? %>\n          <span class=\"bg-light font-italic text-uppercase text-muted\"><%= t('.secret_hashed') %></span>\n        <% else %>\n          <%= secret %>\n        <% end %>\n      </code>\n    </p>\n\n    <h4><%= t('.scopes') %></h4>\n    <p>\n      <code class=\"bg-light\" id=\"scopes\">\n        <% if @application.scopes.present? %>\n          <%= @application.scopes %>\n        <% else %>\n          <span class=\"bg-light font-italic text-uppercase text-muted\"><%= t('.not_defined') %></span>\n        <% end %>\n      </code>\n    </p>\n\n    <h4><%= t('.confidential') %></h4>\n    <p><code class=\"bg-light\" id=\"confidential\"><%= @application.confidential? %></code></p>\n\n    <h4><%= t('.callback_urls') %></h4>\n\n    <% if @application.redirect_uri.present? %>\n      <table>\n        <% @application.redirect_uri.split.each do |uri| %>\n          <tr>\n            <td>\n              <code class=\"bg-light\"><%= uri %></code>\n            </td>\n            <td>\n              <%= link_to t('doorkeeper.applications.buttons.authorize'), oauth_authorization_path(client_id: @application.uid, redirect_uri: uri, response_type: 'code', scope: @application.scopes), class: 'btn btn-success', target: '_blank' %>\n            </td>\n          </tr>\n        <% end %>\n      </table>\n    <% else %>\n      <span class=\"bg-light font-italic text-uppercase text-muted\"><%= t('.not_defined') %></span>\n    <% end %>\n  </div>\n\n  <div class=\"col-md-4\">\n    <h3><%= t('.actions') %></h3>\n\n    <p><%= link_to t('doorkeeper.applications.buttons.edit'), edit_oauth_application_path(@application), class: 'btn btn-primary' %></p>\n\n    <p><%= render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger' %></p>\n  </div>\n</div>\n"
  },
  {
    "path": "app/views/doorkeeper/authorizations/error.html.erb",
    "content": "<div class=\"border-bottom mb-4\">\n  <h1><%= t('doorkeeper.authorizations.error.title') %></h1>\n</div>\n\n<main role=\"main\">\n  <pre>\n    <%= (local_assigns[:error_response] ? error_response : @pre_auth.error_response).body[:error_description] %>\n  </pre>\n</main>\n"
  },
  {
    "path": "app/views/doorkeeper/authorizations/form_post.html.erb",
    "content": "<header class=\"page-header\">\n  <h1><%= t('.title') %></h1>\n</header>\n\n<%= form_tag @pre_auth.redirect_uri, method: :post, name: :redirect_form, authenticity_token: false do %>\n  <% auth.body.compact.each do |key, value| %>\n    <%= hidden_field_tag key, value %>\n  <% end %>\n<% end %>\n\n<script>\n  window.onload = function () {\n    document.forms['redirect_form'].submit();\n  };\n</script>\n"
  },
  {
    "path": "app/views/doorkeeper/authorizations/new.html.erb",
    "content": "<header class=\"page-header\" role=\"banner\">\n  <h1><%= t('.title') %></h1>\n</header>\n\n<main role=\"main\">\n  <p class=\"h4\">\n    <%= raw t('.prompt', client_name: content_tag(:strong, class: 'text-info') { @pre_auth.client.name }) %>\n  </p>\n\n  <% if @pre_auth.scopes.count > 0 %>\n    <div id=\"oauth-permissions\">\n      <p><%= t('.able_to') %></p>\n\n      <ul class=\"text-info\">\n        <% @pre_auth.scopes.each do |scope| %>\n          <li><%= t scope, scope: [:doorkeeper, :scopes] %></li>\n        <% end %>\n      </ul>\n    </div>\n  <% end %>\n\n  <div class=\"actions\">\n    <%= form_tag oauth_authorization_path, method: :post do %>\n      <%= hidden_field_tag :client_id, @pre_auth.client.uid, id: nil %>\n      <%= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri, id: nil %>\n      <%= hidden_field_tag :state, @pre_auth.state, id: nil %>\n      <%= hidden_field_tag :response_type, @pre_auth.response_type, id: nil %>\n      <%= hidden_field_tag :response_mode, @pre_auth.response_mode, id: nil %>\n      <%= hidden_field_tag :scope, @pre_auth.scope, id: nil %>\n      <%= hidden_field_tag :code_challenge, @pre_auth.code_challenge, id: nil %>\n      <%= hidden_field_tag :code_challenge_method, @pre_auth.code_challenge_method, id: nil %>\n      <%= submit_tag t('doorkeeper.authorizations.buttons.authorize'), class: \"btn btn-success btn-lg btn-block\" %>\n    <% end %>\n    <%= form_tag oauth_authorization_path, method: :delete do %>\n      <%= hidden_field_tag :client_id, @pre_auth.client.uid, id: nil %>\n      <%= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri, id: nil %>\n      <%= hidden_field_tag :state, @pre_auth.state, id: nil %>\n      <%= hidden_field_tag :response_type, @pre_auth.response_type, id: nil %>\n      <%= hidden_field_tag :response_mode, @pre_auth.response_mode, id: nil %>\n      <%= hidden_field_tag :scope, @pre_auth.scope, id: nil %>\n      <%= hidden_field_tag :code_challenge, @pre_auth.code_challenge, id: nil %>\n      <%= hidden_field_tag :code_challenge_method, @pre_auth.code_challenge_method, id: nil %>\n      <%= submit_tag t('doorkeeper.authorizations.buttons.deny'), class: \"btn btn-danger btn-lg btn-block\" %>\n    <% end %>\n  </div>\n</main>\n"
  },
  {
    "path": "app/views/doorkeeper/authorizations/show.html.erb",
    "content": "<header class=\"page-header\">\n  <h1><%= t('.title') %></h1>\n</header>\n\n<main role=\"main\">\n  <code id=\"authorization_code\"><%= params[:code] %></code>\n</main>\n"
  },
  {
    "path": "app/views/doorkeeper/authorized_applications/_delete_form.html.erb",
    "content": "<%- submit_btn_css ||= 'btn btn-link' %>\n<%= form_tag oauth_authorized_application_path(application), method: :delete do %>\n  <%= submit_tag t('doorkeeper.authorized_applications.buttons.revoke'), onclick: \"return confirm('#{ t('doorkeeper.authorized_applications.confirmations.revoke') }')\", class: submit_btn_css %>\n<% end %>\n"
  },
  {
    "path": "app/views/doorkeeper/authorized_applications/index.html.erb",
    "content": "<header class=\"page-header\">\n  <h1><%= t('doorkeeper.authorized_applications.index.title') %></h1>\n</header>\n\n<main role=\"main\">\n  <table class=\"table table-striped\">\n    <thead>\n    <tr>\n      <th><%= t('doorkeeper.authorized_applications.index.application') %></th>\n      <th><%= t('doorkeeper.authorized_applications.index.created_at') %></th>\n      <th></th>\n    </tr>\n    </thead>\n    <tbody>\n    <% @applications.each do |application| %>\n      <tr>\n        <td><%= application.name %></td>\n        <td><%= application.created_at.strftime(t('doorkeeper.authorized_applications.index.date_format')) %></td>\n        <td><%= render 'delete_form', application: application %></td>\n      </tr>\n    <% end %>\n    </tbody>\n  </table>\n</main>\n"
  },
  {
    "path": "app/views/layouts/doorkeeper/admin.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title><%= t('doorkeeper.layouts.admin.title') %></title>\n  <%= stylesheet_link_tag \"doorkeeper/admin/application\" %>\n  <%= csrf_meta_tags %>\n</head>\n<body>\n<nav class=\"navbar navbar-expand-lg navbar-dark bg-dark mb-5\">\n  <%= link_to t('doorkeeper.layouts.admin.nav.oauth2_provider'), oauth_applications_path, class: 'navbar-brand' %>\n\n  <div class=\"collapse navbar-collapse\">\n    <ul class=\"navbar-nav mr-auto\">\n      <li class=\"nav-item <%= 'active' if request.path == oauth_applications_path %>\">\n        <%= link_to t('doorkeeper.layouts.admin.nav.applications'), oauth_applications_path, class: 'nav-link' %>\n      </li>\n      <% if respond_to?(:root_path) %>\n        <li class=\"nav-item\">\n          <%= link_to t('doorkeeper.layouts.admin.nav.home'), root_path, class: 'nav-link' %>\n        </li>\n      <% end %>\n    </ul>\n  </div>\n</nav>\n\n<div class=\"doorkeeper-admin container\">\n  <%- if flash[:notice].present? %>\n    <div class=\"alert alert-info\">\n      <%= flash[:notice] %>\n    </div>\n  <% end -%>\n\n  <%= yield %>\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "app/views/layouts/doorkeeper/application.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title><%= t('doorkeeper.layouts.application.title') %></title>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n\n  <%= stylesheet_link_tag \"doorkeeper/application\" %>\n  <%= csrf_meta_tags %>\n</head>\n<body>\n<div id=\"container\">\n  <%- if flash[:notice].present? %>\n    <div class=\"alert alert-info\">\n      <%= flash[:notice] %>\n    </div>\n  <% end -%>\n\n  <%= yield %>\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "benchmark/ruby/client_credentials.rb",
    "content": "# frozen_string_literal: true\n\n$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), \"..\", \"..\", \"lib\"))\n\nbegin\n  require \"bundler/inline\"\nrescue LoadError => e\n  warn \"Bundler version 1.10 or later is required. Please update your Bundler\"\n  raise e\nend\n\ngemfile(true) do\n  source \"https://rubygems.org\"\n\n  gem \"sqlite3\"\n  gem \"rails\"\n  gem \"benchmark-ips\"\nend\n\nrequire \"benchmark/ips\"\nrequire \"ostruct\"\nrequire \"doorkeeper\"\nrequire \"active_support/all\"\nrequire \"active_record/railtie\"\n\nActiveRecord::Base.establish_connection(adapter: \"sqlite3\", database: \":memory:\")\nActiveRecord::Base.logger = ENV[\"LOGGER\"].present? ? Logger.new(STDOUT) : nil\n\n# Load database schema\nload File.expand_path(\"../../spec/dummy/db/schema.rb\", __dir__)\n\nDoorkeeper.configure do\n  orm :active_record\n\n  grant_flows %w[password authorization_code client_credentials]\n\n  skip_authorization do\n    true\n  end\nend\n\nclient = Doorkeeper::Application.create!(\n  name: \"test\",\n  uid: \"123456789\",\n  secret: \"987654321\",\n  redirect_uri: \"https://doorkeeper.test\",\n)\n\ncontext = OpenStruct.new\nrequest = OpenStruct.new\nrequest.parameters = {\n  client_id: client.uid,\n  client_secret: client.secret,\n}.with_indifferent_access\ncontext.request = request\n\nBenchmark.ips do |ips|\n  ips.report(\"Client credentials\") do\n    server = Doorkeeper::Server.new(context)\n    strategy = server.token_request(\"client_credentials\")\n    strategy.authorize\n  end\nend\n"
  },
  {
    "path": "benchmark/wrk/.keep",
    "content": ""
  },
  {
    "path": "bin/console",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\nrequire \"bundler/setup\"\nrequire \"rails/all\"\nrequire \"active_support/all\"\nrequire \"irb\"\nrequire \"debug\"\nrequire \"doorkeeper\"\n\nRails.logger = Logger.new(STDOUT)\n\nRails.logger.info(\"Doorkeeper version: #{Doorkeeper::VERSION::STRING}\")\nRails.logger.info(\"Rails version: #{Rails::VERSION::STRING}\")\n\n# Default Doorkeeper config\nDoorkeeper.configure do\n  orm :active_record\nend\n\n# Generate in-memory database for testing\nActiveRecord::Base.establish_connection(\n  adapter: \"sqlite3\",\n  database: \":memory:\",\n)\n\n# Load database schema\nload File.expand_path(\"../spec/dummy/db/schema.rb\", __dir__)\n\nIRB.start(__FILE__)\n"
  },
  {
    "path": "config/locales/en.yml",
    "content": "en:\n  activerecord:\n    attributes:\n      doorkeeper/application:\n        name: 'Name'\n        redirect_uri: 'Redirect URI'\n    errors:\n      models:\n        doorkeeper/application:\n          attributes:\n            redirect_uri:\n              fragment_present: 'cannot contain a fragment.'\n              invalid_uri: 'must be a valid URI.'\n              unspecified_scheme: 'must specify a scheme.'\n              relative_uri: 'must be an absolute URI.'\n              secured_uri: 'must be an HTTPS/SSL URI.'\n              forbidden_uri: 'is forbidden by the server.'\n            scopes:\n              not_match_configured: \"doesn't match those configured on the server.\"\n\n  doorkeeper:\n    applications:\n      confirmations:\n        destroy: 'Are you sure?'\n      buttons:\n        edit: 'Edit'\n        destroy: 'Destroy'\n        submit: 'Submit'\n        cancel: 'Cancel'\n        authorize: 'Authorize'\n      form:\n        error: 'Whoops! Check your form for possible errors'\n      help:\n        confidential: 'Application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential.'\n        redirect_uri: 'Use one line per URI'\n        blank_redirect_uri: \"Leave it blank if you configured your provider to use Client Credentials, Resource Owner Password Credentials or any other grant type that doesn't require a redirect URI.\"\n        scopes: 'Separate scopes with spaces. Leave blank to use the default scopes.'\n      edit:\n        title: 'Edit application'\n      index:\n        title: 'Your applications'\n        new: 'New Application'\n        name: 'Name'\n        callback_url: 'Callback URL'\n        confidential: 'Confidential?'\n        actions: 'Actions'\n        confidentiality:\n          'yes': 'Yes'\n          'no': 'No'\n      new:\n        title: 'New Application'\n      show:\n        title: 'Application: %{name}'\n        application_id: 'UID:'\n        secret: 'Secret:'\n        secret_hashed: 'Secret hashed'\n        scopes: 'Scopes:'\n        confidential: 'Confidential:'\n        callback_urls: 'Callback URLs:'\n        actions: 'Actions'\n        not_defined: 'Not defined'\n\n    authorizations:\n      buttons:\n        authorize: 'Authorize'\n        deny: 'Deny'\n      error:\n        title: 'An error has occurred'\n      new:\n        title: 'Authorization required'\n        prompt: 'Authorize %{client_name} to use your account?'\n        able_to: 'This application will be able to:'\n      show:\n        title: 'Authorization code:'\n      form_post:\n        title: 'Submit this form'\n\n    authorized_applications:\n      confirmations:\n        revoke: 'Are you sure?'\n      buttons:\n        revoke: 'Revoke'\n      index:\n        title: 'Your authorized applications'\n        application: 'Application'\n        created_at: 'Created At'\n        date_format: '%Y-%m-%d %H:%M:%S'\n\n    pre_authorization:\n      status: 'Pre-authorization'\n\n    errors:\n      messages:\n        # Common error messages\n        invalid_request:\n          unknown: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.'\n          missing_param: 'Missing required parameter: %{value}.'\n          request_not_authorized: 'Request needs to be authorized. Required parameter for authorizing the request is missing or invalid.'\n          invalid_code_challenge: 'Code challenge is required.'\n        invalid_redirect_uri: \"The requested redirect URI is malformed or doesn't match the client redirect URI.\"\n        unauthorized_client: 'The client is not authorized to perform this request using this method.'\n        access_denied: 'The resource owner or authorization server denied the request.'\n        invalid_scope: 'The requested scope is invalid, unknown, or malformed.'\n        invalid_code_challenge_method:\n          zero: 'The authorization server does not support PKCE as there are no accepted code_challenge_method values.'\n          one: 'The code_challenge_method must be %{challenge_methods}.'\n          other: 'The code_challenge_method must be one of %{challenge_methods}.'\n        server_error: 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.'\n        temporarily_unavailable: 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.'\n\n        # Configuration error messages\n        credential_flow_not_configured: 'Resource Owner Password Credentials flow failed due to Doorkeeper.configure.resource_owner_from_credentials being unconfigured.'\n        resource_owner_authenticator_not_configured: 'Resource Owner find failed due to Doorkeeper.configure.resource_owner_authenticator being unconfigured.'\n        admin_authenticator_not_configured: 'Access to admin panel is forbidden due to Doorkeeper.configure.admin_authenticator being unconfigured.'\n\n        # Access grant errors\n        unsupported_response_type: 'The authorization server does not support this response type.'\n        unsupported_response_mode: 'The authorization server does not support this response mode.'\n\n        # Access token errors\n        invalid_client: 'Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method.'\n        invalid_grant: 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.'\n        unsupported_grant_type: 'The authorization grant type is not supported by the authorization server.'\n\n        invalid_token:\n          revoked: \"The access token was revoked\"\n          expired: \"The access token expired\"\n          unknown: \"The access token is invalid\"\n        revoke:\n          unauthorized: \"You are not authorized to revoke this token\"\n\n        forbidden_token:\n          missing_scope: 'Access to this resource requires scope \"%{oauth_scopes}\".'\n\n    flash:\n      applications:\n        create:\n          notice: 'Application created.'\n        destroy:\n          notice: 'Application deleted.'\n        update:\n          notice: 'Application updated.'\n      authorized_applications:\n        destroy:\n          notice: 'Application revoked.'\n\n    layouts:\n      admin:\n        title: 'Doorkeeper'\n        nav:\n          oauth2_provider: 'OAuth2 Provider'\n          applications: 'Applications'\n          home: 'Home'\n      application:\n        title: 'OAuth authorization required'\n"
  },
  {
    "path": "doorkeeper.gemspec",
    "content": "# frozen_string_literal: true\n\n$LOAD_PATH.unshift(File.expand_path(\"lib\", __dir__))\n\nrequire \"doorkeeper/version\"\n\nGem::Specification.new do |gem|\n  gem.name        = \"doorkeeper\"\n  gem.version     = Doorkeeper::VERSION::STRING\n  gem.authors     = [\"Felipe Elias Philipp\", \"Tute Costa\", \"Jon Moss\", \"Nikita Bulai\"]\n  gem.email       = %w[bulaj.nikita@gmail.com]\n  gem.homepage    = \"https://github.com/doorkeeper-gem/doorkeeper\"\n  gem.summary     = \"OAuth 2 provider for Rails and Grape\"\n  gem.description = \"Doorkeeper is an OAuth 2 provider for Rails and Grape.\"\n  gem.license     = \"MIT\"\n\n  gem.files = Dir[\n    \"{app,config,lib,vendor}/**/*\",\n    \"CHANGELOG.md\",\n    \"MIT-LICENSE\",\n    \"README.md\",\n  ]\n  gem.require_paths = [\"lib\"]\n\n  gem.metadata = {\n    \"homepage_uri\" => \"https://github.com/doorkeeper-gem/doorkeeper\",\n    \"changelog_uri\" => \"https://github.com/doorkeeper-gem/doorkeeper/blob/main/CHANGELOG.md\",\n    \"source_code_uri\" => \"https://github.com/doorkeeper-gem/doorkeeper\",\n    \"bug_tracker_uri\" => \"https://github.com/doorkeeper-gem/doorkeeper/issues\",\n    \"documentation_uri\" => \"https://doorkeeper.gitbook.io/guides/\",\n    \"funding_uri\" => \"https://opencollective.com/doorkeeper-gem\",\n  }\n\n  gem.add_dependency \"railties\", \">= 5\"\n  gem.required_ruby_version = \">= 2.7\"\n\n  gem.add_development_dependency \"appraisal\"\n  gem.add_development_dependency \"capybara\"\n  gem.add_development_dependency \"coveralls_reborn\"\n  gem.add_development_dependency \"database_cleaner\", \"~> 2.0\"\n  gem.add_development_dependency \"factory_bot\", \"~> 6.0\"\n  gem.add_development_dependency \"generator_spec\", \"~> 0.10.0\"\n  gem.add_development_dependency \"grape\"\n  gem.add_development_dependency \"rake\", \">= 11.3.0\"\n  gem.add_development_dependency \"rspec-rails\"\n  gem.add_development_dependency \"timecop\"\nend\n"
  },
  {
    "path": "gemfiles/rails_7_0.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"rails\", \"~> 7.0.0\"\ngem \"rspec-core\"\ngem \"rspec-expectations\"\ngem \"rspec-mocks\"\ngem \"rspec-rails\", \"~> 5.0\"\ngem \"rspec-support\"\ngem \"rubocop\", \"~> 1.4\"\ngem \"rubocop-performance\", require: false\ngem \"rubocop-rails\", require: false\ngem \"rubocop-rspec\", require: false\ngem \"bcrypt\", \"~> 3.1\", require: false\ngem \"activerecord-jdbcsqlite3-adapter\", platform: :jruby\ngem \"sprockets-rails\"\ngem \"sqlite3\", \"~> 1.4\", platform: [:ruby, :mswin, :mingw, :x64_mingw]\ngem \"tzinfo-data\", platforms: [:mingw, :mswin, :x64_mingw]\ngem \"timecop\"\ngem \"base64\"\ngem \"drb\"\ngem \"mutex_m\"\ngem \"concurrent-ruby\", \"1.3.4\"\n\ngemspec path: \"../\"\n"
  },
  {
    "path": "gemfiles/rails_7_1.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"rails\", \"~> 7.1.0\"\ngem \"rspec-core\"\ngem \"rspec-expectations\"\ngem \"rspec-mocks\"\ngem \"rspec-rails\", \"~> 7.1\"\ngem \"rspec-support\"\ngem \"rubocop\", \"~> 1.4\"\ngem \"rubocop-performance\", require: false\ngem \"rubocop-rails\", require: false\ngem \"rubocop-rspec\", require: false\ngem \"bcrypt\", \"~> 3.1\", require: false\ngem \"activerecord-jdbcsqlite3-adapter\", platform: :jruby\ngem \"sprockets-rails\"\ngem \"sqlite3\", \"~> 1.4\", platform: [:ruby, :mswin, :mingw, :x64_mingw]\ngem \"tzinfo-data\", platforms: [:mingw, :mswin, :x64_mingw]\ngem \"timecop\"\n\ngemspec path: \"../\"\n"
  },
  {
    "path": "gemfiles/rails_7_2.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"rails\", \"~> 7.2.0\"\ngem \"rspec-core\"\ngem \"rspec-expectations\"\ngem \"rspec-mocks\"\ngem \"rspec-rails\", \"~> 7.1\"\ngem \"rspec-support\"\ngem \"rubocop\", \"~> 1.4\"\ngem \"rubocop-performance\", require: false\ngem \"rubocop-rails\", require: false\ngem \"rubocop-rspec\", require: false\ngem \"bcrypt\", \"~> 3.1\", require: false\ngem \"activerecord-jdbcsqlite3-adapter\", platform: :jruby\ngem \"sprockets-rails\"\ngem \"sqlite3\", \"~> 1.4\", platform: [:ruby, :mswin, :mingw, :x64_mingw]\ngem \"tzinfo-data\", platforms: [:mingw, :mswin, :x64_mingw]\ngem \"timecop\"\n\ngemspec path: \"../\"\n"
  },
  {
    "path": "gemfiles/rails_8_0.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"rails\", \"~> 8.0.0\"\ngem \"rspec-core\"\ngem \"rspec-expectations\"\ngem \"rspec-mocks\"\ngem \"rspec-rails\", \"~> 7.1\"\ngem \"rspec-support\"\ngem \"rubocop\", \"~> 1.4\"\ngem \"rubocop-performance\", require: false\ngem \"rubocop-rails\", require: false\ngem \"rubocop-rspec\", require: false\ngem \"bcrypt\", \"~> 3.1\", require: false\ngem \"activerecord-jdbcsqlite3-adapter\", platform: :jruby\ngem \"sprockets-rails\"\ngem \"sqlite3\", \"~> 2.3\", platform: [:ruby, :mswin, :mingw, :x64_mingw]\ngem \"tzinfo-data\", platforms: [:mingw, :mswin, :x64_mingw]\ngem \"timecop\"\n\ngemspec path: \"../\"\n"
  },
  {
    "path": "gemfiles/rails_edge.gemfile",
    "content": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"rails\", git: \"https://github.com/rails/rails\"\ngem \"rspec-core\"\ngem \"rspec-expectations\"\ngem \"rspec-mocks\"\ngem \"rspec-rails\", \"~> 7.1\"\ngem \"rspec-support\"\ngem \"rubocop\", \"~> 1.4\"\ngem \"rubocop-performance\", require: false\ngem \"rubocop-rails\", require: false\ngem \"rubocop-rspec\", require: false\ngem \"bcrypt\", \"~> 3.1\", require: false\ngem \"activerecord-jdbcsqlite3-adapter\", platform: :jruby\ngem \"sprockets-rails\"\ngem \"sqlite3\", \"~> 2.3\", platform: [:ruby, :mswin, :mingw, :x64_mingw]\ngem \"tzinfo-data\", platforms: [:mingw, :mswin, :x64_mingw]\ngem \"timecop\"\n\ngemspec path: \"../\"\n"
  },
  {
    "path": "lib/doorkeeper/config/abstract_builder.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  class Config\n    # Abstract base class for Doorkeeper and it's extensions configuration\n    # builder. Instantiates and validates gem configuration.\n    #\n    class AbstractBuilder\n      attr_reader :config\n\n      # @param [Class] config class\n      #\n      def initialize(config = Config.new, &block)\n        @config = config\n        instance_eval(&block) if block_given?\n      end\n\n      # Builds and validates configuration.\n      #\n      # @return [Doorkeeper::Config] config instance\n      #\n      def build\n        @config.validate! if @config.respond_to?(:validate!)\n        @config\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/config/option.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  class Config\n    # Doorkeeper configuration option DSL\n    module Option\n      # Defines configuration option\n      #\n      # When you call option, it defines two methods. One method will take place\n      # in the +Config+ class and the other method will take place in the\n      # +Builder+ class.\n      #\n      # The +name+ parameter will set both builder method and config attribute.\n      # If the +:as+ option is defined, the builder method will be the specified\n      # option while the config attribute will be the +name+ parameter.\n      #\n      # If you want to introduce another level of config DSL you can\n      # define +builder_class+ parameter.\n      # Builder should take a block as the initializer parameter and respond to function +build+\n      # that returns the value of the config attribute.\n      #\n      # ==== Options\n      #\n      # * [:+as+] Set the builder method that goes inside +configure+ block\n      # * [+:default+] The default value in case no option was set\n      # * [+:builder_class+] Configuration option builder class\n      #\n      # ==== Examples\n      #\n      #    option :name\n      #    option :name, as: :set_name\n      #    option :name, default: 'My Name'\n      #    option :scopes builder_class: ScopesBuilder\n      #\n      def option(name, options = {})\n        attribute = options[:as] || name\n        attribute_builder = options[:builder_class]\n\n        builder_class.instance_eval do\n          if method_defined?(name)\n            Kernel.warn \"[DOORKEEPER] Option #{name} already defined and will be overridden\"\n            remove_method name\n          end\n\n          define_method name do |*args, &block|\n            if (deprecation_opts = options[:deprecated])\n              warning = \"[DOORKEEPER] #{name} has been deprecated and will soon be removed\"\n              warning = \"#{warning}\\n#{deprecation_opts.fetch(:message)}\" if deprecation_opts.is_a?(Hash)\n\n              Kernel.warn(warning)\n            end\n\n            value = if attribute_builder\n                      attribute_builder.new(&block).build\n                    else\n                      block || args.first\n                    end\n\n            @config.instance_variable_set(:\"@#{attribute}\", value)\n          end\n        end\n\n        define_method attribute do |*_args|\n          if instance_variable_defined?(:\"@#{attribute}\")\n            instance_variable_get(:\"@#{attribute}\")\n          else\n            options[:default]\n          end\n        end\n\n        public attribute\n      end\n\n      def self.extended(base)\n        return if base.respond_to?(:builder_class)\n\n        raise Doorkeeper::MissingConfigurationBuilderClass, \"Define `self.builder_class` method \" \\\n                          \"for #{base} that returns your custom Builder class to use options DSL!\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/config/validations.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  class Config\n    # Doorkeeper configuration validator.\n    #\n    module Validations\n      # Validates configuration options to be set properly.\n      #\n      def validate!\n        validate_reuse_access_token_value\n        validate_token_reuse_limit\n        validate_secret_strategies\n        validate_pkce_code_challenge_methods\n      end\n\n      private\n\n      # Determine whether +reuse_access_token+ and a non-restorable\n      # +token_secret_strategy+ have both been activated.\n      #\n      # In that case, disable reuse_access_token value and warn the user.\n      def validate_reuse_access_token_value\n        strategy = token_secret_strategy\n        return if !reuse_access_token || strategy.allows_restoring_secrets?\n\n        ::Rails.logger.warn(\n          \"[DOORKEEPER] You have configured both reuse_access_token \" \\\n          \"AND '#{strategy}' strategy which cannot restore tokens. \" \\\n          \"This combination is unsupported. reuse_access_token will be disabled\",\n        )\n        @reuse_access_token = false\n      end\n\n      # Validate that the provided strategies are valid for\n      # tokens and applications\n      def validate_secret_strategies\n        token_secret_strategy.validate_for(:token)\n        application_secret_strategy.validate_for(:application)\n      end\n\n      def validate_token_reuse_limit\n        return if !reuse_access_token ||\n                  (token_reuse_limit > 0 && token_reuse_limit <= 100)\n\n        ::Rails.logger.warn(\n          \"[DOORKEEPER] You have configured an invalid value for token_reuse_limit option. \" \\\n          \"It will be set to default 100\",\n        )\n        @token_reuse_limit = 100\n      end\n\n      def validate_pkce_code_challenge_methods\n        return if pkce_code_challenge_methods.all? {|method| method =~ /^plain$|^S256$/ }\n\n        ::Rails.logger.warn(\n          \"[DOORKEEPER] You have configured an invalid value for pkce_code_challenge_methods option. \" \\\n          \"It will be set to default ['plain', 'S256']\",\n        )\n\n        @pkce_code_challenge_methods = ['plain', 'S256']\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/config.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"doorkeeper/config/abstract_builder\"\nrequire \"doorkeeper/config/option\"\nrequire \"doorkeeper/config/validations\"\n\nmodule Doorkeeper\n  # Doorkeeper option DSL could be reused in extensions to build their own\n  # configurations. To use the Option DSL gems need to define `builder_class` method\n  # that returns configuration Builder class. This exception raises when they don't\n  # define it.\n  #\n  class Config\n    # Default Doorkeeper configuration builder\n    class Builder < AbstractBuilder\n      # Provide support for an owner to be assigned to each registered\n      # application (disabled by default)\n      # Optional parameter confirmation: true (default false) if you want\n      # to enforce ownership of a registered application\n      #\n      # @param opts [Hash] the options to confirm if an application owner\n      #   is present\n      # @option opts[Boolean] :confirmation (false)\n      #   Set confirm_application_owner variable\n      def enable_application_owner(opts = {})\n        @config.instance_variable_set(:@enable_application_owner, true)\n        confirm_application_owner if opts[:confirmation].present? && opts[:confirmation]\n      end\n\n      def confirm_application_owner\n        @config.instance_variable_set(:@confirm_application_owner, true)\n      end\n\n      # Provide support for dynamic scopes (e.g. user:*) (disabled by default)\n      # Optional parameter delimiter (default \":\") if you want to customize\n      # the delimiter separating the scope name and matching value.\n      #\n      # @param opts [Hash] the options to configure dynamic scopes\n      def enable_dynamic_scopes(opts = {})\n        @config.instance_variable_set(:@enable_dynamic_scopes, true)\n        @config.instance_variable_set(:@dynamic_scopes_delimiter, opts[:delimiter] || ':')\n      end\n\n      # Define default access token scopes for your provider\n      #\n      # @param scopes [Array] Default set of access (OAuth::Scopes.new)\n      # token scopes\n      def default_scopes(*scopes)\n        @config.instance_variable_set(:@default_scopes, OAuth::Scopes.from_array(scopes))\n      end\n\n      # Define default access token scopes for your provider\n      #\n      # @param scopes [Array] Optional set of access (OAuth::Scopes.new)\n      # token scopes\n      def optional_scopes(*scopes)\n        @config.instance_variable_set(:@optional_scopes, OAuth::Scopes.from_array(scopes))\n      end\n\n      # Define scopes_by_grant_type to limit certain scope to certain grant_type\n      # @param { Hash } with grant_types as keys.\n      # Default set to {} i.e. no limitation on scopes usage\n      def scopes_by_grant_type(hash = {})\n        @config.instance_variable_set(:@scopes_by_grant_type, hash)\n      end\n\n      # Change the way client credentials are retrieved from the request object.\n      # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then\n      # falls back to the `:client_id` and `:client_secret` params from the\n      # `params` object.\n      #\n      # @param methods [Array] Define client credentials\n      def client_credentials(*methods)\n        @config.instance_variable_set(:@client_credentials_methods, methods)\n      end\n\n      # Change the way access token is authenticated from the request object.\n      # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then\n      # falls back to the `:access_token` or `:bearer_token` params from the\n      # `params` object.\n      #\n      # @param methods [Array] Define access token methods\n      def access_token_methods(*methods)\n        @config.instance_variable_set(:@access_token_methods, methods)\n      end\n\n      # Issue access tokens with refresh token (disabled if not set)\n      def use_refresh_token(enabled = true, &block)\n        @config.instance_variable_set(\n          :@refresh_token_enabled,\n          block || enabled,\n        )\n      end\n\n      # Reuse access token for the same resource owner within an application\n      # (disabled by default)\n      # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383\n      def reuse_access_token\n        @config.instance_variable_set(:@reuse_access_token, true)\n      end\n\n      # Enable support for multiple database configurations with read replicas.\n      # When enabled, wraps database write operations to ensure they use the primary\n      # (writable) database when automatic role switching is enabled.\n      #\n      # For ActiveRecord (Rails 6.1+), this uses `ActiveRecord::Base.connected_to(role: :writing)`.\n      # Other ORM extensions can implement their own primary database targeting logic.\n      #\n      # This prevents `ActiveRecord::ReadOnlyError` when using read replicas with Rails\n      # automatic role switching. Enable this if your application uses multiple databases\n      # with automatic role switching for read replicas.\n      #\n      # See: https://guides.rubyonrails.org/active_record_multiple_databases.html#activating-automatic-role-switching\n      def enable_multiple_database_roles\n        @config.instance_variable_set(:@enable_multiple_database_roles, true)\n      end\n\n      # Choose to use the url path for native autorization codes \n      # Enabling this flag sets the authorization code response route for\n      # native redirect uris to oauth/authorize/<code>. The default is\n      # oauth/authorize/native?code=<code>.\n      # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/1143\n      def use_url_path_for_native_authorization\n        @config.instance_variable_set(:@use_url_path_for_native_authorization, true)\n      end\n\n      # TODO: maybe make it more generic for other flows too?\n      # Only allow one valid access token obtained via client credentials\n      # per client. If a new access token is obtained before the old one\n      # expired, the old one gets revoked (disabled by default)\n      def revoke_previous_client_credentials_token\n        @config.instance_variable_set(:@revoke_previous_client_credentials_token, true)\n      end\n\n      # Only allow one valid access token obtained via authorization code\n      # per client. If a new access token is obtained before the old one\n      # expired, the old one gets revoked (disabled by default)\n      def revoke_previous_authorization_code_token\n        @config.instance_variable_set(:@revoke_previous_authorization_code_token, true)\n      end\n\n      # Require non-confidential apps to use PKCE (send a code_verifier) when requesting\n      # an access_token using an authorization code (disabled by default)\n      def force_pkce\n        @config.instance_variable_set(:@force_pkce, true)\n      end\n\n      # Use an API mode for applications generated with --api argument\n      # It will skip applications controller, disable forgery protection\n      def api_only\n        @config.instance_variable_set(:@api_only, true)\n      end\n\n      # Enables polymorphic Resource Owner association for Access Grant and\n      # Access Token models. Requires additional database columns to be setup.\n      def use_polymorphic_resource_owner\n        @config.instance_variable_set(:@polymorphic_resource_owner, true)\n      end\n\n      # Forbids creating/updating applications with arbitrary scopes that are\n      # not in configuration, i.e. `default_scopes` or `optional_scopes`.\n      # (disabled by default)\n      def enforce_configured_scopes\n        @config.instance_variable_set(:@enforce_configured_scopes, true)\n      end\n\n      # Enforce request content type as the spec requires:\n      # disabled by default for backward compatibility.\n      def enforce_content_type\n        @config.instance_variable_set(:@enforce_content_type, true)\n      end\n\n      # Allow optional hashing of input tokens before persisting them.\n      # Will be used for hashing of input token and grants.\n      #\n      # @param using\n      #   Provide a different secret storage implementation class for tokens\n      # @param fallback\n      #   Provide a fallback secret storage implementation class for tokens\n      #   or use :plain to fallback to plain tokens\n      def hash_token_secrets(using: nil, fallback: nil)\n        default = \"::Doorkeeper::SecretStoring::Sha256Hash\"\n        configure_secrets_for :token,\n                              using: using || default,\n                              fallback: fallback\n      end\n\n      # Allow optional hashing of application secrets before persisting them.\n      # Will be used for hashing of input token and grants.\n      #\n      # @param using\n      #   Provide a different secret storage implementation for applications\n      # @param fallback\n      #   Provide a fallback secret storage implementation for applications\n      #   or use :plain to fallback to plain application secrets\n      def hash_application_secrets(using: nil, fallback: nil)\n        default = \"::Doorkeeper::SecretStoring::Sha256Hash\"\n        configure_secrets_for :application,\n                              using: using || default,\n                              fallback: fallback\n      end\n\n      private\n\n      # Configure the secret storing functionality\n      def configure_secrets_for(type, using:, fallback:)\n        raise ArgumentError, \"Invalid type #{type}\" if %i[application token].exclude?(type)\n\n        @config.instance_variable_set(:\"@#{type}_secret_strategy\", using.constantize)\n\n        if fallback.nil?\n          return\n        elsif fallback.to_sym == :plain\n          fallback = \"::Doorkeeper::SecretStoring::Plain\"\n        end\n\n        @config.instance_variable_set(:\"@#{type}_secret_fallback_strategy\", fallback.constantize)\n      end\n    end\n\n    # Replace with `default: Builder` when we drop support of Rails < 5.2\n    mattr_reader(:builder_class) { Builder }\n\n    extend Option\n    include Validations\n\n    option :resource_owner_authenticator,\n           as: :authenticate_resource_owner,\n           default: (lambda do |_routes|\n             ::Rails.logger.warn(\n               I18n.t(\"doorkeeper.errors.messages.resource_owner_authenticator_not_configured\"),\n             )\n\n             nil\n           end)\n\n    option :admin_authenticator,\n           as: :authenticate_admin,\n           default: (lambda do |_routes|\n             ::Rails.logger.warn(\n               I18n.t(\"doorkeeper.errors.messages.admin_authenticator_not_configured\"),\n             )\n\n             head :forbidden\n           end)\n\n    option :resource_owner_from_credentials,\n           default: (lambda do |_routes|\n             ::Rails.logger.warn(\n               I18n.t(\"doorkeeper.errors.messages.credential_flow_not_configured\"),\n             )\n\n             nil\n           end)\n\n    # Hooks for authorization\n    option :before_successful_authorization,      default: ->(_controller, _context = nil) {}\n    option :after_successful_authorization,       default: ->(_controller, _context = nil) {}\n    # Hooks for strategies responses\n    option :before_successful_strategy_response,  default: ->(_request) {}\n    option :after_successful_strategy_response,   default: ->(_request, _response) {}\n    # Allows to customize Token Introspection response\n    option :custom_introspection_response,        default: ->(_token, _context) { {} }\n\n    option :skip_authorization,             default: ->(_routes) {}\n    option :access_token_expires_in,        default: 7200\n    option :custom_access_token_expires_in, default: ->(_context) { nil }\n    option :authorization_code_expires_in,  default: 600\n    option :orm,                            default: :active_record\n    option :native_redirect_uri,            default: \"urn:ietf:wg:oauth:2.0:oob\", deprecated: true\n    option :grant_flows,                    default: %w[authorization_code client_credentials]\n    option :pkce_code_challenge_methods,    default: %w[plain S256]\n    option :handle_auth_errors,             default: :render\n    option :token_lookup_batch_size,        default: 10_000\n    # Sets the token_reuse_limit\n    # It will be used only when reuse_access_token option in enabled\n    # By default it will be 100\n    # It will be used for token reusablity to some threshold percentage\n    # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/1189\n    option :token_reuse_limit,              default: 100\n\n    # Don't require client authentication for password grants. If client credentials\n    # are present they will still be validated, and the grant rejected if the credentials\n    # are invalid.\n    #\n    # This is discouraged. Spec says that password grants always require a client.\n    #\n    # See https://github.com/doorkeeper-gem/doorkeeper/issues/1412#issuecomment-632750422\n    # and https://github.com/doorkeeper-gem/doorkeeper/pull/1420\n    #\n    # Since many applications use this unsafe behavior in the wild, this is kept as a\n    # not-recommended option. You should be aware that you are not following the OAuth\n    # spec, and understand the security implications of doing so.\n    option :skip_client_authentication_for_password_grant,\n           default: false\n\n    # Hook to allow arbitrary user-client authorization\n    option :authorize_resource_owner_for_client,\n           default: ->(_client, _resource_owner) { true }\n\n    # Allows to customize OAuth grant flows that +each+ application support.\n    # You can configure a custom block (or use a class respond to `#call`) that must\n    # return `true` in case Application instance supports requested OAuth grant flow\n    # during the authorization request to the server. This configuration +doesn't+\n    # set flows per application, it only allows to check if application supports\n    # specific grant flow.\n    #\n    # For example you can add an additional database column to `oauth_applications` table,\n    # say `t.array :grant_flows, default: []`, and store allowed grant flows that can\n    # be used with this application there. Then when authorization requested Doorkeeper\n    # will call this block to check if specific Application (passed with client_id and/or\n    # client_secret) is allowed to perform the request for the specific grant type\n    # (authorization, password, client_credentials, etc).\n    #\n    # Example of the block:\n    #\n    #   ->(flow, client) { client.grant_flows.include?(flow) }\n    #\n    # In case this option invocation result is `false`, Doorkeeper server returns\n    # :unauthorized_client error and stops the request.\n    #\n    # @param allow_grant_flow_for_client [Proc] Block or any object respond to #call\n    # @return [Boolean] `true` if allow or `false` if forbid the request\n    #\n    option :allow_grant_flow_for_client,    default: ->(_grant_flow, _client) { true }\n\n    # Allows to forbid specific Application redirect URI's by custom rules.\n    # Doesn't forbid any URI by default.\n    #\n    # @param forbid_redirect_uri [Proc] Block or any object respond to #call\n    #\n    option :forbid_redirect_uri,            default: ->(_uri) { false }\n\n    # WWW-Authenticate Realm (default \"Doorkeeper\").\n    #\n    # @param realm [String] (\"Doorkeeper\") Authentication realm\n    #\n    option :realm,                          default: \"Doorkeeper\"\n\n    # Forces the usage of the HTTPS protocol in non-native redirect uris\n    # (enabled by default in non-development environments). OAuth2\n    # delegates security in communication to the HTTPS protocol so it is\n    # wise to keep this enabled.\n    #\n    # @param [Boolean] boolean_or_block value for the parameter, true by default in\n    # non-development environment\n    #\n    # @yield [uri] Conditional usage of SSL redirect uris.\n    # @yieldparam [URI] Redirect URI\n    # @yieldreturn [Boolean] Indicates necessity of usage of the HTTPS protocol\n    #   in non-native redirect uris\n    #\n    option :force_ssl_in_redirect_uri,      default: !Rails.env.development?\n\n    # Use a custom class for generating the access token.\n    # https://doorkeeper.gitbook.io/guides/configuration/other-configurations#custom-access-token-generator\n    #\n    # @param access_token_generator [String]\n    #   the name of the access token generator class\n    #\n    option :access_token_generator,\n           default: \"Doorkeeper::OAuth::Helpers::UniqueToken\"\n\n    # Allows additional data to be received when granting access to an Application, and for this\n    # additional data to be sent with subsequently generated access tokens. The access grant and\n    # access token models will both need to respond to the specified attribute names.\n    #\n    # @param attributes [Array] The array of custom attribute names to be saved\n    #\n    option :custom_access_token_attributes,\n           default: []\n\n    # Use a custom class for generating the application secret.\n    # https://doorkeeper.gitbook.io/guides/configuration/other-configurations#custom-application-secret-generator\n    #\n    # @param application_secret_generator [String]\n    #   the name of the application secret generator class\n    #\n    option :application_secret_generator,\n           default: \"Doorkeeper::OAuth::Helpers::UniqueToken\"\n\n    # Default access token generator is a SecureRandom class from Ruby stdlib.\n    # This option defines which method will be used to generate a unique token value.\n    #\n    # @param default_generator_method [Symbol]\n    #   the method name of the default access token generator\n    #\n    option :default_generator_method, default: :urlsafe_base64\n\n    # The controller Doorkeeper::ApplicationController inherits from.\n    # Defaults to ActionController::Base.\n    # https://doorkeeper.gitbook.io/guides/configuration/other-configurations#custom-controllers\n    #\n    # @param base_controller [String] the name of the base controller\n    option :base_controller,\n           default: (lambda do\n             api_only ? \"ActionController::API\" : \"ActionController::Base\"\n           end)\n\n    # The controller Doorkeeper::ApplicationMetalController inherits from.\n    # Defaults to ActionController::API.\n    #\n    # @param base_metal_controller [String] the name of the base controller\n    option :base_metal_controller,\n           default: \"ActionController::API\"\n\n    option :access_token_class,\n           default: \"Doorkeeper::AccessToken\"\n\n    option :access_grant_class,\n           default: \"Doorkeeper::AccessGrant\"\n\n    option :application_class,\n           default: \"Doorkeeper::Application\"\n\n    # Allows to set blank redirect URIs for Applications in case\n    # server configured to use URI-less grant flows.\n    #\n    option :allow_blank_redirect_uri,\n           default: (lambda do |grant_flows, _application|\n             grant_flows.exclude?(\"authorization_code\") &&\n               grant_flows.exclude?(\"implicit\")\n           end)\n\n    # Configure protection of token introspection request.\n    # By default this configuration allows to introspect a token by\n    # another token of the same application, or to introspect the token\n    # that belongs to authorized client, or access token has been introspected\n    # is a public one (doesn't belong to any client)\n    #\n    # You can define any custom rule you need or just disable token\n    # introspection at all.\n    #\n    # @param token [Doorkeeper::AccessToken]\n    #   token to be introspected\n    #\n    # @param authorized_client [Doorkeeper::Application]\n    #   authorized client (if request is authorized using Basic auth with\n    #   Client Credentials for example)\n    #\n    # @param authorized_token [Doorkeeper::AccessToken]\n    #   Bearer token used to authorize the request\n    #\n    option :allow_token_introspection,\n           default: (lambda do |token, authorized_client, authorized_token|\n             if authorized_token\n               authorized_token.application == token&.application\n             elsif token&.application\n               authorized_client == token.application\n             else\n               true\n             end\n           end)\n\n    attr_reader :reuse_access_token,\n                :enable_multiple_database_roles,\n                :token_secret_fallback_strategy,\n                :application_secret_fallback_strategy\n\n    def clear_cache!\n      %i[\n        application_model\n        access_token_model\n        access_grant_model\n      ].each do |var|\n        remove_instance_variable(\"@#{var}\") if instance_variable_defined?(\"@#{var}\")\n      end\n    end\n\n    # Doorkeeper Access Token model class.\n    #\n    # @return [ActiveRecord::Base, Mongoid::Document, Sequel::Model]\n    #\n    def access_token_model\n      @access_token_model ||= access_token_class.constantize\n    end\n\n    # Doorkeeper Access Grant model class.\n    #\n    # @return [ActiveRecord::Base, Mongoid::Document, Sequel::Model]\n    #\n    def access_grant_model\n      @access_grant_model ||= access_grant_class.constantize\n    end\n\n    # Doorkeeper Application model class.\n    #\n    # @return [ActiveRecord::Base, Mongoid::Document, Sequel::Model]\n    #\n    def application_model\n      @application_model ||= application_class.constantize\n    end\n\n    def api_only\n      @api_only ||= false\n    end\n\n    def enforce_content_type\n      @enforce_content_type ||= false\n    end\n\n    def refresh_token_enabled?\n      if defined?(@refresh_token_enabled)\n        @refresh_token_enabled\n      else\n        false\n      end\n    end\n\n    def resolve_controller(name)\n      config_option = public_send(:\"#{name}_controller\")\n      controller_name = if config_option.respond_to?(:call)\n                          instance_exec(&config_option)\n                        else\n                          config_option\n                        end\n\n      controller_name.constantize\n    end\n\n    def revoke_previous_client_credentials_token?\n      option_set? :revoke_previous_client_credentials_token\n    end\n\n    def revoke_previous_authorization_code_token?\n      option_set? :revoke_previous_authorization_code_token\n    end\n\n    def force_pkce?\n      option_set? :force_pkce\n    end\n\n    def enforce_configured_scopes?\n      option_set? :enforce_configured_scopes\n    end\n\n    def enable_application_owner?\n      option_set? :enable_application_owner\n    end\n\n    def enable_dynamic_scopes?\n      option_set? :enable_dynamic_scopes\n    end\n\n    def dynamic_scopes_delimiter\n      @dynamic_scopes_delimiter\n    end\n\n    def polymorphic_resource_owner?\n      option_set? :polymorphic_resource_owner\n    end\n\n    def confirm_application_owner?\n      option_set? :confirm_application_owner\n    end\n\n    def raise_on_errors?\n      handle_auth_errors == :raise\n    end\n\n    def redirect_on_errors?\n      handle_auth_errors == :redirect\n    end\n\n    def application_secret_hashed?\n      instance_variable_defined?(:\"@application_secret_strategy\")\n    end\n\n    def token_secret_strategy\n      @token_secret_strategy ||= ::Doorkeeper::SecretStoring::Plain\n    end\n\n    def application_secret_strategy\n      @application_secret_strategy ||= ::Doorkeeper::SecretStoring::Plain\n    end\n\n    def default_scopes\n      @default_scopes ||= OAuth::Scopes.new\n    end\n\n    def optional_scopes\n      @optional_scopes ||= OAuth::Scopes.new\n    end\n\n    def scopes\n      @scopes ||= default_scopes + optional_scopes\n    end\n\n    def scopes_by_grant_type\n      @scopes_by_grant_type ||= {}\n    end\n\n    def pkce_code_challenge_methods_supported\n      return [] unless access_grant_model.pkce_supported?\n      \n      pkce_code_challenge_methods\n    end\n\n    def client_credentials_methods\n      @client_credentials_methods ||= %i[from_basic from_params]\n    end\n\n    def access_token_methods\n      @access_token_methods ||= %i[\n        from_bearer_authorization\n        from_access_token_param\n        from_bearer_param\n      ]\n    end\n\n    def enabled_grant_flows\n      @enabled_grant_flows ||= calculate_grant_flows.map { |name| Doorkeeper::GrantFlow.get(name) }.compact\n    end\n\n    def authorization_response_flows\n      @authorization_response_flows ||= enabled_grant_flows.select(&:handles_response_type?) +\n                                        deprecated_authorization_flows\n    end\n\n    def token_grant_flows\n      @token_grant_flows ||= calculate_token_grant_flows\n    end\n\n    def authorization_response_types\n      authorization_response_flows.map(&:response_type_matches)\n    end\n\n    def token_grant_types\n      token_grant_flows.map(&:grant_type_matches)\n    end\n\n    # [NOTE]: deprecated and will be removed soon\n    def deprecated_token_grant_types_resolver\n      @deprecated_token_grant_types ||= calculate_token_grant_types\n    end\n    \n    def native_authorization_code_route\n      @use_url_path_for_native_authorization = false unless defined?(@use_url_path_for_native_authorization)\n      @use_url_path_for_native_authorization ? '/:code' : '/native'\n    end\n\n    # [NOTE]: deprecated and will be removed soon\n    def deprecated_authorization_flows\n      response_types = calculate_authorization_response_types\n\n      if response_types.any?\n        ::Kernel.warn <<~WARNING\n          Please, don't patch Doorkeeper::Config#calculate_authorization_response_types method.\n          Register your custom grant flows using the public API:\n          `Doorkeeper::GrantFlow.register(grant_flow_name, **options)`.\n        WARNING\n      end\n\n      response_types.map do |response_type|\n        Doorkeeper::GrantFlow::FallbackFlow.new(response_type, response_type_matches: response_type)\n      end\n    end\n\n    # [NOTE]: deprecated and will be removed soon\n    def calculate_authorization_response_types\n      []\n    end\n\n    # [NOTE]: deprecated and will be removed soon\n    def calculate_token_grant_types\n      types = grant_flows - [\"implicit\"]\n      types << \"refresh_token\" if refresh_token_enabled?\n      types\n    end\n\n    # Calculates grant flows configured by the user in Doorkeeper\n    # configuration considering registered aliases that is exposed\n    # to single or multiple other flows.\n    #\n    def calculate_grant_flows\n      configured_flows = grant_flows.map(&:to_s)\n      aliases = Doorkeeper::GrantFlow.aliases.keys.map(&:to_s)\n\n      flows = configured_flows - aliases\n      aliases.each do |flow_alias|\n        next unless configured_flows.include?(flow_alias)\n\n        flows.concat(Doorkeeper::GrantFlow.expand_alias(flow_alias))\n      end\n\n      flows.flatten.uniq\n    end\n\n    def allow_blank_redirect_uri?(application = nil)\n      if allow_blank_redirect_uri.respond_to?(:call)\n        allow_blank_redirect_uri.call(grant_flows, application)\n      else\n        allow_blank_redirect_uri\n      end\n    end\n\n    def allow_grant_flow_for_client?(grant_flow, client)\n      return true unless option_defined?(:allow_grant_flow_for_client)\n\n      allow_grant_flow_for_client.call(grant_flow, client)\n    end\n\n    def option_defined?(name)\n      instance_variable_defined?(\"@#{name}\")\n    end\n\n    private\n\n    # Helper to read boolearized configuration option\n    def option_set?(instance_key)\n      var = instance_variable_get(\"@#{instance_key}\")\n      !!(defined?(var) && var)\n    end\n\n    def calculate_token_grant_flows\n      flows = enabled_grant_flows.select(&:handles_grant_type?)\n      flows << Doorkeeper::GrantFlow.get(\"refresh_token\") if refresh_token_enabled?\n      flows\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/engine.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  class Engine < Rails::Engine\n    initializer \"doorkeeper.params.filter\", after: :load_config_initializers do |app|\n      app.config.to_prepare do\n        Doorkeeper.setup_filter_parameters\n      end\n    end\n\n    initializer \"doorkeeper.routes\" do\n      Doorkeeper::Rails::Routes.install!\n    end\n\n    initializer \"doorkeeper.helpers\" do\n      ActiveSupport.on_load(:action_controller) do\n        include Doorkeeper::Rails::Helpers\n      end\n    end\n\n    config.to_prepare do\n      Doorkeeper.run_orm_hooks\n    end\n\n    if defined?(Sprockets) && Sprockets::VERSION.chr.to_i >= 4\n      initializer \"doorkeeper.assets.precompile\" do |app|\n        # Force users to use:\n        #    //= link doorkeeper/admin/application.css\n        # in Doorkeeper 5 for Sprockets 4 instead of precompile.\n        # Add note to official docs & Wiki\n        app.config.assets.precompile += %w[\n          doorkeeper/application.css\n          doorkeeper/admin/application.css\n        ]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/errors.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Errors\n    class DoorkeeperError < StandardError\n      def type\n        message\n      end\n\n      def self.translate_options\n        {}\n      end\n    end\n\n    class InvalidGrantReuse < DoorkeeperError\n      def type\n        :invalid_grant\n      end\n    end\n\n    class InvalidTokenStrategy < DoorkeeperError\n      def type\n        :unsupported_grant_type\n      end\n    end\n\n    class MissingRequiredParameter < DoorkeeperError\n      attr_reader :missing_param\n\n      def initialize(missing_param)\n        super\n        @missing_param = missing_param\n      end\n\n      def type\n        :invalid_request\n      end\n    end\n\n    class BaseResponseError < DoorkeeperError\n      attr_reader :response\n\n      def initialize(response)\n        @response = response\n      end\n\n      def self.name_for_response\n        self.name.demodulize.underscore.to_sym\n      end\n    end\n\n    class InvalidCodeChallengeMethod < BaseResponseError\n      def self.translate_options\n        challenge_methods = Doorkeeper.config.pkce_code_challenge_methods_supported\n        {\n          challenge_methods: challenge_methods.join(\", \"),\n          count: challenge_methods.length\n        }\n      end\n    end\n\n    UnableToGenerateToken = Class.new(DoorkeeperError)\n    TokenGeneratorNotFound = Class.new(DoorkeeperError)\n    NoOrmCleaner = Class.new(DoorkeeperError)\n\n    InvalidRequest = Class.new(BaseResponseError)\n    InvalidToken = Class.new(BaseResponseError)\n    InvalidClient = Class.new(BaseResponseError)\n    InvalidScope = Class.new(BaseResponseError)\n    InvalidRedirectUri = Class.new(BaseResponseError)\n    InvalidGrant = Class.new(BaseResponseError)\n\n    UnauthorizedClient = Class.new(BaseResponseError)\n    UnsupportedResponseType = Class.new(BaseResponseError)\n    UnsupportedResponseMode = Class.new(BaseResponseError)\n\n    AccessDenied = Class.new(BaseResponseError)\n    ServerError = Class.new(BaseResponseError)\n\n    TokenExpired = Class.new(InvalidToken)\n    TokenRevoked = Class.new(InvalidToken)\n    TokenUnknown = Class.new(InvalidToken)\n    TokenForbidden = Class.new(InvalidToken)\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/grant_flow/fallback_flow.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module GrantFlow\n    class FallbackFlow < Flow\n      def handles_grant_type?\n        false\n      end\n\n      def handles_response_type?\n        false\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/grant_flow/flow.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module GrantFlow\n    class Flow\n      attr_reader :name, :grant_type_matches, :grant_type_strategy,\n                  :response_type_matches, :response_type_strategy,\n                  :response_mode_matches\n\n      def initialize(name, **options)\n        @name = name\n        @grant_type_matches = options[:grant_type_matches]\n        @grant_type_strategy = options[:grant_type_strategy]\n        @response_type_matches = options[:response_type_matches]\n        @response_type_strategy = options[:response_type_strategy]\n        @response_mode_matches = options[:response_mode_matches]\n      end\n\n      def handles_grant_type?\n        grant_type_matches.present?\n      end\n\n      def handles_response_type?\n        response_type_matches.present?\n      end\n\n      def matches_grant_type?(value)\n        grant_type_matches === value\n      end\n\n      def matches_response_type?(value)\n        response_type_matches === value\n      end\n\n      def default_response_mode\n        response_mode_matches[0]\n      end\n\n      def matches_response_mode?(value)\n        response_mode_matches.include?(value)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/grant_flow/registry.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module GrantFlow\n    module Registry\n      mattr_accessor :flows\n      self.flows = {}\n\n      mattr_accessor :aliases\n      self.aliases = {}\n\n      # Allows to register custom OAuth grant flow so that Doorkeeper\n      # could recognize and process it.\n      #\n      def register(name_or_flow, **options)\n        unless name_or_flow.is_a?(Doorkeeper::GrantFlow::Flow)\n          name_or_flow = Flow.new(name_or_flow, **options)\n        end\n\n        flow_key = name_or_flow.name.to_sym\n\n        if flows.key?(flow_key)\n          ::Kernel.warn <<~WARNING\n            [DOORKEEPER] '#{flow_key}' grant flow already registered and will be overridden\n            in #{caller(1..1).first}\n          WARNING\n        end\n\n        flows[flow_key] = name_or_flow\n      end\n\n      # Allows to register aliases that could be used in `grant_flows`\n      # configuration option. It is possible to have aliases like 1:1 or\n      # 1:N, i.e. \"implicit_oidc\" => ['token', 'id_token', 'id_token token'].\n      #\n      def register_alias(alias_name, **options)\n        aliases[alias_name.to_sym] = Array.wrap(options.fetch(:as))\n      end\n\n      def expand_alias(alias_name)\n        aliases.fetch(alias_name.to_sym, [])\n      end\n\n      # [NOTE]: make it to use #fetch after removing fallbacks\n      def get(name)\n        flows[name.to_sym]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/grant_flow.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"doorkeeper/grant_flow/flow\"\nrequire \"doorkeeper/grant_flow/fallback_flow\"\nrequire \"doorkeeper/grant_flow/registry\"\n\nmodule Doorkeeper\n  module GrantFlow\n    extend Registry\n\n    register(\n      :implicit,\n      response_type_matches: \"token\",\n      response_mode_matches: %w[fragment form_post],\n      response_type_strategy: Doorkeeper::Request::Token,\n    )\n\n    register(\n      :authorization_code,\n      response_type_matches: \"code\",\n      response_mode_matches: %w[query fragment form_post],\n      response_type_strategy: Doorkeeper::Request::Code,\n      grant_type_matches: \"authorization_code\",\n      grant_type_strategy: Doorkeeper::Request::AuthorizationCode,\n    )\n\n    register(\n      :client_credentials,\n      grant_type_matches: \"client_credentials\",\n      grant_type_strategy: Doorkeeper::Request::ClientCredentials,\n    )\n\n    register(\n      :password,\n      grant_type_matches: \"password\",\n      grant_type_strategy: Doorkeeper::Request::Password,\n    )\n\n    register(\n      :refresh_token,\n      grant_type_matches: \"refresh_token\",\n      grant_type_strategy: Doorkeeper::Request::RefreshToken,\n    )\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/grape/authorization_decorator.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Grape\n    class AuthorizationDecorator < SimpleDelegator\n      def parameters\n        params\n      end\n\n      def authorization\n        env = __getobj__.env\n        env[\"HTTP_AUTHORIZATION\"] ||\n          env[\"X-HTTP_AUTHORIZATION\"] ||\n          env[\"X_HTTP_AUTHORIZATION\"] ||\n          env[\"REDIRECT_X_HTTP_AUTHORIZATION\"]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/grape/helpers.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"doorkeeper/grape/authorization_decorator\"\n\nmodule Doorkeeper\n  module Grape\n    # Doorkeeper helpers for Grape applications.\n    # Provides helpers for endpoints authorization based on defined set of scopes.\n    module Helpers\n      # These helpers are for grape >= 0.10\n      extend ::Grape::API::Helpers\n      include Doorkeeper::Rails::Helpers\n\n      # endpoint specific scopes > parameter scopes > default scopes\n      def doorkeeper_authorize!(*scopes)\n        endpoint_scopes = endpoint.route_setting(:scopes) ||\n                          endpoint.options[:route_options][:scopes]\n\n        scopes = if endpoint_scopes\n                   Doorkeeper::OAuth::Scopes.from_array(endpoint_scopes)\n                 elsif scopes.present?\n                   Doorkeeper::OAuth::Scopes.from_array(scopes)\n                 end\n\n        super(*scopes)\n      end\n\n      def doorkeeper_render_error_with(error)\n        status_code = error_status_codes[error.status]\n        error!({ error: error.description }, status_code, error.headers)\n      end\n\n      private\n\n      def endpoint\n        env[\"api.endpoint\"]\n      end\n\n      def doorkeeper_token\n        @doorkeeper_token ||= OAuth::Token.authenticate(\n          decorated_request,\n          *Doorkeeper.config.access_token_methods,\n        )\n      end\n\n      def decorated_request\n        AuthorizationDecorator.new(request)\n      end\n\n      def error_status_codes\n        {\n          unauthorized: 401,\n          forbidden: 403,\n        }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/helpers/controller.rb",
    "content": "# frozen_string_literal: true\n\n# Define methods that can be called in any controller that inherits from\n# Doorkeeper::ApplicationMetalController or Doorkeeper::ApplicationController\nmodule Doorkeeper\n  module Helpers\n    # Rails controller helpers.\n    #\n    module Controller\n      def self.included(base)\n        base.helper_method :current_resource_owner if base.respond_to?(:helper_method)\n      end\n\n      private\n\n      # :doc:\n      def authenticate_resource_owner!\n        current_resource_owner\n      end\n\n      # :doc:\n      def current_resource_owner\n        return @current_resource_owner if defined?(@current_resource_owner)\n\n        @current_resource_owner ||= begin\n          instance_eval(&Doorkeeper.config.authenticate_resource_owner)\n        end\n      end\n\n      def resource_owner_from_credentials\n        instance_eval(&Doorkeeper.config.resource_owner_from_credentials)\n      end\n\n      # :doc:\n      def authenticate_admin!\n        instance_eval(&Doorkeeper.config.authenticate_admin)\n      end\n\n      def server\n        @server ||= Server.new(self)\n      end\n\n      # :doc:\n      def doorkeeper_token\n        return @doorkeeper_token if defined?(@doorkeeper_token)\n\n        @doorkeeper_token ||= OAuth::Token.authenticate(request, *config_methods)\n      end\n\n      def config_methods\n        @config_methods ||= Doorkeeper.config.access_token_methods\n      end\n\n      def get_error_response_from_exception(exception)\n        if exception.respond_to?(:response)\n          exception.response\n        elsif exception.type == :invalid_request\n          OAuth::InvalidRequestResponse.new(\n            name: exception.type,\n            state: params[:state],\n            missing_param: exception.missing_param,\n          )\n        else\n          OAuth::ErrorResponse.new(name: exception.type, state: params[:state])\n        end\n      end\n\n      def handle_token_exception(exception)\n        error = get_error_response_from_exception(exception)\n        headers.merge!(error.headers)\n        self.response_body = error.body.to_json\n        self.status = error.status\n      end\n\n      def skip_authorization?\n        !!instance_exec(\n          [server.current_resource_owner, @pre_auth.client],\n          &Doorkeeper.config.skip_authorization\n        )\n      end\n\n      def enforce_content_type\n        if (request.put? || request.post? || request.patch?) && !x_www_form_urlencoded?\n          render json: {}, status: :unsupported_media_type\n        end\n      end\n\n      def x_www_form_urlencoded?\n        request.media_type == \"application/x-www-form-urlencoded\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/models/access_grant_mixin.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module AccessGrantMixin\n    extend ActiveSupport::Concern\n\n    include OAuth::Helpers\n    include Models::Expirable\n    include Models::Revocable\n    include Models::Accessible\n    include Models::Orderable\n    include Models::SecretStorable\n    include Models::Scopes\n    include Models::ResourceOwnerable\n    include Models::Concerns::WriteToPrimary\n    include Models::ExpirationTimeSqlMath\n\n    # Never uses PKCE if PKCE migrations were not generated\n    def uses_pkce?\n      self.class.pkce_supported? && code_challenge.present?\n    end\n\n    module ClassMethods\n      # Searches for Doorkeeper::AccessGrant record with the\n      # specific token value.\n      #\n      # @param token [#to_s] token value (any object that responds to `#to_s`)\n      #\n      # @return [Doorkeeper::AccessGrant, nil]\n      #   AccessGrant object or nil if there is no record with such token\n      #\n      def by_token(token)\n        find_by_plaintext_token(:token, token)\n      end\n\n      # Revokes AccessGrant records that have not been revoked and associated\n      # with the specific Application and Resource Owner.\n      #\n      # @param application_id [Integer]\n      #   ID of the Application\n      # @param resource_owner [ActiveRecord::Base, Integer]\n      #   instance of the Resource Owner model or it's ID\n      #\n      def revoke_all_for(application_id, resource_owner, clock = Time)\n        with_primary_role do\n          by_resource_owner(resource_owner)\n            .where(\n              application_id: application_id,\n              revoked_at: nil,\n            )\n            .update_all(revoked_at: clock.now.utc)\n        end\n      end\n\n      # Implements PKCE code_challenge encoding without base64 padding as described in the spec.\n      # https://datatracker.ietf.org/doc/html/rfc7636#appendix-A\n      #   Appendix A.  Notes on Implementing Base64url Encoding without Padding\n      #\n      #   This appendix describes how to implement a base64url-encoding\n      #   function without padding, based upon the standard base64-encoding\n      #   function that uses padding.\n      #\n      #       To be concrete, example C# code implementing these functions is shown\n      #   below.  Similar code could be used in other languages.\n      #\n      #   static string base64urlencode(byte [] arg)\n      #   {\n      #       string s = Convert.ToBase64String(arg); // Regular base64 encoder\n      #       s = s.Split('=')[0]; // Remove any trailing '='s\n      #       s = s.Replace('+', '-'); // 62nd char of encoding\n      #       s = s.Replace('/', '_'); // 63rd char of encoding\n      #       return s;\n      #   }\n      #\n      #   An example correspondence between unencoded and encoded values\n      #   follows.  The octet sequence below encodes into the string below,\n      #   which when decoded, reproduces the octet sequence.\n      #\n      #   3 236 255 224 193\n      #\n      #   A-z_4ME\n      #\n      # https://ruby-doc.org/stdlib-2.1.3/libdoc/base64/rdoc/Base64.html#method-i-urlsafe_encode64\n      #\n      # urlsafe_encode64(bin)\n      # Returns the Base64-encoded version of bin. This method complies with\n      # \"Base 64 Encoding with URL and Filename Safe Alphabet\" in RFC 4648.\n      # The alphabet uses '-' instead of '+' and '_' instead of '/'.\n\n      # @param code_verifier [#to_s] a one time use value (any object that responds to `#to_s`)\n      #\n      # @return [#to_s] An encoded code challenge based on the provided verifier\n      # suitable for PKCE validation\n      #\n      def generate_code_challenge(code_verifier)\n        Base64.urlsafe_encode64(Digest::SHA256.digest(code_verifier), padding: false)\n      end\n\n      def pkce_supported?\n        column_names.include?(\"code_challenge\")\n      end\n\n      ##\n      # Determines the secret storing transformer\n      # Unless configured otherwise, uses the plain secret strategy\n      #\n      # @return [Doorkeeper::SecretStoring::Base]\n      #\n      def secret_strategy\n        ::Doorkeeper.config.token_secret_strategy\n      end\n\n      ##\n      # Determine the fallback storing strategy\n      # Unless configured, there will be no fallback\n      #\n      # @return [Doorkeeper::SecretStoring::Base]\n      #\n      def fallback_secret_strategy\n        ::Doorkeeper.config.token_secret_fallback_strategy\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/models/access_token_mixin.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module AccessTokenMixin\n    extend ActiveSupport::Concern\n\n    include OAuth::Helpers\n    include Models::Expirable\n    include Models::Reusable\n    include Models::Revocable\n    include Models::Accessible\n    include Models::Orderable\n    include Models::SecretStorable\n    include Models::Scopes\n    include Models::ResourceOwnerable\n    include Models::ExpirationTimeSqlMath\n    include Models::Concerns::WriteToPrimary\n\n    module ClassMethods\n      # Returns an instance of the Doorkeeper::AccessToken with\n      # specific plain text token value.\n      #\n      # @param token [#to_s]\n      #   Plain text token value (any object that responds to `#to_s`)\n      #\n      # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil\n      #   if there is no record with such token\n      #\n      def by_token(token)\n        find_by_plaintext_token(:token, token)\n      end\n\n      # Returns an instance of the Doorkeeper::AccessToken\n      # with specific token value.\n      #\n      # @param refresh_token [#to_s]\n      #   refresh token value (any object that responds to `#to_s`)\n      #\n      # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil\n      #   if there is no record with such refresh token\n      #\n      def by_refresh_token(refresh_token)\n        find_by_plaintext_token(:refresh_token, refresh_token)\n      end\n\n      # Returns an instance of the Doorkeeper::AccessToken\n      # found by previous refresh token. Keep in mind that value\n      # of the previous_refresh_token isn't encrypted using\n      # secrets strategy.\n      #\n      # @param previous_refresh_token [#to_s]\n      #   previous refresh token value (any object that responds to `#to_s`)\n      #\n      # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil\n      #   if there is no record with such refresh token\n      #\n      def by_previous_refresh_token(previous_refresh_token)\n        find_by(refresh_token: previous_refresh_token)\n      end\n\n      # Revokes AccessToken records that have not been revoked and associated\n      # with the specific Application and Resource Owner.\n      #\n      # @param application_id [Integer]\n      #   ID of the Application\n      # @param resource_owner [ActiveRecord::Base, Integer]\n      #   instance of the Resource Owner model or it's ID\n      #\n      def revoke_all_for(application_id, resource_owner, clock = Time)\n        with_primary_role do\n          by_resource_owner(resource_owner)\n            .where(\n              application_id: application_id,\n              revoked_at: nil,\n            )\n            .update_all(revoked_at: clock.now.utc)\n        end\n      end\n\n      # Looking for not revoked Access Token with a matching set of scopes\n      # that belongs to specific Application and Resource Owner.\n      #\n      # @param application [Doorkeeper::Application]\n      #   Application instance\n      # @param resource_owner [ActiveRecord::Base, Integer]\n      #   Resource Owner model instance or it's ID\n      # @param scopes [String, Doorkeeper::OAuth::Scopes]\n      #   set of scopes\n      # @param custom_attributes [Nilable Hash]\n      #   A nil value, or hash with keys corresponding to the custom attributes\n      #   configured with the `custom_access_token_attributes` config option.\n      #   A nil value will ignore custom attributes.\n      #\n      # @return [Doorkeeper::AccessToken, nil] Access Token instance or\n      #   nil if matching record was not found\n      #\n      def matching_token_for(application, resource_owner, scopes, custom_attributes: nil, include_expired: true)\n        tokens = authorized_tokens_for(application&.id, resource_owner)\n        tokens = tokens.not_expired unless include_expired\n        find_matching_token(tokens, application, custom_attributes, scopes)\n      end\n\n      # Interface to enumerate access token records in batches in order not\n      # to bloat the memory. Could be overloaded in any ORM extension.\n      #\n      def find_access_token_in_batches(relation, **args, &block)\n        relation.find_in_batches(**args, &block)\n      end\n\n      # Enumerates AccessToken records in batches to find a matching token.\n      # Batching is required in order not to pollute the memory if Application\n      # has huge amount of associated records.\n      #\n      # ActiveRecord 5.x - 6.x ignores custom ordering so we can't perform a\n      # database sort by created_at, so we need to load all the matching records,\n      # sort them and find latest one.\n      #\n      # @param relation [ActiveRecord::Relation]\n      #   Access tokens relation\n      # @param application [Doorkeeper::Application]\n      #   Application instance\n      # @param scopes [String, Doorkeeper::OAuth::Scopes]\n      #   set of scopes\n      # @param custom_attributes [Nilable Hash]\n      #   A nil value, or hash with keys corresponding to the custom attributes\n      #   configured with the `custom_access_token_attributes` config option.\n      #   A nil value will ignore custom attributes.\n      #\n      # @return [Doorkeeper::AccessToken, nil] Access Token instance or\n      #   nil if matching record was not found\n      #\n      def find_matching_token(relation, application, custom_attributes, scopes)\n        return nil unless relation\n\n        matching_tokens = []\n        batch_size = Doorkeeper.configuration.token_lookup_batch_size\n\n        find_access_token_in_batches(relation, batch_size: batch_size) do |batch|\n          tokens = batch.select do |token|\n            scopes_match?(token.scopes, scopes, application&.scopes) &&\n              custom_attributes_match?(token, custom_attributes)\n          end\n\n          matching_tokens.concat(tokens)\n        end\n\n        matching_tokens.max_by(&:created_at)\n      end\n\n      # Checks whether the token scopes match the scopes from the parameters\n      #\n      # @param token_scopes [#to_s]\n      #   set of scopes (any object that responds to `#to_s`)\n      # @param param_scopes [Doorkeeper::OAuth::Scopes]\n      #   scopes from params\n      # @param app_scopes [Doorkeeper::OAuth::Scopes]\n      #   Application scopes\n      #\n      # @return [Boolean] true if the param scopes match the token scopes,\n      #   and all the param scopes are defined in the application (or in the\n      #   server configuration if the application doesn't define any scopes),\n      #   and false in other cases\n      #\n      def scopes_match?(token_scopes, param_scopes, app_scopes)\n        return true if token_scopes.empty? && param_scopes.empty?\n\n        (token_scopes.sort == param_scopes.sort) &&\n          Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(\n            scope_str: param_scopes.to_s,\n            server_scopes: Doorkeeper.config.scopes,\n            app_scopes: app_scopes,\n          )\n      end\n\n      # Checks whether the token custom attribute values match the custom\n      # attributes from the parameters.\n      #\n      # @param token [Doorkeeper::AccessToken]\n      #   The access token whose custom attributes are being compared\n      #   to the custom_attributes.\n      #\n      # @param custom_attributes [Hash]\n      #   A hash of the attributes for which we want to determine whether\n      #   the token's custom attributes match.\n      #\n      # @return [Boolean] true if the token's custom attribute values\n      #   match those in the custom_attributes, or if both are empty/blank.\n      #   False otherwise.\n      def custom_attributes_match?(token, custom_attributes)\n        return true if custom_attributes.nil?\n\n        token_attribs = token.custom_attributes\n        return true if token_attribs.blank? && custom_attributes.blank?\n\n        Doorkeeper.config.custom_access_token_attributes.all? do |attribute|\n          token_attribs[attribute] == custom_attributes[attribute]\n        end\n      end\n\n      # Looking for not expired AccessToken record with a matching set of\n      # scopes that belongs to specific Application and Resource Owner.\n      # If it doesn't exists - then creates it.\n      #\n      # @param application [Doorkeeper::Application]\n      #   Application instance\n      # @param resource_owner [ActiveRecord::Base, Integer]\n      #   Resource Owner model instance or it's ID\n      # @param scopes [#to_s]\n      #   set of scopes (any object that responds to `#to_s`)\n      # @param token_attributes [Hash]\n      #   Additional attributes to use when creating a token\n      # @option token_attributes [Integer] :expires_in\n      #   token lifetime in seconds\n      # @option token_attributes [Boolean] :use_refresh_token\n      #   whether to use the refresh token\n      #\n      # @return [Doorkeeper::AccessToken] existing record or a new one\n      #\n      def find_or_create_for(application:, resource_owner:, scopes:, **token_attributes)\n        scopes = Doorkeeper::OAuth::Scopes.from_string(scopes) if scopes.is_a?(String)\n\n        if Doorkeeper.config.reuse_access_token\n          custom_attributes = extract_custom_attributes(token_attributes).presence\n          access_token = matching_token_for(\n            application, resource_owner, scopes, custom_attributes: custom_attributes, include_expired: false)\n\n          return access_token if access_token&.reusable?\n        end\n\n        create_for(\n          application: application,\n          resource_owner: resource_owner,\n          scopes: scopes,\n          **token_attributes,\n        )\n      end\n\n      # Creates a not expired AccessToken record with a matching set of\n      # scopes that belongs to specific Application and Resource Owner.\n      #\n      # @param application [Doorkeeper::Application]\n      #   Application instance\n      # @param resource_owner [ActiveRecord::Base, Integer]\n      #   Resource Owner model instance or it's ID\n      # @param scopes [#to_s]\n      #   set of scopes (any object that responds to `#to_s`)\n      # @param token_attributes [Hash]\n      #   Additional attributes to use when creating a token\n      # @option token_attributes [Integer] :expires_in\n      #   token lifetime in seconds\n      # @option token_attributes [Boolean] :use_refresh_token\n      #   whether to use the refresh token\n      #\n      # @return [Doorkeeper::AccessToken] new access token\n      #\n      def create_for(application:, resource_owner:, scopes:, **token_attributes)\n        token_attributes[:application] = application\n        token_attributes[:scopes] = scopes.to_s\n\n        if Doorkeeper.config.polymorphic_resource_owner?\n          token_attributes[:resource_owner] = resource_owner\n        else\n          token_attributes[:resource_owner_id] = resource_owner_id_for(resource_owner)\n        end\n\n        with_primary_role do\n          create!(token_attributes)\n        end\n      end\n\n      # Looking for not revoked Access Token records that belongs to specific\n      # Application and Resource Owner.\n      #\n      # @param application_id [Integer]\n      #   ID of the Application model instance\n      # @param resource_owner [ActiveRecord::Base, Integer]\n      #   Resource Owner model instance or it's ID\n      #\n      # @return [ActiveRecord::Relation]\n      #   collection of matching AccessToken objects\n      #\n      def authorized_tokens_for(application_id, resource_owner)\n        by_resource_owner(resource_owner).where(\n          application_id: application_id,\n          revoked_at: nil,\n        )\n      end\n\n      # Convenience method for backwards-compatibility, return the last\n      # matching token for the given Application and Resource Owner.\n      #\n      # @param application_id [Integer]\n      #   ID of the Application model instance\n      # @param resource_owner [ActiveRecord::Base, Integer]\n      #   ID of the Resource Owner model instance\n      #\n      # @return [Doorkeeper::AccessToken, nil] matching AccessToken object or\n      #   nil if nothing was found\n      #\n      def last_authorized_token_for(application_id, resource_owner)\n        authorized_tokens_for(application_id, resource_owner)\n          .ordered_by(:created_at, :desc)\n          .first\n      end\n\n      ##\n      # Determines the secret storing transformer\n      # Unless configured otherwise, uses the plain secret strategy\n      #\n      # @return [Doorkeeper::SecretStoring::Base]\n      #\n      def secret_strategy\n        ::Doorkeeper.config.token_secret_strategy\n      end\n\n      ##\n      # Determine the fallback storing strategy\n      # Unless configured, there will be no fallback\n      def fallback_secret_strategy\n        ::Doorkeeper.config.token_secret_fallback_strategy\n      end\n\n      # Extracts the token's custom attributes (defined by the\n      # custom_access_token_attributes config option) from the token's attributes.\n      #\n      # @param attributes [Hash]\n      #   A hash of the access token's attributes.\n      # @return [Hash]\n      #   A hash containing only the custom access token attributes.\n      def extract_custom_attributes(attributes)\n        attributes.with_indifferent_access.slice(\n          *Doorkeeper.configuration.custom_access_token_attributes)\n      end\n    end\n\n    # Access Token type: Bearer.\n    # @see https://datatracker.ietf.org/doc/html/rfc6750\n    #   The OAuth 2.0 Authorization Framework: Bearer Token Usage\n    #\n    def token_type\n      \"Bearer\"\n    end\n\n    def use_refresh_token?\n      @use_refresh_token ||= false\n      !!@use_refresh_token\n    end\n\n    # JSON representation of the Access Token instance.\n    #\n    # @return [Hash] hash with token data\n    def as_json(_options = {})\n      {\n        resource_owner_id: resource_owner_id,\n        scope: scopes,\n        expires_in: expires_in_seconds,\n        application: { uid: application.try(:uid) },\n        created_at: created_at.to_i,\n      }.tap do |json|\n        if Doorkeeper.configuration.polymorphic_resource_owner?\n          json[:resource_owner_type] = resource_owner_type\n        end\n      end\n    end\n\n    # The token's custom attributes, as defined by\n    # the custom_access_token_attributes config option.\n    #\n    # @return [Hash] hash of custom access token attributes.\n    def custom_attributes\n      self.class.extract_custom_attributes(attributes)\n    end\n\n    # Indicates whether the token instance have the same credential\n    # as the other Access Token.\n    #\n    # @param access_token [Doorkeeper::AccessToken] other token\n    #\n    # @return [Boolean] true if credentials are same of false in other cases\n    #\n    def same_credential?(access_token)\n      application_id == access_token.application_id &&\n        same_resource_owner?(access_token)\n    end\n\n    # Indicates whether the token instance have the same credential\n    # as the other Access Token.\n    #\n    # @param access_token [Doorkeeper::AccessToken] other token\n    #\n    # @return [Boolean] true if credentials are same of false in other cases\n    #\n    def same_resource_owner?(access_token)\n      if Doorkeeper.configuration.polymorphic_resource_owner?\n        resource_owner == access_token.resource_owner\n      else\n        resource_owner_id == access_token.resource_owner_id\n      end\n    end\n\n    # Indicates if token is acceptable for specific scopes.\n    #\n    # @param scopes [Array<String>] scopes\n    #\n    # @return [Boolean] true if record is accessible and includes scopes or\n    #   false in other cases\n    #\n    def acceptable?(scopes)\n      accessible? && includes_scope?(*scopes)\n    end\n\n    # We keep a volatile copy of the raw refresh token for initial communication\n    # The stored refresh_token may be mapped and not available in cleartext.\n    def plaintext_refresh_token\n      if secret_strategy.allows_restoring_secrets?\n        secret_strategy.restore_secret(self, :refresh_token)\n      else\n        @raw_refresh_token\n      end\n    end\n\n    # We keep a volatile copy of the raw token for initial communication\n    # The stored refresh_token may be mapped and not available in cleartext.\n    #\n    # Some strategies allow restoring stored secrets (e.g. symmetric encryption)\n    # while hashing strategies do not, so you cannot rely on this value\n    # returning a present value for persisted tokens.\n    def plaintext_token\n      if secret_strategy.allows_restoring_secrets?\n        secret_strategy.restore_secret(self, :token)\n      else\n        @raw_token\n      end\n    end\n\n    # Revokes token with `:refresh_token` equal to `:previous_refresh_token`\n    # and clears `:previous_refresh_token` attribute.\n    #\n    def revoke_previous_refresh_token!\n      return if !self.class.refresh_token_revoked_on_use? || previous_refresh_token.blank?\n\n      old_refresh_token&.revoke\n\n      if self.class.respond_to?(:with_primary_role)\n        self.class.with_primary_role { update_attribute(:previous_refresh_token, \"\") }\n      else\n        update_attribute(:previous_refresh_token, \"\")\n      end\n    end\n\n    private\n\n    # Searches for Access Token record with `:refresh_token` equal to\n    # `:previous_refresh_token` value.\n    #\n    # @return [Doorkeeper::AccessToken, nil]\n    #   Access Token record or nil if nothing found\n    #\n    def old_refresh_token\n      @old_refresh_token ||= self.class.by_previous_refresh_token(previous_refresh_token)\n    end\n\n    # Generates refresh token with UniqueToken generator.\n    #\n    # @return [String] refresh token value\n    #\n    def generate_refresh_token\n      @raw_refresh_token = UniqueToken.generate\n      secret_strategy.store_secret(self, :refresh_token, @raw_refresh_token)\n    end\n\n    # Generates and sets the token value with the\n    # configured Generator class (see Doorkeeper.config).\n    #\n    # @return [String] generated token value\n    #\n    # @raise [Doorkeeper::Errors::UnableToGenerateToken]\n    #   custom class doesn't implement .generate method\n    # @raise [Doorkeeper::Errors::TokenGeneratorNotFound]\n    #   custom class doesn't exist\n    #\n    def generate_token\n      self.created_at ||= Time.now.utc\n\n      @raw_token = token_generator.generate(attributes_for_token_generator)\n      secret_strategy.store_secret(self, :token, @raw_token)\n      @raw_token\n    end\n\n    # Set of attributes that would be passed to token generator to\n    # generate unique token based on them.\n    #\n    #  @return [Hash] set of attributes\n    #\n    def attributes_for_token_generator\n      {\n        resource_owner_id: resource_owner_id,\n        scopes: scopes,\n        application: application,\n        expires_in: expires_in,\n        created_at: created_at,\n      }.tap do |attributes|\n        if Doorkeeper.config.polymorphic_resource_owner?\n          attributes[:resource_owner] = resource_owner\n        end\n\n        Doorkeeper.config.custom_access_token_attributes.each do |attribute_name|\n          attributes[attribute_name] = public_send(attribute_name)\n        end\n      end\n    end\n\n    def token_generator\n      generator_name = Doorkeeper.config.access_token_generator\n      generator = generator_name.constantize\n\n      return generator if generator.respond_to?(:generate)\n\n      raise Errors::UnableToGenerateToken, \"#{generator} does not respond to `.generate`.\"\n    rescue NameError\n      raise Errors::TokenGeneratorNotFound, \"#{generator_name} not found\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/models/application_mixin.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module ApplicationMixin\n    extend ActiveSupport::Concern\n\n    include OAuth::Helpers\n    include Models::Orderable\n    include Models::SecretStorable\n    include Models::Scopes\n\n    # :nodoc\n    module ClassMethods\n      # Returns an instance of the Doorkeeper::Application with\n      # specific UID and secret.\n      #\n      # Public/Non-confidential applications will only find by uid if secret is\n      # blank.\n      #\n      # @param uid [#to_s] UID (any object that responds to `#to_s`)\n      # @param secret [#to_s] secret (any object that responds to `#to_s`)\n      #\n      # @return [Doorkeeper::Application, nil]\n      #   Application instance or nil if there is no record with such credentials\n      #\n      def by_uid_and_secret(uid, secret)\n        app = by_uid(uid)\n        return unless app\n        return app if secret.blank? && !app.confidential?\n        return unless app.secret_matches?(secret)\n\n        app\n      end\n\n      # Returns an instance of the Doorkeeper::Application with specific UID.\n      #\n      # @param uid [#to_s] UID (any object that responds to `#to_s`)\n      #\n      # @return [Doorkeeper::Application, nil] Application instance or nil\n      #   if there is no record with such UID\n      #\n      def by_uid(uid)\n        find_by(uid: uid.to_s)\n      end\n\n      ##\n      # Determines the secret storing transformer\n      # Unless configured otherwise, uses the plain secret strategy\n      def secret_strategy\n        ::Doorkeeper.config.application_secret_strategy\n      end\n\n      ##\n      # Determine the fallback storing strategy\n      # Unless configured, there will be no fallback\n      def fallback_secret_strategy\n        ::Doorkeeper.config.application_secret_fallback_strategy\n      end\n    end\n\n    # Set an application's valid redirect URIs.\n    #\n    # @param uris [String, Array<String>] Newline-separated string or array the URI(s)\n    #\n    # @return [String] The redirect URI(s) separated by newlines.\n    #\n    def redirect_uri=(uris)\n      super(uris.is_a?(Array) ? uris.join(\"\\n\") : uris)\n    end\n\n    # Check whether the given plain text secret matches our stored secret\n    #\n    # @param input [#to_s] Plain secret provided by user\n    #        (any object that responds to `#to_s`)\n    #\n    # @return [Boolean] Whether the given secret matches the stored secret\n    #                of this application.\n    #\n    def secret_matches?(input)\n      # return false if either is nil, since secure_compare depends on strings\n      # but Application secrets MAY be nil depending on confidentiality.\n      return false if input.nil? || secret.nil?\n\n      # When matching the secret by comparer function, all is well.\n      return true if secret_strategy.secret_matches?(input, secret)\n\n      # When fallback lookup is enabled, ensure applications\n      # with plain secrets can still be found\n      if fallback_secret_strategy\n        fallback_secret_strategy.secret_matches?(input, secret)\n      else\n        false\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/models/concerns/accessible.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Models\n    module Accessible\n      # Indicates whether the object is accessible (not expired and not revoked).\n      #\n      # @return [Boolean] true if object accessible or false in other case\n      #\n      def accessible?\n        !expired? && !revoked?\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/models/concerns/expirable.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Models\n    module Expirable\n      # Indicates whether the object is expired (`#expires_in` present and\n      # expiration time has come).\n      #\n      # @return [Boolean] true if object expired and false in other case\n      def expired?\n        !!(expires_in && Time.now.utc > expires_at)\n      end\n\n      # Calculates expiration time in seconds.\n      #\n      # @return [Integer, nil] number of seconds if object has expiration time\n      #   or nil if object never expires.\n      def expires_in_seconds\n        return nil if expires_in.nil?\n\n        expires = expires_at - Time.now.utc\n        expires_sec = expires.seconds.round(0)\n        expires_sec > 0 ? expires_sec : 0\n      end\n\n      # Expiration time (date time of creation + TTL).\n      #\n      # @return [Time, nil] expiration time in UTC\n      #   or nil if the object never expires.\n      #\n      def expires_at\n        expires_in && created_at + expires_in.seconds\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/models/concerns/expiration_time_sql_math.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Models\n    module ExpirationTimeSqlMath\n      extend ::ActiveSupport::Concern\n\n      WARNING_MESSAGE = <<~WARNING.squish\n        [DOORKEEPER] Doorkeeper doesn't support expiration time math for your database adapter.\n        Records with an individual expires_in value longer than the global TTL may be incorrectly processed.\n        Please add a class method `custom_expiration_time_sql` to your AccessToken/AccessGrant models/mixins to provide a custom\n        SQL expression to calculate access token expiration time. See lib/doorkeeper/orm/active_record/mixins/access_token.rb\n        for more details.\n      WARNING\n\n      class ExpirationTimeSqlGenerator\n        attr_reader :model\n\n        delegate :table_name, to: :@model\n\n        def initialize(model)\n          @model = model\n        end\n\n        def generate_sql\n          raise \"`generate_sql` should be overridden for a #{self.class.name}!\"\n        end\n      end\n\n      class MySqlExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator\n        def generate_sql\n          Arel.sql(\"DATE_ADD(#{table_name}.created_at, INTERVAL #{table_name}.expires_in SECOND)\")\n        end\n      end\n\n      class SqlLiteExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator\n        def generate_sql\n          Arel.sql(\"DATETIME(#{table_name}.created_at, '+' || #{table_name}.expires_in || ' SECONDS')\")\n        end\n      end\n\n      class SqlServerExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator\n        def generate_sql\n          Arel.sql(\"DATEADD(second, #{table_name}.expires_in, #{table_name}.created_at) AT TIME ZONE 'UTC'\")\n        end\n      end\n\n      class OracleExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator\n        def generate_sql\n          Arel.sql(\"#{table_name}.created_at + INTERVAL to_char(#{table_name}.expires_in) second\")\n        end\n      end\n\n      class PostgresExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator\n        def generate_sql\n          Arel.sql(\"#{table_name}.created_at + #{table_name}.expires_in * INTERVAL '1 SECOND'\")\n        end\n      end\n\n      ADAPTERS_MAPPING = {\n        \"sqlite\" => SqlLiteExpirationTimeSqlGenerator,\n        \"sqlite3\" => SqlLiteExpirationTimeSqlGenerator,\n        \"postgis\" => PostgresExpirationTimeSqlGenerator,\n        \"postgresql\" => PostgresExpirationTimeSqlGenerator,\n        \"mysql\" => MySqlExpirationTimeSqlGenerator,\n        \"mysql2\" => MySqlExpirationTimeSqlGenerator,\n        \"trilogy\" => MySqlExpirationTimeSqlGenerator,\n        \"sqlserver\" => SqlServerExpirationTimeSqlGenerator,\n        \"oracleenhanced\" => OracleExpirationTimeSqlGenerator,\n      }.freeze\n\n      module ClassMethods\n        def supports_expiration_time_math?\n          ADAPTERS_MAPPING.key?(adapter_name.downcase) ||\n            respond_to?(:custom_expiration_time_sql)\n        end\n\n        def expiration_time_sql\n          if respond_to?(:custom_expiration_time_sql)\n            custom_expiration_time_sql\n          else\n            expiration_time_sql_expression\n          end\n        end\n\n        def expiration_time_sql_expression\n          ADAPTERS_MAPPING.fetch(adapter_name.downcase).new(self).generate_sql\n        end\n\n        def adapter_name\n          ActiveRecord::Base.connection.adapter_name\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/models/concerns/orderable.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Models\n    module Orderable\n      extend ActiveSupport::Concern\n\n      module ClassMethods\n        def ordered_by(attribute, direction = :asc)\n          order(attribute => direction)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/models/concerns/ownership.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Models\n    module Ownership\n      extend ActiveSupport::Concern\n\n      included do\n        belongs_to :owner, polymorphic: true, optional: true\n        validates :owner, presence: true, if: :validate_owner?\n      end\n\n      def validate_owner?\n        Doorkeeper.config.confirm_application_owner?\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/models/concerns/polymorphic_resource_owner.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Models\n    module PolymorphicResourceOwner\n      module ForAccessGrant\n        extend ActiveSupport::Concern\n\n        included do\n          if Doorkeeper.config.polymorphic_resource_owner?\n            belongs_to :resource_owner, polymorphic: true, optional: false\n          else\n            validates :resource_owner_id, presence: true\n          end\n        end\n      end\n\n      module ForAccessToken\n        extend ActiveSupport::Concern\n\n        included do\n          if Doorkeeper.config.polymorphic_resource_owner?\n            belongs_to :resource_owner, polymorphic: true, optional: true\n          end\n        end\n      end\n    end\n  end\nend\n\n"
  },
  {
    "path": "lib/doorkeeper/models/concerns/resource_ownerable.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Models\n    module ResourceOwnerable\n      extend ActiveSupport::Concern\n\n      module ClassMethods\n        # Searches for record by Resource Owner considering Doorkeeper\n        # configuration for resource owner association.\n        #\n        # @param resource_owner [ActiveRecord::Base, Integer]\n        #   resource owner\n        #\n        # @return [Doorkeeper::AccessGrant, Doorkeeper::AccessToken]\n        #   collection of records\n        #\n        def by_resource_owner(resource_owner)\n          if Doorkeeper.configuration.polymorphic_resource_owner?\n            where(resource_owner: resource_owner)\n          else\n            where(resource_owner_id: resource_owner_id_for(resource_owner))\n          end\n        end\n\n        protected\n\n        # Backward compatible way to retrieve resource owner itself (if\n        # polymorphic association enabled) or just it's ID.\n        #\n        # @param resource_owner [ActiveRecord::Base, Integer]\n        #   resource owner\n        #\n        # @return [ActiveRecord::Base, Integer]\n        #   instance of Resource Owner or it's ID\n        #\n        def resource_owner_id_for(resource_owner)\n          if resource_owner.respond_to?(:to_key)\n            resource_owner.id\n          else\n            resource_owner\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/models/concerns/reusable.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Models\n    module Reusable\n      # Indicates whether the object is reusable (i.e. It is not expired and\n      # has not crossed reuse_limit).\n      #\n      # @return [Boolean] true if can be reused and false in other case\n      def reusable?\n        return false if expired?\n        return true unless expires_in\n\n        threshold_limit = 100 - Doorkeeper.config.token_reuse_limit\n        expires_in_seconds >= threshold_limit * expires_in / 100\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/models/concerns/revocable.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Models\n    module Revocable\n      # Revokes the object (updates `:revoked_at` attribute setting its value\n      # to the specific time).\n      #\n      # @param clock [Time] time object\n      #\n      def revoke(clock = Time)\n        return if revoked?\n\n        # Wrap in with_primary_role if the model class supports it\n        if self.class.respond_to?(:with_primary_role)\n          self.class.with_primary_role { update_attribute(:revoked_at, clock.now.utc) }\n        else\n          update_attribute(:revoked_at, clock.now.utc)\n        end\n      end\n\n      # Indicates whether the object has been revoked.\n      #\n      # @return [Boolean] true if revoked, false in other case\n      #\n      def revoked?\n        !!(revoked_at && revoked_at <= Time.now.utc)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/models/concerns/scopes.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Models\n    module Scopes\n      def scopes\n        OAuth::Scopes.from_string(scopes_string)\n      end\n\n      def scopes=(value)\n        if value.is_a?(Array)\n          super(Doorkeeper::OAuth::Scopes.from_array(value).to_s)\n        else\n          super(Doorkeeper::OAuth::Scopes.from_string(value.to_s).to_s)\n        end\n      end\n\n      def scopes_string\n        self[:scopes]\n      end\n\n      def includes_scope?(*required_scopes)\n        required_scopes.blank? || required_scopes.any? { |scope| scopes.exists?(scope.to_s) }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/models/concerns/secret_storable.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Models\n    ##\n    # Storable finder to provide lookups for input plaintext values which are\n    # mapped to their stored versions (e.g., hashing, encryption) before lookup.\n    module SecretStorable\n      extend ActiveSupport::Concern\n\n      delegate :secret_strategy,\n               :fallback_secret_strategy,\n               to: :class\n\n      # :nodoc\n      module ClassMethods\n        # Compare the given plaintext with the secret\n        #\n        # @param input [String]\n        #   The plain input to compare.\n        #\n        # @param secret [String]\n        #   The secret value to compare with.\n        #\n        # @return [Boolean]\n        #   Whether input matches secret as per the secret strategy\n        #\n        delegate :secret_matches?, to: :secret_strategy\n\n        # Returns an instance of the Doorkeeper::AccessToken with\n        # specific token value.\n        #\n        # @param attr [Symbol]\n        #   The token attribute we're looking with.\n        #\n        # @param token [#to_s]\n        #   token value (any object that responds to `#to_s`)\n        #\n        # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil\n        #   if there is no record with such token\n        #\n        def find_by_plaintext_token(attr, token)\n          token = token.to_s\n\n          find_by(attr => secret_strategy.transform_secret(token)) ||\n            find_by_fallback_token(attr, token)\n        end\n\n        # Allow looking up previously plain tokens as a fallback\n        # IFF a fallback strategy has been defined\n        #\n        # @param attr [Symbol]\n        #   The token attribute we're looking with.\n        #\n        # @param plain_secret [#to_s]\n        #   plain secret value (any object that responds to `#to_s`)\n        #\n        # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil\n        #   if there is no record with such token\n        #\n        def find_by_fallback_token(attr, plain_secret)\n          return nil unless fallback_secret_strategy\n\n          # Use the previous strategy to look up\n          stored_token = fallback_secret_strategy.transform_secret(plain_secret)\n          find_by(attr => stored_token).tap do |resource|\n            return nil unless resource\n\n            upgrade_fallback_value resource, attr, plain_secret\n          end\n        end\n\n        # Allow implementations in ORMs to replace a plain\n        # value falling back to to avoid it remaining as plain text.\n        #\n        # @param instance\n        #   An instance of this model with a plain value token.\n        #\n        # @param attr\n        #   The secret attribute name to upgrade.\n        #\n        # @param plain_secret\n        #   The plain secret to upgrade.\n        #\n        def upgrade_fallback_value(instance, attr, plain_secret)\n          upgraded = secret_strategy.store_secret(instance, attr, plain_secret)\n          instance.update(attr => upgraded)\n        end\n\n        ##\n        # Determines the secret storing transformer\n        # Unless configured otherwise, uses the plain secret strategy\n        def secret_strategy\n          ::Doorkeeper::SecretStoring::Plain\n        end\n\n        ##\n        # Determine the fallback storing strategy\n        # Unless configured, there will be no fallback\n        def fallback_secret_strategy\n          nil\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/models/concerns/write_to_primary.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Models\n    module Concerns\n      # Provides support for Rails read replicas by ensuring write operations\n      # use the primary database when automatic role switching is enabled.\n      #\n      # When Rails uses automatic role switching with read replicas, GET requests\n      # are routed to read-only databases. However, Doorkeeper may need to write\n      # to the database during GET requests (e.g., creating access tokens during\n      # implicit grant flow). This concern wraps write operations with\n      # `connected_to(role: :writing)` to ensure they use the primary database.\n      #\n      # This concern is only active when:\n      # 1. ActiveRecord supports `connected_to` (Rails 6.1+)\n      # 2. The configuration option is enabled\n      #\n      module WriteToPrimary\n        extend ActiveSupport::Concern\n\n        class_methods do\n          # Executes the given block with a connection to the primary database\n          # for writing, if read replica support is enabled and available.\n          #\n          # @yield Block to execute with write connection\n          # @return The result of the block\n          #\n          def with_primary_role(&block)\n            if should_use_primary_role?\n              ::ActiveRecord::Base.connected_to(role: :writing, &block)\n            else\n              yield\n            end\n          end\n\n          private\n\n          # Determines if we should explicitly use the primary role for writes\n          #\n          # @return [Boolean]\n          #\n          def should_use_primary_role?\n            # Guard clause: return false if ActiveRecord is not available\n            return false unless defined?(::ActiveRecord::Base)\n\n            # Only use primary role if:\n            # 1. The enable_multiple_database_roles option is enabled in config\n            # 2. ActiveRecord supports connected_to (Rails 6.1+)\n            Doorkeeper.config.enable_multiple_database_roles &&\n              ::ActiveRecord::Base.respond_to?(:connected_to)\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/authorization/code.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    module Authorization\n      class Code\n        attr_reader :pre_auth, :resource_owner, :token\n\n        def initialize(pre_auth, resource_owner)\n          @pre_auth = pre_auth\n          @resource_owner = resource_owner\n        end\n\n        def issue_token!\n          return @token if defined?(@token)\n\n          @token = Doorkeeper.config.access_grant_model.with_primary_role do\n            Doorkeeper.config.access_grant_model.create!(access_grant_attributes)\n          end\n        end\n\n        def oob_redirect\n          { action: :show, code: token.plaintext_token }\n        end\n\n        def access_grant?\n          true\n        end\n\n        private\n\n        def authorization_code_expires_in\n          Doorkeeper.config.authorization_code_expires_in\n        end\n\n        def access_grant_attributes\n          attributes = {\n            application_id: pre_auth.client.id,\n            expires_in: authorization_code_expires_in,\n            redirect_uri: pre_auth.redirect_uri,\n            scopes: pre_auth.scopes.to_s,\n          }\n\n          if Doorkeeper.config.polymorphic_resource_owner?\n            attributes[:resource_owner] = resource_owner\n          else\n            attributes[:resource_owner_id] = resource_owner.id\n          end\n\n          pkce_attributes.merge(attributes).merge(custom_attributes)\n        end\n\n        def custom_attributes\n          # Custom access token attributes are saved into the access grant,\n          # and then included in subsequently generated access tokens.\n          @pre_auth.custom_access_token_attributes.to_h.with_indifferent_access\n        end\n\n        def pkce_attributes\n          return {} unless pkce_supported?\n\n          {\n            code_challenge: pre_auth.code_challenge,\n            code_challenge_method: pre_auth.code_challenge_method,\n          }\n        end\n\n        # Ensures firstly, if migration with additional PKCE columns was\n        # generated and migrated\n        def pkce_supported?\n          Doorkeeper.config.access_grant_model.pkce_supported?\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/authorization/context.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    module Authorization\n      class Context\n        attr_reader :client, :grant_type, :resource_owner, :scopes\n\n        def initialize(**attributes)\n          attributes.each do |name, value|\n            instance_variable_set(:\"@#{name}\", value) if respond_to?(name)\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/authorization/token.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    module Authorization\n      class Token\n        attr_reader :pre_auth, :resource_owner, :token\n\n        class << self\n          def build_context(pre_auth_or_oauth_client, grant_type, scopes, resource_owner)\n            oauth_client = if pre_auth_or_oauth_client.respond_to?(:application)\n                             pre_auth_or_oauth_client.application\n                           elsif pre_auth_or_oauth_client.respond_to?(:client)\n                             pre_auth_or_oauth_client.client\n                           else\n                             pre_auth_or_oauth_client\n                           end\n\n            Doorkeeper::OAuth::Authorization::Context.new(\n              client: oauth_client,\n              grant_type: grant_type,\n              scopes: scopes,\n              resource_owner: resource_owner,\n            )\n          end\n\n          def access_token_expires_in(configuration, context)\n            if configuration.option_defined?(:custom_access_token_expires_in)\n              expiration = configuration.custom_access_token_expires_in.call(context)\n              return nil if expiration == Float::INFINITY\n\n              expiration || configuration.access_token_expires_in\n            else\n              configuration.access_token_expires_in\n            end\n          end\n\n          def refresh_token_enabled?(server, context)\n            if server.refresh_token_enabled?.respond_to?(:call)\n              server.refresh_token_enabled?.call(context)\n            else\n              !!server.refresh_token_enabled?\n            end\n          end\n        end\n\n        def initialize(pre_auth, resource_owner)\n          @pre_auth       = pre_auth\n          @resource_owner = resource_owner\n        end\n\n        def issue_token!\n          return @token if defined?(@token)\n\n          context = self.class.build_context(\n            pre_auth.client,\n            Doorkeeper::OAuth::IMPLICIT,\n            pre_auth.scopes,\n            resource_owner,\n          )\n\n          @token = Doorkeeper.config.access_token_model.find_or_create_for(\n            application: application,\n            resource_owner: resource_owner,\n            scopes: pre_auth.scopes,\n            expires_in: self.class.access_token_expires_in(Doorkeeper.config, context),\n            use_refresh_token: false,\n          )\n        end\n\n        def application\n          return unless pre_auth.client\n\n          pre_auth.client.is_a?(Doorkeeper.config.application_model) ? pre_auth.client : pre_auth.client.application\n        end\n\n        def oob_redirect\n          {\n            controller: controller,\n            action: :show,\n            access_token: token.plaintext_token,\n          }\n        end\n\n        def access_token?\n          true\n        end\n\n        private\n\n        def controller\n          @controller ||= begin\n            mapping = Doorkeeper::Rails::Routes.mapping[:token_info] || {}\n            mapping[:controllers] || \"doorkeeper/token_info\"\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/authorization/uri_builder.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"rack/utils\"\n\nmodule Doorkeeper\n  module OAuth\n    module Authorization\n      class URIBuilder\n        class << self\n          def uri_with_query(url, parameters = {})\n            uri = URI.parse(url)\n            original_query = Rack::Utils.parse_query(uri.query)\n            uri.query = build_query(original_query.merge(parameters))\n            uri.to_s\n          end\n\n          def uri_with_fragment(url, parameters = {})\n            uri = URI.parse(url)\n            uri.fragment = build_query(parameters)\n            uri.to_s\n          end\n\n          private\n\n          def build_query(parameters = {})\n            parameters.reject! { |_, value| value.blank? }\n            Rack::Utils.build_query(parameters)\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/authorization_code_request.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class AuthorizationCodeRequest < BaseRequest\n      validate :params,       error: Errors::InvalidRequest\n      validate :client,       error: Errors::InvalidClient\n      validate :grant,        error: Errors::InvalidGrant\n      # @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.2\n      validate :redirect_uri, error: Errors::InvalidGrant\n      validate :code_verifier, error: Errors::InvalidGrant\n\n      attr_reader :grant, :client, :redirect_uri, :access_token, :code_verifier,\n                  :invalid_request_reason, :missing_param\n\n      def initialize(server, grant, client, parameters = {})\n        @server = server\n        @client = client\n        @grant  = grant\n        @grant_type = Doorkeeper::OAuth::AUTHORIZATION_CODE\n        @redirect_uri = parameters[:redirect_uri]\n        @code_verifier = parameters[:code_verifier]\n      end\n\n      private\n\n      def before_successful_response\n        grant.transaction do\n          grant.lock!\n          raise Errors::InvalidGrantReuse if grant.revoked?\n\n          if Doorkeeper.config.revoke_previous_authorization_code_token?\n            revoke_previous_tokens(grant.application, resource_owner)\n          end\n\n          grant.revoke\n\n          find_or_create_access_token(\n            client,\n            resource_owner,\n            grant.scopes,\n            custom_token_attributes_with_data,\n            server,\n          )\n        end\n\n        super\n      end\n\n      def resource_owner\n        if Doorkeeper.config.polymorphic_resource_owner?\n          grant.resource_owner\n        else\n          grant.resource_owner_id\n        end\n      end\n\n      def pkce_supported?\n        Doorkeeper.config.access_grant_model.pkce_supported?\n      end\n\n      def validate_params\n        @missing_param =\n          if grant&.uses_pkce? && code_verifier.blank?\n            :code_verifier\n          elsif client && !client.confidential && Doorkeeper.config.force_pkce? && code_verifier.blank?\n            :code_verifier\n          elsif redirect_uri.blank?\n            :redirect_uri\n          end\n\n        @missing_param.nil?\n      end\n\n      def validate_client\n        client.present?\n      end\n\n      def validate_grant\n        return false unless grant && grant.application_id == client.id\n\n        grant.accessible?\n      end\n\n      def validate_redirect_uri\n        Helpers::URIChecker.valid_for_authorization?(\n          redirect_uri,\n          grant.redirect_uri,\n        )\n      end\n\n      # if either side (server or client) request PKCE, check the verifier\n      # against the DB - if PKCE is supported\n      def validate_code_verifier\n        return true unless pkce_supported?\n        return grant.code_challenge.blank? if code_verifier.blank?\n\n        if grant.code_challenge_method == \"S256\"\n          grant.code_challenge == generate_code_challenge(code_verifier)\n        elsif grant.code_challenge_method == \"plain\"\n          grant.code_challenge == code_verifier\n        else\n          false\n        end\n      end\n\n      def generate_code_challenge(code_verifier)\n        Doorkeeper.config.access_grant_model.generate_code_challenge(code_verifier)\n      end\n\n      def custom_token_attributes_with_data\n        grant\n          .attributes\n          .with_indifferent_access\n          .slice(*Doorkeeper.config.custom_access_token_attributes)\n          .symbolize_keys\n      end\n\n      def revoke_previous_tokens(application, resource_owner)\n        Doorkeeper.config.access_token_model.revoke_all_for(application.id, resource_owner)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/base_request.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class BaseRequest\n      include Validations\n\n      attr_reader :grant_type, :server\n\n      delegate :default_scopes, to: :server\n\n      def authorize\n        if valid?\n          before_successful_response\n          @response = TokenResponse.new(access_token)\n          after_successful_response\n          @response\n        elsif error == Errors::InvalidRequest\n          @response = InvalidRequestResponse.from_request(self)\n        else\n          @response = ErrorResponse.from_request(self)\n        end\n      end\n\n      def scopes\n        @scopes ||= build_scopes\n      end\n\n      def find_or_create_access_token(client, resource_owner, scopes, custom_attributes, server)\n        context = Authorization::Token.build_context(client, grant_type, scopes, resource_owner)\n        application = client.is_a?(Doorkeeper.config.application_model) ? client : client&.application\n\n        token_attributes = {\n          application: application,\n          resource_owner: resource_owner,\n          scopes: scopes,\n          expires_in: Authorization::Token.access_token_expires_in(server, context),\n          use_refresh_token: Authorization::Token.refresh_token_enabled?(server, context),\n        }\n\n        @access_token =\n          Doorkeeper.config.access_token_model.find_or_create_for(**token_attributes.merge(custom_attributes))\n      end\n\n      def before_successful_response\n        Doorkeeper.config.before_successful_strategy_response.call(self)\n      end\n\n      def after_successful_response\n        Doorkeeper.config.after_successful_strategy_response.call(self, @response)\n      end\n\n      private\n\n      def build_scopes\n        if @original_scopes.present?\n          OAuth::Scopes.from_string(@original_scopes)\n        else\n          client_scopes = @client&.scopes\n          return default_scopes if client_scopes.blank?\n\n          # Avoid using Scope#& for dynamic scopes\n          client_scopes.allowed(default_scopes)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/base_response.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class BaseResponse\n      def body\n        {}\n      end\n\n      def description\n        \"\"\n      end\n\n      def headers\n        {}\n      end\n\n      def redirectable?\n        false\n      end\n\n      def redirect_uri\n        \"\"\n      end\n\n      def status\n        :ok\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/client/credentials.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class Client\n      Credentials = Struct.new(:uid, :secret) do\n        class << self\n          def from_request(request, *credentials_methods)\n            credentials_methods.inject(nil) do |_, method|\n              method = self.method(method) if method.is_a?(Symbol)\n              credentials = Credentials.new(*method.call(request))\n              break credentials if credentials.present?\n            end\n          end\n\n          def from_params(request)\n            request.parameters.values_at(:client_id, :client_secret)\n          end\n\n          def from_basic(request)\n            authorization = request.authorization\n            if authorization.present? && authorization =~ /^Basic (.*)/im\n              Base64.decode64(Regexp.last_match(1)).split(/:/, 2)\n            end\n          end\n        end\n\n        # Public clients may have their secret blank, but \"credentials\" are\n        # still present\n        delegate :blank?, to: :uid\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/client.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class Client\n      attr_reader :application\n\n      delegate :id, :name, :uid, :redirect_uri, :scopes, :confidential, to: :@application\n\n      def initialize(application)\n        @application = application\n      end\n\n      def self.find(uid, method = Doorkeeper.config.application_model.method(:by_uid))\n        return unless (application = method.call(uid))\n\n        new(application)\n      end\n\n      def self.authenticate(credentials, method = Doorkeeper.config.application_model.method(:by_uid_and_secret))\n        return if credentials.blank?\n        return unless (application = method.call(credentials.uid, credentials.secret))\n\n        new(application)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/client_credentials/creator.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    module ClientCredentials\n      class Creator\n        def call(client, scopes, attributes = {})\n          existing_token = nil\n\n          if lookup_existing_token?\n            existing_token = find_active_existing_token_for(client, scopes, attributes)\n            return existing_token if Doorkeeper.config.reuse_access_token && existing_token&.reusable?\n          end\n\n          with_revocation(existing_token: existing_token) do\n            application = client.is_a?(Doorkeeper.config.application_model) ? client : client&.application\n            Doorkeeper.config.access_token_model.create_for(\n              application: application,\n              resource_owner: nil,\n              scopes: scopes,\n              **attributes,\n            )\n          end\n        end\n\n        private\n\n        def with_revocation(existing_token:)\n          if existing_token && Doorkeeper.config.revoke_previous_client_credentials_token?\n            existing_token.with_lock do\n              raise Errors::DoorkeeperError, :invalid_token_reuse if existing_token.revoked?\n\n              existing_token.revoke\n\n              yield\n            end\n          else\n            yield\n          end\n        end\n\n        def lookup_existing_token?\n          Doorkeeper.config.reuse_access_token ||\n            Doorkeeper.config.revoke_previous_client_credentials_token?\n        end\n\n        def find_active_existing_token_for(client, scopes, attributes)\n          custom_attributes = Doorkeeper.config.access_token_model.\n            extract_custom_attributes(attributes).presence\n          Doorkeeper.config.access_token_model.matching_token_for(\n            client, nil, scopes, custom_attributes: custom_attributes, include_expired: false)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/client_credentials/issuer.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    module ClientCredentials\n      class Issuer\n        attr_reader :token, :validator, :error\n\n        def initialize(server, validator)\n          @server = server\n          @validator = validator\n        end\n\n        def create(client, scopes, attributes = {}, creator = Creator.new)\n          if validator.valid?\n            @token = create_token(client, scopes, attributes, creator)\n            @error = Errors::ServerError unless @token\n          else\n            @token = false\n            @error = validator.error\n          end\n\n          @token\n        end\n\n        private\n\n        def create_token(client, scopes, attributes, creator)\n          context = Authorization::Token.build_context(\n            client,\n            Doorkeeper::OAuth::CLIENT_CREDENTIALS,\n            scopes,\n            nil,\n          )\n          ttl = Authorization::Token.access_token_expires_in(@server, context)\n\n          creator.call(\n            client,\n            scopes,\n            use_refresh_token: false,\n            expires_in: ttl,\n            **attributes\n          )\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/client_credentials/validator.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    module ClientCredentials\n      class Validator\n        include Validations\n        include OAuth::Helpers\n\n        validate :client, error: Errors::InvalidClient\n        validate :client_supports_grant_flow, error: Errors::UnauthorizedClient\n        validate :scopes, error: Errors::InvalidScope\n\n        def initialize(server, request)\n          @server = server\n          @request = request\n          @client = request.client\n\n          validate\n        end\n\n        private\n\n        def validate_client\n          @client.present?\n        end\n\n        def validate_client_supports_grant_flow\n          return if @client.blank?\n\n          Doorkeeper.config.allow_grant_flow_for_client?(\n            Doorkeeper::OAuth::CLIENT_CREDENTIALS,\n            @client.application,\n          )\n        end\n\n        def validate_scopes\n          application_scopes = if @client.present?\n                                 @client.application.scopes\n                               else\n                                 \"\"\n                               end\n          return true if @request.scopes.blank? && application_scopes.blank?\n\n          ScopeChecker.valid?(\n            scope_str: @request.scopes.to_s,\n            server_scopes: @server.scopes,\n            app_scopes: application_scopes,\n            grant_type: Doorkeeper::OAuth::CLIENT_CREDENTIALS,\n          )\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/client_credentials_request.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class ClientCredentialsRequest < BaseRequest\n      attr_reader :client, :original_scopes, :parameters, :response\n\n      alias error_response response\n\n      delegate :error, to: :issuer\n\n      def initialize(server, client, parameters = {})\n        @client = client\n        @server = server\n        @response = nil\n        @grant_type = Doorkeeper::OAuth::CLIENT_CREDENTIALS\n        @original_scopes = parameters[:scope]\n        @parameters = parameters.except(:scope)\n      end\n\n      def access_token\n        issuer.token\n      end\n\n      def issuer\n        @issuer ||= ClientCredentials::Issuer.new(\n          server,\n          ClientCredentials::Validator.new(server, self),\n        )\n      end\n\n      private\n\n      def valid?\n        issuer.create(client, scopes, custom_token_attributes_with_data)\n      end\n\n      def custom_token_attributes_with_data\n        parameters\n          .with_indifferent_access\n          .slice(*Doorkeeper.config.custom_access_token_attributes)\n          .symbolize_keys\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/code_request.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class CodeRequest\n      attr_reader :pre_auth, :resource_owner\n\n      def initialize(pre_auth, resource_owner)\n        @pre_auth = pre_auth\n        @resource_owner = resource_owner\n      end\n\n      def authorize\n        auth = Authorization::Code.new(pre_auth, resource_owner)\n        auth.issue_token!\n        CodeResponse.new(pre_auth, auth, response_on_fragment: pre_auth.response_mode == \"fragment\")\n      end\n\n      def deny\n        pre_auth.error = Errors::AccessDenied\n        pre_auth.error_response\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/code_response.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class CodeResponse < BaseResponse\n      include OAuth::Helpers\n\n      attr_reader :pre_auth, :auth, :response_on_fragment\n\n      def initialize(pre_auth, auth, options = {})\n        @pre_auth = pre_auth\n        @auth = auth\n        @response_on_fragment = options[:response_on_fragment]\n      end\n\n      def redirectable?\n        true\n      end\n\n      def issued_token\n        auth.token\n      end\n\n      def body\n        if auth.try(:access_token?)\n          {\n            access_token: auth.token.plaintext_token,\n            token_type: auth.token.token_type,\n            expires_in: auth.token.expires_in_seconds,\n            state: pre_auth.state,\n          }\n        elsif auth.try(:access_grant?)\n          {\n            code: auth.token.plaintext_token,\n            state: pre_auth.state,\n          }\n        end\n      end\n\n      def redirect_uri\n        if URIChecker.oob_uri?(pre_auth.redirect_uri)\n          auth.oob_redirect\n        elsif response_on_fragment\n          Authorization::URIBuilder.uri_with_fragment(pre_auth.redirect_uri, body)\n        else\n          Authorization::URIBuilder.uri_with_query(pre_auth.redirect_uri, body)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/error.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    Error = Struct.new(:name, :state, :translate_options) do\n      def description\n        options = (translate_options || {}).merge(\n          scope: %i[doorkeeper errors messages],\n          default: :server_error,\n        )\n\n        I18n.translate(name, **options)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/error_response.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class ErrorResponse < BaseResponse\n      include OAuth::Helpers\n\n      NON_REDIRECTABLE_STATES = %i[invalid_redirect_uri invalid_client unauthorized_client].freeze\n\n      def self.from_request(request, attributes = {})\n        new(\n          attributes.merge(\n            name: error_name_for(request.error),\n            exception_class: exception_class_for(request.error),\n            translate_options: request.error.try(:translate_options),\n            state: request.try(:state),\n            redirect_uri: request.try(:redirect_uri),\n          ),\n        )\n      end\n\n      def self.error_name_for(error)\n        error.respond_to?(:name_for_response) ? error.name_for_response : error\n      end\n\n      def self.exception_class_for(error)\n        return error if error.respond_to?(:name_for_response)\n\n        \"Doorkeeper::Errors::#{error.to_s.classify}\".safe_constantize\n      end\n\n      private_class_method :error_name_for, :exception_class_for\n\n      delegate :name, :description, :state, to: :@error\n\n      def initialize(attributes = {})\n        @error = OAuth::Error.new(*attributes.values_at(:name, :state, :translate_options))\n        @exception_class = attributes[:exception_class]\n        @redirect_uri = attributes[:redirect_uri]\n        @response_on_fragment = attributes[:response_on_fragment]\n      end\n\n      def body\n        {\n          error: name,\n          error_description: description,\n          state: state,\n        }.reject { |_, v| v.blank? }\n      end\n\n      def status\n        if name == :invalid_client || name == :unauthorized_client\n          :unauthorized\n        else\n          :bad_request\n        end\n      end\n\n      def redirectable?\n        !NON_REDIRECTABLE_STATES.include?(name) && !URIChecker.oob_uri?(@redirect_uri)\n      end\n\n      def redirect_uri\n        if @response_on_fragment\n          Authorization::URIBuilder.uri_with_fragment(@redirect_uri, body)\n        else\n          Authorization::URIBuilder.uri_with_query(@redirect_uri, body)\n        end\n      end\n\n      def headers\n        {\n          \"Cache-Control\" => \"no-store, no-cache\",\n          \"Content-Type\" => \"application/json; charset=utf-8\",\n          \"WWW-Authenticate\" => authenticate_info,\n        }\n      end\n\n      def raise_exception!\n        raise exception_class.new(self), description\n      end\n\n      protected\n\n      def realm\n        Doorkeeper.config.realm\n      end\n\n      def exception_class\n        return @exception_class if @exception_class\n        raise NotImplementedError, \"error response must define #exception_class\"\n      end\n\n      private\n\n      def authenticate_info\n        %(Bearer realm=\"#{realm}\", error=\"#{sanitize_error_values(name)}\", error_description=\"#{sanitize_error_values(description)}\")\n      end\n\n      # This method removes any characters that are invalid in error\n      # details per RFC6750.\n      #\n      # > Values for the \"error\" and \"error_description\" attributes\n      # > (specified in Appendixes A.7 and A.8 of [RFC6749]) MUST NOT\n      # > include characters outside the set %x20-21 (\" \" or \"!\") / %x23-5B /\n      # > %x5D-7E (ascii \"#\" to \"~\" without \"\\\").\n      def sanitize_error_values(string)\n        string.to_s.each_char.map do |char|\n          if char.in?(\"\\x20\".encode(\"utf-8\")..\"\\x21\".encode(\"utf-8\")) ||\n            char.in?(\"\\x23\".encode(\"utf-8\")..\"\\x5B\".encode(\"utf-8\")) ||\n             char.in?(\"\\x5D\".encode(\"utf-8\")..\"\\x7E\".encode(\"utf-8\"))\n            char\n          else\n            \"_\"\n          end\n        end.join(\"\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/forbidden_token_response.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class ForbiddenTokenResponse < ErrorResponse\n      def self.from_scopes(scopes, attributes = {})\n        new(attributes.merge(scopes: scopes))\n      end\n\n      def initialize(attributes = {})\n        super(attributes.merge(name: :insufficient_scope, state: :forbidden))\n        @scopes = attributes[:scopes]\n      end\n\n      def status\n        :forbidden\n      end\n\n      def description\n        @description ||= I18n.t(\"doorkeeper.errors.messages.forbidden_token.missing_scope\",\n                                oauth_scopes: @scopes.map(&:to_s).join(\" \"),)\n      end\n\n      protected\n\n      def exception_class\n        Doorkeeper::Errors::TokenForbidden\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/helpers/scope_checker.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    module Helpers\n      module ScopeChecker\n        class Validator\n          attr_reader :parsed_scopes, :scope_str\n\n          def initialize(scope_str, server_scopes, app_scopes, grant_type)\n            @parsed_scopes = OAuth::Scopes.from_string(scope_str)\n            @scope_str = scope_str\n            @valid_scopes = valid_scopes(server_scopes, app_scopes)\n\n            @scopes_by_grant_type = Doorkeeper.config.scopes_by_grant_type[grant_type.to_sym] if grant_type\n          end\n\n          def valid?\n            scope_str.present? &&\n              scope_str !~ /[\\n\\r\\t]/ &&\n              @valid_scopes.has_scopes?(parsed_scopes) &&\n              permitted_to_grant_type?\n          end\n\n          private\n\n          def valid_scopes(server_scopes, app_scopes)\n            app_scopes.presence || server_scopes\n          end\n\n          def permitted_to_grant_type?\n            return true unless @scopes_by_grant_type\n\n            OAuth::Scopes.from_array(@scopes_by_grant_type)\n              .has_scopes?(parsed_scopes)\n          end\n        end\n\n        def self.valid?(scope_str:, server_scopes:, app_scopes: nil, grant_type: nil)\n          Validator.new(\n            scope_str,\n            server_scopes,\n            app_scopes,\n            grant_type,\n          ).valid?\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/helpers/unique_token.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    module Helpers\n      # Default Doorkeeper token generator. Follows OAuth RFC and\n      # could be customized using `default_generator_method` in\n      # configuration.\n      module UniqueToken\n        def self.generate(options = {})\n          # Access Token value must be 1*VSCHAR or\n          # 1*( ALPHA / DIGIT / \"-\" / \".\" / \"_\" / \"~\" / \"+\" / \"/\" ) *\"=\"\n          #\n          # @see https://datatracker.ietf.org/doc/html/rfc6749#appendix-A.12\n          # @see https://datatracker.ietf.org/doc/html/rfc6750#section-2.1\n          #\n          generator = options.delete(:generator) || SecureRandom.method(default_generator_method)\n          token_size = options.delete(:size) || 32\n          generator.call(token_size)\n        end\n\n        # Generator method for default generator class (SecureRandom)\n        #\n        def self.default_generator_method\n          Doorkeeper.config.default_generator_method\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/helpers/uri_checker.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"ipaddr\"\n\nmodule Doorkeeper\n  module OAuth\n    module Helpers\n      module URIChecker\n        def self.valid?(url)\n          return true if oob_uri?(url)\n\n          uri = as_uri(url)\n          valid_scheme?(uri) && iff_host?(uri) && uri.fragment.nil? && uri.opaque.nil?\n        rescue URI::InvalidURIError\n          false\n        end\n\n        def self.matches?(url, client_url)\n          url = as_uri(url)\n          client_url = as_uri(client_url)\n\n          unless client_url.query.nil?\n            return false unless query_matches?(url.query, client_url.query)\n\n            # Clear out queries so rest of URI can be tested. This allows query\n            # params to be in the request but order not mattering.\n            client_url.query = nil\n          end\n\n          # RFC8252, Paragraph 7.3\n          # @see https://datatracker.ietf.org/doc/html/rfc8252#section-7.3\n          if loopback_uri?(url) && loopback_uri?(client_url)\n            url.port = nil\n            client_url.port = nil\n          end\n\n          url.query = nil\n          url == client_url\n        end\n\n        def self.loopback_uri?(uri)\n          IPAddr.new(uri.host).loopback?\n        rescue IPAddr::Error, IPAddr::InvalidAddressError\n          false\n        end\n\n        def self.valid_for_authorization?(url, client_url)\n          valid?(url) && client_url.split.any? { |other_url| matches?(url, other_url) }\n        end\n\n        def self.as_uri(url)\n          URI.parse(url)\n        end\n\n        def self.query_matches?(query, client_query)\n          return true if client_query.blank? && query.blank?\n          return false if client_query.nil? || query.nil?\n\n          # Will return true independent of query order\n          client_query.split(\"&\").sort == query.split(\"&\").sort\n        end\n\n        def self.valid_scheme?(uri)\n          return false if uri.scheme.blank?\n\n          %w[localhost].exclude?(uri.scheme)\n        end\n\n        def self.hypertext_scheme?(uri)\n          %w[http https].include?(uri.scheme)\n        end\n\n        def self.iff_host?(uri)\n          !(hypertext_scheme?(uri) && uri.host.blank?)\n        end\n\n        def self.oob_uri?(uri)\n          NonStandard::IETF_WG_OAUTH2_OOB_METHODS.include?(uri)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/hooks/context.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    module Hooks\n      class Context\n        attr_reader :auth, :pre_auth\n\n        def initialize(**attributes)\n          attributes.each do |name, value|\n            instance_variable_set(:\"@#{name}\", value) if respond_to?(name)\n          end\n        end\n\n        def issued_token\n          auth&.issued_token\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/invalid_request_response.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class InvalidRequestResponse < ErrorResponse\n      attr_reader :reason\n\n      def self.from_request(request, attributes = {})\n        new(\n          attributes.merge(\n            state: request.try(:state),\n            redirect_uri: request.try(:redirect_uri),\n            missing_param: request.try(:missing_param),\n            reason: request.try(:invalid_request_reason),\n          ),\n        )\n      end\n\n      def initialize(attributes = {})\n        super(attributes.merge(name: :invalid_request))\n        @missing_param = attributes[:missing_param]\n        @reason = @missing_param.nil? ? attributes[:reason] : :missing_param\n      end\n\n      def status\n        :bad_request\n      end\n\n      def description\n        I18n.translate(\n          reason,\n          scope: %i[doorkeeper errors messages invalid_request],\n          default: :unknown,\n          value: @missing_param,\n        )\n      end\n\n      def exception_class\n        Doorkeeper::Errors::InvalidRequest\n      end\n\n      def redirectable?\n        super && @missing_param != :client_id\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/invalid_token_response.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class InvalidTokenResponse < ErrorResponse\n      attr_reader :reason\n\n      def self.from_access_token(access_token, attributes = {})\n        reason = if access_token&.revoked?\n                   :revoked\n                 elsif access_token&.expired?\n                   :expired\n                 else\n                   :unknown\n                 end\n\n        new(attributes.merge(reason: reason))\n      end\n\n      def initialize(attributes = {})\n        super(attributes.merge(name: :invalid_token, state: :unauthorized))\n        @reason = attributes[:reason] || :unknown\n      end\n\n      def status\n        :unauthorized\n      end\n\n      def description\n        @description ||=\n          I18n.translate(\n            @reason,\n            scope: %i[doorkeeper errors messages invalid_token],\n          )\n      end\n\n      protected\n\n      def exception_class\n        errors_mapping.fetch(reason)\n      end\n\n      private\n\n      def errors_mapping\n        {\n          expired: Doorkeeper::Errors::TokenExpired,\n          revoked: Doorkeeper::Errors::TokenRevoked,\n          unknown: Doorkeeper::Errors::TokenUnknown,\n        }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/nonstandard.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class NonStandard\n      # These are not part of the OAuth 2 specification but are still in use by Google\n      # and in some other implementations. Native applications should use one of the\n      # approaches discussed in RFC8252. OOB is 'Out of Band'\n\n      # This value signals to the Google Authorization Server that the authorization\n      # code should be returned in the title bar of the browser, with the page text\n      # prompting the user to copy the code and paste it in the application.\n      # This is useful when the client (such as a Windows application) cannot listen\n      # on an HTTP port without significant client configuration.\n\n      # When you use this value, your application can then detect that the page has loaded, and can\n      # read the title of the HTML page to obtain the authorization code. It is then up to your\n      # application to close the browser window if you want to ensure that the user never sees the\n      # page that contains the authorization code. The mechanism for doing this varies from platform\n      # to platform.\n      #\n      # If your platform doesn't allow you to detect that the page has loaded or read the title of\n      # the page, you can have the user paste the code back to your application, as prompted by the\n      # text in the confirmation page that the OAuth 2.0 server generates.\n      IETF_WG_OAUTH2_OOB = \"urn:ietf:wg:oauth:2.0:oob\"\n\n      # This is identical to urn:ietf:wg:oauth:2.0:oob, but the text in the confirmation page that\n      # the OAuth 2.0 server generates won't instruct the user to copy the authorization code, but\n      # instead will simply ask the user to close the window.\n      #\n      # This is useful when your application reads the title of the HTML page (by checking window\n      # titles on the desktop, for example) to obtain the authorization code, but can't close the\n      # page on its own.\n      IETF_WG_OAUTH2_OOB_AUTO = \"urn:ietf:wg:oauth:2.0:oob:auto\"\n\n      IETF_WG_OAUTH2_OOB_METHODS = [IETF_WG_OAUTH2_OOB, IETF_WG_OAUTH2_OOB_AUTO].freeze\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/password_access_token_request.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class PasswordAccessTokenRequest < BaseRequest\n      include OAuth::Helpers\n\n      validate :client, error: Errors::InvalidClient\n      validate :client_supports_grant_flow, error: Errors::UnauthorizedClient\n      validate :resource_owner, error: Errors::InvalidGrant\n      validate :scopes, error: Errors::InvalidScope\n\n      attr_reader :client, :credentials, :resource_owner, :parameters, :access_token\n\n      def initialize(server, client, credentials, resource_owner, parameters = {})\n        @server          = server\n        @resource_owner  = resource_owner\n        @client          = client\n        @credentials     = credentials\n        @parameters      = parameters\n        @original_scopes = parameters[:scope]\n        @grant_type      = Doorkeeper::OAuth::PASSWORD\n      end\n\n      private\n\n      def before_successful_response\n        find_or_create_access_token(client, resource_owner, scopes, {}, server)\n        super\n      end\n\n      def validate_scopes\n        return true if scopes.blank?\n\n        ScopeChecker.valid?(\n          scope_str: scopes.to_s,\n          server_scopes: server.scopes,\n          app_scopes: client.try(:scopes),\n          grant_type: grant_type,\n        )\n      end\n\n      def validate_resource_owner\n        resource_owner.present?\n      end\n\n      # Section 4.3.2. Access Token Request for Resource Owner Password Credentials Grant:\n      #\n      #   If the client type is confidential or the client was issued client credentials (or assigned\n      #   other authentication requirements), the client MUST authenticate with the authorization\n      #   server as described in Section 3.2.1.\n      #\n      #   The authorization server MUST:\n      #\n      #    o  require client authentication for confidential clients or for any  client that was\n      #       issued client credentials (or with other authentication requirements)\n      #\n      #    o  authenticate the client if client authentication is included,\n      #\n      #   @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.3\n      #\n      def validate_client\n        if Doorkeeper.config.skip_client_authentication_for_password_grant\n          client.present? || (!parameters[:client_id] && credentials.blank?)\n        else\n          client.present?\n        end\n      end\n\n      def validate_client_supports_grant_flow\n        Doorkeeper.config.allow_grant_flow_for_client?(grant_type, client&.application)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/pre_authorization.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class PreAuthorization\n      include Validations\n\n      validate :client_id, error: Errors::InvalidRequest\n      validate :client, error: Errors::InvalidClient\n      validate :client_supports_grant_flow, error: Errors::UnauthorizedClient\n      validate :resource_owner_authorize_for_client, error: Errors::InvalidClient\n      validate :redirect_uri, error: Errors::InvalidRedirectUri\n      validate :params, error: Errors::InvalidRequest\n      validate :response_type, error: Errors::UnsupportedResponseType\n      validate :response_mode, error: Errors::UnsupportedResponseMode\n      validate :scopes, error: Errors::InvalidScope\n      validate :code_challenge, error: Errors::InvalidRequest\n      validate :code_challenge_method, error: Errors::InvalidCodeChallengeMethod\n\n      attr_reader :client, :code_challenge, :code_challenge_method, :missing_param,\n                  :redirect_uri, :resource_owner, :response_type, :state,\n                  :authorization_response_flow, :response_mode, :custom_access_token_attributes,\n                  :invalid_request_reason\n\n      def initialize(server, parameters = {}, resource_owner = nil)\n        @server = server\n        @client_id = parameters[:client_id]\n        @response_type = parameters[:response_type]\n        @response_mode = parameters[:response_mode]\n        @redirect_uri = parameters[:redirect_uri]\n        @scope = parameters[:scope]\n        @state = parameters[:state]\n        @code_challenge = parameters[:code_challenge]\n        @code_challenge_method = parameters[:code_challenge_method]\n        @resource_owner = resource_owner\n        @custom_access_token_attributes = parameters.slice(*Doorkeeper.config.custom_access_token_attributes).to_h\n      end\n\n      def authorizable?\n        valid?\n      end\n\n      def scopes\n        Scopes.from_string(scope)\n      end\n\n      def scope\n        @scope.presence || (server.default_scopes.presence && build_scopes)\n      end\n\n      def error_response\n        if error == Errors::InvalidRequest\n          OAuth::InvalidRequestResponse.from_request(\n            self,\n            response_on_fragment: response_on_fragment?,\n          )\n        else\n          OAuth::ErrorResponse.from_request(self, response_on_fragment: response_on_fragment?)\n        end\n      end\n\n      def as_json(_options = nil)\n        pre_auth_hash\n      end\n\n      def form_post_response?\n        response_mode == \"form_post\"\n      end\n\n      private\n\n      attr_reader :client_id, :server\n\n      def build_scopes\n        client_scopes = client.scopes\n        if client_scopes.blank?\n          server.default_scopes.to_s\n        else\n          server.default_scopes.allowed(client_scopes).to_s\n        end\n      end\n\n      def validate_client_id\n        @missing_param = :client_id if client_id.blank?\n        @missing_param.nil?\n      end\n\n      def validate_client\n        @client = OAuth::Client.find(client_id)\n        @client.present?\n      end\n\n      def validate_client_supports_grant_flow\n        Doorkeeper.config.allow_grant_flow_for_client?(grant_type, client.application)\n      end\n\n      def validate_resource_owner_authorize_for_client\n        # The `authorize_resource_owner_for_client` config option is used for this validation\n        client.application.authorized_for_resource_owner?(@resource_owner)\n      end\n\n      def validate_redirect_uri\n        return false if redirect_uri.blank?\n\n        Helpers::URIChecker.valid_for_authorization?(\n          redirect_uri,\n          client.redirect_uri,\n        )\n      end\n\n      def validate_params\n        @missing_param = if response_type.blank?\n                           :response_type\n                         elsif @scope.blank? && server.default_scopes.blank?\n                           :scope\n                         end\n\n        @missing_param.nil?\n      end\n\n      def validate_response_type\n        server.authorization_response_flows.any? do |flow|\n          if flow.matches_response_type?(response_type)\n            @authorization_response_flow = flow\n            true\n          end\n        end\n      end\n\n      def validate_response_mode\n        if response_mode.blank?\n          @response_mode = authorization_response_flow.default_response_mode\n          return true\n        end\n\n        authorization_response_flow.matches_response_mode?(response_mode)\n      end\n\n      def validate_scopes\n        Helpers::ScopeChecker.valid?(\n          scope_str: scope,\n          server_scopes: server.scopes,\n          app_scopes: client.scopes,\n          grant_type: grant_type,\n        )\n      end\n\n      def validate_code_challenge\n        return true unless Doorkeeper.config.force_pkce?\n        return true if client.confidential\n        return true if code_challenge.present?\n\n        @invalid_request_reason = :invalid_code_challenge\n        false\n      end\n\n      def validate_code_challenge_method\n        return true unless Doorkeeper.config.access_grant_model.pkce_supported?\n\n        code_challenge.blank? ||\n          (code_challenge_method.present? && Doorkeeper.config.pkce_code_challenge_methods_supported.include?(code_challenge_method))\n      end\n\n      def response_on_fragment?\n        return response_type == \"token\" if response_mode.nil?\n\n        response_mode == \"fragment\"\n      end\n\n      def grant_type\n        response_type == \"code\" ? AUTHORIZATION_CODE : IMPLICIT\n      end\n\n      def pre_auth_hash\n        {\n          client_id: client.uid,\n          redirect_uri: redirect_uri,\n          state: state,\n          response_type: response_type,\n          scope: scope,\n          client_name: client.name,\n          status: I18n.t(\"doorkeeper.pre_authorization.status\"),\n        }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/refresh_token_request.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class RefreshTokenRequest < BaseRequest\n      include OAuth::Helpers\n\n      validate :token_presence, error: Errors::InvalidRequest\n      validate :token,        error: Errors::InvalidGrant\n      validate :client,       error: Errors::InvalidClient\n      validate :client_match, error: Errors::InvalidGrant\n      validate :scope,        error: Errors::InvalidScope\n\n      attr_reader :access_token, :client, :credentials, :refresh_token\n      attr_reader :missing_param\n\n      def initialize(server, refresh_token, credentials, parameters = {})\n        @server = server\n        @refresh_token = refresh_token\n        @credentials = credentials\n        @grant_type = Doorkeeper::OAuth::REFRESH_TOKEN\n        @original_scopes = parameters[:scope] || parameters[:scopes]\n        @refresh_token_parameter = parameters[:refresh_token]\n        @client = load_client(credentials) if credentials\n      end\n\n      private\n\n      def load_client(credentials)\n        Doorkeeper.config.application_model.by_uid_and_secret(credentials.uid, credentials.secret)\n      end\n\n      def before_successful_response\n        if refresh_token_revoked_on_use?\n          # No locking needed when refresh tokens are revoked on use\n          # because the old token is revoked later when the new token is used.\n          # This allows multiple concurrent refresh requests to succeed during the\n          # transition period, after which the old refresh token will be revoked.\n          raise Errors::InvalidGrantReuse if refresh_token.revoked?\n          create_access_token\n        else\n          # Use locking when refresh tokens are revoked immediately\n          # to prevent race conditions where multiple tokens could be created\n          refresh_token.with_lock do\n            raise Errors::InvalidGrantReuse if refresh_token.revoked?\n            refresh_token.revoke\n            create_access_token\n          end\n        end\n        super\n      end\n\n      def refresh_token_revoked_on_use?\n        Doorkeeper.config.access_token_model.refresh_token_revoked_on_use?\n      end\n\n      def default_scopes\n        refresh_token.scopes\n      end\n\n      def create_access_token\n        attributes = {}.merge(custom_token_attributes_with_data)\n\n        resource_owner =\n          if Doorkeeper.config.polymorphic_resource_owner?\n            refresh_token.resource_owner\n          else\n            refresh_token.resource_owner_id\n          end\n\n        if refresh_token_revoked_on_use?\n          attributes[:previous_refresh_token] = refresh_token.refresh_token\n        end\n\n        # RFC6749\n        # 1.5.  Refresh Token\n        #\n        # Refresh tokens are issued to the client by the authorization server and are\n        # used to obtain a new access token when the current access token\n        # becomes invalid or expires, or to obtain additional access tokens\n        # with identical or narrower scope (access tokens may have a shorter\n        # lifetime and fewer permissions than authorized by the resource\n        # owner).\n        #\n        # Here we assume that TTL of the token received after refreshing should be\n        # the same as that of the original token.\n        #\n        @access_token = Doorkeeper.config.access_token_model.create_for(\n          application: refresh_token.application,\n          resource_owner: resource_owner,\n          scopes: scopes,\n          expires_in: refresh_token.expires_in,\n          use_refresh_token: true,\n          **attributes,\n        )\n      end\n\n      def validate_token_presence\n        @missing_param = :refresh_token if refresh_token.blank? && @refresh_token_parameter.blank?\n\n        @missing_param.nil?\n      end\n\n      def validate_token\n        refresh_token.present? && !refresh_token.revoked?\n      end\n\n      def validate_client\n        return true if credentials.blank?\n\n        client.present?\n      end\n\n      # @see https://datatracker.ietf.org/doc/html/rfc6749#section-1.5\n      #\n      def validate_client_match\n        return true if refresh_token.application_id.blank?\n\n        client && refresh_token.application_id == client.id\n      end\n\n      def validate_scope\n        if @original_scopes.present?\n          ScopeChecker.valid?(\n            scope_str: @original_scopes,\n            server_scopes: refresh_token.scopes,\n          )\n        else\n          true\n        end\n      end\n\n      def custom_token_attributes_with_data\n        refresh_token\n        .attributes\n        .with_indifferent_access\n        .slice(*Doorkeeper.config.custom_access_token_attributes)\n        .symbolize_keys\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/scopes.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class Scopes\n      include Enumerable\n      include Comparable\n\n      DYNAMIC_SCOPE_WILDCARD = \"*\"\n\n      def self.from_string(string)\n        string ||= \"\"\n        new.tap do |scope|\n          scope.add(*string.split)\n        end\n      end\n\n      def self.from_array(array)\n        new.tap do |scope|\n          scope.add(*array)\n        end\n      end\n\n      delegate :each, :empty?, to: :@scopes\n\n      def initialize\n        @scopes = []\n      end\n\n      def exists?(scope)\n        scope = scope.to_s\n\n        @scopes.any? do |allowed_scope|\n          if dynamic_scopes_enabled? && dynamic_scopes_present?(allowed_scope, scope)\n            dynamic_scope_match?(allowed_scope, scope)\n          else\n            allowed_scope == scope\n          end\n        end\n      end\n\n      def add(*scopes)\n        @scopes.push(*scopes.map(&:to_s))\n        @scopes.uniq!\n      end\n\n      def all\n        @scopes\n      end\n\n      def to_s\n        @scopes.join(\" \")\n      end\n\n      def scopes?(scopes)\n        scopes.all? { |scope| exists?(scope) }\n      end\n\n      alias has_scopes? scopes?\n\n      def +(other)\n        self.class.from_array(all + to_array(other))\n      end\n\n      def <=>(other)\n        if other.respond_to?(:map)\n          map(&:to_s).sort <=> other.map(&:to_s).sort\n        else\n          super\n        end\n      end\n\n      # DEPRECATED: With dynamic scopes, #allowed should be called because\n      # A & B doesn't really make sense with dynamic scopes.\n      #\n      # For example, if A = user:* and B is user:1, A & B = [].\n      # If we modified this method to take dynamic scopes into an account, then order\n      # becomes important, and this would violate the principle that A & B = B & A.\n      def &(other)\n        return allowed(other) if dynamic_scopes_enabled?\n\n        self.class.from_array(all & to_array(other))\n      end\n\n      # Returns a set of scopes that are allowed, taking dynamic\n      # scopes into account. This instance's scopes is taken as the allowed set,\n      # and the passed value is the set to filter.\n      #\n      # @param other The set of scopes to filter\n      def allowed(other)\n        filtered_scopes = other.select { |scope| self.exists?(scope) }\n        self.class.from_array(filtered_scopes)\n      end\n\n      private\n\n      def dynamic_scopes_enabled?\n        Doorkeeper.config.enable_dynamic_scopes?\n      end\n\n      def dynamic_scope_delimiter\n        return unless dynamic_scopes_enabled?\n\n        @dynamic_scope_delimiter ||= Doorkeeper.config.dynamic_scopes_delimiter\n      end\n\n      def dynamic_scopes_present?(allowed, requested)\n        allowed.include?(dynamic_scope_delimiter) && requested.include?(dynamic_scope_delimiter)\n      end\n\n      def dynamic_scope_match?(allowed, requested)\n        allowed_pattern = allowed.split(dynamic_scope_delimiter, 2)\n        request_pattern = requested.split(dynamic_scope_delimiter, 2)\n\n        return false if allowed_pattern[0] != request_pattern[0]\n        return false if allowed_pattern[1].blank?\n        return false if request_pattern[1].blank?\n        return true  if allowed_pattern[1] == DYNAMIC_SCOPE_WILDCARD && allowed_pattern[1].present?\n\n        allowed_pattern[1] == request_pattern[1]\n      end\n\n      def to_array(other)\n        case other\n        when Scopes\n          other.all\n        else\n          other.to_a\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/token.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class Token\n      class << self\n        def from_request(request, *methods)\n          methods.inject(nil) do |_, method|\n            method = self.method(method) if method.is_a?(Symbol)\n            credentials = method.call(request)\n            break credentials if credentials.present?\n          end\n        end\n\n        def authenticate(request, *methods)\n          if (token = from_request(request, *methods))\n            access_token = Doorkeeper.config.access_token_model.by_token(token)\n            if access_token.present? && Doorkeeper.config.refresh_token_enabled?\n              access_token.revoke_previous_refresh_token!\n            end\n            access_token\n          end\n        end\n\n        def from_access_token_param(request)\n          request.parameters[:access_token]\n        end\n\n        def from_bearer_param(request)\n          request.parameters[:bearer_token]\n        end\n\n        def from_bearer_authorization(request)\n          pattern = /^Bearer /i\n          header = request.authorization\n          token_from_header(header, pattern) if match?(header, pattern)\n        end\n\n        def from_basic_authorization(request)\n          pattern = /^Basic /i\n          header = request.authorization\n          token_from_basic_header(header, pattern) if match?(header, pattern)\n        end\n\n        private\n\n        def token_from_basic_header(header, pattern)\n          encoded_header = token_from_header(header, pattern)\n          decode_basic_credentials_token(encoded_header)\n        end\n\n        def decode_basic_credentials_token(encoded_header)\n          Base64.decode64(encoded_header).split(/:/, 2).first\n        end\n\n        def token_from_header(header, pattern)\n          header.gsub(pattern, \"\")\n        end\n\n        def match?(header, pattern)\n          header&.match(pattern)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/token_introspection.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    # RFC7662 OAuth 2.0 Token Introspection\n    #\n    # @see https://datatracker.ietf.org/doc/html/rfc7662\n    class TokenIntrospection\n      attr_reader :token, :error, :invalid_request_reason\n\n      def initialize(server, token)\n        @server = server\n        @token = token\n      end\n\n      def authorized?\n        authorize!\n        @error.blank?\n      end\n\n      def error_response\n        return if @error.blank?\n\n        if @error == Errors::InvalidToken\n          OAuth::InvalidTokenResponse.from_access_token(authorized_token)\n        elsif @error == Errors::InvalidRequest\n          OAuth::InvalidRequestResponse.from_request(self)\n        else\n          OAuth::ErrorResponse.from_request(self)\n        end\n      end\n\n      def to_json(*)\n        active? ? success_response : failure_response\n      end\n\n      private\n\n      attr_reader :server\n\n      # If the protected resource uses OAuth 2.0 client credentials to\n      # authenticate to the introspection endpoint and its credentials are\n      # invalid, the authorization server responds with an HTTP 401\n      # (Unauthorized) as described in Section 5.2 of OAuth 2.0 [RFC6749].\n      #\n      # Endpoint must first validate the authentication.\n      # If the authentication is invalid, the endpoint should respond with\n      # an HTTP 401 status code and an invalid_client response.\n      #\n      # @see https://www.oauth.com/oauth2-servers/token-introspection-endpoint/\n      #\n      # To prevent token scanning attacks, the endpoint MUST also require\n      # some form of authorization to access this endpoint, such as client\n      # authentication as described in OAuth 2.0 [RFC6749] or a separate\n      # OAuth 2.0 access token such as the bearer token described in OAuth\n      # 2.0 Bearer Token Usage [RFC6750].\n      #\n      def authorize!\n        # Requested client authorization\n        if server.credentials\n          authorize_using_basic_auth!\n        elsif authorized_token\n          authorize_using_bearer_token!\n        else\n          @error = Errors::InvalidRequest\n          @invalid_request_reason = :request_not_authorized\n        end\n      end\n\n      def authorize_using_basic_auth!\n        # Note that a properly formed and authorized query for an inactive or\n        # otherwise invalid token (or a token the protected resource is not\n        # allowed to know about) is not considered an error response by this\n        # specification. In these cases, the authorization server MUST instead\n        # respond with an introspection response with the \"active\" field set to\n        # \"false\" as described in Section 2.2.\n        @error = Errors::InvalidClient unless authorized_client\n      end\n\n      def authorize_using_bearer_token!\n        # Requested bearer token authorization\n        #\n        #  If the protected resource uses an OAuth 2.0 bearer token to authorize\n        #  its call to the introspection endpoint and the token used for\n        #  authorization does not contain sufficient privileges or is otherwise\n        #  invalid for this request, the authorization server responds with an\n        #  HTTP 401 code as described in Section 3 of OAuth 2.0 Bearer Token\n        #  Usage [RFC6750].\n        #\n        @error = Errors::InvalidToken unless valid_authorized_token?\n      end\n\n      # Client Authentication\n      def authorized_client\n        @authorized_client ||= server.credentials && server.client\n      end\n\n      # Bearer Token Authentication\n      def authorized_token\n        @authorized_token ||= Doorkeeper.authenticate(server.context.request)\n      end\n\n      # 2.2. Introspection Response\n      def success_response\n        response = {\n          active: true,\n          scope: @token.scopes_string,\n          client_id: @token.try(:application).try(:uid),\n          token_type: @token.token_type,\n          iat: @token.created_at.to_i,\n        }\n        # `exp` is OPTIONAL per RFC 7662 §2.2; omit it for non-expiring tokens\n        # so clients don't interpret `0` as \"expired at 1970-01-01\".\n        response[:exp] = @token.expires_at.to_i if @token.expires_at\n\n        customize_response(response)\n      end\n\n      # If the introspection call is properly authorized but the token is not\n      # active, does not exist on this server, or the protected resource is\n      # not allowed to introspect this particular token, then the\n      # authorization server MUST return an introspection response with the\n      # \"active\" field set to \"false\".  Note that to avoid disclosing too\n      # much of the authorization server's state to a third party, the\n      # authorization server SHOULD NOT include any additional information\n      # about an inactive token, including why the token is inactive.\n      #\n      # @see https://datatracker.ietf.org/doc/html/rfc7662 2.2. Introspection Response\n      #\n      def failure_response\n        {\n          active: false,\n        }\n      end\n\n      # Boolean indicator of whether or not the presented token\n      # is currently active.  The specifics of a token's \"active\" state\n      # will vary depending on the implementation of the authorization\n      # server and the information it keeps about its tokens, but a \"true\"\n      # value return for the \"active\" property will generally indicate\n      # that a given token has been issued by this authorization server,\n      # has not been revoked by the resource owner, and is within its\n      # given time window of validity (e.g., after its issuance time and\n      # before its expiration time).\n      #\n      # Any other error is considered an \"inactive\" token.\n      #\n      # * The token requested does not exist or is invalid\n      # * The token expired\n      # * The token was issued to a different client than is making this request\n      #\n      # Since resource servers using token introspection rely on the\n      # authorization server to determine the state of a token, the\n      # authorization server MUST perform all applicable checks against a\n      # token's state. For instance, these tests include the following:\n      #\n      #    o  If the token can expire, the authorization server MUST determine\n      #       whether or not the token has expired.\n      #    o  If the token can be issued before it is able to be used, the\n      #       authorization server MUST determine whether or not a token's valid\n      #       period has started yet.\n      #    o  If the token can be revoked after it was issued, the authorization\n      #       server MUST determine whether or not such a revocation has taken\n      #       place.\n      #    o  If the token has been signed, the authorization server MUST\n      #       validate the signature.\n      #    o  If the token can be used only at certain resource servers, the\n      #       authorization server MUST determine whether or not the token can\n      #       be used at the resource server making the introspection call.\n      #\n      def active?\n        if authorized_client\n          valid_token? && token_introspection_allowed?(auth_client: authorized_client.application)\n        else\n          valid_token?\n        end\n      end\n\n      # Token can be valid only if it is not expired or revoked.\n      def valid_token?\n        @token&.accessible?\n      end\n\n      def valid_authorized_token?\n        !authorized_token_matches_introspected? &&\n          authorized_token.accessible? &&\n          token_introspection_allowed?(auth_token: authorized_token)\n      end\n\n      # RFC7662 Section 2.1\n      def authorized_token_matches_introspected?\n        authorized_token.token == @token&.token\n      end\n\n      # Config constraints for introspection in Doorkeeper.config.allow_token_introspection\n      def token_introspection_allowed?(auth_client: nil, auth_token: nil)\n        allow_introspection = Doorkeeper.config.allow_token_introspection\n        return allow_introspection unless allow_introspection.respond_to?(:call)\n\n        allow_introspection.call(@token, auth_client, auth_token)\n      end\n\n      # Allows to customize introspection response.\n      # Provides context (controller) and token for generating developer-specific\n      # response.\n      #\n      # @see https://datatracker.ietf.org/doc/html/rfc7662#section-2.2\n      #\n      def customize_response(response)\n        customized_response = Doorkeeper.config.custom_introspection_response.call(\n          token,\n          server.context,\n        )\n        return response if customized_response.blank?\n\n        response.merge(customized_response)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/token_request.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class TokenRequest\n      attr_reader :pre_auth, :resource_owner\n\n      def initialize(pre_auth, resource_owner)\n        @pre_auth = pre_auth\n        @resource_owner = resource_owner\n      end\n\n      def authorize\n        auth = Authorization::Token.new(pre_auth, resource_owner)\n        auth.issue_token!\n        CodeResponse.new(pre_auth, auth, response_on_fragment: true)\n      end\n\n      def deny\n        pre_auth.error = Errors::AccessDenied\n        pre_auth.error_response\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth/token_response.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    class TokenResponse\n      attr_reader :token\n\n      alias issued_token token\n\n      def initialize(token)\n        @token = token\n      end\n\n      def body\n        @body ||= {\n          \"access_token\" => token.plaintext_token,\n          \"token_type\" => token.token_type,\n          \"expires_in\" => token.expires_in_seconds,\n          \"refresh_token\" => token.plaintext_refresh_token,\n          \"scope\" => token.scopes_string,\n          \"created_at\" => token.created_at.to_i,\n        }.reject { |_, value| value.blank? }\n      end\n\n      def status\n        :ok\n      end\n\n      def headers\n        {\n          \"Cache-Control\" => \"no-store, no-cache\",\n          \"Content-Type\" => \"application/json; charset=utf-8\",\n          \"Pragma\" => \"no-cache\",\n        }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/oauth.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module OAuth\n    GRANT_TYPES = [\n      AUTHORIZATION_CODE = \"authorization_code\",\n      IMPLICIT = \"implicit\",\n      PASSWORD = \"password\",\n      CLIENT_CREDENTIALS = \"client_credentials\",\n      REFRESH_TOKEN = \"refresh_token\",\n    ].freeze\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/orm/active_record/access_grant.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"doorkeeper/orm/active_record/mixins/access_grant\"\n\nmodule Doorkeeper\n  class AccessGrant < ::ActiveRecord::Base\n    include Doorkeeper::Orm::ActiveRecord::Mixins::AccessGrant\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/orm/active_record/access_token.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"doorkeeper/orm/active_record/mixins/access_token\"\n\nmodule Doorkeeper\n  class AccessToken < ::ActiveRecord::Base\n    include Doorkeeper::Orm::ActiveRecord::Mixins::AccessToken\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/orm/active_record/application.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"doorkeeper/orm/active_record/redirect_uri_validator\"\nrequire \"doorkeeper/orm/active_record/mixins/application\"\n\nmodule Doorkeeper\n  class Application < ::ActiveRecord::Base\n    include ::Doorkeeper::Orm::ActiveRecord::Mixins::Application\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/orm/active_record/mixins/access_grant.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper::Orm::ActiveRecord::Mixins\n  module AccessGrant\n    extend ActiveSupport::Concern\n\n    included do\n      self.table_name = compute_doorkeeper_table_name\n      self.strict_loading_by_default = false if respond_to?(:strict_loading_by_default)\n\n      include ::Doorkeeper::AccessGrantMixin\n\n      belongs_to :application, class_name: Doorkeeper.config.application_class.to_s,\n                               optional: true,\n                               inverse_of: :access_grants\n\n      validates :application_id,\n                :token,\n                :expires_in,\n                :redirect_uri,\n                presence: true\n\n      validates :token, uniqueness: { case_sensitive: true }\n\n      before_validation :generate_token, on: :create\n\n      # We keep a volatile copy of the raw token for initial communication\n      # The stored refresh_token may be mapped and not available in cleartext.\n      #\n      # Some strategies allow restoring stored secrets (e.g. symmetric encryption)\n      # while hashing strategies do not, so you cannot rely on this value\n      # returning a present value for persisted tokens.\n      def plaintext_token\n        if secret_strategy.allows_restoring_secrets?\n          secret_strategy.restore_secret(self, :token)\n        else\n          @raw_token\n        end\n      end\n\n      private\n\n      # Generates token value with UniqueToken class.\n      #\n      # @return [String] token value\n      #\n      def generate_token\n        @raw_token = Doorkeeper::OAuth::Helpers::UniqueToken.generate\n        secret_strategy.store_secret(self, :token, @raw_token)\n      end\n    end\n\n    module ClassMethods\n      private\n\n      def compute_doorkeeper_table_name\n        table_name = \"oauth_access_grant\"\n        table_name = table_name.pluralize if pluralize_table_names\n        \"#{table_name_prefix}#{table_name}#{table_name_suffix}\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/orm/active_record/mixins/access_token.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper::Orm::ActiveRecord::Mixins\n  module AccessToken\n    extend ActiveSupport::Concern\n\n    included do\n      self.table_name = compute_doorkeeper_table_name\n      self.strict_loading_by_default = false if respond_to?(:strict_loading_by_default)\n\n      include ::Doorkeeper::AccessTokenMixin\n\n      belongs_to :application, class_name: Doorkeeper.config.application_class.to_s,\n                               inverse_of: :access_tokens,\n                               optional: true\n\n      validates :token, presence: true, uniqueness: { case_sensitive: true }\n      validates :refresh_token, uniqueness: { case_sensitive: true }, if: :use_refresh_token?\n\n      # @attr_writer [Boolean, nil] use_refresh_token\n      #   indicates the possibility of using refresh token\n      attr_writer :use_refresh_token\n\n      before_validation :generate_token, on: :create\n      before_validation :generate_refresh_token,\n                        on: :create, if: :use_refresh_token?\n    end\n\n    module ClassMethods\n      # Searches for not revoked Access Tokens associated with the\n      # specific Resource Owner.\n      #\n      # @param resource_owner [ActiveRecord::Base]\n      #   Resource Owner model instance\n      #\n      # @return [ActiveRecord::Relation]\n      #   active Access Tokens for Resource Owner\n      #\n      def active_for(resource_owner)\n        by_resource_owner(resource_owner).where(revoked_at: nil)\n      end\n\n      # Determines if refresh tokens should be revoked only when the new access token is used,\n      # rather than immediately upon refresh. This is based on the presence of the\n      # `previous_refresh_token` column in the database.\n      #\n      # When true (column exists):\n      # - Refresh tokens are NOT immediately revoked\n      # - New access token stores the old refresh token value in `previous_refresh_token`\n      # - Old refresh token is revoked later when the new access token is first used\n      # - Multiple concurrent refresh requests can succeed (no database locks)\n      # - Better database performance and lower latency\n      #\n      # When false (column does not exist):\n      # - Refresh tokens are immediately revoked using database locks\n      # - Only one concurrent refresh request can succeed\n      # - May experience database lock contention under high load\n      #\n      # To enable the revoke-on-use feature and improve performance:\n      #   rails generate doorkeeper:previous_refresh_token\n      #   rails db:migrate\n      #\n      # @return [Boolean] true if previous_refresh_token column exists\n      #\n      def refresh_token_revoked_on_use?\n        column_names.include?(\"previous_refresh_token\")\n      end\n\n      # Returns non-expired and non-revoked access tokens\n      def not_expired\n        relation = where(revoked_at: nil)\n\n        if supports_expiration_time_math?\n          # have not reached the expiration time or it never expires\n          relation.where(\"#{expiration_time_sql} > ?\", Time.now.utc).or(\n            relation.where(expires_in: nil)\n          )\n        else\n          ::Kernel.warn(::Doorkeeper::Models::ExpirationTimeSqlMath::WARNING_MESSAGE)\n\n          relation\n        end\n      end\n\n      private\n\n      def compute_doorkeeper_table_name\n        table_name = \"oauth_access_token\"\n        table_name = table_name.pluralize if pluralize_table_names\n        \"#{table_name_prefix}#{table_name}#{table_name_suffix}\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/orm/active_record/mixins/application.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper::Orm::ActiveRecord::Mixins\n  module Application\n    extend ActiveSupport::Concern\n\n    included do\n      self.table_name = compute_doorkeeper_table_name\n      self.strict_loading_by_default = false if respond_to?(:strict_loading_by_default)\n\n      include ::Doorkeeper::ApplicationMixin\n\n      has_many :access_grants,\n               foreign_key: :application_id,\n               dependent: :delete_all,\n               class_name: Doorkeeper.config.access_grant_class.to_s\n\n      has_many :access_tokens,\n               foreign_key: :application_id,\n               dependent: :delete_all,\n               class_name: Doorkeeper.config.access_token_class.to_s\n\n      validates :name, :uid, presence: true\n      validates :secret, presence: true, if: -> { secret_required? }\n      validates :uid, uniqueness: { case_sensitive: true }\n      validates :confidential, inclusion: { in: [true, false] }\n\n      validates_with Doorkeeper::RedirectUriValidator, attributes: [:redirect_uri]\n\n      validate :scopes_match_configured, if: :enforce_scopes?\n\n      before_validation :generate_uid, :generate_secret, on: :create\n\n      has_many :authorized_tokens,\n               -> { where(revoked_at: nil) },\n               foreign_key: :application_id,\n               class_name: Doorkeeper.config.access_token_class.to_s\n\n      has_many :authorized_applications,\n               through: :authorized_tokens,\n               source: :application\n\n      # Generates a new secret for this application, intended to be used\n      # for rotating the secret or in case of compromise.\n      #\n      # @return [String] new transformed secret value\n      #\n      def renew_secret\n        @raw_secret = secret_generator.generate\n        secret_strategy.store_secret(self, :secret, @raw_secret)\n      end\n\n      # We keep a volatile copy of the raw secret for initial communication\n      # The stored refresh_token may be mapped and not available in cleartext.\n      #\n      # Some strategies allow restoring stored secrets (e.g. symmetric encryption)\n      # while hashing strategies do not, so you cannot rely on this value\n      # returning a present value for persisted tokens.\n      def plaintext_secret\n        if secret_strategy.allows_restoring_secrets?\n          secret_strategy.restore_secret(self, :secret)\n        else\n          @raw_secret\n        end\n      end\n\n      # Represents client as set of it's attributes in JSON format.\n      # This is the right way how we want to override ActiveRecord #to_json.\n      #\n      # Respects privacy settings and serializes minimum set of attributes\n      # for public/private clients and full set for authorized owners.\n      #\n      # @return [Hash] entity attributes for JSON\n      #\n      def as_json(options = {})\n        # if application belongs to some owner we need to check if it's the same as\n        # the one passed in the options or check if we render the client as an owner\n        if (respond_to?(:owner) && owner && owner == options[:current_resource_owner]) ||\n           options[:as_owner]\n          # Owners can see all the client attributes, fallback to ActiveModel serialization\n          super\n        else\n          # if application has no owner or it's owner doesn't match one from the options\n          # we render only minimum set of attributes that could be exposed to a public\n          only = extract_serializable_attributes(options)\n          super(options.merge(only: only))\n        end\n      end\n\n      def authorized_for_resource_owner?(resource_owner)\n        Doorkeeper.configuration.authorize_resource_owner_for_client.call(self, resource_owner)\n      end\n\n      # We need to hook into this method to allow serializing plan-text secrets\n      # when secrets hashing enabled.\n      #\n      # @param key [String] attribute name\n      #\n      def read_attribute_for_serialization(key)\n        return super unless key.to_s == \"secret\"\n\n        plaintext_secret || secret\n      end\n\n      private\n\n      def secret_generator\n        generator_name = Doorkeeper.config.application_secret_generator\n        generator = generator_name.constantize\n\n        return generator if generator.respond_to?(:generate)\n\n        raise Errors::UnableToGenerateToken, \"#{generator} does not respond to `.generate`.\"\n      rescue NameError\n        raise Errors::TokenGeneratorNotFound, \"#{generator_name} not found\"\n      end\n\n      def generate_uid\n        self.uid = Doorkeeper::OAuth::Helpers::UniqueToken.generate if uid.blank?\n      end\n\n      def generate_secret\n        return if secret.present? || !secret_required?\n\n        renew_secret\n      end\n\n      def scopes_match_configured\n        if scopes.present? && !Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(\n          scope_str: scopes.to_s,\n          server_scopes: Doorkeeper.config.scopes,\n        )\n          errors.add(:scopes, :not_match_configured)\n        end\n      end\n\n      def enforce_scopes?\n        Doorkeeper.config.enforce_configured_scopes?\n      end\n\n      def secret_required?\n        confidential? ||\n          !self.class.columns.detect { |column| column.name == \"secret\" }&.null\n      end\n\n      # Helper method to extract collection of serializable attribute names\n      # considering serialization options (like `only`, `except` and so on).\n      #\n      # @param options [Hash] serialization options\n      #\n      # @return [Array<String>]\n      #   collection of attributes to be serialized using #as_json\n      #\n      def extract_serializable_attributes(options = {})\n        opts = options.try(:dup) || {}\n        only = Array.wrap(opts[:only]).map(&:to_s)\n\n        only = if only.blank?\n                 client_serializable_attributes\n               else\n                 only & client_serializable_attributes\n               end\n\n        only -= Array.wrap(opts[:except]).map(&:to_s) if opts.key?(:except)\n        only.uniq\n      end\n\n      # Collection of attributes that could be serialized for public.\n      # Override this method if you need additional attributes to be serialized.\n      #\n      # @return [Array<String>] collection of serializable attributes\n      #\n      # NOTE: `serializable_attributes` method already taken by Rails >= 6\n      #\n      def client_serializable_attributes\n        attributes = %w[id name created_at]\n        attributes << \"uid\" unless confidential?\n        attributes\n      end\n    end\n\n    module ClassMethods\n      # Returns Applications associated with active (not revoked) Access Tokens\n      # that are owned by the specific Resource Owner.\n      #\n      # @param resource_owner [ActiveRecord::Base]\n      #   Resource Owner model instance\n      #\n      # @return [ActiveRecord::Relation]\n      #   Applications authorized for the Resource Owner\n      #\n      def authorized_for(resource_owner)\n        resource_access_tokens = Doorkeeper.config.access_token_model.active_for(resource_owner)\n        where(id: resource_access_tokens.select(:application_id).distinct)\n      end\n\n      # Revokes AccessToken and AccessGrant records that have not been revoked and\n      # associated with the specific Application and Resource Owner.\n      #\n      # @param resource_owner [ActiveRecord::Base]\n      #   instance of the Resource Owner model\n      #\n      def revoke_tokens_and_grants_for(id, resource_owner)\n        Doorkeeper.config.access_token_model.revoke_all_for(id, resource_owner)\n        Doorkeeper.config.access_grant_model.revoke_all_for(id, resource_owner)\n      end\n\n      private\n\n      def compute_doorkeeper_table_name\n        table_name = \"oauth_application\"\n        table_name = table_name.pluralize if pluralize_table_names\n        \"#{table_name_prefix}#{table_name}#{table_name_suffix}\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/orm/active_record/redirect_uri_validator.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"uri\"\n\nmodule Doorkeeper\n  # ActiveModel validator for redirect URI validation in according\n  # to OAuth standards and Doorkeeper configuration.\n  class RedirectUriValidator < ActiveModel::EachValidator\n    def validate_each(record, attribute, value)\n      if value.blank?\n        return if Doorkeeper.config.allow_blank_redirect_uri?(record)\n\n        record.errors.add(attribute, :blank)\n      else\n        value.split.each do |val|\n          next if oob_redirect_uri?(val)\n\n          uri = ::URI.parse(val)\n          record.errors.add(attribute, :forbidden_uri) if forbidden_uri?(uri)\n          record.errors.add(attribute, :fragment_present) unless uri.fragment.nil?\n          record.errors.add(attribute, :unspecified_scheme) if unspecified_scheme?(uri)\n          record.errors.add(attribute, :relative_uri) if relative_uri?(uri)\n          record.errors.add(attribute, :secured_uri) if invalid_ssl_uri?(uri)\n          record.errors.add(attribute, :invalid_uri) if unspecified_host?(uri)\n        end\n      end\n    rescue URI::InvalidURIError\n      record.errors.add(attribute, :invalid_uri)\n    end\n\n    private\n\n    def oob_redirect_uri?(uri)\n      Doorkeeper::OAuth::NonStandard::IETF_WG_OAUTH2_OOB_METHODS.include?(uri)\n    end\n\n    def forbidden_uri?(uri)\n      Doorkeeper.config.forbid_redirect_uri.call(uri)\n    end\n\n    def unspecified_scheme?(uri)\n      return true if uri.opaque.present?\n\n      %w[localhost].include?(uri.try(:scheme))\n    end\n\n    def unspecified_host?(uri)\n      uri.is_a?(URI::HTTP) && uri.host.blank?\n    end\n\n    def relative_uri?(uri)\n      uri.scheme.nil? && uri.host.blank?\n    end\n\n    def invalid_ssl_uri?(uri)\n      forces_ssl = Doorkeeper.config.force_ssl_in_redirect_uri\n      non_https = uri.try(:scheme) == \"http\"\n\n      if forces_ssl.respond_to?(:call)\n        forces_ssl.call(uri) && non_https\n      else\n        forces_ssl && non_https\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/orm/active_record/stale_records_cleaner.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Orm\n    module ActiveRecord\n      # Helper class to clear stale and non-active tokens and grants.\n      # Used by Doorkeeper Rake tasks.\n      #\n      class StaleRecordsCleaner\n        def initialize(base_scope)\n          @base_scope = base_scope\n        end\n\n        # Clears revoked records\n        def clean_revoked\n          table = @base_scope.arel_table\n\n          @base_scope\n            .where.not(revoked_at: nil)\n            .where(table[:revoked_at].lt(Time.current))\n            .in_batches(&:delete_all)\n        end\n\n        # Clears expired records\n        def clean_expired(ttl)\n          table = @base_scope.arel_table\n          model_class = @base_scope.is_a?(::ActiveRecord::Relation) ? @base_scope.klass : @base_scope\n\n          scope = @base_scope\n            .where.not(expires_in: nil)\n            .where(table[:created_at].lt(Time.current - ttl))\n\n          if model_class.respond_to?(:supports_expiration_time_math?) && model_class.supports_expiration_time_math?\n            scope = scope.where(\"#{model_class.expiration_time_sql} < ?\", Time.current)\n          else\n            ::Kernel.warn(::Doorkeeper::Models::ExpirationTimeSqlMath::WARNING_MESSAGE)\n          end\n\n          scope.in_batches(&:delete_all)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/orm/active_record.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  autoload :AccessGrant, \"doorkeeper/orm/active_record/access_grant\"\n  autoload :AccessToken, \"doorkeeper/orm/active_record/access_token\"\n  autoload :Application, \"doorkeeper/orm/active_record/application\"\n  autoload :RedirectUriValidator, \"doorkeeper/orm/active_record/redirect_uri_validator\"\n\n  module Models\n    autoload :Ownership, \"doorkeeper/models/concerns/ownership\"\n  end\n\n  # ActiveRecord ORM for Doorkeeper entity models.\n  # Consists of three main OAuth entities:\n  #   * Access Token\n  #   * Access Grant\n  #   * Application (client)\n  #\n  # Do a lazy loading of all the required and configured stuff.\n  #\n  module Orm\n    module ActiveRecord\n      autoload :StaleRecordsCleaner, \"doorkeeper/orm/active_record/stale_records_cleaner\"\n\n      module Mixins\n        autoload :AccessGrant, \"doorkeeper/orm/active_record/mixins/access_grant\"\n        autoload :AccessToken, \"doorkeeper/orm/active_record/mixins/access_token\"\n        autoload :Application, \"doorkeeper/orm/active_record/mixins/application\"\n      end\n\n      def self.run_hooks\n        initialize_configured_associations\n      end\n\n      def self.initialize_configured_associations\n        # NOTE: on_load block is instance_exec'd on ActiveRecord::Base,\n        #       so use fully qualified references (e.g. Doorkeeper.config).\n        ActiveSupport.on_load(:active_record) do\n          if Doorkeeper.config.enable_application_owner?\n            Doorkeeper.config.application_model.include ::Doorkeeper::Models::Ownership\n          end\n\n          Doorkeeper.config.access_grant_model.include ::Doorkeeper::Models::PolymorphicResourceOwner::ForAccessGrant\n          Doorkeeper.config.access_token_model.include ::Doorkeeper::Models::PolymorphicResourceOwner::ForAccessToken\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/rails/helpers.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Rails\n    module Helpers\n      def doorkeeper_authorize!(*scopes)\n        @_doorkeeper_scopes = scopes.presence || Doorkeeper.config.default_scopes\n\n        doorkeeper_render_error unless valid_doorkeeper_token?\n      end\n\n      def doorkeeper_unauthorized_render_options(**); end\n\n      def doorkeeper_forbidden_render_options(**); end\n\n      def valid_doorkeeper_token?\n        doorkeeper_token&.acceptable?(@_doorkeeper_scopes)\n      end\n\n      private\n\n      def doorkeeper_render_error\n        error = doorkeeper_error\n        error.raise_exception! if Doorkeeper.config.raise_on_errors?\n\n        headers.merge!(error.headers.reject { |k| k == \"Content-Type\" })\n        doorkeeper_render_error_with(error)\n      end\n\n      def doorkeeper_render_error_with(error)\n        options = doorkeeper_render_options(error) || {}\n        status = doorkeeper_status_for_error(\n          error, options.delete(:respond_not_found_when_forbidden),\n        )\n        if options.blank?\n          head status\n        else\n          options[:status] = status\n          options[:layout] = false if options[:layout].nil?\n          render options\n        end\n      end\n\n      def doorkeeper_error\n        if doorkeeper_invalid_token_response?\n          OAuth::InvalidTokenResponse.from_access_token(doorkeeper_token)\n        else\n          OAuth::ForbiddenTokenResponse.from_scopes(@_doorkeeper_scopes)\n        end\n      end\n\n      def doorkeeper_render_options(error)\n        if doorkeeper_invalid_token_response?\n          doorkeeper_unauthorized_render_options(error: error)\n        else\n          doorkeeper_forbidden_render_options(error: error)\n        end\n      end\n\n      def doorkeeper_status_for_error(error, respond_not_found_when_forbidden)\n        if respond_not_found_when_forbidden && error.status == :forbidden\n          :not_found\n        else\n          error.status\n        end\n      end\n\n      def doorkeeper_invalid_token_response?\n        !doorkeeper_token || !doorkeeper_token.accessible?\n      end\n\n      def doorkeeper_token\n        return @doorkeeper_token if defined?(@doorkeeper_token)\n\n        @doorkeeper_token = OAuth::Token.authenticate(\n          request,\n          *Doorkeeper.config.access_token_methods,\n        )\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/rails/routes/abstract_router.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Rails\n    # Abstract router module that implements base behavior\n    # for generating and mapping Rails routes.\n    #\n    # Could be reused in Doorkeeper extensions.\n    #\n    module AbstractRouter\n      extend ActiveSupport::Concern\n\n      attr_reader :routes\n\n      def initialize(routes, mapper = Mapper.new, &block)\n        @routes = routes\n        @mapping = mapper.map(&block)\n      end\n\n      def generate_routes!(**_options)\n        raise NotImplementedError, \"must be redefined for #{self.class.name}!\"\n      end\n\n      private\n\n      def map_route(name, method)\n        return if @mapping.skipped?(name)\n\n        send(method, @mapping[name])\n\n        mapping[name] = @mapping[name]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/rails/routes/mapper.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Rails\n    class Routes # :nodoc:\n      class Mapper\n        def initialize(mapping = Mapping.new)\n          @mapping = mapping\n        end\n\n        def map(&block)\n          instance_eval(&block) if block\n          @mapping\n        end\n\n        def controllers(controller_names = {})\n          @mapping.controllers.merge!(controller_names)\n        end\n\n        def skip_controllers(*controller_names)\n          @mapping.skips = controller_names\n        end\n\n        def as(alias_names = {})\n          @mapping.as.merge!(alias_names)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/rails/routes/mapping.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Rails\n    class Routes # :nodoc:\n      class Mapping\n        attr_accessor :controllers, :as, :skips\n\n        def initialize\n          @controllers = {\n            authorizations: \"doorkeeper/authorizations\",\n            applications: \"doorkeeper/applications\",\n            authorized_applications: \"doorkeeper/authorized_applications\",\n            tokens: \"doorkeeper/tokens\",\n            token_info: \"doorkeeper/token_info\",\n          }\n\n          @as = {\n            authorizations: :authorization,\n            tokens: :token,\n            token_info: :token_info,\n          }\n\n          @skips = []\n        end\n\n        def [](routes)\n          {\n            controllers: @controllers[routes],\n            as: @as[routes],\n          }\n        end\n\n        def skipped?(controller)\n          @skips.include?(controller)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/rails/routes/registry.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Rails\n    class Routes\n      # Thread-safe registry of any Doorkeeper additional routes.\n      # Used to allow implementing of Doorkeeper extensions that must\n      # use their own routes.\n      #\n      module Registry\n        ROUTES_ACCESS_LOCK = Mutex.new\n        ROUTES_DEFINITION_LOCK = Mutex.new\n\n        InvalidRouterClass = Class.new(StandardError)\n\n        # Collection of additional registered routes for Doorkeeper.\n        #\n        # @return [Array<Object>] set of registered routes\n        #\n        def registered_routes\n          ROUTES_DEFINITION_LOCK.synchronize do\n            @registered_routes ||= Set.new\n          end\n        end\n\n        # Registers additional routes in the Doorkeeper registry\n        #\n        # @param [Object] routes\n        #   routes class\n        #\n        def register_routes(routes)\n          if !routes.is_a?(Module) || !(routes < AbstractRouter)\n            raise InvalidRouterClass, \"routes class must include Doorkeeper::Rails::AbstractRouter\"\n          end\n\n          ROUTES_ACCESS_LOCK.synchronize do\n            registered_routes << routes\n          end\n        end\n\n        alias register register_routes\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/rails/routes.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"doorkeeper/rails/routes/mapping\"\nrequire \"doorkeeper/rails/routes/mapper\"\nrequire \"doorkeeper/rails/routes/abstract_router\"\nrequire \"doorkeeper/rails/routes/registry\"\n\nmodule Doorkeeper\n  module Rails\n    class Routes # :nodoc:\n      module Helper\n        def use_doorkeeper(options = {}, &block)\n          Doorkeeper::Rails::Routes.new(self, &block).generate_routes!(options)\n        end\n      end\n\n      include AbstractRouter\n      extend Registry\n\n      mattr_reader :mapping do\n        {}\n      end\n\n      def self.install!\n        ActionDispatch::Routing::Mapper.include Doorkeeper::Rails::Routes::Helper\n\n        registered_routes.each(&:install!)\n      end\n\n      def initialize(routes, mapper = Mapper.new, &block)\n        super\n      end\n\n      def generate_routes!(options)\n        routes.scope options[:scope] || \"oauth\", as: \"oauth\" do\n          map_route(:authorizations, :authorization_routes)\n          map_route(:tokens, :token_routes)\n          map_route(:tokens, :revoke_routes)\n          map_route(:tokens, :introspect_routes) if introspection_routes?\n          map_route(:applications, :application_routes)\n          map_route(:authorized_applications, :authorized_applications_routes)\n          map_route(:token_info, :token_info_routes)\n        end\n      end\n\n      private\n\n      def authorization_routes(mapping)\n        routes.resource(\n          :authorization,\n          path: \"authorize\",\n          only: %i[create destroy],\n          as: mapping[:as],\n          controller: mapping[:controllers],\n        ) do\n          routes.get native_authorization_code_route, action: :show, on: :member\n          routes.get '/', action: :new, on: :member\n        end\n      end\n\n      def token_routes(mapping)\n        routes.resource(\n          :token,\n          path: \"token\",\n          only: [:create], as: mapping[:as],\n          controller: mapping[:controllers],\n        )\n      end\n\n      def revoke_routes(mapping)\n        routes.post \"revoke\", controller: mapping[:controllers], action: :revoke\n      end\n\n      def introspect_routes(mapping)\n        routes.post \"introspect\", controller: mapping[:controllers], action: :introspect\n      end\n\n      def token_info_routes(mapping)\n        routes.resource(\n          :token_info,\n          path: \"token/info\",\n          only: [:show], as: mapping[:as],\n          controller: mapping[:controllers],\n        )\n      end\n\n      def application_routes(mapping)\n        routes.resources :doorkeeper_applications,\n                         controller: mapping[:controllers],\n                         as: :applications,\n                         path: \"applications\"\n      end\n\n      def authorized_applications_routes(mapping)\n        routes.resources :authorized_applications,\n                         only: %i[index destroy],\n                         controller: mapping[:controllers]\n      end\n\n      def native_authorization_code_route\n        Doorkeeper.configuration.native_authorization_code_route\n      end\n\n      def introspection_routes?\n        Doorkeeper.configured? &&\n          !Doorkeeper.config.allow_token_introspection.is_a?(FalseClass)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/rake/db.rake",
    "content": "# frozen_string_literal: true\n\nnamespace :doorkeeper do\n  namespace :db do\n    desc \"Removes stale data from doorkeeper related database tables\"\n    task cleanup: [\n      \"doorkeeper:db:cleanup:revoked_tokens\",\n      \"doorkeeper:db:cleanup:expired_tokens\",\n      \"doorkeeper:db:cleanup:revoked_grants\",\n      \"doorkeeper:db:cleanup:expired_grants\",\n    ]\n\n    namespace :cleanup do\n      desc \"Removes stale access tokens\"\n      task revoked_tokens: \"doorkeeper:setup\" do\n        cleaner = Doorkeeper::StaleRecordsCleaner.new(Doorkeeper.config.access_token_model)\n        cleaner.clean_revoked\n      end\n\n      desc \"Removes expired (TTL passed) access tokens\"\n      task expired_tokens: \"doorkeeper:setup\" do\n        expirable_tokens = Doorkeeper.config.access_token_model.where(refresh_token: nil)\n        cleaner = Doorkeeper::StaleRecordsCleaner.new(expirable_tokens)\n        cleaner.clean_expired(Doorkeeper.config.access_token_expires_in)\n      end\n\n      desc \"Removes stale access grants\"\n      task revoked_grants: \"doorkeeper:setup\" do\n        cleaner = Doorkeeper::StaleRecordsCleaner.new(Doorkeeper.config.access_grant_model)\n        cleaner.clean_revoked\n      end\n\n      desc \"Removes expired (TTL passed) access grants\"\n      task expired_grants: \"doorkeeper:setup\" do\n        cleaner = Doorkeeper::StaleRecordsCleaner.new(Doorkeeper.config.access_grant_model)\n        cleaner.clean_expired(Doorkeeper.config.authorization_code_expires_in)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/rake/setup.rake",
    "content": "# frozen_string_literal: true\n\nnamespace :doorkeeper do\n  task setup: :environment do\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/rake.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Rake\n    class << self\n      def load_tasks\n        glob = File.join(File.absolute_path(__dir__), \"rake\", \"*.rake\")\n        Dir[glob].each do |rake_file|\n          load rake_file\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/request/authorization_code.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Request\n    class AuthorizationCode < Strategy\n      delegate :client, :parameters, to: :server\n\n      def request\n        @request ||= OAuth::AuthorizationCodeRequest.new(\n          Doorkeeper.config,\n          grant,\n          client,\n          parameters,\n        )\n      end\n\n      private\n\n      def grant\n        raise Errors::MissingRequiredParameter, :code if parameters[:code].blank?\n\n        Doorkeeper.config.access_grant_model.by_token(parameters[:code])\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/request/client_credentials.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Request\n    class ClientCredentials < Strategy\n      delegate :client, :parameters, to: :server\n\n      def request\n        @request ||= OAuth::ClientCredentialsRequest.new(\n          Doorkeeper.config,\n          client,\n          parameters,\n        )\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/request/code.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Request\n    class Code < Strategy\n      delegate :current_resource_owner, to: :server\n\n      def pre_auth\n        server.context.send(:pre_auth)\n      end\n\n      def request\n        @request ||= OAuth::CodeRequest.new(pre_auth, current_resource_owner)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/request/password.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Request\n    class Password < Strategy\n      delegate :credentials, :resource_owner, :parameters, :client, to: :server\n\n      def request\n        @request ||= OAuth::PasswordAccessTokenRequest.new(\n          Doorkeeper.config,\n          client,\n          credentials,\n          resource_owner,\n          parameters,\n        )\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/request/refresh_token.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Request\n    class RefreshToken < Strategy\n      delegate :credentials, :parameters, to: :server\n\n      def refresh_token\n        Doorkeeper.config.access_token_model.by_refresh_token(parameters[:refresh_token])\n      end\n\n      def request\n        @request ||= OAuth::RefreshTokenRequest.new(\n          Doorkeeper.config,\n          refresh_token,\n          credentials,\n          parameters,\n        )\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/request/strategy.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Request\n    class Strategy\n      attr_reader :server\n\n      delegate :authorize, to: :request\n\n      def initialize(server)\n        @server = server\n      end\n\n      def request\n        raise NotImplementedError, \"request strategies must define #request\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/request/token.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Request\n    class Token < Strategy\n      delegate :current_resource_owner, to: :server\n\n      def pre_auth\n        server.context.send(:pre_auth)\n      end\n\n      def request\n        @request ||= OAuth::TokenRequest.new(pre_auth, current_resource_owner)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/request.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Request\n    class << self\n      def authorization_strategy(response_type)\n        grant_flow = authorization_flows.detect do |flow|\n          flow.matches_response_type?(response_type)\n        end\n\n        if grant_flow\n          grant_flow.response_type_strategy\n        else\n          # [NOTE]: this will be removed in a newer versions of Doorkeeper.\n          # For retro-compatibility only\n          build_fallback_strategy_class(response_type)\n        end\n      end\n\n      def token_strategy(grant_type)\n        raise Errors::MissingRequiredParameter, :grant_type if grant_type.blank?\n\n        grant_flow = token_flows.detect do |flow|\n          flow.matches_grant_type?(grant_type)\n        end\n\n        if grant_flow\n          grant_flow.grant_type_strategy\n        else\n          # [NOTE]: this will be removed in a newer versions of Doorkeeper.\n          # For retro-compatibility only\n          raise Errors::InvalidTokenStrategy unless available.include?(grant_type.to_s)\n\n          strategy_class = build_fallback_strategy_class(grant_type)\n          raise Errors::InvalidTokenStrategy unless strategy_class\n\n          strategy_class\n        end\n      end\n\n      private\n\n      def authorization_flows\n        Doorkeeper.configuration.authorization_response_flows\n      end\n\n      def token_flows\n        Doorkeeper.configuration.token_grant_flows\n      end\n\n      # [NOTE]: this will be removed in a newer versions of Doorkeeper.\n      # For retro-compatibility only\n      def available\n        Doorkeeper.config.deprecated_token_grant_types_resolver\n      end\n\n      def build_fallback_strategy_class(grant_or_request_type)\n        strategy_class_name = grant_or_request_type.to_s.tr(\" \", \"_\").camelize\n        fallback_strategy = \"Doorkeeper::Request::#{strategy_class_name}\".constantize\n\n        ::Kernel.warn <<~WARNING\n          [DOORKEEPER] #{fallback_strategy} found using fallback, it must be\n          registered using `Doorkeeper::GrantFlow.register(grant_flow_name, **options)`.\n          This functionality will be removed in a newer versions of Doorkeeper.\n        WARNING\n\n        fallback_strategy\n      rescue NameError\n        raise Errors::InvalidTokenStrategy\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/revocable_tokens/revocable_access_token.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module RevocableTokens\n    class RevocableAccessToken\n      attr_reader :token\n\n      def initialize(token)\n        @token = token\n      end\n\n      def revocable?\n        token.accessible?\n      end\n\n      def revoke\n        token.revoke\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/revocable_tokens/revocable_refresh_token.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module RevocableTokens\n    class RevocableRefreshToken\n      attr_reader :token\n\n      def initialize(token)\n        @token = token\n      end\n\n      def revocable?\n        !token.revoked?\n      end\n\n      def revoke\n        token.revoke\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/secret_storing/base.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module SecretStoring\n    ##\n    # Base class for secret storing, including common helpers\n    class Base\n      ##\n      # Return the value to be stored by the database\n      # used for looking up a database value.\n      # @param plain_secret The plain secret input / generated\n      def self.transform_secret(_plain_secret)\n        raise NotImplementedError\n      end\n\n      ##\n      # Transform and store the given secret attribute => value\n      # pair used for safely storing the attribute\n      # @param resource The model instance being modified\n      # @param attribute The secret attribute\n      # @param plain_secret The plain secret input / generated\n      def self.store_secret(resource, attribute, plain_secret)\n        transformed_value = transform_secret(plain_secret)\n        resource.public_send(:\"#{attribute}=\", transformed_value)\n\n        transformed_value\n      end\n\n      ##\n      # Return the restored value from the database\n      # @param resource The resource instance to act on\n      # @param attribute The secret attribute to restore\n      # as retrieved from the database.\n      def self.restore_secret(_resource, _attribute)\n        raise NotImplementedError\n      end\n\n      ##\n      # Determines whether this strategy supports restoring\n      # secrets from the database. This allows detecting users\n      # trying to use a non-restorable strategy with +reuse_access_tokens+.\n      def self.allows_restoring_secrets?\n        false\n      end\n\n      ##\n      # Determines what secrets this strategy is applicable for\n      def self.validate_for(model)\n        valid = %i[token application]\n        return true if valid.include?(model.to_sym)\n\n        raise ArgumentError, \"'#{name}' can not be used for #{model}.\"\n      end\n\n      ##\n      # Securely compare the given +input+ value with a +stored+ value\n      # processed by +transform_secret+.\n      def self.secret_matches?(input, stored)\n        transformed_input = transform_secret(input)\n        ActiveSupport::SecurityUtils.secure_compare transformed_input, stored\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/secret_storing/bcrypt.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module SecretStoring\n    ##\n    # Plain text secret storing, which is the default\n    # but also provides fallback lookup if\n    # other secret storing mechanisms are enabled.\n    class BCrypt < Base\n      ##\n      # Return the value to be stored by the database\n      # @param plain_secret The plain secret input / generated\n      def self.transform_secret(plain_secret)\n        ::BCrypt::Password.create(plain_secret.to_s)\n      end\n\n      ##\n      # Securely compare the given +input+ value with a +stored+ value\n      # processed by +transform_secret+.\n      def self.secret_matches?(input, stored)\n        ::BCrypt::Password.new(stored.to_s) == input.to_s\n      rescue ::BCrypt::Errors::InvalidHash\n        false\n      end\n\n      ##\n      # Determines whether this strategy supports restoring\n      # secrets from the database. This allows detecting users\n      # trying to use a non-restorable strategy with +reuse_access_tokens+.\n      def self.allows_restoring_secrets?\n        false\n      end\n\n      ##\n      # Determines what secrets this strategy is applicable for\n      def self.validate_for(model)\n        unless model.to_sym == :application\n          raise ArgumentError,\n                \"'#{name}' can only be used for storing application secrets.\"\n        end\n\n        unless bcrypt_present?\n          raise ArgumentError,\n                \"'#{name}' requires the 'bcrypt' gem being loaded.\"\n        end\n\n        true\n      end\n\n      ##\n      # Test if we can require the BCrypt gem\n      def self.bcrypt_present?\n        require \"bcrypt\"\n        true\n      rescue LoadError\n        false\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/secret_storing/plain.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module SecretStoring\n    ##\n    # Plain text secret storing, which is the default\n    # but also provides fallback lookup if\n    # other secret storing mechanisms are enabled.\n    class Plain < Base\n      ##\n      # Return the value to be stored by the database\n      # @param plain_secret The plain secret input / generated\n      def self.transform_secret(plain_secret)\n        plain_secret\n      end\n\n      ##\n      # Return the restored value from the database\n      # @param resource The resource instance to act on\n      # @param attribute The secret attribute to restore\n      # as retrieved from the database.\n      def self.restore_secret(resource, attribute)\n        resource.public_send(attribute)\n      end\n\n      ##\n      # Plain values obviously allow restoring\n      def self.allows_restoring_secrets?\n        true\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/secret_storing/sha256_hash.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module SecretStoring\n    ##\n    # Plain text secret storing, which is the default\n    # but also provides fallback lookup if\n    # other secret storing mechanisms are enabled.\n    class Sha256Hash < Base\n      ##\n      # Return the value to be stored by the database\n      # @param plain_secret The plain secret input / generated\n      def self.transform_secret(plain_secret)\n        ::Digest::SHA256.hexdigest plain_secret\n      end\n\n      ##\n      # Determines whether this strategy supports restoring\n      # secrets from the database. This allows detecting users\n      # trying to use a non-restorable strategy with +reuse_access_tokens+.\n      def self.allows_restoring_secrets?\n        false\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/server.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  class Server\n    attr_reader :context\n\n    def initialize(context)\n      @context = context\n    end\n\n    def authorization_request(strategy)\n      klass = Request.authorization_strategy(strategy)\n      klass.new(self)\n    end\n\n    def token_request(strategy)\n      klass = Request.token_strategy(strategy)\n      klass.new(self)\n    end\n\n    # TODO: context should be the request\n    def parameters\n      context.request.parameters\n    end\n\n    def client\n      @client ||= OAuth::Client.authenticate(credentials)\n    end\n\n    def current_resource_owner\n      context.send :current_resource_owner\n    end\n\n    # TODO: Use configuration and evaluate proper context on block\n    def resource_owner\n      context.send :resource_owner_from_credentials\n    end\n\n    def credentials\n      methods = Doorkeeper.config.client_credentials_methods\n      @credentials ||= OAuth::Client::Credentials.from_request(context.request, *methods)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/stale_records_cleaner.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  class StaleRecordsCleaner\n    CLEANER_CLASS = \"StaleRecordsCleaner\"\n\n    def self.for(base_scope)\n      orm_adapter = \"doorkeeper/orm/#{configured_orm}\".classify\n\n      orm_cleaner = \"#{orm_adapter}::#{CLEANER_CLASS}\".constantize\n      orm_cleaner.new(base_scope)\n    rescue NameError\n      raise Doorkeeper::Errors::NoOrmCleaner, \"'#{configured_orm}' ORM has no cleaner!\"\n    end\n\n    def self.new(base_scope)\n      self.for(base_scope)\n    end\n\n    def self.configured_orm\n      Doorkeeper.config.orm\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/validations.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Validations\n    extend ActiveSupport::Concern\n\n    attr_accessor :error\n\n    def validate\n      @error = nil\n\n      self.class.validations.each do |validation|\n        @error = validation[:options][:error] unless send(\"validate_#{validation[:attribute]}\")\n        break if @error\n      end\n    end\n\n    def valid?\n      validate\n      @error.nil?\n    end\n\n    module ClassMethods\n      def validate(attribute, options = {})\n        validations << { attribute: attribute, options: options }\n      end\n\n      def validations\n        @validations ||= []\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper/version.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module VERSION\n    # Semantic versioning\n    MAJOR = 5\n    MINOR = 9\n    TINY = 0\n    PRE = nil\n\n    # Full version number\n    STRING = [MAJOR, MINOR, TINY, PRE].compact.join(\".\")\n  end\nend\n"
  },
  {
    "path": "lib/doorkeeper.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"doorkeeper/config\"\nrequire \"doorkeeper/engine\"\n\n# Main Doorkeeper namespace.\n#\nmodule Doorkeeper\n  autoload :Errors, \"doorkeeper/errors\"\n  autoload :GrantFlow, \"doorkeeper/grant_flow\"\n  autoload :OAuth, \"doorkeeper/oauth\"\n  autoload :Rake, \"doorkeeper/rake\"\n  autoload :Request, \"doorkeeper/request\"\n  autoload :Server, \"doorkeeper/server\"\n  autoload :StaleRecordsCleaner, \"doorkeeper/stale_records_cleaner\"\n  autoload :Validations, \"doorkeeper/validations\"\n  autoload :VERSION, \"doorkeeper/version\"\n\n  autoload :AccessGrantMixin, \"doorkeeper/models/access_grant_mixin\"\n  autoload :AccessTokenMixin, \"doorkeeper/models/access_token_mixin\"\n  autoload :ApplicationMixin, \"doorkeeper/models/application_mixin\"\n\n  module Helpers\n    autoload :Controller, \"doorkeeper/helpers/controller\"\n  end\n\n  module Request\n    autoload :Strategy, \"doorkeeper/request/strategy\"\n    autoload :AuthorizationCode, \"doorkeeper/request/authorization_code\"\n    autoload :ClientCredentials, \"doorkeeper/request/client_credentials\"\n    autoload :Code, \"doorkeeper/request/code\"\n    autoload :Password, \"doorkeeper/request/password\"\n    autoload :RefreshToken, \"doorkeeper/request/refresh_token\"\n    autoload :Token, \"doorkeeper/request/token\"\n  end\n\n  module RevocableTokens\n    autoload :RevocableAccessToken, \"doorkeeper/revocable_tokens/revocable_access_token\"\n    autoload :RevocableRefreshToken, \"doorkeeper/revocable_tokens/revocable_refresh_token\"\n  end\n\n  module OAuth\n    autoload :BaseRequest, \"doorkeeper/oauth/base_request\"\n    autoload :AuthorizationCodeRequest, \"doorkeeper/oauth/authorization_code_request\"\n    autoload :BaseResponse, \"doorkeeper/oauth/base_response\"\n    autoload :CodeResponse, \"doorkeeper/oauth/code_response\"\n    autoload :Client, \"doorkeeper/oauth/client\"\n    autoload :ClientCredentialsRequest, \"doorkeeper/oauth/client_credentials_request\"\n    autoload :CodeRequest, \"doorkeeper/oauth/code_request\"\n    autoload :ErrorResponse, \"doorkeeper/oauth/error_response\"\n    autoload :Error, \"doorkeeper/oauth/error\"\n    autoload :InvalidTokenResponse, \"doorkeeper/oauth/invalid_token_response\"\n    autoload :InvalidRequestResponse, \"doorkeeper/oauth/invalid_request_response\"\n    autoload :ForbiddenTokenResponse, \"doorkeeper/oauth/forbidden_token_response\"\n    autoload :NonStandard, \"doorkeeper/oauth/nonstandard\"\n    autoload :PasswordAccessTokenRequest, \"doorkeeper/oauth/password_access_token_request\"\n    autoload :PreAuthorization, \"doorkeeper/oauth/pre_authorization\"\n    autoload :RefreshTokenRequest, \"doorkeeper/oauth/refresh_token_request\"\n    autoload :Scopes, \"doorkeeper/oauth/scopes\"\n    autoload :Token, \"doorkeeper/oauth/token\"\n    autoload :TokenIntrospection, \"doorkeeper/oauth/token_introspection\"\n    autoload :TokenRequest, \"doorkeeper/oauth/token_request\"\n    autoload :TokenResponse, \"doorkeeper/oauth/token_response\"\n\n    module Authorization\n      autoload :Code, \"doorkeeper/oauth/authorization/code\"\n      autoload :Context, \"doorkeeper/oauth/authorization/context\"\n      autoload :Token, \"doorkeeper/oauth/authorization/token\"\n      autoload :URIBuilder, \"doorkeeper/oauth/authorization/uri_builder\"\n    end\n\n    class Client\n      autoload :Credentials, \"doorkeeper/oauth/client/credentials\"\n    end\n\n    module ClientCredentials\n      autoload :Validator, \"doorkeeper/oauth/client_credentials/validator\"\n      autoload :Creator, \"doorkeeper/oauth/client_credentials/creator\"\n      autoload :Issuer, \"doorkeeper/oauth/client_credentials/issuer\"\n    end\n\n    module Helpers\n      autoload :ScopeChecker, \"doorkeeper/oauth/helpers/scope_checker\"\n      autoload :URIChecker, \"doorkeeper/oauth/helpers/uri_checker\"\n      autoload :UniqueToken, \"doorkeeper/oauth/helpers/unique_token\"\n    end\n\n    module Hooks\n      autoload :Context, \"doorkeeper/oauth/hooks/context\"\n    end\n  end\n\n  module Models\n    autoload :Accessible, \"doorkeeper/models/concerns/accessible\"\n    autoload :Expirable, \"doorkeeper/models/concerns/expirable\"\n    autoload :ExpirationTimeSqlMath, \"doorkeeper/models/concerns/expiration_time_sql_math\"\n    autoload :Orderable, \"doorkeeper/models/concerns/orderable\"\n    autoload :PolymorphicResourceOwner, \"doorkeeper/models/concerns/polymorphic_resource_owner\"\n    autoload :Scopes, \"doorkeeper/models/concerns/scopes\"\n    autoload :Reusable, \"doorkeeper/models/concerns/reusable\"\n    autoload :ResourceOwnerable, \"doorkeeper/models/concerns/resource_ownerable\"\n    autoload :Revocable, \"doorkeeper/models/concerns/revocable\"\n    autoload :SecretStorable, \"doorkeeper/models/concerns/secret_storable\"\n\n    module Concerns\n      autoload :WriteToPrimary, \"doorkeeper/models/concerns/write_to_primary\"\n    end\n  end\n\n  module Orm\n    autoload :ActiveRecord, \"doorkeeper/orm/active_record\"\n  end\n\n  module Rails\n    autoload :Helpers, \"doorkeeper/rails/helpers\"\n    autoload :Routes, \"doorkeeper/rails/routes\"\n  end\n\n  module SecretStoring\n    autoload :Base, \"doorkeeper/secret_storing/base\"\n    autoload :Plain, \"doorkeeper/secret_storing/plain\"\n    autoload :Sha256Hash, \"doorkeeper/secret_storing/sha256_hash\"\n    autoload :BCrypt, \"doorkeeper/secret_storing/bcrypt\"\n  end\n\n  class << self\n    attr_reader :orm_adapter\n\n    def configure(&block)\n      @config = Config::Builder.new(&block).build\n      setup\n      @config\n    end\n\n    # @return [Doorkeeper::Config] configuration instance\n    #\n    def configuration\n      @config || configure\n    end\n\n    def configured?\n      !@config.nil?\n    end\n\n    alias config configuration\n\n    def setup\n      setup_orm_adapter\n\n      # Deprecated, will be removed soon\n      unless configuration.orm == :active_record\n        setup_orm_models\n        setup_application_owner\n      end\n    end\n\n    def setup_filter_parameters\n      return unless defined?(::Rails) && ::Rails.application && configured?\n\n      parameters = %w[client_secret authentication_token access_token refresh_token]\n      parameters << \"code\" if configuration.grant_flows.include?(\"authorization_code\")\n      filter = /^(#{Regexp.union(parameters)})$/\n      filter_params = ::Rails.application.config.filter_parameters\n      filter_params << filter unless filter_params.include?(filter)\n    end\n\n    def setup_orm_adapter\n      @orm_adapter = \"doorkeeper/orm/#{configuration.orm}\".classify.constantize\n    rescue NameError => e\n      raise e, \"ORM adapter not found (#{configuration.orm})\", <<-ERROR_MSG.strip_heredoc\n        [DOORKEEPER] ORM adapter not found (#{configuration.orm}), or there was an error\n        trying to load it.\n\n        You probably need to add the related gem for this adapter to work with\n        doorkeeper.\n      ERROR_MSG\n    end\n\n    def run_orm_hooks\n      config.clear_cache!\n\n      if @orm_adapter.respond_to?(:run_hooks)\n        @orm_adapter.run_hooks\n      else\n        ::Kernel.warn <<~MSG.strip_heredoc\n          [DOORKEEPER] ORM \"#{configuration.orm}\" should move all it's setup logic under `#run_hooks` method for\n          the #{@orm_adapter.name}. Later versions of Doorkeeper will no longer support `setup_orm_models` and\n          `setup_application_owner` API.\n        MSG\n      end\n    end\n\n    def setup_orm_models\n      @orm_adapter.initialize_models!\n    end\n\n    def setup_application_owner\n      @orm_adapter.initialize_application_owner!\n    end\n\n    def authenticate(request, methods = Doorkeeper.config.access_token_methods)\n      OAuth::Token.authenticate(request, *methods)\n    end\n\n    def gem_version\n      ::Gem::Version.new(::Doorkeeper::VERSION::STRING)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/generators/doorkeeper/application_owner_generator.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"rails/generators\"\nrequire \"rails/generators/active_record\"\n\nmodule Doorkeeper\n  # Generates migration to add reference to owner of the\n  # Doorkeeper application.\n  #\n  class ApplicationOwnerGenerator < ::Rails::Generators::Base\n    include ::Rails::Generators::Migration\n    source_root File.expand_path(\"templates\", __dir__)\n    desc \"Provide support for client application ownership.\"\n\n    def application_owner\n      migration_template(\n        \"add_owner_to_application_migration.rb.erb\",\n        \"db/migrate/add_owner_to_application.rb\",\n        migration_version: migration_version,\n      )\n    end\n\n    def self.next_migration_number(dirname)\n      ActiveRecord::Generators::Base.next_migration_number(dirname)\n    end\n\n    private\n\n    def migration_version\n      \"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/generators/doorkeeper/confidential_applications_generator.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"rails/generators\"\nrequire \"rails/generators/active_record\"\n\nmodule Doorkeeper\n  # Generates migration to add confidential column to Doorkeeper\n  # applications table.\n  #\n  class ConfidentialApplicationsGenerator < ::Rails::Generators::Base\n    include ::Rails::Generators::Migration\n    source_root File.expand_path(\"templates\", __dir__)\n    desc \"Add confidential column to Doorkeeper applications\"\n\n    def confidential_applications\n      migration_template(\n        \"add_confidential_to_applications.rb.erb\",\n        \"db/migrate/add_confidential_to_applications.rb\",\n        migration_version: migration_version,\n      )\n    end\n\n    def self.next_migration_number(dirname)\n      ActiveRecord::Generators::Base.next_migration_number(dirname)\n    end\n\n    private\n\n    def migration_version\n      \"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/generators/doorkeeper/enable_polymorphic_resource_owner_generator.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"rails/generators\"\nrequire \"rails/generators/active_record\"\n\nmodule Doorkeeper\n  # Generates migration with polymorphic resource owner required\n  # database columns for Doorkeeper Access Token and Access Grant\n  # models.\n  #\n  class EnablePolymorphicResourceOwnerGenerator < ::Rails::Generators::Base\n    include ::Rails::Generators::Migration\n    source_root File.expand_path(\"templates\", __dir__)\n    desc \"Provide support for polymorphic Resource Owner.\"\n\n    def enable_polymorphic_resource_owner\n      migration_template(\n        \"enable_polymorphic_resource_owner_migration.rb.erb\",\n        \"db/migrate/enable_polymorphic_resource_owner.rb\",\n        migration_version: migration_version,\n      )\n      gsub_file(\n        \"config/initializers/doorkeeper.rb\",\n        \"# use_polymorphic_resource_owner\",\n        \"use_polymorphic_resource_owner\",\n      )\n    end\n\n    def self.next_migration_number(dirname)\n      ActiveRecord::Generators::Base.next_migration_number(dirname)\n    end\n\n    private\n\n    def migration_version\n      \"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/generators/doorkeeper/install_generator.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"rails/generators\"\nrequire \"rails/generators/active_record\"\n\nmodule Doorkeeper\n  # Setup doorkeeper into Rails application: locales, routes, etc.\n  #\n  class InstallGenerator < ::Rails::Generators::Base\n    include ::Rails::Generators::Migration\n    source_root File.expand_path(\"templates\", __dir__)\n    desc \"Installs Doorkeeper.\"\n\n    def install\n      template \"initializer.rb\", \"config/initializers/doorkeeper.rb\"\n      copy_file File.expand_path(\"../../../config/locales/en.yml\", __dir__),\n                \"config/locales/doorkeeper.en.yml\"\n      route \"use_doorkeeper\"\n      readme \"README\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/generators/doorkeeper/migration_generator.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"rails/generators\"\nrequire \"rails/generators/active_record\"\n\nmodule Doorkeeper\n  # Copies main Doorkeeper migration into parent Rails application.\n  #\n  class MigrationGenerator < ::Rails::Generators::Base\n    include ::Rails::Generators::Migration\n    source_root File.expand_path(\"templates\", __dir__)\n    desc \"Installs Doorkeeper migration file.\"\n\n    def install\n      migration_template(\n        \"migration.rb.erb\",\n        \"db/migrate/create_doorkeeper_tables.rb\",\n        migration_version: migration_version,\n      )\n    end\n\n    def self.next_migration_number(dirname)\n      ActiveRecord::Generators::Base.next_migration_number(dirname)\n    end\n\n    private\n\n    def migration_version\n      \"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/generators/doorkeeper/pkce_generator.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"rails/generators\"\nrequire \"rails/generators/active_record\"\n\nmodule Doorkeeper\n  # Generates migration with PKCE required database columns for\n  # Doorkeeper tables.\n  #\n  class PkceGenerator < ::Rails::Generators::Base\n    include ::Rails::Generators::Migration\n    source_root File.expand_path(\"templates\", __dir__)\n    desc \"Provide support for PKCE.\"\n\n    def pkce\n      migration_template(\n        \"enable_pkce_migration.rb.erb\",\n        \"db/migrate/enable_pkce.rb\",\n        migration_version: migration_version,\n      )\n    end\n\n    def self.next_migration_number(dirname)\n      ActiveRecord::Generators::Base.next_migration_number(dirname)\n    end\n\n    private\n\n    def migration_version\n      \"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/generators/doorkeeper/previous_refresh_token_generator.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"rails/generators\"\nrequire \"rails/generators/active_record\"\n\nmodule Doorkeeper\n  # Generates migration to add previous refresh token column to the\n  # database for Doorkeeper tables.\n  #\n  class PreviousRefreshTokenGenerator < ::Rails::Generators::Base\n    include ::Rails::Generators::Migration\n    source_root File.expand_path(\"templates\", __dir__)\n    desc \"Support revoke refresh token on access token use\"\n\n    def self.next_migration_number(path)\n      ActiveRecord::Generators::Base.next_migration_number(path)\n    end\n\n    def previous_refresh_token\n      return unless no_previous_refresh_token_column?\n\n      migration_template(\n        \"add_previous_refresh_token_to_access_tokens.rb.erb\",\n        \"db/migrate/add_previous_refresh_token_to_access_tokens.rb\",\n      )\n    end\n\n    private\n\n    def migration_version\n      \"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]\"\n    end\n\n    def no_previous_refresh_token_column?\n      !ActiveRecord::Base.connection.column_exists?(\n        :oauth_access_tokens,\n        :previous_refresh_token,\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/generators/doorkeeper/remove_applications_secret_not_null_constraint_generator.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"rails/generators\"\nrequire \"rails/generators/active_record\"\n\nmodule Doorkeeper\n  # Generates migration with which drops NOT NULL constraint and allows not\n  # to bloat the database with redundant secret value.\n  #\n  class RemoveApplicationsSecretNotNullConstraintGenerator < ::Rails::Generators::Base\n    include ::Rails::Generators::Migration\n    source_root File.expand_path(\"templates\", __dir__)\n    desc \"Removes NOT NULL constraint for OAuth2 applications.\"\n\n    def remove_applications_secret_not_null_constraint\n      migration_template(\n        \"remove_applications_secret_not_null_constraint.rb.erb\",\n        \"db/migrate/remove_applications_secret_not_null_constraint.rb\",\n        migration_version: migration_version,\n      )\n    end\n\n    def self.next_migration_number(dirname)\n      ActiveRecord::Generators::Base.next_migration_number(dirname)\n    end\n\n    private\n\n    def migration_version\n      \"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/generators/doorkeeper/templates/README",
    "content": "===============================================================================\n\nThere is a setup that you need to do before you can use doorkeeper.\n\nStep 1.\nGo to config/initializers/doorkeeper.rb and configure\nresource_owner_authenticator block.\n\nStep 2.\nChoose the ORM:\n\nIf you want to use ActiveRecord run:\n\n  rails generate doorkeeper:migration\n\nAnd run\n\n  rake db:migrate\n\nStep 3.\nThat's it, that's all. Enjoy!\n\n===============================================================================\n\n"
  },
  {
    "path": "lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb",
    "content": "# frozen_string_literal: true\n\nclass AddConfidentialToApplications < ActiveRecord::Migration<%= migration_version %>\n  def change\n    add_column(\n      :oauth_applications,\n      :confidential,\n      :boolean,\n      null: false,\n      default: true\n    )\n  end\nend\n"
  },
  {
    "path": "lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb",
    "content": "# frozen_string_literal: true\n\nclass AddOwnerToApplication < ActiveRecord::Migration<%= migration_version %>\n  def change\n    add_column :oauth_applications, :owner_id, :bigint, null: true\n    add_column :oauth_applications, :owner_type, :string, null: true\n    add_index :oauth_applications, [:owner_id, :owner_type]\n  end\nend\n"
  },
  {
    "path": "lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb",
    "content": "# frozen_string_literal: true\n\nclass AddPreviousRefreshTokenToAccessTokens < ActiveRecord::Migration<%= migration_version %>\n  def change\n    add_column(\n      :oauth_access_tokens,\n      :previous_refresh_token,\n      :string,\n      default: \"\",\n      null: false\n    )\n  end\nend\n"
  },
  {
    "path": "lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb",
    "content": "# frozen_string_literal: true\n\nclass EnablePkce < ActiveRecord::Migration<%= migration_version %>\n  def change\n    add_column :oauth_access_grants, :code_challenge, :string, null: true\n    add_column :oauth_access_grants, :code_challenge_method, :string, null: true\n  end\nend\n"
  },
  {
    "path": "lib/generators/doorkeeper/templates/enable_polymorphic_resource_owner_migration.rb.erb",
    "content": "# frozen_string_literal: true\n\nclass EnablePolymorphicResourceOwner < ActiveRecord::Migration<%= migration_version %>\n  def change\n    add_column :oauth_access_tokens, :resource_owner_type, :string\n    add_column :oauth_access_grants, :resource_owner_type, :string\n    change_column_null :oauth_access_grants, :resource_owner_type, false\n\n    add_index :oauth_access_tokens,\n              [:resource_owner_id, :resource_owner_type],\n              name: 'polymorphic_owner_oauth_access_tokens'\n\n    add_index :oauth_access_grants,\n              [:resource_owner_id, :resource_owner_type],\n              name: 'polymorphic_owner_oauth_access_grants'\n  end\nend\n"
  },
  {
    "path": "lib/generators/doorkeeper/templates/initializer.rb",
    "content": "# frozen_string_literal: true\n\nDoorkeeper.configure do\n  # Change the ORM that doorkeeper will use (requires ORM extensions installed).\n  # Check the list of supported ORMs here: https://github.com/doorkeeper-gem/doorkeeper#orms\n  orm :active_record\n\n  # Enable support for multiple database configurations with read replicas.\n  # When enabled, Doorkeeper will wrap database write operations to ensure they\n  # use the primary (writable) database when automatic role switching is enabled.\n  #\n  # For ActiveRecord (Rails 6.1+), this uses `ActiveRecord::Base.connected_to(role: :writing)`.\n  # Other ORM extensions can implement their own primary database targeting logic.\n  #\n  # enable_multiple_database_roles\n  #\n  # This prevents `ActiveRecord::ReadOnlyError` when using read replicas with Rails\n  # automatic role switching. Enable this if your application uses multiple databases\n  # with automatic role switching for read replicas.\n  #\n  # See: https://guides.rubyonrails.org/active_record_multiple_databases.html#activating-automatic-role-switching\n\n  # This block will be called to check whether the resource owner is authenticated or not.\n  resource_owner_authenticator do\n    raise \"Please configure doorkeeper resource_owner_authenticator block located in #{__FILE__}\"\n    # Put your resource owner authentication logic here.\n    # Example implementation:\n    #   User.find_by(id: session[:user_id]) || redirect_to(new_user_session_url)\n  end\n\n  # If you didn't skip applications controller from Doorkeeper routes in your application routes.rb\n  # file then you need to declare this block in order to restrict access to the web interface for\n  # adding oauth authorized applications. In other case it will return 403 Forbidden response\n  # every time somebody will try to access the admin web interface.\n  #\n  # admin_authenticator do\n  #   # Put your admin authentication logic here.\n  #   # Example implementation:\n  #\n  #   if current_user\n  #     head :forbidden unless current_user.admin?\n  #   else\n  #     redirect_to sign_in_url\n  #   end\n  # end\n\n  # You can use your own model classes if you need to extend (or even override) default\n  # Doorkeeper models such as `Application`, `AccessToken` and `AccessGrant.\n  #\n  # By default Doorkeeper ActiveRecord ORM uses its own classes:\n  #\n  # access_token_class \"Doorkeeper::AccessToken\"\n  # access_grant_class \"Doorkeeper::AccessGrant\"\n  # application_class \"Doorkeeper::Application\"\n  #\n  # Don't forget to include Doorkeeper ORM mixins into your custom models:\n  #\n  #   *  ::Doorkeeper::Orm::ActiveRecord::Mixins::AccessToken - for access token\n  #   *  ::Doorkeeper::Orm::ActiveRecord::Mixins::AccessGrant - for access grant\n  #   *  ::Doorkeeper::Orm::ActiveRecord::Mixins::Application - for application (OAuth2 clients)\n  #\n  # For example:\n  #\n  # access_token_class \"MyAccessToken\"\n  #\n  # class MyAccessToken < ApplicationRecord\n  #   include ::Doorkeeper::Orm::ActiveRecord::Mixins::AccessToken\n  #\n  #   self.table_name = \"hey_i_wanna_my_name\"\n  #\n  #   def destroy_me!\n  #     destroy\n  #   end\n  # end\n\n  # Enables polymorphic Resource Owner association for Access Tokens and Access Grants.\n  # By default this option is disabled.\n  #\n  # Make sure you properly setup you database and have all the required columns (run\n  # `bundle exec rails generate doorkeeper:enable_polymorphic_resource_owner` and execute Rails\n  # migrations).\n  #\n  # If this option enabled, Doorkeeper will store not only Resource Owner primary key\n  # value, but also it's type (class name). See \"Polymorphic Associations\" section of\n  # Rails guides: https://guides.rubyonrails.org/association_basics.html#polymorphic-associations\n  #\n  # [NOTE] If you apply this option on already existing project don't forget to manually\n  # update `resource_owner_type` column in the database and fix migration template as it will\n  # set NOT NULL constraint for Access Grants table.\n  #\n  # use_polymorphic_resource_owner\n\n  # If you are planning to use Doorkeeper in Rails 5 API-only application, then you might\n  # want to use API mode that will skip all the views management and change the way how\n  # Doorkeeper responds to a requests.\n  #\n  # api_only\n\n  # Enforce token request content type to application/x-www-form-urlencoded.\n  # It is not enabled by default to not break prior versions of the gem.\n  #\n  # enforce_content_type\n\n  # Authorization Code expiration time (default: 10 minutes).\n  #\n  # authorization_code_expires_in 10.minutes\n\n  # Access token expiration time (default: 2 hours).\n  # If you set this to `nil` Doorkeeper will not expire the token and omit expires_in in response.\n  # It is RECOMMENDED to set expiration time explicitly.\n  # Prefer access_token_expires_in 100.years or similar,\n  # which would be functionally equivalent and avoid the risk of unexpected behavior by callers.\n  #\n  # access_token_expires_in 2.hours\n\n  # Assign custom TTL for access tokens. Will be used instead of access_token_expires_in\n  # option if defined. In case the block returns `nil` value Doorkeeper fallbacks to\n  # +access_token_expires_in+ configuration option value. If you really need to issue a\n  # non-expiring access token (which is not recommended) then you need to return\n  # Float::INFINITY from this block.\n  #\n  # `context` has the following properties available:\n  #\n  #   * `client` - the OAuth client application (see Doorkeeper::OAuth::Client)\n  #   * `grant_type` - the grant type of the request (see Doorkeeper::OAuth)\n  #   * `scopes` - the requested scopes (see Doorkeeper::OAuth::Scopes)\n  #   * `resource_owner` - authorized resource owner instance (if present)\n  #\n  # custom_access_token_expires_in do |context|\n  #   context.client.additional_settings.implicit_oauth_expiration\n  # end\n\n  # Use a custom class for generating the access token.\n  # See https://doorkeeper.gitbook.io/guides/configuration/other-configurations#custom-access-token-generator\n  #\n  # access_token_generator '::Doorkeeper::JWT'\n\n  # The controller +Doorkeeper::ApplicationController+ inherits from.\n  # Defaults to +ActionController::Base+ unless +api_only+ is set, which changes the default to\n  # +ActionController::API+. The return value of this option must be a stringified class name.\n  # See https://doorkeeper.gitbook.io/guides/configuration/other-configurations#custom-controllers\n  #\n  # base_controller 'ApplicationController'\n\n  # Reuse access token for the same resource owner within an application (disabled by default).\n  #\n  # This option protects your application from creating new tokens before old **valid** one becomes\n  # expired so your database doesn't bloat. Keep in mind that when this option is enabled Doorkeeper\n  # doesn't update existing token expiration time, it will create a new token instead if no active matching\n  # token found for the application, resources owner and/or set of scopes.\n  # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383\n  #\n  # You can not enable this option together with +hash_token_secrets+.\n  #\n  # reuse_access_token\n\n  # In case you enabled `reuse_access_token` option Doorkeeper will try to find matching\n  # token using `matching_token_for` Access Token API that searches for valid records\n  # in batches in order not to pollute the memory with all the database records. By default\n  # Doorkeeper uses batch size of 10 000 records. You can increase or decrease this value\n  # depending on your needs and server capabilities.\n  #\n  # token_lookup_batch_size 10_000\n\n  # Set a limit for token_reuse if using reuse_access_token option\n  #\n  # This option limits token_reusability to some extent.\n  # If not set then access_token will be reused unless it expires.\n  # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/1189\n  #\n  # This option should be a percentage(i.e. (0,100])\n  #\n  # token_reuse_limit 100\n\n  # Only allow one valid access token obtained via client credentials\n  # per client. If a new access token is obtained before the old one\n  # expired, the old one gets revoked (disabled by default)\n  #\n  # When enabling this option, make sure that you do not expect multiple processes\n  # using the same credentials at the same time (e.g. web servers spanning\n  # multiple machines and/or processes).\n  #\n  # revoke_previous_client_credentials_token\n\n  # Only allow one valid access token obtained via authorization code\n  # per client. If a new access token is obtained before the old one\n  # expired, the old one gets revoked (disabled by default)\n  #\n  # revoke_previous_authorization_code_token\n\n  # Require non-confidential clients to use PKCE when using an authorization code\n  # to obtain an access_token (disabled by default)\n  #\n  # force_pkce\n\n  # Hash access and refresh tokens before persisting them.\n  # This will disable the possibility to use +reuse_access_token+\n  # since plain values can no longer be retrieved.\n  #\n  # Note: If you are already a user of doorkeeper and have existing tokens\n  # in your installation, they will be invalid without adding 'fallback: :plain'.\n  #\n  # hash_token_secrets\n  # By default, token secrets will be hashed using the\n  # +Doorkeeper::Hashing::SHA256+ strategy.\n  #\n  # If you wish to use another hashing implementation, you can override\n  # this strategy as follows:\n  #\n  # hash_token_secrets using: '::Doorkeeper::Hashing::MyCustomHashImpl'\n  #\n  # Keep in mind that changing the hashing function will invalidate all existing\n  # secrets, if there are any.\n\n  # Hash application secrets before persisting them.\n  #\n  # hash_application_secrets\n  #\n  # By default, applications will be hashed\n  # with the +Doorkeeper::SecretStoring::SHA256+ strategy.\n  #\n  # If you wish to use bcrypt for application secret hashing, uncomment\n  # this line instead:\n  #\n  # hash_application_secrets using: '::Doorkeeper::SecretStoring::BCrypt'\n\n  # When the above option is enabled, and a hashed token or secret is not found,\n  # you can allow to fall back to another strategy. For users upgrading\n  # doorkeeper and wishing to enable hashing, you will probably want to enable\n  # the fallback to plain tokens.\n  #\n  # This will ensure that old access tokens and secrets\n  # will remain valid even if the hashing above is enabled.\n  #\n  # This can be done by adding 'fallback: plain', e.g. :\n  #\n  # hash_application_secrets using: '::Doorkeeper::SecretStoring::BCrypt', fallback: :plain\n\n  # Issue access tokens with refresh token (disabled by default), you may also\n  # pass a block which accepts `context` to customize when to give a refresh\n  # token or not. Similar to +custom_access_token_expires_in+, `context` has\n  # the following properties:\n  #\n  # `client` - the OAuth client application (see Doorkeeper::OAuth::Client)\n  # `grant_type` - the grant type of the request (see Doorkeeper::OAuth)\n  # `scopes` - the requested scopes (see Doorkeeper::OAuth::Scopes)\n  #\n  # use_refresh_token\n\n  # Provide support for an owner to be assigned to each registered application (disabled by default)\n  # Optional parameter confirmation: true (default: false) if you want to enforce ownership of\n  # a registered application\n  # NOTE: you must also run the rails g doorkeeper:application_owner generator\n  # to provide the necessary support\n  #\n  # enable_application_owner confirmation: false\n\n  # Define access token scopes for your provider\n  # For more information go to\n  # https://doorkeeper.gitbook.io/guides/ruby-on-rails/scopes\n  #\n  # default_scopes  :public\n  # optional_scopes :write, :update\n\n  # Allows to restrict only certain scopes for grant_type.\n  # By default, all the scopes will be available for all the grant types.\n  #\n  # Keys to this hash should be the name of grant_type and\n  # values should be the array of scopes for that grant type.\n  # Note: scopes should be from configured_scopes (i.e. default or optional)\n  #\n  # scopes_by_grant_type password: [:write], client_credentials: [:update]\n\n  # Forbids creating/updating applications with arbitrary scopes that are\n  # not in configuration, i.e. +default_scopes+ or +optional_scopes+.\n  # (disabled by default)\n  #\n  # enforce_configured_scopes\n\n  # Change the way client credentials are retrieved from the request object.\n  # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then\n  # falls back to the `:client_id` and `:client_secret` params from the `params` object.\n  # Check out https://github.com/doorkeeper-gem/doorkeeper/wiki/Changing-how-clients-are-authenticated\n  # for more information on customization\n  #\n  # client_credentials :from_basic, :from_params\n\n  # Change the way access token is authenticated from the request object.\n  # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then\n  # falls back to the `:access_token` or `:bearer_token` params from the `params` object.\n  # Check out https://github.com/doorkeeper-gem/doorkeeper/wiki/Changing-how-clients-are-authenticated\n  # for more information on customization\n  #\n  # access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param\n\n  # Forces the usage of the HTTPS protocol in non-native redirect uris (enabled\n  # by default in non-development environments). OAuth2 delegates security in\n  # communication to the HTTPS protocol so it is wise to keep this enabled.\n  #\n  # Callable objects such as proc, lambda, block or any object that responds to\n  # #call can be used in order to allow conditional checks (to allow non-SSL\n  # redirects to localhost for example).\n  #\n  # force_ssl_in_redirect_uri !Rails.env.development?\n  #\n  # force_ssl_in_redirect_uri { |uri| uri.host != 'localhost' }\n\n  # Specify what redirect URI's you want to block during Application creation.\n  # Any redirect URI is allowed by default.\n  #\n  # You can use this option in order to forbid URI's with 'javascript' scheme\n  # for example.\n  #\n  # forbid_redirect_uri { |uri| uri.scheme.to_s.downcase == 'javascript' }\n\n  # Allows to set blank redirect URIs for Applications in case Doorkeeper configured\n  # to use URI-less OAuth grant flows like Client Credentials or Resource Owner\n  # Password Credentials. The option is on by default and checks configured grant\n  # types, but you **need** to manually drop `NOT NULL` constraint from `redirect_uri`\n  # column for `oauth_applications` database table.\n  #\n  # You can completely disable this feature with:\n  #\n  # allow_blank_redirect_uri false\n  #\n  # Or you can define your custom check:\n  #\n  # allow_blank_redirect_uri do |grant_flows, client|\n  #   client.superapp?\n  # end\n\n  # Specify how authorization errors should be handled.\n  # By default, doorkeeper renders json errors when access token\n  # is invalid, expired, revoked or has invalid scopes.\n  #\n  # If you want to render error response yourself (i.e. rescue exceptions),\n  # set +handle_auth_errors+ to `:raise` and rescue Doorkeeper::Errors::InvalidToken\n  # or following specific errors:\n  #\n  #   Doorkeeper::Errors::TokenForbidden, Doorkeeper::Errors::TokenExpired,\n  #   Doorkeeper::Errors::TokenRevoked, Doorkeeper::Errors::TokenUnknown\n  #\n  # handle_auth_errors :raise\n  #\n  # If you want to redirect back to the client application in accordance with\n  # https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1, you can set\n  # +handle_auth_errors+ to :redirect\n  #\n  # handle_auth_errors :redirect\n\n  # Customize token introspection response.\n  # Allows to add your own fields to default one that are required by the OAuth spec\n  # for the introspection response. It could be `sub`, `aud` and so on.\n  # This configuration option can be a proc, lambda or any Ruby object responds\n  # to `.call` method and result of it's invocation must be a Hash.\n  #\n  # custom_introspection_response do |token, context|\n  #   {\n  #     \"sub\": \"Z5O3upPC88QrAjx00dis\",\n  #     \"aud\": \"https://protected.example.net/resource\",\n  #     \"username\": User.find(token.resource_owner_id).username\n  #   }\n  # end\n  #\n  # or\n  #\n  # custom_introspection_response CustomIntrospectionResponder\n\n  # Specify what grant flows are enabled in array of Strings. The valid\n  # strings and the flows they enable are:\n  #\n  # \"authorization_code\" => Authorization Code Grant Flow\n  # \"implicit\"           => Implicit Grant Flow\n  # \"password\"           => Resource Owner Password Credentials Grant Flow\n  # \"client_credentials\" => Client Credentials Grant Flow\n  #\n  # If not specified, Doorkeeper enables authorization_code and\n  # client_credentials.\n  #\n  # implicit and password grant flows have risks that you should understand\n  # before enabling:\n  #   https://datatracker.ietf.org/doc/html/rfc6819#section-4.4.2\n  #   https://datatracker.ietf.org/doc/html/rfc6819#section-4.4.3\n  #\n  # grant_flows %w[authorization_code client_credentials]\n\n  # Allows to customize OAuth grant flows that +each+ application support.\n  # You can configure a custom block (or use a class respond to `#call`) that must\n  # return `true` in case Application instance supports requested OAuth grant flow\n  # during the authorization request to the server. This configuration +doesn't+\n  # set flows per application, it only allows to check if application supports\n  # specific grant flow.\n  #\n  # For example you can add an additional database column to `oauth_applications` table,\n  # say `t.array :grant_flows, default: []`, and store allowed grant flows that can\n  # be used with this application there. Then when authorization requested Doorkeeper\n  # will call this block to check if specific Application (passed with client_id and/or\n  # client_secret) is allowed to perform the request for the specific grant type\n  # (authorization, password, client_credentials, etc).\n  #\n  # Example of the block:\n  #\n  #   ->(flow, client) { client.grant_flows.include?(flow) }\n  #\n  # In case this option invocation result is `false`, Doorkeeper server returns\n  # :unauthorized_client error and stops the request.\n  #\n  # @param allow_grant_flow_for_client [Proc] Block or any object respond to #call\n  # @return [Boolean] `true` if allow or `false` if forbid the request\n  #\n  # allow_grant_flow_for_client do |grant_flow, client|\n  #   # `grant_flows` is an Array column with grant\n  #   # flows that application supports\n  #\n  #   client.grant_flows.include?(grant_flow)\n  # end\n\n  # If you need arbitrary Resource Owner-Client authorization you can enable this option\n  # and implement the check your need. Config option must respond to #call and return\n  # true in case resource owner authorized for the specific application or false in other\n  # cases.\n  #\n  # By default all Resource Owners are authorized to any Client (application).\n  #\n  # authorize_resource_owner_for_client do |client, resource_owner|\n  #   resource_owner.admin? || client.owners_allowlist.include?(resource_owner)\n  # end\n\n  # Allows additional data fields to be sent while granting access to an application,\n  # and for this additional data to be included in subsequently generated access tokens.\n  # The 'authorizations/new' page will need to be overridden to include this additional data\n  # in the request params when granting access. The access grant and access token models\n  # will both need to respond to these additional data fields, and have a database column\n  # to store them in.\n  #\n  # Example:\n  # You have a multi-tenanted platform and want to be able to grant access to a specific\n  # tenant, rather than all the tenants a user has access to. You can use this config\n  # option to specify that a ':tenant_id' will be passed when authorizing. This tenant_id\n  # will be included in the access tokens. When a request is made with one of these access\n  # tokens, you can check that the requested data belongs to the specified tenant.\n  #\n  # Default value is an empty Array: []\n  # custom_access_token_attributes [:tenant_id]\n\n  # Hook into the strategies' request & response life-cycle in case your\n  # application needs advanced customization or logging:\n  #\n  # before_successful_strategy_response do |request|\n  #   puts \"BEFORE HOOK FIRED! #{request}\"\n  # end\n  #\n  # after_successful_strategy_response do |request, response|\n  #   puts \"AFTER HOOK FIRED! #{request}, #{response}\"\n  # end\n\n  # Hook into Authorization flow in order to implement Single Sign Out\n  # or add any other functionality. Inside the block you have an access\n  # to `controller` (authorizations controller instance) and `context`\n  # (Doorkeeper::OAuth::Hooks::Context instance) which provides pre auth\n  # or auth objects with issued token based on hook type (before or after).\n  #\n  # before_successful_authorization do |controller, context|\n  #   Rails.logger.info(controller.request.params.inspect)\n  #\n  #   Rails.logger.info(context.pre_auth.inspect)\n  # end\n  #\n  # after_successful_authorization do |controller, context|\n  #   controller.session[:logout_urls] <<\n  #     Doorkeeper::Application\n  #       .find_by(controller.request.params.slice(:redirect_uri))\n  #       .logout_uri\n  #\n  #   Rails.logger.info(context.auth.inspect)\n  #   Rails.logger.info(context.issued_token)\n  # end\n\n  # Under some circumstances you might want to have applications auto-approved,\n  # so that the user skips the authorization step.\n  # For example if dealing with a trusted application.\n  #\n  # skip_authorization do |resource_owner, client|\n  #   client.superapp? or resource_owner.admin?\n  # end\n\n  # Configure custom constraints for the Token Introspection request.\n  # By default this configuration option allows to introspect a token by another\n  # token of the same application, OR to introspect the token that belongs to\n  # authorized client (from authenticated client) OR when token doesn't\n  # belong to any client (public token). Otherwise requester has no access to the\n  # introspection and it will return response as stated in the RFC.\n  #\n  # Block arguments:\n  #\n  # @param token [Doorkeeper::AccessToken]\n  #   token to be introspected\n  #\n  # @param authorized_client [Doorkeeper::Application]\n  #   authorized client (if request is authorized using Basic auth with\n  #   Client Credentials for example)\n  #\n  # @param authorized_token [Doorkeeper::AccessToken]\n  #   Bearer token used to authorize the request\n  #\n  # In case the block returns `nil` or `false` introspection responses with 401 status code\n  # when using authorized token to introspect, or you'll get 200 with { \"active\": false } body\n  # when using authorized client to introspect as stated in the\n  # RFC 7662 section 2.2. Introspection Response.\n  #\n  # Using with caution:\n  # Keep in mind that these three parameters pass to block can be nil as following case:\n  #  `authorized_client` is nil if and only if `authorized_token` is present, and vice versa.\n  #  `token` will be nil if and only if `authorized_token` is present.\n  # So remember to use `&` or check if it is present before calling method on\n  # them to make sure you doesn't get NoMethodError exception.\n  #\n  # You can define your custom check:\n  #\n  # allow_token_introspection do |token, authorized_client, authorized_token|\n  #   if authorized_token\n  #     # customize: require `introspection` scope\n  #     authorized_token.application == token&.application ||\n  #       authorized_token.scopes.include?(\"introspection\")\n  #   elsif token.application\n  #     # `protected_resource` is a new database boolean column, for example\n  #     authorized_client == token.application || authorized_client.protected_resource?\n  #   else\n  #     # public token (when token.application is nil, token doesn't belong to any application)\n  #     true\n  #   end\n  # end\n  #\n  # Or you can completely disable any token introspection:\n  #\n  # allow_token_introspection false\n  #\n  # If you need to block the request at all, then configure your routes.rb or web-server\n  # like nginx to forbid the request.\n\n  # WWW-Authenticate Realm (default: \"Doorkeeper\").\n  #\n  # realm \"Doorkeeper\"\nend\n"
  },
  {
    "path": "lib/generators/doorkeeper/templates/migration.rb.erb",
    "content": "# frozen_string_literal: true\n\nclass CreateDoorkeeperTables < ActiveRecord::Migration<%= migration_version %>\n  def change\n    create_table :oauth_applications do |t|\n      t.string  :name,    null: false\n      t.string  :uid,     null: false\n      # Remove `null: false` or use conditional constraint if you are planning to use public clients.\n      t.string  :secret,  null: false\n\n      # Remove `null: false` if you are planning to use grant flows\n      # that doesn't require redirect URI to be used during authorization\n      # like Client Credentials flow or Resource Owner Password.\n      t.text    :redirect_uri, null: false\n      t.string  :scopes,       null: false, default: ''\n      t.boolean :confidential, null: false, default: true\n      t.timestamps             null: false\n    end\n\n    add_index :oauth_applications, :uid, unique: true\n\n    create_table :oauth_access_grants do |t|\n      t.references :resource_owner,  null: false\n      t.references :application,     null: false\n      t.string   :token,             null: false\n      t.integer  :expires_in,        null: false\n      t.text     :redirect_uri,      null: false\n      t.string   :scopes,            null: false, default: ''\n      t.datetime :created_at,        null: false\n      t.datetime :revoked_at\n    end\n\n    add_index :oauth_access_grants, :token, unique: true\n    add_foreign_key(\n      :oauth_access_grants,\n      :oauth_applications,\n      column: :application_id\n    )\n\n    create_table :oauth_access_tokens do |t|\n      t.references :resource_owner, index: true\n\n      # Remove `null: false` if you are planning to use Password\n      # Credentials Grant flow that doesn't require an application.\n      t.references :application,    null: false\n\n      # If you use a custom token generator you may need to change this column\n      # from string to text, so that it accepts tokens larger than 255\n      # characters. More info on custom token generators in:\n      # https://github.com/doorkeeper-gem/doorkeeper/tree/v3.0.0.rc1#custom-access-token-generator\n      #\n      # t.text :token, null: false\n      t.string :token, null: false\n\n      t.string   :refresh_token\n      t.integer  :expires_in\n      t.string   :scopes\n      t.datetime :created_at, null: false\n      t.datetime :revoked_at\n\n      # The authorization server MAY issue a new refresh token, in which case\n      # *the client MUST discard the old refresh token* and replace it with the\n      # new refresh token. The authorization server MAY revoke the old\n      # refresh token after issuing a new refresh token to the client.\n      # @see https://datatracker.ietf.org/doc/html/rfc6749#section-6\n      #\n      # Doorkeeper implementation: if there is a `previous_refresh_token` column,\n      # refresh tokens will be revoked after a related access token is used.\n      # If there is no `previous_refresh_token` column, previous tokens are\n      # revoked as soon as a new access token is created.\n      #\n      # Comment out this line if you want refresh tokens to be instantly\n      # revoked after use.\n      t.string   :previous_refresh_token, null: false, default: \"\"\n    end\n\n    add_index :oauth_access_tokens, :token, unique: true\n\n    # See https://github.com/doorkeeper-gem/doorkeeper/issues/1592\n    if ActiveRecord::Base.connection.adapter_name == \"SQLServer\"\n      execute <<~SQL.squish\n        CREATE UNIQUE NONCLUSTERED INDEX index_oauth_access_tokens_on_refresh_token ON oauth_access_tokens(refresh_token)\n        WHERE refresh_token IS NOT NULL\n      SQL\n    else\n      add_index :oauth_access_tokens, :refresh_token, unique: true\n    end\n\n    add_foreign_key(\n      :oauth_access_tokens,\n      :oauth_applications,\n      column: :application_id\n    )\n\n    # Uncomment below to ensure a valid reference to the resource owner's table\n    # add_foreign_key :oauth_access_grants, <model>, column: :resource_owner_id\n    # add_foreign_key :oauth_access_tokens, <model>, column: :resource_owner_id\n  end\nend\n"
  },
  {
    "path": "lib/generators/doorkeeper/templates/remove_applications_secret_not_null_constraint.rb.erb",
    "content": "# frozen_string_literal: true\n\nclass RemoveApplicationsSecretNotNullConstraint < ActiveRecord::Migration<%= migration_version %>\n  def change\n    change_column_null :oauth_applications, :secret, true\n  end\nend\n"
  },
  {
    "path": "lib/generators/doorkeeper/views_generator.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  module Generators\n    # Generates doorkeeper views for Rails application\n    #\n    class ViewsGenerator < ::Rails::Generators::Base\n      source_root File.expand_path(\"../../../app/views\", __dir__)\n\n      desc \"Copies default Doorkeeper views and layouts to your application.\"\n\n      def manifest\n        directory \"doorkeeper\", \"app/views/doorkeeper\"\n        directory \"layouts/doorkeeper\", \"app/views/layouts/doorkeeper\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/application_controller_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper_integration\"\n\nRSpec.describe Doorkeeper::ApplicationController, type: :controller do\n  describe \"current_resource_owner view helper\" do\n    controller(described_class) do\n      def index\n        render inline: \"<%= current_resource_owner %>\"\n      end\n    end\n\n    it \"is registered as a helper method\" do\n      expect(described_class._helper_methods).to include(:current_resource_owner)\n    end\n\n    it \"is callable from views\" do\n      allow(controller).to receive(:current_resource_owner).and_return(\"owner-sentinel\")\n\n      get :index\n      expect(response.body).to include(\"owner-sentinel\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/application_metal_controller_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper_integration\"\n\nRSpec.describe Doorkeeper::ApplicationMetalController, type: :controller do\n  render_views\n\n  controller(described_class) do\n    def index\n      render json: {}, status: 200\n    end\n\n    def create\n      render json: {}, status: 200\n    end\n  end\n\n  it \"lacks `helper_method` so the included hook becomes a no-op\" do\n    expect(described_class).not_to respond_to(:helper_method)\n  end\n\n  it \"lazy run hooks\" do\n    i = 0\n    ActiveSupport.on_load(:doorkeeper_metal_controller) { i += 1 }\n\n    expect(i).to eq 1\n  end\n\n  describe \"enforce_content_type\" do\n    before { allow(Doorkeeper.config).to receive(:enforce_content_type).and_return(flag) }\n\n    context \"when enabled\" do\n      let(:flag) { true }\n\n      it \"returns a 200 for the requests without body\" do\n        get :index, params: {}\n        expect(response).to have_http_status 200\n      end\n\n      it \"returns a 200 for the requests with body and correct media type\" do\n        post :create, params: {}, as: :url_encoded_form\n        expect(response).to have_http_status 200\n      end\n\n      it \"returns a 415 for the requests with body and incorrect media type\" do\n        post :create, params: {}, as: :json\n        expect(response).to have_http_status 415\n      end\n    end\n\n    context \"when disabled\" do\n      let(:flag) { false }\n\n      it \"returns a 200 for the correct media type\" do\n        get :index, as: :url_encoded_form\n        expect(response).to have_http_status 200\n      end\n\n      it \"returns a 200 for an incorrect media type\" do\n        get :index, as: :json\n        expect(response).to have_http_status 200\n      end\n\n      it \"returns a 200 for the requests with body and incorrect media type\" do\n        post :create, params: {}, as: :json\n        expect(response).to have_http_status 200\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/applications_controller_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::ApplicationsController, type: :controller do\n  render_views\n\n  context \"when JSON API used\" do\n    before do\n      allow(Doorkeeper.configuration).to receive(:api_only).and_return(true)\n      allow(Doorkeeper.configuration).to receive(:authenticate_admin).and_return(->(*) { true })\n    end\n\n    it \"creates an application\" do\n      expect do\n        post :create, params: {\n          doorkeeper_application: {\n            name: \"Example\",\n            redirect_uri: \"https://example.com\",\n          }, format: :json,\n        }\n      end.to(change { Doorkeeper::Application.count })\n\n      expect(response).to be_successful\n\n      expect(json_response).to include(\"id\", \"name\", \"uid\", \"secret\", \"redirect_uri\", \"scopes\")\n\n      application = Doorkeeper::Application.last\n      secret_from_response = json_response[\"secret\"]\n      expect(application).to be_secret_matches(secret_from_response)\n\n      expect(json_response[\"name\"]).to eq(\"Example\")\n      expect(json_response[\"redirect_uri\"]).to eq(\"https://example.com\")\n    end\n\n    it \"returns validation errors on wrong create params\" do\n      expect do\n        post :create, params: {\n          doorkeeper_application: {\n            name: \"Example\",\n          }, format: :json,\n        }\n      end.not_to(change { Doorkeeper::Application.count })\n\n      expect(response).to have_http_status(422)\n\n      expect(json_response).to include(\"errors\")\n    end\n\n    it \"returns validations on wrong create params (unspecified scheme)\" do\n      expect do\n        post :create, params: {\n          doorkeeper_application: {\n            name: \"Example\",\n            redirect_uri: \"app.com:80\",\n          }, format: :json,\n        }\n      end.not_to(change { Doorkeeper::Application.count })\n\n      expect(response).to have_http_status(422)\n\n      expect(json_response).to include(\"errors\")\n    end\n\n    it \"returns application info\" do\n      application = FactoryBot.create(:application, name: \"Change me\")\n\n      get :show, params: { id: application.id, format: :json }\n\n      expect(response).to be_successful\n\n      expect(json_response).to include(\"id\", \"name\", \"uid\", \"secret\", \"redirect_uri\", \"scopes\")\n    end\n\n    it \"updates application\" do\n      application = FactoryBot.create(:application, name: \"Change me\")\n\n      put :update, params: {\n        id: application.id,\n        doorkeeper_application: {\n          name: \"Example App\",\n          redirect_uri: \"https://example.com\",\n        }, format: :json,\n      }\n\n      expect(application.reload.name).to eq \"Example App\"\n\n      expect(json_response).to include(\"id\", \"name\", \"uid\", \"secret\", \"redirect_uri\", \"scopes\")\n    end\n\n    it \"returns validation errors on wrong update params\" do\n      application = FactoryBot.create(:application, name: \"Change me\")\n\n      put :update, params: {\n        id: application.id,\n        doorkeeper_application: {\n          name: \"Example App\",\n          redirect_uri: \"localhost:3000\",\n        }, format: :json,\n      }\n\n      expect(response).to have_http_status(422)\n\n      expect(json_response).to include(\"errors\")\n    end\n\n    it \"destroys an application\" do\n      application = FactoryBot.create(:application)\n\n      delete :destroy, params: { id: application.id, format: :json }\n\n      expect(response).to have_http_status(204)\n      expect(Doorkeeper::Application.count).to be_zero\n    end\n  end\n\n  context \"when admin is not authenticated\" do\n    before do\n      allow(Doorkeeper.config).to receive(:authenticate_admin).and_return(proc do\n        redirect_to main_app.root_url\n      end)\n    end\n\n    it \"redirects as set in Doorkeeper.authenticate_admin\" do\n      get :index\n      expect(response).to redirect_to(controller.main_app.root_url)\n    end\n\n    it \"does not create application\" do\n      expect do\n        post :create, params: {\n          doorkeeper_application: {\n            name: \"Example\",\n            redirect_uri: \"https://example.com\",\n          },\n        }\n      end.not_to(change { Doorkeeper::Application.count })\n    end\n  end\n\n  context \"when admin is authenticated\" do\n    before do\n      allow(Doorkeeper.configuration).to receive(:authenticate_admin).and_return(->(*) { true })\n    end\n\n    context \"when application secrets are hashed\" do\n      before do\n        allow(Doorkeeper.configuration)\n          .to receive(:application_secret_strategy).and_return(Doorkeeper::SecretStoring::Sha256Hash)\n      end\n\n      it \"shows the application secret after creating a new application\" do\n        expect do\n          post :create, params: {\n            doorkeeper_application: {\n              name: \"Example\",\n              redirect_uri: \"https://example.com\",\n            },\n          }\n        end.to change { Doorkeeper::Application.count }.by(1)\n\n        application = Doorkeeper::Application.last\n\n        secret_from_flash = flash[:application_secret]\n        expect(secret_from_flash).not_to be_empty\n        expect(application).to be_secret_matches(secret_from_flash)\n        expect(response).to redirect_to(controller.main_app.oauth_application_url(application.id))\n\n        get :show, params: { id: application.id, format: :html }\n\n        # We don't know the application secret here (because its hashed) so we can not assert its text on the page\n        # Instead, we read it from the page and then check if it matches the application secret\n        code_element = /code.*id=\"secret\">\\s*\\K([^<]*)/m.match(response.body)\n        secret_from_page = code_element[1].strip\n\n        expect(response.body).to have_selector(\"code#application_id\", text: application.uid)\n        expect(response.body).to have_selector(\"code#secret\")\n        expect(secret_from_page).not_to be_empty\n        expect(application).to be_secret_matches(secret_from_page)\n      end\n\n      it \"does not show an application secret when application did already exist\" do\n        application = FactoryBot.create(:application)\n        get :show, params: { id: application.id, format: :html }\n\n        expect(response.body).to have_selector(\"code#application_id\", text: application.uid)\n        expect(response.body).to have_selector(\"code#secret\", text: \"\")\n      end\n\n      it \"returns the application details in a json response\" do\n        expect do\n          post :create, params: {\n            doorkeeper_application: {\n              name: \"Example\",\n              redirect_uri: \"https://example.com\",\n            }, format: :json,\n          }\n        end.to(change { Doorkeeper::Application.count })\n\n        expect(response).to be_successful\n\n        expect(json_response).to include(\"id\", \"name\", \"uid\", \"secret\", \"redirect_uri\", \"scopes\")\n\n        application = Doorkeeper::Application.last\n        secret_from_response = json_response[\"secret\"]\n        expect(application).to be_secret_matches(secret_from_response)\n\n        expect(json_response[\"name\"]).to eq(\"Example\")\n        expect(json_response[\"redirect_uri\"]).to eq(\"https://example.com\")\n      end\n    end\n\n    it \"sorts applications by created_at\" do\n      first_application = FactoryBot.create(:application)\n      second_application = FactoryBot.create(:application)\n      expect(Doorkeeper::Application).to receive(:ordered_by).and_call_original\n\n      get :index\n\n      expect(response.body).to have_selector(\"tbody tr:first-child#application_#{first_application.id}\")\n      expect(response.body).to have_selector(\"tbody tr:last-child#application_#{second_application.id}\")\n    end\n\n    it \"creates application\" do\n      expect do\n        post :create, params: {\n          doorkeeper_application: {\n            name: \"Example\",\n            redirect_uri: \"https://example.com\",\n          },\n        }\n      end.to change { Doorkeeper::Application.count }.by(1)\n\n      expect(response).to be_redirect\n    end\n\n    it \"shows application details\" do\n      application = FactoryBot.create(:application)\n      get :show, params: { id: application.id, format: :html }\n\n      expect(response.body).to have_selector(\"code#application_id\", text: application.uid)\n      expect(response.body).to have_selector(\"code#secret\", text: application.plaintext_secret)\n    end\n\n    it \"does not allow mass assignment of uid or secret\" do\n      application = FactoryBot.create(:application)\n      put :update, params: {\n        id: application.id,\n        doorkeeper_application: {\n          uid: \"1A2B3C4D\",\n          secret: \"1A2B3C4D\",\n        },\n      }\n\n      expect(application.reload.uid).not_to eq \"1A2B3C4D\"\n    end\n\n    it \"updates application\" do\n      application = FactoryBot.create(:application)\n      put :update, params: {\n        id: application.id, doorkeeper_application: {\n          name: \"Example\",\n          redirect_uri: \"https://example.com\",\n        },\n      }\n\n      expect(application.reload.name).to eq \"Example\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/authorizations_controller_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::AuthorizationsController, type: :controller do\n  include AuthorizationRequestHelper\n\n  render_views\n\n  class ActionDispatch::TestResponse\n    def query_params\n      @query_params ||= begin\n        fragment = URI.parse(location).fragment\n        Rack::Utils.parse_query(fragment)\n      end\n    end\n  end\n\n  let(:client) { FactoryBot.create :application }\n  let(:user) { User.create!(name: \"Joe\", password: \"sekret\") }\n\n  let(:access_token) do\n    FactoryBot.build :access_token,\n                     resource_owner_id: user.id,\n                     resource_owner_type: user.class.name,\n                     application_id: client.id,\n                     scopes: \"default\"\n  end\n\n  let(:response_json_body) { JSON.parse(response.body) }\n\n  before do\n    Doorkeeper.configure do\n      orm DOORKEEPER_ORM\n      default_scopes :default\n\n      custom_access_token_expires_in(lambda do |context|\n        context.grant_type == Doorkeeper::OAuth::IMPLICIT ? 1234 : nil\n      end)\n    end\n\n    allow(Doorkeeper.config).to receive(:grant_flows).and_return([\"implicit\"])\n    allow(Doorkeeper.config).to receive(:authenticate_resource_owner).and_return(->(_) { authenticator_method })\n    allow(subject).to receive(:authenticator_method).and_return(user)\n  end\n\n  describe \"POST #create\" do\n    context \"without response_mode parameter\" do\n      before do\n        post :create, params: { client_id: client.uid, response_type: \"token\", redirect_uri: client.redirect_uri }\n      end\n\n      it \"redirects after authorization\" do\n        expect(response).to be_redirect\n        expect(subject).to receive(:authenticator_method).at_most(:once)\n      end\n\n      it \"redirects to client redirect uri\" do\n        expect(response.location).to match(/^#{client.redirect_uri}/)\n      end\n\n      it \"includes access token in fragment\" do\n        expect(response.query_params[\"access_token\"]).to eq(Doorkeeper::AccessToken.first.token)\n      end\n\n      it \"includes token type in fragment\" do\n        expect(response.query_params[\"token_type\"]).to eq(\"Bearer\")\n      end\n\n      it \"includes token expiration in fragment\" do\n        expect(response.query_params[\"expires_in\"].to_i).to eq(1234)\n      end\n\n      it \"issues the token for the current client\" do\n        expect(Doorkeeper::AccessToken.first.application_id).to eq(client.id)\n      end\n\n      it \"issues the token for the current resource owner\" do\n        expect(Doorkeeper::AccessToken.first.resource_owner_id).to eq(user.id)\n      end\n    end\n\n    context \"with 'form_post' as response_mode\" do\n      before do\n        post :create, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n          response_mode: \"form_post\",\n        }\n      end\n\n      it \"renders 200 status\" do\n        expect(response.status).to eq 200\n      end\n\n      it \"issues a token\" do\n        expect(Doorkeeper::AccessToken.count).to eq(1)\n      end\n\n      it \"issues the token for the current client\" do\n        expect(Doorkeeper::AccessToken.first.application_id).to eq(client.id)\n      end\n\n      it \"issues the token for the current resource owner\" do\n        expect(Doorkeeper::AccessToken.first.resource_owner_id).to eq(user.id)\n      end\n    end\n  end\n\n  describe \"POST #create in API mode\" do\n    context \"without response_mode parameter\" do\n      before do\n        allow(Doorkeeper.config).to receive(:api_only).and_return(true)\n        post :create, params: { client_id: client.uid, response_type: \"token\", redirect_uri: client.redirect_uri }\n      end\n\n      let(:redirect_uri) { response_json_body[\"redirect_uri\"] }\n\n      it \"renders success after authorization\" do\n        expect(response).to be_successful\n      end\n\n      it \"renders correct redirect uri\" do\n        expect(redirect_uri).to match(/^#{client.redirect_uri}/)\n      end\n\n      it \"includes access token in fragment\" do\n        expect(redirect_uri.match(/access_token=([a-zA-Z0-9\\-_]+)&?/)[1]).to eq(Doorkeeper::AccessToken.first.token)\n      end\n\n      it \"includes token type in fragment\" do\n        expect(redirect_uri.match(/token_type=(\\w+)&?/)[1]).to eq \"Bearer\"\n      end\n\n      it \"includes token expiration in fragment\" do\n        expect(redirect_uri.match(/expires_in=(\\d+)&?/)[1].to_i).to eq 1234\n      end\n\n      it \"issues the token for the current client\" do\n        expect(Doorkeeper::AccessToken.first.application_id).to eq(client.id)\n      end\n\n      it \"issues the token for the current resource owner\" do\n        expect(Doorkeeper::AccessToken.first.resource_owner_id).to eq(user.id)\n      end\n    end\n\n    context \"with 'form_post' as response_mode\" do\n      before do\n        allow(Doorkeeper.config).to receive(:api_only).and_return(true)\n        post :create, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n          response_mode: \"form_post\",\n        }\n      end\n\n      it \"renders success after authorization\" do\n        expect(response).to be_successful\n      end\n\n      it \"renders correct status\" do\n        expect(response_json_body[\"status\"]).to eq \"post\"\n      end\n\n      it \"renders correct redirect uri\" do\n        expect(response_json_body[\"redirect_uri\"]).to eq(client.redirect_uri)\n      end\n\n      it \"includes access token in fragment\" do\n        expect(response_json_body[\"body\"][\"access_token\"]).to eq(Doorkeeper::AccessToken.first.token)\n      end\n\n      it \"includes token type in fragment\" do\n        expect(response_json_body[\"body\"][\"token_type\"]).to eq \"Bearer\"\n      end\n\n      it \"includes token expiration in fragment\" do\n        expect(response_json_body[\"body\"][\"expires_in\"]).to eq 1234\n      end\n\n      it \"issues the token for the current client\" do\n        expect(Doorkeeper::AccessToken.first.application_id).to eq(client.id)\n      end\n\n      it \"issues the token for the current resource owner\" do\n        expect(Doorkeeper::AccessToken.first.resource_owner_id).to eq(user.id)\n      end\n    end\n  end\n\n  describe \"POST #create with errors\" do\n    context \"when missing client_id\" do\n      before do\n        post :create, params: {\n          client_id: \"\",\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n        }\n      end\n\n      it \"renders 400 error\" do\n        expect(response.status).to eq 400\n      end\n\n      it \"includes error name\" do\n        expect(response_json_body[\"error\"]).to eq(\"invalid_request\")\n      end\n\n      it \"includes error description\" do\n        expect(response_json_body[\"error_description\"]).to eq(\n          translated_invalid_request_error_message(:missing_param, :client_id),\n        )\n      end\n\n      it \"does not issue any access token\" do\n        expect(Doorkeeper::AccessToken.all).to be_empty\n      end\n    end\n\n    context \"when client can not use grant flow\" do\n      before do\n        config_is_set(:allow_grant_flow_for_client, ->(*_) { false })\n        post :create, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n        }\n      end\n\n      it \"renders 401 error\" do\n        expect(response.status).to eq 401\n      end\n\n      it \"includes error name\" do\n        expect(response_json_body[\"error\"]).to eq(\"unauthorized_client\")\n      end\n\n      it \"includes error description\" do\n        expect(response_json_body[\"error_description\"]).to eq(\n          translated_error_message(:unauthorized_client),\n        )\n      end\n\n      it \"does not issue any access token\" do\n        expect(Doorkeeper::AccessToken.all).to be_empty\n      end\n    end\n\n    context \"when user cannot access application\" do\n      before do\n        allow(Doorkeeper.configuration).to receive(:authorize_resource_owner_for_client).and_return(->(*_) { false })\n        post :create, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n        }\n      end\n\n      it \"renders 401 error\" do\n        expect(response.status).to eq 401\n      end\n\n      it \"includes error name\" do\n        expect(response_json_body[\"error\"]).to eq(\"invalid_client\")\n      end\n\n      it \"includes error description\" do\n        expect(response_json_body[\"error_description\"]).to eq(\n          translated_error_message(:invalid_client),\n        )\n      end\n\n      it \"does not issue any access token\" do\n        expect(Doorkeeper::AccessToken.all).to be_empty\n      end\n    end\n\n    context \"when other error happens\" do\n      before do\n        default_scopes_exist :public\n\n        post :create, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          scope: \"invalid\",\n          redirect_uri: client.redirect_uri,\n        }\n      end\n\n      it \"redirects after authorization\" do\n        expect(response).to be_redirect\n      end\n\n      it \"redirects to client redirect uri\" do\n        expect(response.location).to match(/^#{client.redirect_uri}/)\n      end\n\n      it \"does not include access token in fragment\" do\n        expect(response.query_params[\"access_token\"]).to be_nil\n      end\n\n      it \"includes error in fragment\" do\n        expect(response.query_params[\"error\"]).to eq(\"invalid_scope\")\n      end\n\n      it \"includes error description in fragment\" do\n        expect(response.query_params[\"error_description\"]).to eq(translated_error_message(:invalid_scope))\n      end\n\n      it \"does not issue any access token\" do\n        expect(Doorkeeper::AccessToken.all).to be_empty\n      end\n    end\n\n    context \"with 'form_post' as response_mode\" do\n      before do\n        default_scopes_exist :public\n\n        post :create, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          scope: \"invalid\",\n          redirect_uri: client.redirect_uri,\n          response_mode: \"form_post\",\n        }\n      end\n\n      it \"redirects after authorization\" do\n        expect(response.status).to eq 200\n      end\n\n      it \"does not issue any access token\" do\n        expect(Doorkeeper::AccessToken.all).to be_empty\n      end\n\n      it \"includes the error in the redirect post\" do\n        expect(response.body).to include(\"invalid_scope\")\n      end\n    end\n  end\n\n  describe \"POST #create in API mode with errors\" do\n    before { config_is_set(:api_only, true) }\n\n    context \"when missing client_id\" do\n      before do\n        post :create, params: {\n          client_id: \"\",\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n        }\n      end\n\n      it \"renders 400 error\" do\n        expect(response.status).to eq 400\n      end\n\n      it \"includes error name\" do\n        expect(response_json_body[\"error\"]).to eq(\"invalid_request\")\n      end\n\n      it \"includes error description\" do\n        expect(response_json_body[\"error_description\"]).to eq(\n          translated_invalid_request_error_message(:missing_param, :client_id),\n        )\n      end\n\n      it \"does not issue any access token\" do\n        expect(Doorkeeper::AccessToken.all).to be_empty\n      end\n    end\n\n    context \"when client can not use grant flow\" do\n      before do\n        config_is_set(:allow_grant_flow_for_client, ->(*_) { false })\n        post :create, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n        }\n      end\n\n      it \"renders 401 error\" do\n        expect(response.status).to eq 401\n      end\n\n      it \"includes error name\" do\n        expect(response_json_body[\"error\"]).to eq(\"unauthorized_client\")\n      end\n\n      it \"includes error description\" do\n        expect(response_json_body[\"error_description\"]).to eq(\n          translated_error_message(:unauthorized_client),\n        )\n      end\n\n      it \"does not issue any access token\" do\n        expect(Doorkeeper::AccessToken.all).to be_empty\n      end\n    end\n\n    context \"when user cannot access application\" do\n      before do\n        allow(Doorkeeper.configuration).to receive(:authorize_resource_owner_for_client).and_return(->(*_) { false })\n\n        post :create, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n        }\n      end\n\n      it \"renders 401 error\" do\n        expect(response.status).to eq 401\n      end\n\n      it \"includes error name\" do\n        expect(response_json_body[\"error\"]).to eq(\"invalid_client\")\n      end\n\n      it \"includes error description\" do\n        expect(response_json_body[\"error_description\"]).to eq(\n          translated_error_message(:invalid_client),\n        )\n      end\n\n      it \"does not issue any access token\" do\n        expect(Doorkeeper::AccessToken.all).to be_empty\n      end\n    end\n\n    context \"when other error happens\" do\n      before do\n        default_scopes_exist :public\n\n        post :create, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          scope: \"invalid\",\n          redirect_uri: client.redirect_uri,\n        }\n      end\n\n      let(:redirect_uri) { response_json_body[\"redirect_uri\"] }\n\n      it \"renders 400 error\" do\n        expect(response.status).to eq 400\n      end\n\n      it \"includes correct redirect URI\" do\n        expect(redirect_uri).to match(/^#{client.redirect_uri}/)\n      end\n\n      it \"does not include access token in fragment\" do\n        expect(redirect_uri.match(/access_token=([a-f0-9]+)&?/)).to be_nil\n      end\n\n      it \"includes error in redirect uri\" do\n        expect(redirect_uri.match(/error=([a-z_]+)&?/)[1]).to eq \"invalid_scope\"\n      end\n\n      it \"includes error description in redirect uri\" do\n        expect(redirect_uri.match(/error_description=(.+)&?/)[1]).not_to be_nil\n      end\n\n      it \"does not issue any access token\" do\n        expect(Doorkeeper::AccessToken.all).to be_empty\n      end\n    end\n\n    context \"with 'form_post' as response_mode\" do\n      before do\n        default_scopes_exist :public\n\n        post :create, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          scope: \"invalid\",\n          redirect_uri: client.redirect_uri,\n          response_mode: \"form_post\",\n        }\n      end\n\n      it \"renders 400 error\" do\n        expect(response.status).to eq 400\n      end\n\n      it \"renders correct status\" do\n        expect(response_json_body[\"status\"]).to eq \"post\"\n      end\n\n      it \"renders correct redirect uri\" do\n        expect(response_json_body[\"redirect_uri\"]).to eq(client.redirect_uri)\n      end\n\n      it \"includes access token in fragment\" do\n        expect(response_json_body[\"body\"][\"access_token\"]).to be_nil\n      end\n\n      it \"includes token type in fragment\" do\n        expect(response_json_body[\"body\"][\"error\"]).to eq \"invalid_scope\"\n      end\n\n      it \"includes token expiration in fragment\" do\n        expect(response_json_body[\"body\"][\"error_description\"]).not_to be_nil\n      end\n\n      it \"does not issue any access token\" do\n        expect(Doorkeeper::AccessToken.all).to be_empty\n      end\n    end\n  end\n\n  describe \"POST #create with application already authorized\" do\n    before do\n      allow(Doorkeeper.config).to receive(:reuse_access_token).and_return(true)\n\n      access_token.save!\n\n      post :create, params: {\n        client_id: client.uid,\n        response_type: \"token\",\n        redirect_uri: client.redirect_uri,\n      }\n    end\n\n    it \"returns the existing access token in a fragment\" do\n      expect(response.query_params[\"access_token\"]).to eq(access_token.token)\n    end\n\n    it \"does not creates a new access token\" do\n      expect(Doorkeeper::AccessToken.count).to eq(1)\n    end\n  end\n\n  describe \"POST #create with callbacks\" do\n    after do\n      client.update_attribute :redirect_uri, \"urn:ietf:wg:oauth:2.0:oob\"\n    end\n\n    describe \"when successful\" do\n      after do\n        post :create, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n        }\n      end\n\n      it \"calls :before_successful_authorization callback\" do\n        expect(Doorkeeper.config)\n          .to receive_message_chain(:before_successful_authorization, :call)\n          .with(instance_of(described_class), instance_of(Doorkeeper::OAuth::Hooks::Context))\n      end\n\n      it \"calls :after_successful_authorization callback\" do\n        expect(Doorkeeper.config)\n          .to receive_message_chain(:after_successful_authorization, :call)\n          .with(instance_of(described_class), instance_of(Doorkeeper::OAuth::Hooks::Context))\n      end\n    end\n\n    describe \"with errors\" do\n      after do\n        post :create, params: { client_id: client.uid, response_type: \"token\", redirect_uri: \"bad_uri\" }\n      end\n\n      it \"does not call :before_successful_authorization callback\" do\n        expect(Doorkeeper.config).not_to receive(:before_successful_authorization)\n      end\n\n      it \"does not call :after_successful_authorization callback\" do\n        expect(Doorkeeper.config).not_to receive(:after_successful_authorization)\n      end\n    end\n  end\n\n  describe \"GET #new token request with native url and skip_authorization true\" do\n    before do\n      allow(Doorkeeper.config).to receive(:skip_authorization).and_return(proc do\n        true\n      end)\n\n      client.update_attribute :redirect_uri, \"urn:ietf:wg:oauth:2.0:oob\"\n\n      get :new, params: {\n        client_id: client.uid,\n        response_type: \"token\",\n        redirect_uri: client.redirect_uri,\n      }\n    end\n\n    it \"redirects immediately\" do\n      expect(response).to be_redirect\n      expect(response.location).to match(%r{/oauth/token/info\\?access_token=})\n    end\n\n    it \"does not issue a grant\" do\n      expect(Doorkeeper::AccessGrant.count).to be 0\n    end\n\n    it \"issues a token\" do\n      expect(Doorkeeper::AccessToken.count).to be 1\n    end\n  end\n\n  describe \"GET #new code request with native url and skip_authorization true\" do\n    before do\n      allow(Doorkeeper.config).to receive(:grant_flows).and_return(%w[authorization_code])\n      allow(Doorkeeper.config).to receive(:skip_authorization).and_return(proc do\n        true\n      end)\n\n      client.update_attribute :redirect_uri, \"urn:ietf:wg:oauth:2.0:oob\"\n\n      get :new, params: {\n        client_id: client.uid,\n        response_type: \"code\",\n        redirect_uri: client.redirect_uri,\n      }\n    end\n\n    it \"redirects immediately\" do\n      expect(response).to be_redirect\n      expect(response.location)\n        .to match(%r{/oauth/authorize/native\\?code=#{Doorkeeper::AccessGrant.first.token}})\n    end\n\n    it \"issues a grant\" do\n      expect(Doorkeeper::AccessGrant.count).to be 1\n    end\n\n    it \"does not issue a token\" do\n      expect(Doorkeeper::AccessToken.count).to be 0\n    end\n\n    context 'with use_url_path_for_native_authorization' do\n      around(:each) do |example|\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          use_url_path_for_native_authorization\n        end\n\n        Rails.application.reload_routes!\n\n        example.run\n\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n        end\n\n        Rails.application.reload_routes!\n      end\n\n      it 'should redirect immediately' do\n        expect(response).to be_redirect\n        expect(response.location).to match(/oauth\\/authorize\\/#{Doorkeeper::AccessGrant.first.token}/)\n      end\n\n      it 'should issue a grant' do\n        expect(Doorkeeper::AccessGrant.count).to be 1\n      end\n\n      it 'should not issue a token' do\n        expect(Doorkeeper::AccessToken.count).to be 0\n      end\n    end\n  end\n\n  describe \"GET #new with skip_authorization true\" do\n    before do\n      allow(Doorkeeper.config).to receive(:skip_authorization).and_return(proc do\n        true\n      end)\n\n      get :new, params: params\n    end\n\n    context \"without response_mode parameter\" do\n      let(:params) do\n        {\n          client_id: client.uid,\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n        }\n      end\n\n      it \"redirects immediately\" do\n        expect(response).to be_redirect\n        expect(response.location).to match(/^#{client.redirect_uri}/)\n      end\n\n      it \"issues a token\" do\n        expect(Doorkeeper::AccessToken.count).to be 1\n      end\n\n      it \"includes token type in fragment\" do\n        expect(response.query_params[\"token_type\"]).to eq(\"Bearer\")\n      end\n\n      it \"includes token expiration in fragment\" do\n        expect(response.query_params[\"expires_in\"].to_i).to eq(1234)\n      end\n\n      it \"issues the token for the current client\" do\n        expect(Doorkeeper::AccessToken.first.application_id).to eq(client.id)\n      end\n\n      it \"issues the token for the current resource owner\" do\n        expect(Doorkeeper::AccessToken.first.resource_owner_id).to eq(user.id)\n      end\n    end\n\n    context \"with 'form_post' as response_mode\" do\n      let(:params) do\n        {\n          client_id: client.uid,\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n          response_mode: \"form_post\",\n        }\n      end\n\n      it \"renders 200 status\" do\n        expect(response.status).to eq 200\n      end\n\n      it \"issues a token\" do\n        expect(Doorkeeper::AccessToken.count).to be 1\n      end\n\n      it \"issues the token for the current client\" do\n        expect(Doorkeeper::AccessToken.first.application_id).to eq(client.id)\n      end\n\n      it \"issues the token for the current resource owner\" do\n        expect(Doorkeeper::AccessToken.first.resource_owner_id).to eq(user.id)\n      end\n    end\n  end\n\n  describe \"GET #new with skip_authorization false\" do\n    let(:params) do\n      {\n        client_id: client.uid,\n        response_type: \"token\",\n        redirect_uri: client.redirect_uri,\n      }\n    end\n\n    before do\n      allow(Doorkeeper.config.access_token_model).to receive(:matching_token_for).and_return(true)\n      client.update_attribute :confidential, confidential_client\n\n      get :new, params: params\n    end\n\n    context \"with matching token and confidential application\" do\n      let(:confidential_client) { true }\n\n      it \"redirects immediately\" do\n        expect(subject).not_to receive(:render)\n        expect(response).to be_redirect\n        expect(response.location).to match(/^#{client.redirect_uri}/)\n      end\n\n      it \"issues a token\" do\n        expect(Doorkeeper::AccessToken.count).to be 1\n      end\n    end\n\n    context \"with matching token and non-confidential application\" do\n      let(:confidential_client) { false }\n\n      it \"renders the new view\" do\n        expect(response).to be_successful\n        expect(subject).to render_with :new\n      end\n\n      it \"doesn't issue a token\" do\n        expect(Doorkeeper::AccessToken.count).to be 0\n      end\n    end\n  end\n\n  describe \"GET #new in API mode\" do\n    before do\n      allow(Doorkeeper.config).to receive(:api_only).and_return(true)\n\n      get :new, params: {\n        client_id: client.uid,\n        response_type: \"token\",\n        redirect_uri: client.redirect_uri,\n      }\n    end\n\n    it \"renders success\" do\n      expect(response).to be_successful\n    end\n\n    it \"sets status to pre-authorization\" do\n      expect(json_response[\"status\"]).to eq(I18n.t(\"doorkeeper.pre_authorization.status\"))\n    end\n\n    it \"sets correct values\" do\n      expect(json_response[\"client_id\"]).to eq(client.uid)\n      expect(json_response[\"redirect_uri\"]).to eq(client.redirect_uri)\n      expect(json_response[\"state\"]).to be_nil\n      expect(json_response[\"response_type\"]).to eq(\"token\")\n      expect(json_response[\"scope\"]).to eq(\"default\")\n    end\n  end\n\n  describe \"GET #new in API mode with skip_authorization true\" do\n    before do\n      allow(Doorkeeper.configuration).to receive(:skip_authorization).and_return(proc { true })\n      allow(Doorkeeper.configuration).to receive(:api_only).and_return(true)\n\n      get :new, params: params\n    end\n\n    context \"without response_mode parameter\" do\n      let(:params) do\n        {\n          client_id: client.uid,\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n        }\n      end\n\n      it \"renders success\" do\n        expect(response).to be_successful\n      end\n\n      it \"issues a token\" do\n        expect(Doorkeeper::AccessToken.count).to be 1\n      end\n\n      it \"sets status to redirect\" do\n        expect(JSON.parse(response.body)[\"status\"]).to eq(\"redirect\")\n      end\n\n      it \"sets redirect_uri to correct value\" do\n        redirect_uri = JSON.parse(response.body)[\"redirect_uri\"]\n        expect(redirect_uri).not_to be_nil\n        expect(redirect_uri.match(/token_type=(\\w+)&?/)[1]).to eq \"Bearer\"\n        expect(redirect_uri.match(/expires_in=(\\d+)&?/)[1].to_i).to eq 1234\n        expect(\n          redirect_uri.match(/access_token=([a-zA-Z0-9\\-_]+)&?/)[1],\n        ).to eq Doorkeeper::AccessToken.first.token\n      end\n\n      it \"issues the token for the current client\" do\n        expect(Doorkeeper::AccessToken.first.application_id).to eq(client.id)\n      end\n\n      it \"issues the token for the current resource owner\" do\n        expect(Doorkeeper::AccessToken.first.resource_owner_id).to eq(user.id)\n      end\n    end\n\n    context \"with 'form_post' as response_mode\" do\n      let(:params) do\n        {\n          client_id: client.uid,\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n          response_mode: \"form_post\",\n        }\n      end\n\n      it \"renders success\" do\n        expect(response).to be_successful\n      end\n\n      it \"renders correct status\" do\n        expect(response_json_body[\"status\"]).to eq \"post\"\n      end\n\n      it \"renders correct redirect uri\" do\n        expect(response_json_body[\"redirect_uri\"]).to eq(client.redirect_uri)\n      end\n\n      it \"includes access token in fragment\" do\n        expect(response_json_body[\"body\"][\"access_token\"]).to eq(Doorkeeper::AccessToken.first.token)\n      end\n\n      it \"includes token type in fragment\" do\n        expect(response_json_body[\"body\"][\"token_type\"]).to eq \"Bearer\"\n      end\n\n      it \"includes token expiration in fragment\" do\n        expect(response_json_body[\"body\"][\"expires_in\"]).to eq 1234\n      end\n\n      it \"issues a token\" do\n        expect(Doorkeeper::AccessToken.count).to be 1\n      end\n\n      it \"issues the token for the current client\" do\n        expect(Doorkeeper::AccessToken.first.application_id).to eq(client.id)\n      end\n\n      it \"issues the token for the current resource owner\" do\n        expect(Doorkeeper::AccessToken.first.resource_owner_id).to eq(user.id)\n      end\n    end\n  end\n\n  describe \"GET #new with errors\" do\n    context \"without valid params\" do\n      before do\n        default_scopes_exist :public\n        get :new, params: { an_invalid: \"request\" }\n      end\n\n      it \"does not redirect\" do\n        expect(response).not_to be_redirect\n      end\n\n      it \"does not issue any token\" do\n        expect(Doorkeeper::AccessGrant.count).to eq 0\n        expect(Doorkeeper::AccessToken.count).to eq 0\n      end\n    end\n\n    context \"when user cannot access application\" do\n      before do\n        allow(Doorkeeper.configuration).to receive(:authorize_resource_owner_for_client).and_return(->(*_) { false })\n\n        get :new, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n        }\n      end\n\n      it \"does not redirect\" do\n        expect(response).not_to be_redirect\n      end\n\n      it \"does not issue any token\" do\n        expect(Doorkeeper::AccessGrant.count).to eq 0\n        expect(Doorkeeper::AccessToken.count).to eq 0\n      end\n    end\n  end\n\n  describe \"GET #new in API mode with errors\" do\n    before do\n      allow(Doorkeeper.configuration).to receive(:api_only).and_return(true)\n      default_scopes_exist :public\n    end\n\n    context \"without valid params\" do\n      before do\n        get :new, params: { an_invalid: \"request\" }\n      end\n\n      it \"renders bad request\" do\n        expect(response).to have_http_status(:bad_request)\n      end\n\n      it \"includes error in body\" do\n        expect(response_json_body[\"error\"]).to eq(\"invalid_request\")\n      end\n\n      it \"includes error description in body\" do\n        expect(response_json_body[\"error_description\"])\n          .to eq(translated_invalid_request_error_message(:missing_param, :client_id))\n      end\n\n      it \"does not issue any token\" do\n        expect(Doorkeeper::AccessGrant.count).to eq 0\n        expect(Doorkeeper::AccessToken.count).to eq 0\n      end\n    end\n\n    context \"when user cannot access application\" do\n      before do\n        allow(Doorkeeper.configuration).to receive(:authorize_resource_owner_for_client).and_return(->(*_) { false })\n\n        get :new, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n        }\n      end\n\n      it \"renders unauthorized\" do\n        expect(response).to have_http_status(:unauthorized)\n      end\n\n      it \"includes error in body\" do\n        expect(response_json_body[\"error\"]).to eq(\"invalid_client\")\n      end\n\n      it \"includes error description in body\" do\n        expect(response_json_body[\"error_description\"])\n          .to eq(translated_error_message(:invalid_client))\n      end\n\n      it \"does not issue any token\" do\n        expect(Doorkeeper::AccessGrant.count).to eq 0\n        expect(Doorkeeper::AccessToken.count).to eq 0\n      end\n    end\n\n    context \"with 'form_post' as response_mode\" do\n      before do\n        post :create, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          scope: \"invalid\",\n          redirect_uri: client.redirect_uri,\n          response_mode: \"form_post\",\n        }\n      end\n\n      it \"renders 400 error\" do\n        expect(response.status).to eq 400\n      end\n\n      it \"renders correct status\" do\n        expect(response_json_body[\"status\"]).to eq \"post\"\n      end\n\n      it \"renders correct redirect uri\" do\n        expect(response_json_body[\"redirect_uri\"]).to eq(client.redirect_uri)\n      end\n\n      it \"includes access token in fragment\" do\n        expect(response_json_body[\"body\"][\"access_token\"]).to be_nil\n      end\n\n      it \"includes token type in fragment\" do\n        expect(response_json_body[\"body\"][\"error\"]).to eq \"invalid_scope\"\n      end\n\n      it \"includes token expiration in fragment\" do\n        expect(response_json_body[\"body\"][\"error_description\"]).not_to be_nil\n      end\n\n      it \"does not issue any access token\" do\n        expect(Doorkeeper::AccessToken.all).to be_empty\n      end\n    end\n  end\n\n  describe \"GET #new with errors with handle_auth_errors :redirect\" do\n    before { config_is_set(:handle_auth_errors, :redirect) }\n\n    context \"without valid params\" do\n      before do\n        default_scopes_exist :public\n        get :new, params: { an_invalid: \"request\" }\n      end\n\n      it \"does not redirect\" do\n        expect(response).not_to be_redirect\n      end\n\n      it \"does not issue any token\" do\n        expect(Doorkeeper::AccessGrant.count).to eq 0\n        expect(Doorkeeper::AccessToken.count).to eq 0\n      end\n    end\n\n    context \"invalid scope\" do\n      before do\n        default_scopes_exist :public\n        get :new, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          scope: \"invalid\",\n          redirect_uri: client.redirect_uri,\n          state: \"return-this\",\n        }\n      end\n\n      it \"redirects to client redirect uri\" do\n        expect(response).to be_redirect\n        expect(response.location).to match(/^#{client.redirect_uri}/)\n      end\n\n      it \"includes error in fragment\" do\n        expect(response.query_params[\"error\"]).to eq(\"invalid_scope\")\n      end\n\n      it \"includes error description in fragment\" do\n        expect(response.query_params[\"error_description\"]).to eq(translated_error_message(:invalid_scope))\n      end\n\n      it \"includes state in fragment\" do\n        expect(response.query_params[\"state\"]).to eq(\"return-this\")\n      end\n\n      it \"does not issue any token\" do\n        expect(Doorkeeper::AccessGrant.count).to eq 0\n        expect(Doorkeeper::AccessToken.count).to eq 0\n      end\n    end\n\n    context \"invalid scope with form_post response mode\" do\n      before do\n        default_scopes_exist :public\n        get :new, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          scope: \"invalid\",\n          redirect_uri: client.redirect_uri,\n          state: \"return-this\",\n          response_mode: \"form_post\",\n        }\n      end\n\n      it \"renders the form_post page\" do\n        expect(response.status).to eq(200)\n      end\n\n      it \"includes the error in the redirect post\" do\n        expect(response.body).to include(\"invalid_scope\")\n      end\n    end\n\n    context \"invalid redirect_uri\" do\n      before do\n        default_scopes_exist :public\n        get :new, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          redirect_uri: \"invalid\",\n        }\n      end\n\n      it \"does not redirect\" do\n        expect(response).not_to be_redirect\n      end\n\n      it \"does not issue any token\" do\n        expect(Doorkeeper::AccessGrant.count).to eq 0\n        expect(Doorkeeper::AccessToken.count).to eq 0\n      end\n    end\n\n    context \"with client_id and redirect_uri\" do\n      before do\n        default_scopes_exist :public\n        get :new, params: {\n          client_id: client.uid,\n          redirect_uri: client.redirect_uri,\n          response_mode: \"fragment\"\n        }\n      end\n\n      it \"redirects to client redirect uri\" do\n        expect(response).to be_redirect\n        expect(response.location).to match(/^#{client.redirect_uri}/)\n      end\n\n      it \"includes error in fragment\" do\n        expect(response.query_params[\"error\"]).to eq(\"invalid_request\")\n      end\n\n      it \"includes error description in fragment\" do\n        expect(response.query_params[\"error_description\"]).to eq(translated_invalid_request_error_message(:missing_param, :response_type))\n      end\n\n      it \"does not issue any token\" do\n        expect(Doorkeeper::AccessGrant.count).to eq 0\n        expect(Doorkeeper::AccessToken.count).to eq 0\n      end\n    end\n  end\n\n  describe \"GET #new with errors with handle_auth_errors :raise\" do\n    before { config_is_set(:handle_auth_errors, :raise) }\n\n    context \"without valid params\" do\n      before do\n        default_scopes_exist :public\n      end\n\n      it \"raises InvalidRequest error\" do\n        expect { get :new, params: { an_invalid: \"request\" } }.to raise_error(Doorkeeper::Errors::InvalidRequest)\n      end\n\n      it \"does not issue any token\" do\n        expect do\n          get :new, params: { an_invalid: \"request\" }\n        rescue Doorkeeper::Errors::InvalidRequest\n        end.not_to change(Doorkeeper::AccessGrant, :count)\n\n        expect do\n          get :new, params: { an_invalid: \"request\" }\n        rescue Doorkeeper::Errors::InvalidRequest\n        end.not_to change(Doorkeeper::AccessToken, :count)\n      end\n    end\n\n    context \"invalid client_id\" do\n      before do\n        default_scopes_exist :public\n      end\n\n      it \"raises InvalidClient error\" do\n        expect { get :new, params: { client_id: \"invalid\" } }.to raise_error(Doorkeeper::Errors::InvalidClient)\n      end\n    end\n\n    context \"invalid scope\" do\n      before do\n        default_scopes_exist :public\n      end\n\n      it \"raises InvalidScope error\" do\n        expect do\n          get :new, params: {\n            client_id: client.uid,\n            response_type: \"token\",\n            scope: \"invalid\",\n            redirect_uri: client.redirect_uri,\n            state: \"return-this\",\n          }\n        end.to raise_error(Doorkeeper::Errors::InvalidScope)\n      end\n    end\n\n    context \"invalid redirect_uri\" do\n      before do\n        default_scopes_exist :public\n      end\n\n      it \"raises InvalidRedirectUri error\" do\n        expect do\n          get :new, params: {\n            client_id: client.uid,\n            response_type: \"token\",\n            redirect_uri: \"invalid\",\n          }\n        end.to raise_error(Doorkeeper::Errors::InvalidRedirectUri)\n      end\n    end\n  end\n\n  describe \"POST #create with errors with handle_auth_errors :raise\" do\n    before { config_is_set(:handle_auth_errors, :raise) }\n\n    context \"without valid params\" do\n      before do\n        default_scopes_exist :public\n      end\n\n      it \"raises InvalidRequest error\" do\n        expect { post :create, params: { an_invalid: \"request\" } }.to raise_error(Doorkeeper::Errors::InvalidRequest)\n      end\n\n      it \"does not issue any token\" do\n        expect do\n          post :create, params: { an_invalid: \"request\" }\n        rescue Doorkeeper::Errors::InvalidRequest\n        end.not_to change(Doorkeeper::AccessGrant, :count)\n\n        expect do\n          post :create, params: { an_invalid: \"request\" }\n        rescue Doorkeeper::Errors::InvalidRequest\n        end.not_to change(Doorkeeper::AccessToken, :count)\n      end\n    end\n\n    context \"invalid client_id\" do\n      before do\n        default_scopes_exist :public\n      end\n\n      it \"raises InvalidClient error\" do\n        expect { post :create, params: { client_id: \"invalid\" } }.to raise_error(Doorkeeper::Errors::InvalidClient)\n      end\n    end\n\n    context \"invalid scope\" do\n      before do\n        default_scopes_exist :public\n      end\n\n      it \"raises InvalidScope error\" do\n        expect do\n          post :create, params: {\n            client_id: client.uid,\n            response_type: \"token\",\n            scope: \"invalid\",\n            redirect_uri: client.redirect_uri,\n            state: \"return-this\",\n          }\n        end.to raise_error(Doorkeeper::Errors::InvalidScope)\n      end\n    end\n\n    context \"invalid redirect_uri\" do\n      before do\n        default_scopes_exist :public\n      end\n\n      it \"raises InvalidRedirectUri error\" do\n        expect do\n          post :create, params: {\n            client_id: client.uid,\n            response_type: \"token\",\n            redirect_uri: \"invalid\",\n          }\n        end.to raise_error(Doorkeeper::Errors::InvalidRedirectUri)\n      end\n    end\n  end\n\n  describe \"GET #new with callbacks\" do\n    after do\n      client.update_attribute :redirect_uri, \"urn:ietf:wg:oauth:2.0:oob\"\n      get :new, params: { client_id: client.uid, response_type: \"token\", redirect_uri: client.redirect_uri }\n    end\n\n    describe \"when authorizing\" do\n      before do\n        allow(Doorkeeper.configuration).to receive(:skip_authorization).and_return(proc { true })\n      end\n\n      it \"calls :before_successful_authorization callback\" do\n        expect(Doorkeeper.configuration)\n          .to receive_message_chain(:before_successful_authorization, :call)\n          .with(instance_of(described_class), instance_of(Doorkeeper::OAuth::Hooks::Context))\n      end\n\n      it \"calls :after_successful_authorization callback\" do\n        expect(Doorkeeper.configuration)\n          .to receive_message_chain(:after_successful_authorization, :call)\n          .with(instance_of(described_class), instance_of(Doorkeeper::OAuth::Hooks::Context))\n      end\n    end\n\n    describe \"when not authorizing\" do\n      before do\n        allow(Doorkeeper.configuration).to receive(:skip_authorization).and_return(proc { false })\n      end\n\n      it \"does not call :before_successful_authorization callback\" do\n        expect(Doorkeeper.configuration).not_to receive(:before_successful_authorization)\n      end\n\n      it \"does not call :after_successful_authorization callback\" do\n        expect(Doorkeeper.configuration).not_to receive(:after_successful_authorization)\n      end\n    end\n\n    describe \"when not authorizing in api mode\" do\n      before do\n        allow(Doorkeeper.configuration).to receive(:skip_authorization).and_return(proc { false })\n        allow(Doorkeeper.configuration).to receive(:api_only).and_return(true)\n      end\n\n      it \"does not call :before_successful_authorization callback\" do\n        expect(Doorkeeper.configuration).not_to receive(:before_successful_authorization)\n      end\n\n      it \"does not call :after_successful_authorization callback\" do\n        expect(Doorkeeper.configuration).not_to receive(:after_successful_authorization)\n      end\n    end\n  end\n\n  describe \"authorize response memoization\" do\n    it \"memoizes the result of the authorization\" do\n      pre_auth = double(:pre_auth, authorizable?: true)\n      allow(subject).to receive(:pre_auth) { pre_auth }\n      strategy = double(:strategy, authorize: true)\n      expect(strategy).to receive(:authorize).once\n      allow(subject).to receive(:strategy) { strategy }\n      allow(subject).to receive(:create) do\n        2.times { subject.send :authorize_response }\n        subject.render json: {}, status: :ok\n      end\n\n      post :create\n    end\n  end\n\n  describe \"strong parameters\" do\n    it \"ignores non-scalar scope parameter\" do\n      get :new, params: {\n        client_id: client.uid,\n        response_type: \"token\",\n        redirect_uri: client.redirect_uri,\n        scope: { \"0\" => \"profile\" },\n      }\n\n      expect(response).to be_successful\n    end\n  end\n\n  describe \"DELETE #destroy\" do\n    context \"without form_post response mode\" do\n      before do\n        delete :destroy, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n        }\n      end\n\n      it \"redirects\" do\n        expect(response).to be_redirect\n      end\n\n      it \"redirects to client redirect uri\" do\n        expect(response.location).to match(/^#{client.redirect_uri}/)\n      end\n\n      it \"includes error in fragment\" do\n        expect(response.query_params[\"error\"]).to eq(\"access_denied\")\n      end\n    end\n\n    context \"with form_post response mode\" do\n      before do\n        delete :destroy, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n          response_mode: \"form_post\",\n        }\n      end\n\n      it \"redirects after authorization\" do\n        expect(response.status).to eq(200)\n      end\n\n      it \"includes the error in the redirect post\" do\n        expect(response.body).to include(\"access_denied\")\n      end\n    end\n\n    context \"with invalid params\" do\n      before do\n        delete :destroy, params: {\n          client_id: client.uid,\n          response_type: \"blabla\",\n          redirect_uri: client.redirect_uri,\n        }\n      end\n\n      it \"renders the error page correctly\" do\n        expect(response.status).to eq(200)\n      end\n\n      it \"includes the error in the page\" do\n        expect(response.body).to include(\n          translated_error_message(:unsupported_grant_type),\n        )\n      end\n    end\n  end\n\n  describe \"DELETE #destroy in API mode\" do\n    before do\n      allow(Doorkeeper.config).to receive(:api_only).and_return(true)\n    end\n\n    context \"without form_post response mode\" do\n      before do\n        delete :destroy, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n        }\n      end\n\n      it \"renders bad request\" do\n        expect(response).to have_http_status(:bad_request)\n      end\n\n      it \"includes access_denied in the redirect uri\" do\n        expect(response_json_body[\"redirect_uri\"].match(/error=(\\w+)&?/)[1]).to eq(\"access_denied\")\n      end\n    end\n\n    context \"with form_post response mode\" do\n      before do\n        delete :destroy, params: {\n          client_id: client.uid,\n          response_type: \"token\",\n          redirect_uri: client.redirect_uri,\n          response_mode: \"form_post\",\n        }\n      end\n\n      it \"renders bad request\" do\n        expect(response).to have_http_status(:bad_request)\n      end\n\n      it \"includes the correct redirect uri\" do\n        expect(response_json_body[\"redirect_uri\"]).to eq(client.redirect_uri)\n      end\n\n      it \"includes access_denied in the body\" do\n        expect(response_json_body[\"body\"][\"error\"]).to eq(\"access_denied\")\n      end\n    end\n\n    context \"with invalid params\" do\n      before do\n        delete :destroy, params: {\n          client_id: client.uid,\n          response_type: \"blabla\",\n          redirect_uri: client.redirect_uri,\n          response_mode: \"form_post\",\n        }\n      end\n\n      it \"renders bad request\" do\n        expect(response).to have_http_status(:bad_request)\n      end\n\n      it \"includes error in body\" do\n        expect(response_json_body[\"error\"]).to eq(\"unsupported_grant_type\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/protected_resources_controller_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nmodule ControllerActions\n  def index\n    render plain: \"index\"\n  end\n\n  def show\n    render plain: \"show\"\n  end\n\n  def doorkeeper_unauthorized_render_options(*); end\n\n  def doorkeeper_forbidden_render_options(*); end\nend\n\nRSpec.describe \"doorkeeper authorize filter\" do\n  render_views\n\n  context \"when accepts token code specified as\" do\n    controller do\n      before_action :doorkeeper_authorize!\n\n      def index\n        render plain: \"index\"\n      end\n    end\n\n    let(:token_string) { \"1A2BC3\" }\n    let(:token) do\n      double(\n        Doorkeeper::AccessToken,\n        acceptable?: true, previous_refresh_token: \"\",\n        revoke_previous_refresh_token!: true,\n      )\n    end\n\n    it \"access_token param\" do\n      expect(Doorkeeper::AccessToken).to receive(:by_token).with(token_string).and_return(token)\n      get :index, params: { access_token: token_string }\n    end\n\n    it \"bearer_token param\" do\n      expect(Doorkeeper::AccessToken).to receive(:by_token).with(token_string).and_return(token)\n      get :index, params: { bearer_token: token_string }\n    end\n\n    it \"Authorization header\" do\n      expect(Doorkeeper::AccessToken).to receive(:by_token).with(token_string).and_return(token)\n      request.env[\"HTTP_AUTHORIZATION\"] = \"Bearer #{token_string}\"\n      get :index\n    end\n\n    it \"different kind of Authorization header\" do\n      expect(Doorkeeper::AccessToken).not_to receive(:by_token)\n      request.env[\"HTTP_AUTHORIZATION\"] = \"MAC #{token_string}\"\n      get :index\n    end\n\n    it \"does not change Authorization header value\" do\n      expect(Doorkeeper::AccessToken).to receive(:by_token).twice.and_return(token)\n      request.env[\"HTTP_AUTHORIZATION\"] = \"Bearer #{token_string}\"\n      get :index\n      controller.send(:remove_instance_variable, :@doorkeeper_token)\n      get :index\n    end\n  end\n\n  context \"when defined for all actions\" do\n    controller do\n      before_action :doorkeeper_authorize!\n\n      include ControllerActions\n    end\n\n    context \"with valid token\", token: :valid do\n      it \"allows into index action\" do\n        get :index, params: { access_token: token_string }\n        expect(response).to be_successful\n      end\n\n      it \"allows into show action\" do\n        get :show, params: { id: \"4\", access_token: token_string }\n        expect(response).to be_successful\n      end\n    end\n\n    context \"with invalid token\", token: :invalid do\n      it \"does not allow into index action\" do\n        get :index, params: { access_token: token_string }\n        expect(response.status).to eq 401\n        expect(response.header[\"WWW-Authenticate\"]).to match(/^Bearer/)\n      end\n\n      it \"does not allow into show action\" do\n        get :show, params: { id: \"4\", access_token: token_string }\n        expect(response.status).to eq 401\n        expect(response.header[\"WWW-Authenticate\"]).to match(/^Bearer/)\n      end\n    end\n  end\n\n  context \"when defined with scopes\" do\n    controller do\n      before_action -> { doorkeeper_authorize! :write }\n\n      include ControllerActions\n    end\n\n    let(:token_string) { \"1A2DUWE\" }\n\n    it \"allows if the token has particular scopes\" do\n      token = double(\n        Doorkeeper::AccessToken,\n        accessible?: true, scopes: %w[write public],\n        previous_refresh_token: \"\",\n        revoke_previous_refresh_token!: true,\n      )\n      expect(token).to receive(:acceptable?).with([:write]).and_return(true)\n      expect(\n        Doorkeeper::AccessToken,\n      ).to receive(:by_token).with(token_string).and_return(token)\n\n      get :index, params: { access_token: token_string }\n      expect(response).to be_successful\n    end\n\n    it \"does not allow if the token does not include given scope\" do\n      token = double(\n        Doorkeeper::AccessToken,\n        accessible?: true, scopes: [\"public\"], revoked?: false,\n        expired?: false, previous_refresh_token: \"\",\n        revoke_previous_refresh_token!: true,\n      )\n      expect(\n        Doorkeeper::AccessToken,\n      ).to receive(:by_token).with(token_string).and_return(token)\n      expect(token).to receive(:acceptable?).with([:write]).and_return(false)\n\n      get :index, params: { access_token: token_string }\n      expect(response.status).to eq 403\n      expect(response.header[\"WWW-Authenticate\"]).to include('error=\"insufficient_scope\"')\n    end\n  end\n\n  context \"when custom unauthorized render options are configured\" do\n    controller do\n      before_action :doorkeeper_authorize!\n\n      include ControllerActions\n    end\n\n    context \"with a JSON custom render\", token: :invalid do\n      before do\n        module ControllerActions\n          remove_method :doorkeeper_unauthorized_render_options\n\n          def doorkeeper_unauthorized_render_options(error: nil)\n            { json: ActiveSupport::JSON.encode(error_message: error.description) }\n          end\n        end\n      end\n\n      after do\n        module ControllerActions\n          remove_method :doorkeeper_unauthorized_render_options\n\n          def doorkeeper_unauthorized_render_options(error: nil); end\n        end\n      end\n\n      it \"renders a custom JSON response\", token: :invalid do\n        get :index, params: { access_token: token_string }\n        expect(response.status).to eq 401\n        expect(response.content_type).to include(\"application/json\")\n        expect(response.header[\"WWW-Authenticate\"]).to match(/^Bearer/)\n\n        expect(json_response).not_to be_nil\n        expect(json_response[\"error_message\"]).to match(\"token is invalid\")\n      end\n    end\n\n    context \"with a text custom render\", token: :invalid do\n      before do\n        module ControllerActions\n          remove_method :doorkeeper_unauthorized_render_options\n\n          def doorkeeper_unauthorized_render_options(**)\n            { plain: \"Unauthorized\" }\n          end\n        end\n      end\n\n      after do\n        module ControllerActions\n          remove_method :doorkeeper_unauthorized_render_options\n\n          def doorkeeper_unauthorized_render_options(error: nil); end\n        end\n      end\n\n      it \"renders a custom text response\", token: :invalid do\n        get :index, params: { access_token: token_string }\n        expect(response.status).to eq 401\n        expect(response.content_type).to include(\"text/plain\")\n        expect(response.header[\"WWW-Authenticate\"]).to match(/^Bearer/)\n        expect(response.body).to eq(\"Unauthorized\")\n      end\n    end\n  end\n\n  context \"when custom forbidden render options are configured\" do\n    before do\n      expect(Doorkeeper::AccessToken).to receive(:by_token).with(token_string).and_return(token)\n      expect(token).to receive(:acceptable?).with([:write]).and_return(false)\n    end\n\n    after do\n      module ControllerActions\n        remove_method :doorkeeper_forbidden_render_options\n\n        def doorkeeper_forbidden_render_options(*); end\n      end\n    end\n\n    controller do\n      before_action -> { doorkeeper_authorize! :write }\n\n      include ControllerActions\n    end\n\n    let(:token) do\n      double(\n        Doorkeeper::AccessToken,\n        accessible?: true, scopes: [\"public\"], revoked?: false,\n        expired?: false, previous_refresh_token: \"\",\n        revoke_previous_refresh_token!: true,\n      )\n    end\n\n    let(:token_string) { \"1A2DUWE\" }\n\n    context \"with a JSON custom render\" do\n      before do\n        module ControllerActions\n          remove_method :doorkeeper_forbidden_render_options\n\n          def doorkeeper_forbidden_render_options(*)\n            { json: { error_message: \"Forbidden\" } }\n          end\n        end\n      end\n\n      it \"renders a custom JSON response\" do\n        get :index, params: { access_token: token_string }\n        expect(response.header[\"WWW-Authenticate\"]).to include('error=\"insufficient_scope\"')\n        expect(response.content_type).to include(\"application/json\")\n        expect(response.status).to eq 403\n\n        expect(json_response).not_to be_nil\n        expect(json_response[\"error_message\"]).to match(\"Forbidden\")\n      end\n    end\n\n    context \"with a status and JSON custom render\" do\n      before do\n        module ControllerActions\n          remove_method :doorkeeper_forbidden_render_options\n          def doorkeeper_forbidden_render_options(*)\n            { json: { error_message: \"Not Found\" },\n              respond_not_found_when_forbidden: true, }\n          end\n        end\n      end\n\n      it \"overrides the default status code\" do\n        get :index, params: { access_token: token_string }\n        expect(response.status).to eq 404\n      end\n    end\n\n    context \"with a text custom render\" do\n      before do\n        module ControllerActions\n          remove_method :doorkeeper_forbidden_render_options\n\n          def doorkeeper_forbidden_render_options(*)\n            { plain: \"Forbidden\" }\n          end\n        end\n      end\n\n      it \"renders a custom status code and text response\" do\n        get :index, params: { access_token: token_string }\n        expect(response.header[\"WWW-Authenticate\"]).to include('error=\"insufficient_scope\"')\n        expect(response.status).to eq 403\n        expect(response.body).to eq(\"Forbidden\")\n      end\n    end\n\n    context \"with a status and text custom render\" do\n      before do\n        module ControllerActions\n          remove_method :doorkeeper_forbidden_render_options\n\n          def doorkeeper_forbidden_render_options(*)\n            { respond_not_found_when_forbidden: true, plain: \"Not Found\" }\n          end\n        end\n      end\n\n      it \"overrides the default status code\" do\n        get :index, params: { access_token: token_string }\n        expect(response.status).to eq 404\n      end\n    end\n  end\n\n  context \"when handle_auth_errors option is set to :raise\" do\n    subject(:request) { get :index, params: { access_token: token_string } }\n\n    before do\n      config_is_set(:handle_auth_errors, :raise)\n    end\n\n    controller do\n      before_action :doorkeeper_authorize!\n      include ControllerActions\n    end\n\n    context \"when token is unknown\" do\n      it \"raises Doorkeeper::Errors::TokenUnknown exception\", token: :invalid do\n        expect { request }.to raise_error(Doorkeeper::Errors::TokenUnknown)\n      end\n    end\n\n    context \"when token is expired\" do\n      it \"raises Doorkeeper::Errors::TokenExpired exception\", token: :expired do\n        expect { request }.to raise_error(Doorkeeper::Errors::TokenExpired)\n      end\n    end\n\n    context \"when token is revoked\" do\n      it \"raises Doorkeeper::Errors::TokenRevoked exception\", token: :revoked do\n        expect { request }.to raise_error(Doorkeeper::Errors::TokenRevoked)\n      end\n    end\n\n    context \"when token is forbidden\" do\n      it \"raises Doorkeeper::Errors::TokenForbidden exception\", token: :forbidden do\n        expect { request }.to raise_error(Doorkeeper::Errors::TokenForbidden)\n      end\n    end\n\n    context \"when token is valid\" do\n      it \"allows into index action\", token: :valid do\n        expect(response).to be_successful\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/token_info_controller_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::TokenInfoController, type: :controller do\n  render_views\n\n  describe \"when requesting token info with valid token\" do\n    let(:doorkeeper_token) { FactoryBot.create(:access_token) }\n\n    describe \"successful request\" do\n      it \"responds with token info\" do\n        get :show, params: { access_token: doorkeeper_token.token }\n\n        expect(response.body).to eq(doorkeeper_token.to_json)\n      end\n\n      it \"responds with a 200 status\" do\n        get :show, params: { access_token: doorkeeper_token.token }\n\n        expect(response.status).to eq 200\n      end\n    end\n\n    describe \"invalid token response\" do\n      it \"responds with 401 when doorkeeper_token is not valid\" do\n        get :show\n\n        expect(response.status).to eq 401\n        expect(response.headers[\"WWW-Authenticate\"]).to match(/^Bearer/)\n      end\n\n      it \"responds with 401 when doorkeeper_token is invalid, expired or revoked\" do\n        allow(controller).to receive(:doorkeeper_token).and_return(doorkeeper_token)\n        allow(doorkeeper_token).to receive(:accessible?).and_return(false)\n\n        get :show\n\n        expect(response.status).to eq 401\n        expect(response.headers[\"WWW-Authenticate\"]).to match(/^Bearer/)\n      end\n\n      it \"responds body message for error\" do\n        get :show\n\n        expect(response.body).to eq(\n          Doorkeeper::OAuth::InvalidTokenResponse.new.body.to_json,\n        )\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/controllers/tokens_controller_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::TokensController, type: :controller do\n  render_views\n\n  subject(:json) { JSON.parse(response.body) }\n\n  let(:client) { FactoryBot.create :application }\n  let!(:user)  { User.create!(name: \"Joe\", password: \"sekret\") }\n\n  before do\n    Doorkeeper.configure do\n      orm DOORKEEPER_ORM\n      resource_owner_from_credentials do\n        User.first\n      end\n    end\n\n    allow(Doorkeeper.configuration).to receive(:grant_flows).and_return([\"password\"])\n  end\n\n  describe \"POST #create\" do\n    before do\n      post :create, params: {\n        client_id: client.uid,\n        client_secret: client.secret,\n        grant_type: \"password\",\n      }\n    end\n\n    it \"responds after authorization\" do\n      expect(response).to be_successful\n    end\n\n    it \"includes access token in response\" do\n      expect(json[\"access_token\"]).to eq(Doorkeeper::AccessToken.first.token)\n    end\n\n    it \"includes token type in response\" do\n      expect(json[\"token_type\"]).to eq(\"Bearer\")\n    end\n\n    it \"includes token expiration in response\" do\n      expect(json[\"expires_in\"].to_i).to eq(Doorkeeper.configuration.access_token_expires_in)\n    end\n\n    it \"issues the token for the current client\" do\n      expect(Doorkeeper::AccessToken.first.application_id).to eq(client.id)\n    end\n\n    it \"issues the token for the current resource owner\" do\n      expect(Doorkeeper::AccessToken.first.resource_owner_id).to eq(user.id)\n    end\n  end\n\n  describe \"POST #create with errors\" do\n    let(:grant_type) { \"password\" }\n\n    before do\n      post :create, params: {\n        client_id: client.uid,\n        client_secret: \"invalid\",\n        grant_type:,\n      }\n    end\n\n    it \"responds after authorization\" do\n      expect(response).to be_unauthorized\n    end\n\n    it \"include error in response\" do\n      expect(json[\"error\"]).to eq(\"invalid_client\")\n    end\n\n    it \"include error_description in response\" do\n      expect(json[\"error_description\"]).to be_present\n    end\n\n    it \"does not include access token in response\" do\n      expect(json[\"access_token\"]).to be_nil\n    end\n\n    it \"does not include token type in response\" do\n      expect(json[\"token_type\"]).to be_nil\n    end\n\n    it \"does not include token expiration in response\" do\n      expect(json[\"expires_in\"]).to be_nil\n    end\n\n    it \"does not issue any access token\" do\n      expect(Doorkeeper::AccessToken.all).to be_empty\n    end\n\n    context \"when controller is extended\" do\n      controller(Doorkeeper::TokensController) do\n        def create\n          headers.merge!(\"Custom-Header\" => authorize_response.headers)\n\n          super\n        end\n      end\n\n      let(:grant_type) { \"refresh_token\" }\n\n      it \"still handles errors\" do\n        expect(response).to be_bad_request\n      end\n    end\n  end\n\n  describe \"POST #create with callbacks\" do\n    after do\n      client.update_attribute :redirect_uri, \"urn:ietf:wg:oauth:2.0:oob\"\n    end\n\n    describe \"when successful\" do\n      after do\n        post :create, params: {\n          client_id: client.uid,\n          client_secret: client.secret,\n          grant_type: \"password\",\n        }\n      end\n\n      it \"calls :before_successful_authorization callback\" do\n        expect(Doorkeeper.configuration)\n          .to receive_message_chain(:before_successful_authorization, :call).with(instance_of(described_class), nil)\n      end\n\n      it \"calls :after_successful_authorization callback\" do\n        expect(Doorkeeper.configuration)\n          .to receive_message_chain(:after_successful_authorization, :call)\n          .with(instance_of(described_class), instance_of(Doorkeeper::OAuth::Hooks::Context))\n      end\n    end\n\n    describe \"with errors\" do\n      after do\n        post :create, params: {\n          client_id: client.uid,\n          client_secret: \"invalid\",\n          grant_type: \"password\",\n        }\n      end\n\n      it \"calls :before_successful_authorization callback\" do\n        expect(Doorkeeper.configuration)\n          .to receive_message_chain(:before_successful_authorization, :call).with(instance_of(described_class), nil)\n      end\n\n      it \"does not call :after_successful_authorization callback\" do\n        expect(Doorkeeper.configuration).not_to receive(:after_successful_authorization)\n      end\n    end\n  end\n\n  describe \"POST #create with custom error\" do\n    it \"returns the error response with a custom message\" do\n      # I18n looks for `doorkeeper.errors.messages.custom_message` in locale files\n      custom_message = \"my_message\"\n      allow(I18n).to receive(:translate)\n        .with(\n          custom_message,\n          hash_including(scope: %i[doorkeeper errors messages]),\n        )\n        .and_return(\"Authorization custom message\")\n\n      doorkeeper_error = Doorkeeper::Errors::DoorkeeperError.new(custom_message)\n\n      strategy = double(:strategy)\n      request = double(token_request: strategy)\n      allow(strategy).to receive(:authorize).and_raise(doorkeeper_error)\n      allow(controller).to receive(:server).and_return(request)\n\n      post :create\n\n      expected_response_body = {\n        \"error\" => custom_message,\n        \"error_description\" => \"Authorization custom message\",\n      }\n      expect(response.status).to eq 400\n      expect(response.headers[\"WWW-Authenticate\"]).to match(/Bearer/)\n      expect(JSON.parse(response.body)).to eq expected_response_body\n    end\n  end\n\n  # https://datatracker.ietf.org/doc/html/rfc7009#section-2.2\n  describe \"POST #revoke\" do\n    let(:client) { FactoryBot.create(:application) }\n    let(:revoked_at) { nil }\n    let(:access_token) do\n      FactoryBot.create(\n        :access_token,\n        application: client,\n        revoked_at: revoked_at,\n        use_refresh_token: true\n      )\n    end\n\n    context \"when associated app is public\" do\n      let(:client) { FactoryBot.create(:application, confidential: false) }\n\n      it \"returns 200\" do\n        post :revoke, params: { client_id: client.uid, token: access_token.token }\n\n        expect(response.status).to eq 200\n      end\n\n      it \"does not revoke the access token when token_type_hint == refresh_token\" do\n        post :revoke, params: {\n          client_id: client.uid,\n          token: access_token.token,\n          token_type_hint: \"refresh_token\",\n        }\n\n        expect(response.status).to eq 200\n\n        expect(access_token.reload).to have_attributes(revoked?: false)\n      end\n\n      it \"revokes the refresh token when token_type_hint == refresh_token\" do\n        post :revoke, params: {\n          client_id: client.uid,\n          token: access_token.refresh_token,\n          token_type_hint: \"refresh_token\",\n        }\n\n        expect(response.status).to eq 200\n\n        expect(access_token.reload).to have_attributes(revoked?: true)\n      end\n\n      it \"revokes the refresh token when token_type_hint not passed\" do\n        post :revoke, params: {\n          client_id: client.uid,\n          token: access_token.refresh_token,\n        }\n\n        expect(response.status).to eq 200\n\n        expect(access_token.reload).to have_attributes(revoked?: true)\n      end\n\n      context \"when access_token has already been revoked\" do\n        let(:revoked_at) { 1.day.ago.floor }\n\n        it \"does not update the revoked_at when the access token has already been revoked\" do\n          post :revoke, params: {\n            client_id: client.uid,\n            token: access_token.token,\n          }\n\n          expect(response.status).to eq 200\n\n          expect(access_token.reload).to have_attributes(revoked_at: revoked_at)\n        end\n\n        it \"does not update the revoked_at when the refresh token has already been revoked\" do\n          post :revoke, params: {\n            client_id: client.uid,\n            token: access_token.refresh_token,\n          }\n\n          expect(response.status).to eq 200\n\n          expect(access_token.reload).to have_attributes(revoked_at: revoked_at)\n        end\n      end\n\n      it \"does not revoke when the access token has expired\" do\n        access_token.update!(created_at: access_token.created_at - access_token.expires_in - 1)\n\n        post :revoke, params: {\n          client_id: client.uid,\n          token: access_token.token,\n        }\n\n        expect(response.status).to eq 200\n\n        expect(access_token.reload).to have_attributes(revoked?: false)\n      end\n\n      it \"revokes the refresh token after the access token has expired\" do\n        access_token.update!(created_at: access_token.created_at - access_token.expires_in - 1)\n\n        post :revoke, params: {\n          client_id: client.uid,\n          token: access_token.refresh_token,\n        }\n\n        expect(response.status).to eq 200\n\n        expect(access_token.reload).to have_attributes(revoked?: true)\n      end\n    end\n\n    context \"when associated app is confidential\" do\n      let(:client) { FactoryBot.create(:application, confidential: true) }\n      let(:oauth_client) { Doorkeeper::OAuth::Client.new(client) }\n      let(:server) { instance_double(Doorkeeper::Server) }\n\n      before do\n        allow(Doorkeeper::Server).to receive(:new).and_return(server)\n        allow(server).to receive(:client).and_return(oauth_client)\n      end\n\n      it \"returns 200\" do\n        post :revoke, params: { token: access_token.token }\n\n        expect(response.status).to eq 200\n      end\n\n      it \"revokes the access token\" do\n        post :revoke, params: { token: access_token.token }\n\n        expect(access_token.reload).to have_attributes(revoked?: true)\n      end\n\n      context \"when authorization fails\" do\n        let(:some_other_client) { FactoryBot.create(:application, confidential: true) }\n        let(:oauth_client) { Doorkeeper::OAuth::Client.new(some_other_client) }\n\n        it \"returns 403\" do\n          post :revoke, params: { token: access_token.token }\n\n          expect(response.status).to eq 403\n        end\n\n        it \"does not revoke the access token\" do\n          post :revoke, params: { token: access_token.token }\n\n          expect(access_token.reload).to have_attributes(revoked?: false)\n        end\n      end\n    end\n  end\n\n  describe \"POST #introspect\" do\n    let(:client) { FactoryBot.create(:application) }\n    let(:access_token) { FactoryBot.create(:access_token, application: client) }\n    let(:token_for_introspection) { FactoryBot.create(:access_token, application: client) }\n\n    context \"when authorized using valid Bearer token\" do\n      it \"responds with full token introspection\" do\n        request.headers[\"Authorization\"] = \"Bearer #{access_token.token}\"\n\n        post :introspect, params: { token: token_for_introspection.token }\n\n        expect(json_response).to include(\"active\" => true)\n        expect(json_response).to include(\"client_id\", \"token_type\", \"exp\", \"iat\")\n      end\n    end\n\n    context \"when authorized using Client Credentials of the client that token is issued to\" do\n      it \"responds with full token introspection\" do\n        request.headers[\"Authorization\"] = basic_auth_header_for_client(client)\n\n        post :introspect, params: { token: token_for_introspection.token }\n\n        expect(json_response).to match(\n          \"active\" => true,\n          \"client_id\" => client.uid,\n          \"token_type\" => \"Bearer\",\n          \"scope\" => nil,\n          \"exp\" => an_instance_of(Integer),\n          \"iat\" => an_instance_of(Integer),\n        )\n      end\n    end\n\n    context \"when token introspection disabled\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          allow_token_introspection false\n        end\n      end\n\n      it \"responds with invalid_token error for bearer auth\" do\n        request.headers[\"Authorization\"] = \"Bearer #{access_token.token}\"\n\n        post :introspect, params: { token: token_for_introspection.token }\n\n        response_status_should_be 401\n\n        expect(json_response).not_to include(\"active\")\n        expect(json_response).to include(\"error\" => \"invalid_token\")\n      end\n\n      it \"responds with access_denied error for basic auth\" do\n        request.headers[\"Authorization\"] = basic_auth_header_for_client(client)\n\n        post :introspect, params: { token: token_for_introspection.token }\n\n        response_status_should_be 200\n\n        expect(json_response).to include(\"active\" => false)\n      end\n    end\n\n    context \"when custom introspection response configured\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          custom_introspection_response do |_token, _context|\n            {\n              sub: \"Z5O3upPC88QrAjx00dis\",\n              aud: \"https://protected.example.net/resource\",\n            }\n          end\n        end\n      end\n\n      it \"responds with full token introspection\" do\n        request.headers[\"Authorization\"] = \"Bearer #{access_token.token}\"\n\n        post :introspect, params: { token: token_for_introspection.token }\n\n        expect(json_response).to match(\n          \"active\" => true,\n          \"client_id\" => client.uid,\n          \"token_type\" => \"Bearer\",\n          \"scope\" => nil,\n          \"exp\" => an_instance_of(Integer),\n          \"iat\" => an_instance_of(Integer),\n          \"aud\" => \"https://protected.example.net/resource\",\n          \"sub\" => \"Z5O3upPC88QrAjx00dis\",\n        )\n      end\n    end\n\n    context \"when access token is public\" do\n      let(:token_for_introspection) { FactoryBot.create(:access_token, application: nil) }\n\n      it \"responds with full token introspection\" do\n        request.headers[\"Authorization\"] = basic_auth_header_for_client(client)\n\n        post :introspect, params: { token: token_for_introspection.token }\n\n        expect(json_response).to match(\n          \"active\" => true,\n          \"client_id\" => nil,\n          \"token_type\" => \"Bearer\",\n          \"scope\" => nil,\n          \"exp\" => an_instance_of(Integer),\n          \"iat\" => an_instance_of(Integer),\n        )\n      end\n    end\n\n    context \"when token never expires (expires_in is nil)\" do\n      let(:token_for_introspection) { FactoryBot.create(:access_token, application: client, expires_in: nil) }\n\n      it \"omits the exp field per RFC 7662\" do\n        request.headers[\"Authorization\"] = basic_auth_header_for_client(client)\n\n        post :introspect, params: { token: token_for_introspection.token }\n\n        expect(json_response).to match(\n          \"active\" => true,\n          \"client_id\" => client.uid,\n          \"token_type\" => \"Bearer\",\n          \"scope\" => nil,\n          \"iat\" => an_instance_of(Integer),\n        )\n        expect(json_response).not_to have_key(\"exp\")\n      end\n    end\n\n    context \"when token was issued to a different client than is making this request\" do\n      let(:different_client) { FactoryBot.create(:application) }\n\n      it \"responds with only active state\" do\n        request.headers[\"Authorization\"] = basic_auth_header_for_client(different_client)\n\n        post :introspect, params: { token: token_for_introspection.token }\n\n        expect(response).to be_successful\n\n        expect(json_response).to match(\"active\" => false)\n      end\n    end\n\n    context \"when introspection request authorized by a client and allow_token_introspection is true\" do\n      let(:different_client) { FactoryBot.create(:application) }\n\n      before do\n        allow(Doorkeeper.configuration).to receive(:allow_token_introspection).and_return(proc do\n          true\n        end)\n      end\n\n      it \"responds with full token introspection\" do\n        request.headers[\"Authorization\"] = basic_auth_header_for_client(different_client)\n\n        post :introspect, params: { token: token_for_introspection.token }\n\n        expect(json_response).to match(\n          \"active\" => true,\n          \"client_id\" => client.uid,\n          \"token_type\" => \"Bearer\",\n          \"scope\" => nil,\n          \"exp\" => an_instance_of(Integer),\n          \"iat\" => an_instance_of(Integer),\n        )\n      end\n    end\n\n    context \"when allow_token_introspection requires authorized token with special scope\" do\n      let(:access_token) { FactoryBot.create(:access_token, scopes: \"introspection\") }\n\n      before do\n        allow(Doorkeeper.configuration).to receive(:allow_token_introspection).and_return(proc do |_token, _client, authorized_token|\n          authorized_token.scopes.include?(\"introspection\")\n        end)\n      end\n\n      it \"responds with full token introspection if authorized token has introspection scope\" do\n        request.headers[\"Authorization\"] = \"Bearer #{access_token.token}\"\n\n        post :introspect, params: { token: token_for_introspection.token }\n\n        expect(json_response).to match(\n          \"active\" => true,\n          \"client_id\" => client.uid,\n          \"token_type\" => \"Bearer\",\n          \"scope\" => nil,\n          \"exp\" => an_instance_of(Integer),\n          \"iat\" => an_instance_of(Integer),\n        )\n      end\n\n      it \"responds with invalid_token error if authorized token doesn't have introspection scope\" do\n        access_token.update(scopes: \"read write\")\n\n        request.headers[\"Authorization\"] = \"Bearer #{access_token.token}\"\n\n        post :introspect, params: { token: token_for_introspection.token }\n\n        response_status_should_be 401\n\n        expect(json_response).to match(\n          \"error\" => \"invalid_token\",\n          \"error_description\" => an_instance_of(String),\n          \"state\" => \"unauthorized\",\n        )\n      end\n    end\n\n    context \"when authorized using invalid Bearer token\" do\n      let(:access_token) do\n        FactoryBot.create(:access_token, application: client, revoked_at: 1.day.ago)\n      end\n\n      it \"responds with invalid_token error\" do\n        request.headers[\"Authorization\"] = \"Bearer #{access_token.token}\"\n\n        post :introspect, params: { token: token_for_introspection.token }\n\n        response_status_should_be 401\n\n        expect(json_response).to match(\n          \"error\" => \"invalid_token\",\n          \"error_description\" => an_instance_of(String),\n          \"state\" => \"unauthorized\",\n        )\n      end\n    end\n\n    context \"when authorized using the Bearer token that need to be introspected\" do\n      it \"responds with invalid token error\" do\n        request.headers[\"Authorization\"] = \"Bearer #{access_token.token}\"\n\n        post :introspect, params: { token: access_token.token }\n\n        response_status_should_be 401\n\n        expect(json_response).to match(\n          \"error\" => \"invalid_token\",\n          \"error_description\" => an_instance_of(String),\n          \"state\" => \"unauthorized\",\n        )\n      end\n    end\n\n    context \"when invalid credentials used to authorize\" do\n      let(:client) { double(uid: \"123123\", secret: \"666999\") }\n      let(:access_token) { FactoryBot.create(:access_token) }\n\n      it \"responds with invalid_client error\" do\n        request.headers[\"Authorization\"] = basic_auth_header_for_client(client)\n\n        post :introspect, params: { token: access_token.token }\n\n        expect(response).not_to be_successful\n        response_status_should_be 401\n\n        expect(json_response).to match(\n          \"error\" => \"invalid_client\",\n          \"error_description\" => an_instance_of(String),\n        )\n      end\n    end\n\n    context \"when wrong token value used\" do\n      context \"when authorized using client credentials\" do\n        it \"responds with only active state\" do\n          request.headers[\"Authorization\"] = basic_auth_header_for_client(client)\n\n          post :introspect, params: { token: SecureRandom.hex(16) }\n\n          expect(json_response).to match(\"active\" => false)\n        end\n      end\n\n      context \"when authorized using valid Bearer token\" do\n        it \"responds with invalid_token error\" do\n          request.headers[\"Authorization\"] = \"Bearer #{access_token.token}\"\n\n          post :introspect, params: { token: SecureRandom.hex(16) }\n\n          response_status_should_be 401\n\n          expect(json_response).to match(\n            \"error\" => \"invalid_token\",\n            \"error_description\" => an_instance_of(String),\n            \"state\" => \"unauthorized\",\n          )\n        end\n      end\n    end\n\n    context \"when requested access token expired\" do\n      let(:token_for_introspection) do\n        FactoryBot.create(:access_token, application: client, created_at: 1.year.ago)\n      end\n\n      it \"responds with only active state\" do\n        request.headers[\"Authorization\"] = basic_auth_header_for_client(client)\n\n        post :introspect, params: { token: token_for_introspection.token }\n\n        expect(json_response).to match(\"active\" => false)\n      end\n    end\n\n    context \"when requested Access Token revoked\" do\n      let(:token_for_introspection) do\n        FactoryBot.create(:access_token, application: client, revoked_at: 1.year.ago)\n      end\n\n      it \"responds with only active state\" do\n        request.headers[\"Authorization\"] = basic_auth_header_for_client(client)\n\n        post :introspect, params: { token: token_for_introspection.token }\n\n        expect(json_response).to match(\"active\" => false)\n      end\n    end\n\n    context \"when unauthorized (no bearer token or client credentials)\" do\n      let(:token_for_introspection) { FactoryBot.create(:access_token) }\n\n      it \"responds with invalid_request error\" do\n        post :introspect, params: { token: token_for_introspection.token }\n\n        expect(response).not_to be_successful\n        response_status_should_be 400\n\n        expect(json_response).to match(\n          \"error\" => \"invalid_request\",\n          \"error_description\" => I18n.t(\"doorkeeper.errors.messages.invalid_request.request_not_authorized\"),\n        )\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/doorkeeper/redirect_uri_validator_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::RedirectUriValidator do\n  subject(:client) do\n    FactoryBot.create(:application)\n  end\n\n  it \"is valid when the uri is a uri\" do\n    client.redirect_uri = \"https://example.com/callback\"\n    expect(client).to be_valid\n  end\n\n  # Most mobile and desktop operating systems allow apps to register a custom URL\n  # scheme that will launch the app when a URL with that scheme is visited from\n  # the system browser.\n  #\n  # @see https://www.oauth.com/oauth2-servers/redirect-uris/redirect-uris-native-apps/\n  it \"is valid when the uri is custom native URI\" do\n    client.redirect_uri = \"myapp:/callback\"\n    expect(client).to be_valid\n  end\n\n  it \"is valid when the uri has a query parameter\" do\n    client.redirect_uri = \"https://example.com/abcd?xyz=123\"\n    expect(client).to be_valid\n  end\n\n  it \"accepts nonstandard oob redirect uri\" do\n    client.redirect_uri = \"urn:ietf:wg:oauth:2.0:oob\"\n    expect(client).to be_valid\n  end\n\n  it \"accepts nonstandard oob:auto redirect uri\" do\n    client.redirect_uri = \"urn:ietf:wg:oauth:2.0:oob:auto\"\n    expect(client).to be_valid\n  end\n\n  it \"is invalid when the uri is not a uri\" do\n    client.redirect_uri = \"]\"\n    expect(client).not_to be_valid\n    expect(client.errors[:redirect_uri].first).to eq(I18n.t(\"activerecord.errors.models.doorkeeper/application.attributes.redirect_uri.invalid_uri\"))\n  end\n\n  it \"is invalid when the uri is relative\" do\n    client.redirect_uri = \"/abcd\"\n    expect(client).not_to be_valid\n    expect(client.errors[:redirect_uri].first).to eq(I18n.t(\"activerecord.errors.models.doorkeeper/application.attributes.redirect_uri.relative_uri\"))\n  end\n\n  it \"is invalid when the uri has a fragment\" do\n    client.redirect_uri = \"https://example.com/abcd#xyz\"\n    expect(client).not_to be_valid\n    expect(client.errors[:redirect_uri].first).to eq(I18n.t(\"activerecord.errors.models.doorkeeper/application.attributes.redirect_uri.fragment_present\"))\n  end\n\n  it \"is invalid when scheme resolves to localhost (needs an explict scheme)\" do\n    client.redirect_uri = \"localhost:80\"\n    expect(client).to be_invalid\n    expect(client.errors[:redirect_uri].first).to eq(I18n.t(\"activerecord.errors.models.doorkeeper/application.attributes.redirect_uri.unspecified_scheme\"))\n  end\n\n  it \"is invalid if an ip address\" do\n    client.redirect_uri = \"127.0.0.1:8080\"\n    expect(client).to be_invalid\n  end\n\n  it \"accepts an ip address based URI if a scheme is specified\" do\n    client.redirect_uri = \"https://127.0.0.1:8080\"\n    expect(client).to be_valid\n  end\n\n  it \"is invalid when host is not specified\" do\n    client.redirect_uri = \"https://\"\n    expect(client).to be_invalid\n    expect(client.errors[:redirect_uri].first).to eq(I18n.t(\"activerecord.errors.models.doorkeeper/application.attributes.redirect_uri.invalid_uri\"))\n  end\n\n  context \"when force secured uri configured\" do\n    it \"accepts a valid uri\" do\n      client.redirect_uri = \"https://example.com/callback\"\n      expect(client).to be_valid\n    end\n\n    it \"accepts custom scheme redirect uri (as per rfc8252 section 7.1)\" do\n      client.redirect_uri = \"com.example.app:/oauth/callback\"\n      expect(client).to be_valid\n    end\n\n    it \"accepts custom scheme redirect uri (as per rfc8252 section 7.1) #2\" do\n      client.redirect_uri = \"com.example.app:/test\"\n      expect(client).to be_valid\n    end\n\n    it \"accepts custom scheme redirect uri (common misconfiguration we have decided to allow)\" do\n      client.redirect_uri = \"com.example.app://oauth/callback\"\n      expect(client).to be_valid\n    end\n\n    it \"accepts custom scheme redirect uri (common misconfiguration we have decided to allow) #2\" do\n      client.redirect_uri = \"com.example.app://test\"\n      expect(client).to be_valid\n    end\n\n    it \"accepts a non secured protocol when disabled\" do\n      client.redirect_uri = \"http://example.com/callback\"\n      allow(Doorkeeper.configuration).to receive(\n        :force_ssl_in_redirect_uri,\n      ).and_return(false)\n      expect(client).to be_valid\n    end\n\n    it \"accepts a non secured protocol when conditional option defined\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        force_ssl_in_redirect_uri { |uri| uri.host != \"localhost\" }\n      end\n\n      application = FactoryBot.build(:application, redirect_uri: \"http://localhost/callback\")\n      expect(application).to be_valid\n\n      application = FactoryBot.build(:application, redirect_uri: \"https://test.com/callback\")\n      expect(application).to be_valid\n\n      application = FactoryBot.build(:application, redirect_uri: \"http://localhost2/callback\")\n      expect(application).not_to be_valid\n\n      application = FactoryBot.build(:application, redirect_uri: \"https://test.com/callback\")\n      expect(application).to be_valid\n    end\n\n    it \"forbids redirect uri if required\" do\n      client.redirect_uri = \"javascript://document.cookie\"\n\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        forbid_redirect_uri { |uri| uri.scheme == \"javascript\" }\n      end\n\n      expect(client).to be_invalid\n      expect(client.errors[:redirect_uri].first).to eq(\"is forbidden by the server.\")\n\n      client.redirect_uri = \"https://localhost/callback\"\n      expect(client).to be_valid\n    end\n\n    it \"invalidates the uri when the uri does not use a secure protocol\" do\n      client.redirect_uri = \"http://example.com/callback\"\n      expect(client).not_to be_valid\n      error = client.errors[:redirect_uri].first\n      expect(error).to eq(I18n.t(\"activerecord.errors.models.doorkeeper/application.attributes.redirect_uri.secured_uri\"))\n    end\n  end\n\n  context \"with multiple redirect uri\" do\n    it \"invalidates the second uri when the first uri is native uri\" do\n      client.redirect_uri = \"urn:ietf:wg:oauth:2.0:oob\\nexample.com/callback\"\n      expect(client).to be_invalid\n    end\n  end\n\n  context \"with blank redirect URI\" do\n    it \"forbids blank redirect uri by default\" do\n      client.redirect_uri = \"\"\n\n      expect(client).to be_invalid\n      expect(client.errors[:redirect_uri]).not_to be_blank\n    end\n\n    it \"forbids blank redirect uri by custom condition\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        allow_blank_redirect_uri do |_grant_flows, application|\n          application.name == \"admin app\"\n        end\n      end\n\n      client.name = \"test app\"\n      client.redirect_uri = \"\"\n\n      expect(client).to be_invalid\n      expect(client.errors[:redirect_uri]).not_to be_blank\n\n      client.name = \"admin app\"\n      expect(client).to be_valid\n    end\n  end\nend\n"
  },
  {
    "path": "spec/doorkeeper/server_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::Server do\n  subject(:server) do\n    described_class.new(context)\n  end\n\n  let(:fake_class) { double :fake_class }\n  let(:context) { double :context }\n\n  describe \".authorization_request\" do\n    it \"raises error when strategy does not match phase\" do\n      expect do\n        server.token_request(:code)\n      end.to raise_error(Doorkeeper::Errors::InvalidTokenStrategy)\n    end\n\n    context \"when only Authorization Code strategy is enabled\" do\n      before do\n        allow(Doorkeeper.configuration)\n          .to receive(:grant_flows)\n          .and_return([\"authorization_code\"])\n      end\n\n      it \"raises error when using the disabled Client Credentials strategy\" do\n        expect do\n          server.token_request(:client_credentials)\n        end.to raise_error(Doorkeeper::Errors::InvalidTokenStrategy)\n      end\n    end\n\n    it \"builds the request with selected strategy\" do\n      stub_const \"Doorkeeper::Request::Code\", fake_class\n      expect(fake_class).to receive(:new).with(server)\n      expect(::Kernel).to receive(:warn)\n      server.authorization_request :code\n    end\n\n    it \"builds the request with composite strategy name\" do\n      Doorkeeper.configure do\n        grant_flows [\"id_token token\"]\n      end\n\n      stub_const \"Doorkeeper::Request::IdTokenToken\", fake_class\n      expect(fake_class).to receive(:new).with(server)\n      expect(::Kernel).to receive(:warn)\n      server.authorization_request \"id_token token\"\n    end\n  end\nend\n"
  },
  {
    "path": "spec/doorkeeper/stale_records_cleaner_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::StaleRecordsCleaner do\n  let(:cleaner) { described_class.new(model) }\n  let(:models_by_name) do\n    {\n      access_token: Doorkeeper::AccessToken,\n      access_grant: Doorkeeper::AccessGrant,\n    }\n  end\n  let(:resource_owner) { FactoryBot.create(:resource_owner) }\n\n  context \"when ORM has no cleaner class\" do\n    it \"raises an error\" do\n      allow(Doorkeeper.configuration).to receive(:orm).and_return(\"hibernate\")\n\n      expect do\n        described_class.for(Doorkeeper::AccessToken)\n      end.to raise_error(Doorkeeper::Errors::NoOrmCleaner, /has no cleaner/)\n    end\n  end\n\n  %i[access_token access_grant].each do |model_name|\n    context \"(#{model_name})\" do\n      let(:model) { models_by_name.fetch(model_name) }\n\n      describe \"#clean_revoked\" do\n        context \"with revoked record\" do\n          before do\n            FactoryBot.create model_name,\n                              revoked_at: Time.current - 1.minute,\n                              resource_owner_id: resource_owner.id,\n                              resource_owner_type: resource_owner.class.name\n          end\n\n          it \"removes the record\" do\n            expect { cleaner.clean_revoked }.to change(model, :count).to(0)\n          end\n        end\n\n        context \"with record revoked in the future\" do\n          before do\n            FactoryBot.create model_name, revoked_at: Time.current + 1.minute,\n                                          resource_owner_id: resource_owner.id,\n                                          resource_owner_type: resource_owner.class.name\n          end\n\n          it \"keeps the record\" do\n            expect { cleaner.clean_revoked }.not_to(change(model, :count))\n          end\n        end\n\n        context \"with unrevoked record\" do\n          before do\n            FactoryBot.create model_name, revoked_at: nil,\n                                          resource_owner_id: resource_owner.id,\n                                          resource_owner_type: resource_owner.class.name\n          end\n\n          it \"keeps the record\" do\n            expect { cleaner.clean_revoked }.not_to(change(model, :count))\n          end\n        end\n      end\n\n      describe \"#clean_expired\" do\n        let(:ttl) { 500 }\n        let(:expiry_border) { ttl.seconds.ago }\n\n        context \"with record that is past the threshold and expired\" do\n          before do\n            FactoryBot.create model_name, created_at: expiry_border - 1.minute,\n                                          expires_in: ttl,\n                                          resource_owner_id: resource_owner.id,\n                                          resource_owner_type: resource_owner.class.name\n          end\n\n          it \"removes the record\" do\n            expect { cleaner.clean_expired(ttl) }.to change(model, :count).to(0)\n          end\n        end\n\n        context \"with record that is past the threshold, but not expired\" do\n          before do\n            FactoryBot.create model_name,\n                              created_at: expiry_border - 1.minute,\n                              expires_in: 2 * ttl,\n                              resource_owner_id: resource_owner.id,\n                              resource_owner_type: resource_owner.class.name\n          end\n\n          it \"keeps the record\" do\n            expect { cleaner.clean_expired(ttl) }.not_to(change(model, :count))\n          end\n        end\n\n        context \"with record that is within the threshold and expired\" do\n          before do\n            FactoryBot.create model_name, created_at: expiry_border + 1.minute,\n                                          expires_in: ttl,\n                                          resource_owner_id: resource_owner.id,\n                                          resource_owner_type: resource_owner.class.name\n          end\n\n          it \"keeps the record\" do\n            expect { cleaner.clean_expired(ttl) }.not_to(change(model, :count))\n          end\n        end\n\n        context \"with record that is within the threshold, but not expired\" do\n          before do\n            FactoryBot.create model_name, created_at: expiry_border + 1.minute,\n                                          expires_in: 2 * ttl,\n                                          resource_owner_id: resource_owner.id,\n                                          resource_owner_type: resource_owner.class.name\n          end\n\n          it \"keeps the record\" do\n            expect { cleaner.clean_expired(ttl) }.not_to(change(model, :count))\n          end\n        end\n\n        context \"when the model uses an unsupported database adapter\" do\n          before do\n            allow(model).to receive(:adapter_name).and_return(\"unsupported_db\")\n          end\n\n          it \"emits a warning\" do\n            expect(Kernel).to receive(:warn).with(/\\[DOORKEEPER\\].*doesn't support expiration time math for your database adapter/)\n            cleaner.clean_expired(ttl)\n          end\n        end\n\n        if model_name == :access_token\n          context \"with record that is past the threshold, but never expires\" do\n            before do\n              FactoryBot.create model_name, created_at: expiry_border - 1.minute,\n                                            expires_in: nil,\n                                            resource_owner_id: resource_owner.id,\n                                            resource_owner_type: resource_owner.class.name\n            end\n\n            it \"keeps the record\" do\n              expect { cleaner.clean_expired(ttl) }.not_to(change(model, :count))\n            end\n          end\n\n          context \"with record that is within the threshold, but never expires\" do\n            before do\n              FactoryBot.create model_name, created_at: expiry_border + 1.minute,\n                                            expires_in: nil,\n                                            resource_owner_id: resource_owner.id,\n                                            resource_owner_type: resource_owner.class.name\n            end\n\n            it \"keeps the record\" do\n              expect { cleaner.clean_expired(ttl) }.not_to(change(model, :count))\n            end\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/doorkeeper/version_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::VERSION do\n  describe \"#gem_version\" do\n    it \"returns Gem::Version instance\" do\n      expect(Doorkeeper.gem_version).to be_an_instance_of(Gem::Version)\n    end\n  end\n\n  describe \"VERSION\" do\n    it \"returns gem version string\" do\n      expect(Doorkeeper::VERSION::STRING).to match(/^\\d+\\.\\d+\\.\\d+(\\.\\w+)?$/)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/dummy/Rakefile",
    "content": "#!/usr/bin/env rake\n# frozen_string_literal: true\n\n# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.\n\nrequire File.expand_path(\"config/application\", __dir__)\n\nDummy::Application.load_tasks\n"
  },
  {
    "path": "spec/dummy/app/assets/config/manifest.js",
    "content": "// JS and CSS bundles\n//\n"
  },
  {
    "path": "spec/dummy/app/controllers/application_controller.rb",
    "content": "# frozen_string_literal: true\n\nclass ApplicationController < ActionController::Base\n  protect_from_forgery with: :exception\nend\n"
  },
  {
    "path": "spec/dummy/app/controllers/custom_authorizations_controller.rb",
    "content": "# frozen_string_literal: true\n\nclass CustomAuthorizationsController < ::ApplicationController\n  %w[index show new create edit update destroy].each do |action|\n    define_method action do\n      render nothing: true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/dummy/app/controllers/full_protected_resources_controller.rb",
    "content": "# frozen_string_literal: true\n\nclass FullProtectedResourcesController < ApplicationController\n  before_action -> { doorkeeper_authorize! :write, :admin }, only: :show\n  before_action :doorkeeper_authorize!, only: :index\n\n  def index\n    render plain: \"index\"\n  end\n\n  def show\n    render plain: \"show\"\n  end\nend\n"
  },
  {
    "path": "spec/dummy/app/controllers/home_controller.rb",
    "content": "# frozen_string_literal: true\n\nclass HomeController < ApplicationController\n  def index; end\n\n  def sign_in\n    session[:user_id] = if Rails.env.development?\n                          User.first || User.create!(name: \"Joe\", password: \"sekret\")\n                        else\n                          User.first\n                        end\n    redirect_to \"/\"\n  end\n\n  def callback\n    render plain: \"ok\"\n  end\nend\n"
  },
  {
    "path": "spec/dummy/app/controllers/metal_controller.rb",
    "content": "# frozen_string_literal: true\n\nclass MetalController < ActionController::Metal\n  include AbstractController::Callbacks\n  include ActionController::Head\n  include Doorkeeper::Rails::Helpers\n\n  before_action :doorkeeper_authorize!\n\n  def index\n    self.response_body = { ok: true }.to_json\n  end\nend\n"
  },
  {
    "path": "spec/dummy/app/controllers/semi_protected_resources_controller.rb",
    "content": "# frozen_string_literal: true\n\nclass SemiProtectedResourcesController < ApplicationController\n  before_action :doorkeeper_authorize!, only: :index\n\n  def index\n    render plain: \"protected index\"\n  end\n\n  def show\n    render plain: \"non protected show\"\n  end\nend\n"
  },
  {
    "path": "spec/dummy/app/helpers/application_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule ApplicationHelper\n  def current_user\n    @current_user ||= User.find_by(id: session[:user_id])\n  end\nend\n"
  },
  {
    "path": "spec/dummy/app/models/user.rb",
    "content": "# frozen_string_literal: true\n\nclass ApplicationRecord < ::ActiveRecord::Base\n  self.abstract_class = true\nend\n\nclass User < ApplicationRecord\n  def self.authenticate!(name, password)\n    User.where(name: name, password: password).first\n  end\nend\n"
  },
  {
    "path": "spec/dummy/app/views/home/index.html.erb",
    "content": ""
  },
  {
    "path": "spec/dummy/app/views/layouts/application.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>Dummy</title>\n  <%= csrf_meta_tags %>\n</head>\n<body>\n\n<%= link_to \"Sign in\", '/sign_in' %>\n\n<%= yield %>\n\n</body>\n</html>\n"
  },
  {
    "path": "spec/dummy/config/application.rb",
    "content": "require File.expand_path(\"boot\", __dir__)\n\nrequire \"rails\"\n\n%w[\n  action_controller/railtie\n  action_view/railtie\n  action_cable/engine\n  sprockets/railtie\n].each do |railtie|\n  begin\n    require railtie\n  rescue LoadError => e\n    puts \"Error loading '#{railtie}' (#{e.message})\"\n  end\nend\n\nBundler.require(*Rails.groups)\n\nrequire \"yaml\"\n\norm = if DOORKEEPER_ORM =~ /mongoid/\n        Mongoid.load!(File.join(File.dirname(File.expand_path(__FILE__)), \"#{DOORKEEPER_ORM}.yml\"))\n        :mongoid\n      else\n        DOORKEEPER_ORM\n      end\nrequire \"#{orm}/railtie\"\n\nmodule Dummy\n  class Application < Rails::Application\n    if Rails.gem_version < Gem::Version.new(\"5.1\")\n      config.action_controller.per_form_csrf_tokens = true\n      config.action_controller.forgery_protection_origin_check = true\n\n      ActiveSupport.to_time_preserves_timezone = true\n\n      if DOORKEEPER_ORM =~ /active_record/\n        config.active_record.belongs_to_required_by_default = true\n      end\n\n      config.ssl_options = { hsts: { subdomains: true } }\n    else\n      config.load_defaults \"#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}\"\n    end\n\n    # Settings in config/environments/* take precedence over those specified here.\n    # Application configuration should go into files in config/initializers\n    # -- all .rb files in that directory are automatically loaded.\n  end\nend\n"
  },
  {
    "path": "spec/dummy/config/boot.rb",
    "content": "require \"rubygems\"\nrequire \"bundler/setup\"\n\norm = ENV[\"BUNDLE_GEMFILE\"].match(/Gemfile\\.(.+)\\.rb/)\nDOORKEEPER_ORM = (orm && orm[1]) || :active_record unless defined?(DOORKEEPER_ORM)\n\n$LOAD_PATH.unshift File.expand_path(\"../../../lib\", __dir__)\n"
  },
  {
    "path": "spec/dummy/config/database.yml",
    "content": "development:\n  adapter: sqlite3\n  database: db/development.sqlite3\n  pool: 5\n  timeout: 5000\n\ntest:\n  adapter: sqlite3\n  database: \":memory:\"\n  timeout: 500\n\nproduction:\n  adapter: sqlite3\n  database: \":memory:\"\n  timeout: 500\n"
  },
  {
    "path": "spec/dummy/config/environment.rb",
    "content": "# Load the rails application\nrequire File.expand_path(\"application\", __dir__)\n\n# Initialize the rails application\nRails.application.initialize!\n"
  },
  {
    "path": "spec/dummy/config/environments/development.rb",
    "content": "# frozen_string_literal: true\n\nDummy::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb\n\n  # In the development environment your application's code is reloaded on\n  # every request.  This slows down response time but is perfect for development\n  # since you don't have to restart the web server when you make code changes.\n  config.cache_classes = false\n\n  # Show full error reports and disable caching\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n\n  # Don't care if the mailer can't send\n  # config.action_mailer.raise_delivery_errors = false\n\n  # Print deprecation notices to the Rails logger\n  config.active_support.deprecation = :log\n\n  # Only use best-standards-support built into browsers\n  config.action_dispatch.best_standards_support = :builtin\n\n  # Do not compress assets\n  config.assets.compress = false\n\n  # Expands the lines which load the assets\n  config.assets.debug = true\n\n  config.eager_load = false\nend\n"
  },
  {
    "path": "spec/dummy/config/environments/production.rb",
    "content": "# frozen_string_literal: true\n\nDummy::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb\n\n  # Code is not reloaded between requests\n  config.cache_classes = true\n\n  # Full error reports are disabled and caching is turned on\n  config.consider_all_requests_local       = false\n  config.action_controller.perform_caching = true\n\n  # Disable Rails's static asset server (Apache or nginx will already do this)\n  config.serve_static_assets = false\n\n  # Compress JavaScripts and CSS\n  config.assets.compress = true\n\n  # Don't fallback to assets pipeline if a precompiled asset is missed\n  config.assets.compile = false\n\n  # Generate digests for assets URLs\n  config.assets.digest = true\n\n  # Defaults to Rails.root.join(\"public/assets\")\n  # config.assets.manifest = YOUR_PATH\n\n  # Specifies the header that your server uses for sending files\n  # config.action_dispatch.x_sendfile_header = \"X-Sendfile\" # for apache\n  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx\n\n  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.\n  # config.force_ssl = true\n\n  # See everything in the log (default is :info)\n  # config.log_level = :debug\n\n  # Use a different logger for distributed setups\n  # config.logger = SyslogLogger.new\n\n  # Use a different cache store in production\n  # config.cache_store = :mem_cache_store\n\n  # Enable serving of images, stylesheets, and JavaScripts from an asset server\n  # config.action_controller.asset_host = \"http://assets.example.com\"\n\n  # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)\n  # config.assets.precompile += %w( search.js )\n\n  # Disable delivery errors, bad email addresses will be ignored\n  # config.action_mailer.raise_delivery_errors = false\n\n  # Enable threaded mode\n  # config.threadsafe!\n\n  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to\n  # the I18n.default_locale when a translation can not be found)\n  config.i18n.fallbacks = true\n\n  # Send deprecation notices to registered listeners\n  config.active_support.deprecation = :notify\n\n  config.eager_load = true\nend\n"
  },
  {
    "path": "spec/dummy/config/environments/test.rb",
    "content": "# frozen_string_literal: true\n\nDummy::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb\n\n  # The test environment is used exclusively to run your application's\n  # test suite.  You never need to work with it otherwise.  Remember that\n  # your test database is \"scratch space\" for the test suite and is wiped\n  # and recreated between test runs.  Don't rely on the data there!\n  config.cache_classes = true\n\n  config.assets.enabled = true\n  config.assets.version = \"1.0\"\n  config.assets.digest = false\n\n  # Do not eager load code on boot. This avoids loading your whole application\n  # just for the purpose of running a single test. If you are using a tool that\n  # preloads Rails for running tests, you may have to set it to true.\n  config.eager_load = false\n\n  # Show full error reports and disable caching\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n\n  # Raise exceptions instead of rendering exception templates\n  # Rails 7.1 deprecated false in favor of :none, but we need to use false for\n  # backwards compatibility: https://github.com/rails/rails/pull/45867\n  config.action_dispatch.show_exceptions =\n    Gem::Version.new(Rails.version) >= Gem::Version.new('7.1.0') ? :none : false\n\n  # Disable request forgery protection in test environment\n  config.action_controller.allow_forgery_protection = false\n\n  # Tell Action Mailer not to deliver emails to the real world.\n  # The :test delivery method accumulates sent emails in the\n  # ActionMailer::Base.deliveries array.\n  # config.action_mailer.delivery_method = :test\n\n  # Use SQL instead of Active Record's schema dumper when creating the test database.\n  # This is necessary if your schema can't be completely dumped by the schema dumper,\n  # like if you have constraints or database-specific column types\n  # config.active_record.schema_format = :sql\n\n  # Print deprecation notices to the stderr\n  config.active_support.deprecation = :stderr\n\n  config.eager_load = true\nend\n"
  },
  {
    "path": "spec/dummy/config/initializers/backtrace_silencers.rb",
    "content": "# frozen_string_literal: true\n\n# Be sure to restart your server when you modify this file.\n\n# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.\n# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }\n\n# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.\n# Rails.backtrace_cleaner.remove_silencers!\n"
  },
  {
    "path": "spec/dummy/config/initializers/doorkeeper.rb",
    "content": "# frozen_string_literal: true\n\nDoorkeeper.configure do\n  # Change the ORM that doorkeeper will use.\n  orm DOORKEEPER_ORM\n\n  # This block will be called to check whether the resource owner is authenticated or not.\n  resource_owner_authenticator do\n    # Put your resource owner authentication logic here.\n    User.where(id: session[:user_id]).first || redirect_to(root_url, alert: \"Needs sign in.\")\n  end\n\n  # If you didn't skip applications controller from Doorkeeper routes in your application routes.rb\n  # file then you need to declare this block in order to restrict access to the web interface for\n  # adding oauth authorized applications. In other case it will return 403 Forbidden response\n  # every time somebody will try to access the admin web interface.\n  #\n  # admin_authenticator do\n  #   # Put your admin authentication logic here.\n  #   # Example implementation:\n  #   Admin.find_by_id(session[:admin_id]) || redirect_to(new_admin_session_url)\n  # end\n\n  # Authorization Code expiration time (default 10 minutes).\n  # authorization_code_expires_in 10.minutes\n\n  # Access token expiration time (default 2 hours).\n  # If you want to disable expiration, set this to nil.\n  # access_token_expires_in 2.hours\n\n  # Reuse access token for the same resource owner within an application (disabled by default)\n  # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383\n  # reuse_access_token\n\n  # Issue access tokens with refresh token (disabled by default)\n  use_refresh_token\n\n  # Forbids creating/updating applications with arbitrary scopes that are\n  # not in configuration, i.e. `default_scopes` or `optional_scopes`.\n  # (disabled by default)\n  #\n  # enforce_configured_scopes\n\n  # Use the url path for the native authorization code flow. Enabling this flag sets the authorization\n  # code response route for native redirect uris to oauth/authorize/<code>. The default is oauth/authorize/native?code=<code>.\n  # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/1143\n  # use_url_path_for_native_authorization\n\n  # Provide support for an owner to be assigned to each registered application (disabled by default)\n  # Optional parameter confirmation: true (default false) if you want to enforce ownership of\n  # a registered application\n  # Note: you must also run the rails g doorkeeper:application_owner generator to provide the necessary support\n  # enable_application_owner confirmation: false\n\n  # Define access token scopes for your provider\n  # For more information go to\n  # https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes\n  default_scopes  :public\n  optional_scopes :write, :update\n\n  # Change the way client credentials are retrieved from the request object.\n  # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then\n  # falls back to the `:client_id` and `:client_secret` params from the `params` object.\n  # Check out the wiki for more information on customization\n  # client_credentials :from_basic, :from_params\n\n  # Change the way access token is authenticated from the request object.\n  # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then\n  # falls back to the `:access_token` or `:bearer_token` params from the `params` object.\n  # Check out the wiki for more information on customization\n  # access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param\n\n  # Forces the usage of the HTTPS protocol in non-native redirect uris (enabled\n  # by default in non-development environments). OAuth2 delegates security in\n  # communication to the HTTPS protocol so it is wise to keep this enabled.\n  #\n  # force_ssl_in_redirect_uri !Rails.env.development?\n\n  # Specify what grant flows are enabled in array of Strings. The valid\n  # strings and the flows they enable are:\n  #\n  # \"authorization_code\" => Authorization Code Grant Flow\n  # \"implicit\"           => Implicit Grant Flow\n  # \"password\"           => Resource Owner Password Credentials Grant Flow\n  # \"client_credentials\" => Client Credentials Grant Flow\n  #\n  # If not specified, Doorkeeper enables authorization_code and\n  # client_credentials.\n  #\n  # implicit and password grant flows have risks that you should understand\n  # before enabling:\n  #   https://datatracker.ietf.org/doc/html/rfc6819#section-4.4.2\n  #   https://datatracker.ietf.org/doc/html/rfc6819#section-4.4.3\n  #\n  # grant_flows %w[authorization_code client_credentials]\n\n  # Hook into the strategies' request & response life-cycle in case your\n  # application needs advanced customization or logging:\n  #\n  # before_successful_strategy_response do |request|\n  #   puts \"BEFORE HOOK FIRED! #{request}\"\n  # end\n  #\n  # after_successful_strategy_response do |request, response|\n  #   puts \"AFTER HOOK FIRED! #{request}, #{response}\"\n  # end\n\n  # Under some circumstances you might want to have applications auto-approved,\n  # so that the user skips the authorization step.\n  # For example if dealing with a trusted application.\n  # skip_authorization do |resource_owner, client|\n  #   client.superapp? or resource_owner.admin?\n  # end\n\n  # Configure custom constraints for the Token Introspection request.\n  # By default this configuration option allows to introspect a token by another\n  # token of the same application, OR to introspect the token that belongs to\n  # authorized client (from authenticated client) OR when token doesn't\n  # belong to any client (public token). Otherwise requester has no access to the\n  # introspection and it will return response as stated in the RFC.\n  #\n  # Block arguments:\n  #\n  # @param token [Doorkeeper::AccessToken]\n  #   token to be introspected\n  #\n  # @param authorized_client [Doorkeeper::Application]\n  #   authorized client (if request is authorized using Basic auth with\n  #   Client Credentials for example)\n  #\n  # @param authorized_token [Doorkeeper::AccessToken]\n  #   Bearer token used to authorize the request\n  #\n  # In case the block returns `nil` or `false` introspection responses with 401 status code\n  # when using authorized token to introspect, or you'll get 200 with { \"active\": false } body\n  # when using authorized client to introspect as stated in the\n  # RFC 7662 section 2.2. Introspection Response.\n  #\n  # Using with caution:\n  # Keep in mind that these three parameters pass to block can be nil as following case:\n  #  `authorized_client` is nil if and only if `authorized_token` is present, and vice versa.\n  #  `token` will be nil if and only if `authorized_token` is present.\n  # So remember to use `&` or check if it is present before calling method on\n  # them to make sure you doesn't get NoMethodError exception.\n  #\n  # You can define your custom check:\n  #\n  # allow_token_introspection do |token, authorized_client, authorized_token|\n  #   if authorized_token\n  #     # customize: require `introspection` scope\n  #     authorized_token.application == token&.application ||\n  #       authorized_token.scopes.include?(\"introspection\")\n  #   elsif token.application\n  #     # `protected_resource` is a new database boolean column, for example\n  #     authorized_client == token.application || authorized_client.protected_resource?\n  #   else\n  #     # public token (when token.application is nil, token doesn't belong to any application)\n  #     true\n  #   end\n  # end\n  #\n  # Or you can completely disable any token introspection:\n  #\n  # allow_token_introspection false\n  #\n  # If you need to block the request at all, then configure your routes.rb or web-server\n  # like nginx to forbid the request.\n\n  # WWW-Authenticate Realm (default \"Doorkeeper\").\n  realm \"Doorkeeper\"\nend\n"
  },
  {
    "path": "spec/dummy/config/initializers/secret_token.rb",
    "content": "# frozen_string_literal: true\n\n# Be sure to restart your server when you modify this file.\n\n# Your secret key for verifying the integrity of signed cookies.\n# If you change this key, all old signed cookies will become invalid!\n# Make sure the secret is at least 30 characters and all random,\n# no regular words or you'll be exposed to dictionary attacks.\nDummy::Application.config.secret_key_base =\n  \"c00157b5a1bb6181792f0f4a8a080485de7bab9987e6cf159\"\n"
  },
  {
    "path": "spec/dummy/config/initializers/session_store.rb",
    "content": "# frozen_string_literal: true\n\n# Be sure to restart your server when you modify this file.\n\nDummy::Application.config.session_store :cookie_store, key: \"_dummy_session\"\n\n# Use the database for sessions instead of the cookie-based default,\n# which shouldn't be used to store highly confidential information\n# (create the session table with \"rails generate session_migration\")\n# Dummy::Application.config.session_store :active_record_store\n"
  },
  {
    "path": "spec/dummy/config/initializers/wrap_parameters.rb",
    "content": "# frozen_string_literal: true\n\n# Be sure to restart your server when you modify this file.\n#\n# This file contains settings for ActionController::ParamsWrapper which\n# is enabled by default.\n\n# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.\nActiveSupport.on_load(:action_controller) do\n  wrap_parameters format: [:json]\nend\n\n# Disable root element in JSON by default.\nActiveSupport.on_load(:active_record) do\n  self.include_root_in_json = false\nend\n"
  },
  {
    "path": "spec/dummy/config/locales/doorkeeper.en.yml",
    "content": "en:\n  doorkeeper:\n    scopes:\n      public: \"Access your public data\"\n      write:  \"Update your data\"\n"
  },
  {
    "path": "spec/dummy/config/routes.rb",
    "content": "Rails.application.routes.draw do\n  use_doorkeeper\n\n  resources :semi_protected_resources\n  resources :full_protected_resources\n\n  get \"metal.json\" => \"metal#index\"\n\n  get \"/callback\", to: \"home#callback\"\n  get \"/sign_in\",  to: \"home#sign_in\"\n\n  root to: \"home#index\"\nend\n"
  },
  {
    "path": "spec/dummy/config.ru",
    "content": "# frozen_string_literal: true\n\n# This file is used by Rack-based servers to start the application.\n\nrequire ::File.expand_path('config/environment', __dir__)\nrun Dummy::Application\n"
  },
  {
    "path": "spec/dummy/db/migrate/20111122132257_create_users.rb",
    "content": "# frozen_string_literal: true\n\nclass CreateUsers < ActiveRecord::Migration[4.2]\n  def change\n    create_table :users do |t|\n      t.string :name\n\n      t.timestamps\n    end\n  end\nend\n"
  },
  {
    "path": "spec/dummy/db/migrate/20120312140401_add_password_to_users.rb",
    "content": "# frozen_string_literal: true\n\nclass AddPasswordToUsers < ActiveRecord::Migration[4.2]\n  def change\n    add_column :users, :password, :string\n  end\nend\n"
  },
  {
    "path": "spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb",
    "content": "# frozen_string_literal: true\n\nclass CreateDoorkeeperTables < ActiveRecord::Migration[4.2]\n  def change\n    create_table :oauth_applications do |t|\n      t.string  :name,    null: false\n      t.string  :uid,     null: false\n      t.string  :secret\n\n      # Remove `null: false` if you are planning to use grant flows\n      # that doesn't require redirect URI to be used during authorization\n      # like Client Credentials flow or Resource Owner Password.\n      t.text    :redirect_uri, null: false\n      t.string  :scopes,       null: false, default: \"\"\n      t.timestamps             null: false\n    end\n\n    add_index :oauth_applications, :uid, unique: true\n\n    create_table :oauth_access_grants do |t|\n      t.references :resource_owner,  null: false, polymorphic: true\n      t.references :application,     null: false\n      t.string   :token,             null: false\n      t.integer  :expires_in,        null: false\n      t.text     :redirect_uri,      null: false\n      t.datetime :created_at,        null: false\n      t.datetime :revoked_at\n      t.string   :scopes, null: false, default: \"\"\n    end\n\n    add_index :oauth_access_grants, :token, unique: true\n    add_foreign_key(\n      :oauth_access_grants,\n      :oauth_applications,\n      column: :application_id,\n    )\n\n    create_table :oauth_access_tokens do |t|\n      t.references :resource_owner, index: true, polymorphic: true\n      t.references :application,    null: false\n\n      # If you use a custom token generator you may need to change this column\n      # from string to text, so that it accepts tokens larger than 255\n      # characters. More info on custom token generators in:\n      # https://github.com/doorkeeper-gem/doorkeeper/tree/v3.0.0.rc1#custom-access-token-generator\n      #\n      # t.text :token, null: false\n      t.string :token, null: false\n\n      t.string   :refresh_token\n      t.integer  :expires_in\n      t.datetime :revoked_at\n      t.datetime :created_at, null: false\n      t.string   :scopes\n    end\n\n    add_index :oauth_access_tokens, :token, unique: true\n    add_index :oauth_access_tokens, :refresh_token, unique: true\n    add_foreign_key(\n      :oauth_access_tokens,\n      :oauth_applications,\n      column: :application_id,\n    )\n\n    # Uncomment below to ensure a valid reference to the resource owner's table\n    add_foreign_key :oauth_access_grants, :users, column: :resource_owner_id\n    add_foreign_key :oauth_access_tokens, :users, column: :resource_owner_id\n  end\nend\n"
  },
  {
    "path": "spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb",
    "content": "# frozen_string_literal: true\n\nclass AddOwnerToApplication < ActiveRecord::Migration[4.2]\n  def change\n    add_column :oauth_applications, :owner_id, :integer, null: true\n    add_column :oauth_applications, :owner_type, :string, null: true\n    add_index :oauth_applications, %i[owner_id owner_type]\n  end\nend\n"
  },
  {
    "path": "spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb",
    "content": "# frozen_string_literal: true\n\nclass AddPreviousRefreshTokenToAccessTokens < ActiveRecord::Migration[4.2]\n  def change\n    add_column(\n      :oauth_access_tokens,\n      :previous_refresh_token,\n      :string,\n      default: \"\",\n      null: false,\n    )\n  end\nend\n"
  },
  {
    "path": "spec/dummy/db/migrate/20170822064514_enable_pkce.rb",
    "content": "# frozen_string_literal: true\n\nclass EnablePkce < ActiveRecord::Migration[4.2]\n  def change\n    add_column :oauth_access_grants, :code_challenge, :string, null: true\n    add_column :oauth_access_grants, :code_challenge_method, :string, null: true\n  end\nend\n"
  },
  {
    "path": "spec/dummy/db/migrate/20180210183654_add_confidential_to_applications.rb",
    "content": "# frozen_string_literal: true\n\nclass AddConfidentialToApplications < ActiveRecord::Migration[5.1]\n  def change\n    add_column(\n      :oauth_applications,\n      :confidential,\n      :boolean,\n      null: false,\n      default: true, # maintaining backwards compatibility: require secrets\n    )\n  end\nend\n"
  },
  {
    "path": "spec/dummy/db/migrate/20230205064514_add_custom_attributes.rb",
    "content": "# frozen_string_literal: true\n\nclass AddCustomAttributes < ActiveRecord::Migration[4.2]\n  def change\n    add_column :oauth_access_grants, :tenant_name, :string\n    add_column :oauth_access_tokens, :tenant_name, :string\n  end\nend\n"
  },
  {
    "path": "spec/dummy/db/schema.rb",
    "content": "# This file is auto-generated from the current state of the database. Instead\n# of editing this file, please use the migrations feature of Active Record to\n# incrementally modify your database, and then regenerate this schema definition.\n#\n# Note that this schema.rb definition is the authoritative source for your\n# database schema. If you need to create the application database on another\n# system, you should be using db:schema:load, not running all the migrations\n# from scratch. The latter is a flawed and unsustainable approach (the more migrations\n# you'll amass, the slower it'll run and the greater likelihood for issues).\n#\n# It's strongly recommended that you check this file into your version control system.\n\nActiveRecord::Schema.define(version: 20230205064514) do\n\n  create_table \"oauth_access_grants\", force: :cascade do |t|\n    t.integer \"resource_owner_id\", null: false\n    t.string \"resource_owner_type\" # [NOTE] null: false skipped to allow test pass\n    t.integer \"application_id\", null: false\n    t.string \"token\", null: false\n    t.integer \"expires_in\", null: false\n    t.text \"redirect_uri\", null: false\n    t.datetime \"created_at\", null: false\n    t.datetime \"revoked_at\"\n    t.string \"scopes\"\n    t.string \"tenant_name\"\n    unless ENV[\"WITHOUT_PKCE\"]\n      t.string   \"code_challenge\"\n      t.string   \"code_challenge_method\"\n    end\n    t.index [\"token\"], name: \"index_oauth_access_grants_on_token\", unique: true\n  end\n\n  create_table \"oauth_access_tokens\", force: :cascade do |t|\n    t.integer \"resource_owner_id\"\n    t.string \"resource_owner_type\"\n    t.integer \"application_id\"\n    t.string \"token\", null: false\n    t.string \"refresh_token\"\n    t.integer \"expires_in\"\n    t.datetime \"revoked_at\"\n    t.datetime \"created_at\", null: false\n    t.string \"scopes\"\n    t.string \"previous_refresh_token\", default: \"\", null: false\n    t.string \"tenant_name\"\n    t.index [\"refresh_token\"], name: \"index_oauth_access_tokens_on_refresh_token\", unique: true\n    t.index [\"resource_owner_id\"], name: \"index_oauth_access_tokens_on_resource_owner_id\"\n    t.index [\"token\"], name: \"index_oauth_access_tokens_on_token\", unique: true\n  end\n\n  create_table \"oauth_applications\", force: :cascade do |t|\n    t.string \"name\", null: false\n    t.string \"uid\", null: false\n    t.string \"secret\"\n    t.text \"redirect_uri\"\n    t.string \"scopes\", default: \"\", null: false\n    t.datetime \"created_at\", null: false\n    t.datetime \"updated_at\", null: false\n    t.integer \"owner_id\"\n    t.string \"owner_type\"\n    t.boolean \"confidential\", default: true, null: false\n    t.index [\"owner_id\", \"owner_type\"], name: \"index_oauth_applications_on_owner_id_and_owner_type\"\n    t.index [\"uid\"], name: \"index_oauth_applications_on_uid\", unique: true\n  end\n\n  create_table \"users\", force: :cascade do |t|\n    t.string \"name\"\n    t.datetime \"created_at\"\n    t.datetime \"updated_at\"\n    t.string \"password\"\n  end\n\nend\n"
  },
  {
    "path": "spec/dummy/public/404.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The page you were looking for doesn't exist (404)</title>\n  <style type=\"text/css\">\n    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n    div.dialog {\n      width: 25em;\n      padding: 0 4em;\n      margin: 4em auto 0 auto;\n      border: 1px solid #ccc;\n      border-right-color: #999;\n      border-bottom-color: #999;\n    }\n    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/404.html -->\n  <div class=\"dialog\">\n    <h1>The page you were looking for doesn't exist.</h1>\n    <p>You may have mistyped the address or the page may have moved.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "spec/dummy/public/422.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The change you wanted was rejected (422)</title>\n  <style type=\"text/css\">\n    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n    div.dialog {\n      width: 25em;\n      padding: 0 4em;\n      margin: 4em auto 0 auto;\n      border: 1px solid #ccc;\n      border-right-color: #999;\n      border-bottom-color: #999;\n    }\n    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/422.html -->\n  <div class=\"dialog\">\n    <h1>The change you wanted was rejected.</h1>\n    <p>Maybe you tried to change something you didn't have access to.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "spec/dummy/public/500.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>We're sorry, but something went wrong (500)</title>\n  <style type=\"text/css\">\n    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n    div.dialog {\n      width: 25em;\n      padding: 0 4em;\n      margin: 4em auto 0 auto;\n      border: 1px solid #ccc;\n      border-right-color: #999;\n      border-bottom-color: #999;\n    }\n    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/500.html -->\n  <div class=\"dialog\">\n    <h1>We're sorry, but something went wrong.</h1>\n    <p>We've been notified about this issue and we'll take a look at it shortly.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "spec/dummy/script/rails",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n# This command will automatically be run when you run \"rails\" with Rails 3 gems\n# installed from the root of your application.\n\nAPP_PATH = File.expand_path(\"../config/application\", __dir__)\nrequire File.expand_path(\"../config/boot\", __dir__)\nrequire \"rails/commands\"\n"
  },
  {
    "path": "spec/factories.rb",
    "content": "# frozen_string_literal: true\n\nFactoryBot.define do\n  factory :access_grant, class: \"Doorkeeper::AccessGrant\" do\n    sequence(:resource_owner_id) { |n| n }\n    application\n    redirect_uri { \"https://app.com/callback\" }\n    expires_in { 100 }\n    scopes { \"public write\" }\n  end\n\n  factory :access_token, class: \"Doorkeeper::AccessToken\" do\n    sequence(:resource_owner_id) { |n| n }\n    application\n    expires_in { 2.hours }\n\n    factory :clientless_access_token do\n      application { nil }\n    end\n  end\n\n  factory :application, class: \"Doorkeeper::Application\" do\n    sequence(:name) { |n| \"Application #{n}\" }\n    redirect_uri { \"https://app.com/callback\" }\n  end\n\n  # do not name this factory :user, otherwise it will conflict with factories\n  # from applications that use doorkeeper factories in their own tests\n  factory :doorkeeper_testing_user, class: :user, aliases: [:resource_owner]\nend\n"
  },
  {
    "path": "spec/generators/application_owner_generator_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\nrequire \"generators/doorkeeper/application_owner_generator\"\n\nRSpec.describe Doorkeeper::ApplicationOwnerGenerator do\n  include GeneratorSpec::TestCase\n\n  tests described_class\n  destination ::File.expand_path('tmp/dummy', __dir__)\n\n  describe \"after running the generator\" do\n    before do\n      prepare_destination\n    end\n\n    it \"creates a migration with a version specifier\" do\n      stub_const(\"ActiveRecord::VERSION::MAJOR\", 5)\n      stub_const(\"ActiveRecord::VERSION::MINOR\", 0)\n\n      run_generator\n\n      assert_migration \"db/migrate/add_owner_to_application.rb\" do |migration|\n        assert migration.include?(\"ActiveRecord::Migration[5.0]\\n\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/generators/confidential_applications_generator_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\nrequire \"generators/doorkeeper/confidential_applications_generator\"\n\nRSpec.describe Doorkeeper::ConfidentialApplicationsGenerator do\n  include GeneratorSpec::TestCase\n\n  tests described_class\n  destination ::File.expand_path('tmp/dummy', __dir__)\n\n  describe \"after running the generator\" do\n    before do\n      prepare_destination\n    end\n\n    it \"creates a migration with a version specifier\" do\n      stub_const(\"ActiveRecord::VERSION::MAJOR\", 5)\n      stub_const(\"ActiveRecord::VERSION::MINOR\", 0)\n\n      run_generator\n\n      assert_migration \"db/migrate/add_confidential_to_applications.rb\" do |migration|\n        assert migration.include?(\"ActiveRecord::Migration[5.0]\\n\")\n        assert migration.include?(\":confidential\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/generators/enable_polymorphic_resource_owner_generator_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\nrequire \"generators/doorkeeper/enable_polymorphic_resource_owner_generator\"\n\nRSpec.describe Doorkeeper::EnablePolymorphicResourceOwnerGenerator do\n  include GeneratorSpec::TestCase\n\n  tests described_class\n  destination ::File.expand_path('tmp/dummy', __dir__)\n\n  describe \"after running the generator\" do\n    before do\n      prepare_destination\n      FileUtils.mkdir_p(::File.expand_path(\"config/initializers\", Pathname(destination_root)))\n      FileUtils.copy_file(\n        ::File.expand_path(\"../../lib/generators/doorkeeper/templates/initializer.rb\", __dir__),\n        ::File.expand_path(\"config/initializers/doorkeeper.rb\", Pathname.new(destination_root)),\n      )\n    end\n\n    it \"creates a migration with a version specifier and changes the initializer\" do\n      stub_const(\"ActiveRecord::VERSION::MAJOR\", 5)\n      stub_const(\"ActiveRecord::VERSION::MINOR\", 0)\n\n      run_generator\n\n      assert_migration \"db/migrate/enable_polymorphic_resource_owner.rb\" do |migration|\n        assert migration.include?(\"ActiveRecord::Migration[5.0]\\n\")\n      end\n\n      # generator_spec gem requires such block definition :(\n      #\n      # rubocop:disable Style/BlockDelimiters\n      expect(destination_root).to(have_structure {\n        directory \"config\" do\n          directory \"initializers\" do\n            file \"doorkeeper.rb\" do\n              contains \"  use_polymorphic_resource_owner\"\n            end\n          end\n        end\n      })\n      # rubocop:enable Style/BlockDelimiters\n    end\n  end\nend\n"
  },
  {
    "path": "spec/generators/install_generator_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\nrequire \"generators/doorkeeper/install_generator\"\n\nRSpec.describe Doorkeeper::InstallGenerator do\n  include GeneratorSpec::TestCase\n\n  tests described_class\n  destination ::File.expand_path('tmp/dummy', __dir__)\n\n  describe \"after running the generator\" do\n    before do\n      prepare_destination\n      FileUtils.mkdir(::File.expand_path(\"config\", Pathname(destination_root)))\n      FileUtils.mkdir(::File.expand_path(\"db\", Pathname(destination_root)))\n      FileUtils.copy_file(\n        ::File.expand_path('templates/routes.rb', __dir__),\n        ::File.expand_path(\"config/routes.rb\", Pathname.new(destination_root)),\n      )\n      run_generator\n    end\n\n    it \"creates an initializer file\" do\n      assert_file \"config/initializers/doorkeeper.rb\"\n    end\n\n    it \"copies the locale file\" do\n      assert_file \"config/locales/doorkeeper.en.yml\"\n    end\n\n    it \"adds sample route\" do\n      assert_file \"config/routes.rb\", /use_doorkeeper/\n    end\n  end\nend\n"
  },
  {
    "path": "spec/generators/migration_generator_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\nrequire \"generators/doorkeeper/migration_generator\"\n\nRSpec.describe Doorkeeper::MigrationGenerator do\n  include GeneratorSpec::TestCase\n\n  tests described_class\n  destination ::File.expand_path('tmp/dummy', __dir__)\n\n  describe \"after running the generator\" do\n    before do\n      prepare_destination\n    end\n\n    it \"creates a migration with a version specifier\" do\n      stub_const(\"ActiveRecord::VERSION::MAJOR\", 5)\n      stub_const(\"ActiveRecord::VERSION::MINOR\", 0)\n\n      run_generator\n\n      assert_migration \"db/migrate/create_doorkeeper_tables.rb\" do |migration|\n        assert migration.include?(\"ActiveRecord::Migration[5.0]\\n\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/generators/pkce_generator_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\nrequire \"generators/doorkeeper/pkce_generator\"\n\nRSpec.describe Doorkeeper::PkceGenerator do\n  include GeneratorSpec::TestCase\n\n  tests described_class\n  destination ::File.expand_path('tmp/dummy', __dir__)\n\n  describe \"after running the generator\" do\n    before do\n      prepare_destination\n    end\n\n    it \"creates a migration with a version specifier\" do\n      stub_const(\"ActiveRecord::VERSION::MAJOR\", 5)\n      stub_const(\"ActiveRecord::VERSION::MINOR\", 0)\n\n      run_generator\n\n      assert_migration \"db/migrate/enable_pkce.rb\" do |migration|\n        assert migration.include?(\"ActiveRecord::Migration[5.0]\\n\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/generators/previous_refresh_token_generator_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\nrequire \"generators/doorkeeper/previous_refresh_token_generator\"\n\nRSpec.describe Doorkeeper::PreviousRefreshTokenGenerator do\n  include GeneratorSpec::TestCase\n\n  tests described_class\n  destination ::File.expand_path('tmp/dummy', __dir__)\n\n  describe \"after running the generator\" do\n    before do\n      prepare_destination\n\n      allow_any_instance_of(described_class).to(\n        receive(:no_previous_refresh_token_column?).and_return(true),\n      )\n    end\n\n    it \"creates a migration with a version specifier\" do\n      stub_const(\"ActiveRecord::VERSION::MAJOR\", 5)\n      stub_const(\"ActiveRecord::VERSION::MINOR\", 0)\n\n      run_generator\n\n      assert_migration \"db/migrate/add_previous_refresh_token_to_access_tokens.rb\" do |migration|\n        assert migration.include?(\"ActiveRecord::Migration[5.0]\\n\")\n      end\n    end\n\n    context \"when file already exist\" do\n      it \"does not create a migration\" do\n        allow_any_instance_of(described_class).to(\n          receive(:no_previous_refresh_token_column?).and_call_original,\n        )\n\n        run_generator\n\n        assert_no_migration \"db/migrate/add_previous_refresh_token_to_access_tokens.rb\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/generators/remove_applications_secret_not_null_constraint_generator_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\nrequire \"generators/doorkeeper/remove_applications_secret_not_null_constraint_generator\"\n\nRSpec.describe Doorkeeper::RemoveApplicationsSecretNotNullConstraintGenerator do\n  include GeneratorSpec::TestCase\n\n  tests described_class\n  destination ::File.expand_path('tmp/dummy', __dir__)\n\n  describe \"after running the generator\" do\n    before do\n      prepare_destination\n    end\n\n    it \"creates a migration with a version specifier\" do\n      run_generator\n\n      assert_migration \"db/migrate/remove_applications_secret_not_null_constraint.rb\" do |migration|\n        assert migration.include?(\"change_column_null :oauth_applications, :secret\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/generators/templates/routes.rb",
    "content": "# frozen_string_literal: true\n\nRails.application.routes.draw do\nend\n"
  },
  {
    "path": "spec/generators/views_generator_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\nrequire \"generators/doorkeeper/views_generator\"\n\nRSpec.describe Doorkeeper::Generators::ViewsGenerator do\n  include GeneratorSpec::TestCase\n\n  tests described_class\n  destination File.expand_path(\"tmp/dummy\", __dir__)\n\n  before do\n    prepare_destination\n  end\n\n  it \"create all views\" do\n    run_generator\n    assert_file \"app/views/doorkeeper/applications/_form.html.erb\"\n    assert_file \"app/views/doorkeeper/applications/edit.html.erb\"\n    assert_file \"app/views/doorkeeper/applications/index.html.erb\"\n    assert_file \"app/views/doorkeeper/applications/new.html.erb\"\n    assert_file \"app/views/doorkeeper/applications/show.html.erb\"\n\n    assert_file \"app/views/doorkeeper/authorizations/error.html.erb\"\n    assert_file \"app/views/doorkeeper/authorizations/new.html.erb\"\n\n    assert_file \"app/views/doorkeeper/authorized_applications/index.html.erb\"\n  end\nend\n"
  },
  {
    "path": "spec/grape/grape_integration_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\nrequire \"grape\"\nrequire \"rack/test\"\nrequire \"doorkeeper/grape/helpers\"\n\n# Test Grape API application\nmodule GrapeApp\n  class API < Grape::API\n    version \"v1\", using: :path\n    format :json\n    prefix :api\n\n    helpers Doorkeeper::Grape::Helpers\n\n    resource :protected do\n      before do\n        doorkeeper_authorize!\n      end\n\n      desc \"Protected resource, requires token.\"\n\n      get :status do\n        { token: doorkeeper_token.token }\n      end\n    end\n\n    resource :protected_with_endpoint_scopes do\n      before do\n        doorkeeper_authorize!\n      end\n\n      desc \"Protected resource, requires token with scopes (defined in endpoint).\"\n\n      get :status, scopes: [:admin] do\n        { response: \"OK\" }\n      end\n    end\n\n    resource :protected_with_helper_scopes do\n      before do\n        doorkeeper_authorize! :admin\n      end\n\n      desc \"Protected resource, requires token with scopes (defined in helper).\"\n\n      get :status do\n        { response: \"OK\" }\n      end\n    end\n\n    resource :public do\n      desc \"Public resource, no token required.\"\n\n      get :status do\n        { response: \"OK\" }\n      end\n    end\n  end\nend\n\nRSpec.describe \"Grape integration\" do\n  include Rack::Test::Methods\n\n  def app\n    GrapeApp::API\n  end\n\n  def json_body\n    JSON.parse(last_response.body)\n  end\n\n  let(:client) { FactoryBot.create(:application) }\n  let(:resource) { FactoryBot.create(:doorkeeper_testing_user, name: \"Joe\", password: \"sekret\") }\n  let(:access_token) { client_is_authorized(client, resource) }\n\n  context \"with valid Access Token\" do\n    it \"successfully requests protected resource\" do\n      get \"api/v1/protected/status.json?access_token=#{access_token.token}\"\n\n      expect(last_response).to be_successful\n\n      expect(json_body[\"token\"]).to eq(access_token.token)\n    end\n\n    it \"successfully requests protected resource with token that has required scopes (Grape endpoint)\" do\n      access_token = client_is_authorized(client, resource, scopes: \"admin\")\n\n      get \"api/v1/protected_with_endpoint_scopes/status.json?access_token=#{access_token.token}\"\n\n      expect(last_response).to be_successful\n      expect(json_body).to have_key(\"response\")\n    end\n\n    it \"successfully requests protected resource with token that has required scopes (Doorkeeper helper)\" do\n      access_token = client_is_authorized(client, resource, scopes: \"admin\")\n\n      get \"api/v1/protected_with_helper_scopes/status.json?access_token=#{access_token.token}\"\n\n      expect(last_response).to be_successful\n      expect(json_body).to have_key(\"response\")\n    end\n\n    it \"successfully requests public resource\" do\n      get \"api/v1/public/status.json\"\n\n      expect(last_response).to be_successful\n      expect(json_body).to have_key(\"response\")\n    end\n  end\n\n  context \"with invalid Access Token\" do\n    it \"fails without access token\" do\n      get \"api/v1/protected/status.json\"\n\n      expect(last_response).not_to be_successful\n      expect(json_body).to have_key(\"error\")\n    end\n\n    it \"fails for access token without scopes\" do\n      get \"api/v1/protected_with_endpoint_scopes/status.json?access_token=#{access_token.token}\"\n\n      expect(last_response).not_to be_successful\n      expect(json_body).to have_key(\"error\")\n    end\n\n    it \"fails for access token with invalid scopes\" do\n      access_token = client_is_authorized(client, resource, scopes: \"read write\")\n\n      get \"api/v1/protected_with_endpoint_scopes/status.json?access_token=#{access_token.token}\"\n\n      expect(last_response).not_to be_successful\n      expect(json_body).to have_key(\"error\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/helpers/doorkeeper/dashboard_helper_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::DashboardHelper do\n  describe \"#doorkeeper_errors_for\" do\n    let(:object) { double errors: { method: messages } }\n    let(:messages) { [\"first message\", \"second message\"] }\n\n    context \"when object has errors\" do\n      it \"returns error messages\" do\n        messages.each do |message|\n          expect(helper.doorkeeper_errors_for(object, :method)).to include(\n            message.capitalize,\n          )\n        end\n      end\n    end\n\n    context \"when object has no errors\" do\n      it \"returns nil\" do\n        expect(helper.doorkeeper_errors_for(object, :amonter_method)).to be_nil\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/config_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::Config do\n  subject(:config) { Doorkeeper.config }\n\n  describe \"resource_owner_authenticator\" do\n    it \"sets the block that is accessible via authenticate_resource_owner\" do\n      block = proc {}\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        resource_owner_authenticator(&block)\n      end\n\n      expect(config.authenticate_resource_owner).to eq(block)\n    end\n\n    it \"prints warning message by default\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n      end\n\n      expect(Rails.logger).to receive(:warn).with(\n        I18n.t(\"doorkeeper.errors.messages.resource_owner_authenticator_not_configured\"),\n      )\n      config.authenticate_resource_owner.call(nil)\n    end\n  end\n\n  describe \"resource_owner_from_credentials\" do\n    it \"sets the block that is accessible via authenticate_resource_owner\" do\n      block = proc {}\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        resource_owner_from_credentials(&block)\n      end\n\n      expect(config.resource_owner_from_credentials).to eq(block)\n    end\n\n    it \"prints warning message by default\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n      end\n\n      expect(Rails.logger).to receive(:warn).with(\n        I18n.t(\"doorkeeper.errors.messages.credential_flow_not_configured\"),\n      )\n      config.resource_owner_from_credentials.call(nil)\n    end\n  end\n\n  describe \"setup_orm\" do\n    it \"adds specific error message to NameError exception\" do\n      expect do\n        Doorkeeper.configure { orm \"hibernate\" }\n        Doorkeeper.setup\n      end.to raise_error(NameError, /ORM adapter not found \\(hibernate\\)/)\n    end\n\n    it \"does not change other exceptions\" do\n      allow(Doorkeeper).to receive(:setup_orm_adapter) { raise NoMethodError }\n\n      expect do\n        Doorkeeper.configure { orm \"hibernate\" }\n        Doorkeeper.setup\n      end.to raise_error(NoMethodError)\n    end\n  end\n\n  describe \"admin_authenticator\" do\n    it \"sets the block that is accessible via authenticate_admin\" do\n      default_behaviour = \"default behaviour\"\n      allow(described_class).to receive(:head).and_return(default_behaviour)\n\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n      end\n\n      expect(config.authenticate_admin.call({})).to eq(default_behaviour)\n    end\n\n    it \"could be customized with a block\" do\n      block = proc {}\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        admin_authenticator(&block)\n      end\n\n      expect(config.authenticate_admin).to eq(block)\n    end\n  end\n\n  describe \"access_token_expires_in\" do\n    it \"has 2 hours by default\" do\n      expect(config.access_token_expires_in).to eq(2.hours)\n    end\n\n    it \"can change the value\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        access_token_expires_in 4.hours\n      end\n      expect(config.access_token_expires_in).to eq(4.hours)\n    end\n\n    it \"can be set to nil\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        access_token_expires_in nil\n      end\n\n      expect(config.access_token_expires_in).to be_nil\n    end\n  end\n\n  describe \"scopes\" do\n    it \"has default scopes\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        default_scopes :public\n      end\n\n      expect(config.default_scopes).to include(\"public\")\n    end\n\n    it \"has optional scopes\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        optional_scopes :write, :update\n      end\n\n      expect(config.optional_scopes).to include(\"write\", \"update\")\n    end\n\n    it \"has all scopes\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        default_scopes :normal\n        optional_scopes :admin\n      end\n\n      expect(config.scopes).to include(\"normal\", \"admin\")\n    end\n  end\n\n  describe \"scopes_by_grant_type\" do\n    it \"is {} by default\" do\n      expect(config.scopes_by_grant_type).to eq({})\n    end\n\n    it \"has hash value\" do\n      hash = {}\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        scopes_by_grant_type hash\n      end\n\n      expect(config.scopes_by_grant_type).to eq(hash)\n    end\n  end\n\n  describe \"use_refresh_token\" do\n    it \"is false by default\" do\n      expect(config.refresh_token_enabled?).to eq(false)\n    end\n\n    it \"can change the value\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        use_refresh_token\n      end\n\n      expect(config.refresh_token_enabled?).to eq(true)\n    end\n\n    it \"can accept a boolean parameter\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        use_refresh_token false\n      end\n\n      expect(config.refresh_token_enabled?).to eq(false)\n    end\n\n    it \"can accept a block parameter\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        use_refresh_token { |_context| nil }\n      end\n\n      expect(config.refresh_token_enabled?).to be_a(Proc)\n    end\n\n    it \"does not includes 'refresh_token' in token_grant_flows\" do\n      expect(config.token_grant_flows).not_to include Doorkeeper::GrantFlow.get(\"refresh_token\")\n    end\n\n    context \"when enabled\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          use_refresh_token\n        end\n      end\n\n      it \"includes 'refresh_token' in token_grant_flows\" do\n        expect(config.token_grant_flows).to include Doorkeeper::GrantFlow.get(\"refresh_token\")\n      end\n    end\n  end\n\n  describe \"token_reuse_limit\" do\n    it \"is 100 by default\" do\n      expect(config.token_reuse_limit).to eq(100)\n    end\n\n    it \"can change the value\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        token_reuse_limit 90\n      end\n\n      expect(config.token_reuse_limit).to eq(90)\n    end\n\n    it \"sets the value to 100 if invalid value is being set\" do\n      expect(Rails.logger).to receive(:warn).with(/will be set to default 100/)\n\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        reuse_access_token\n        token_reuse_limit 110\n      end\n\n      expect(config.token_reuse_limit).to eq(100)\n    end\n  end\n\n  describe \"enforce_configured_scopes\" do\n    it \"is false by default\" do\n      expect(config.enforce_configured_scopes?).to eq(false)\n    end\n\n    it \"can change the value\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        enforce_configured_scopes\n      end\n\n      expect(config.enforce_configured_scopes?).to eq(true)\n    end\n  end\n\n  describe 'use_url_path_for_native_authorization' do\n    around(:each) do |example|\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        use_url_path_for_native_authorization\n      end\n\n      Rails.application.reload_routes!\n\n      subject { Doorkeeper.configuration }\n\n      example.run\n\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n      end\n\n      Rails.application.reload_routes!\n    end\n\n    it 'sets the native authorization code route /:code' do\n      expect(subject.native_authorization_code_route).to eq('/:code')\n    end\n  end\n\n  describe \"client_credentials\" do\n    it \"has defaults order\" do\n      expect(config.client_credentials_methods)\n        .to eq(%i[from_basic from_params])\n    end\n\n    it \"can change the value\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        client_credentials :from_digest, :from_params\n      end\n\n      expect(config.client_credentials_methods)\n        .to eq(%i[from_digest from_params])\n    end\n  end\n\n  describe \"force_ssl_in_redirect_uri\" do\n    it \"is true by default in non-development environments\" do\n      expect(config.force_ssl_in_redirect_uri).to eq(true)\n    end\n\n    it \"can change the value\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        force_ssl_in_redirect_uri(false)\n      end\n\n      expect(config.force_ssl_in_redirect_uri).to eq(false)\n    end\n\n    it \"can be a callable object\" do\n      block = proc { false }\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        force_ssl_in_redirect_uri(&block)\n      end\n\n      expect(config.force_ssl_in_redirect_uri).to eq(block)\n      expect(config.force_ssl_in_redirect_uri.call).to eq(false)\n    end\n  end\n\n  describe \"access_token_methods\" do\n    it \"has defaults order\" do\n      expect(config.access_token_methods)\n        .to eq(%i[from_bearer_authorization from_access_token_param from_bearer_param])\n    end\n\n    it \"can change the value\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        access_token_methods :from_access_token_param, :from_bearer_param\n      end\n\n      expect(config.access_token_methods)\n        .to eq(%i[from_access_token_param from_bearer_param])\n    end\n  end\n\n  describe \"forbid_redirect_uri\" do\n    it \"is false by default\" do\n      expect(config.forbid_redirect_uri.call(URI.parse(\"https://localhost\"))).to eq(false)\n    end\n\n    it \"can be a callable object\" do\n      block = proc { true }\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        forbid_redirect_uri(&block)\n      end\n\n      expect(config.forbid_redirect_uri).to eq(block)\n      expect(config.forbid_redirect_uri.call).to eq(true)\n    end\n  end\n\n  describe \"enable_dynamic_scopes\" do\n    it \"is disabled by default\" do\n      expect(Doorkeeper.config.enable_dynamic_scopes?).not_to be(true)\n    end\n\n    context \"when enabled with default delimiter\" do\n      before do\n        Doorkeeper.configure do\n          enable_dynamic_scopes\n        end\n      end\n\n      it 'returns true' do\n        expect(Doorkeeper.config.enable_dynamic_scopes?).to be(true)\n        expect(Doorkeeper.config.dynamic_scopes_delimiter).to eq(\":\")\n      end\n    end\n\n    context \"when enabled with custom delimiter\" do\n      before do\n        Doorkeeper.configure do\n          enable_dynamic_scopes(delimiter: \"-\")\n        end\n      end\n\n      it 'returns true' do\n        expect(Doorkeeper.config.enable_dynamic_scopes?).to be(true)\n        expect(Doorkeeper.config.dynamic_scopes_delimiter).to eq(\"-\")\n      end\n    end\n  end\n\n  describe \"enable_application_owner\" do\n    it \"is disabled by default\" do\n      expect(Doorkeeper.config.enable_application_owner?).not_to be(true)\n    end\n\n    if DOORKEEPER_ORM == :active_record\n      context \"when enabled without confirmation\", active_record: true do\n        class ApplicationWithOwner < ActiveRecord::Base\n          include Doorkeeper::Orm::ActiveRecord::Mixins::Application\n        end\n\n        before do\n          Doorkeeper.configure do\n            orm DOORKEEPER_ORM\n            enable_application_owner\n\n            application_class \"ApplicationWithOwner\"\n          end\n\n          Doorkeeper.run_orm_hooks\n        end\n\n        it \"adds support for application owner\" do\n          instance = ApplicationWithOwner.new(FactoryBot.attributes_for(:application))\n\n          expect(instance).to respond_to :owner\n          expect(instance).to be_valid\n        end\n\n        it \"Doorkeeper.configuration.confirm_application_owner? returns false\" do\n          expect(Doorkeeper.config.confirm_application_owner?).not_to be(true)\n        end\n      end\n    end\n\n    if DOORKEEPER_ORM == :active_record\n      context \"when enabled with confirmation set to true\", active_record: true do\n        class ApplicationWithOwner < ActiveRecord::Base\n          include Doorkeeper::Orm::ActiveRecord::Mixins::Application\n        end\n\n        before do\n          Doorkeeper.configure do\n            orm DOORKEEPER_ORM\n            enable_application_owner confirmation: true\n\n            application_class \"ApplicationWithOwner\"\n          end\n\n          Doorkeeper.run_orm_hooks\n        end\n\n        it \"adds support for application owner\" do\n          instance = ApplicationWithOwner.new(FactoryBot.attributes_for(:application))\n\n          expect(instance).to respond_to :owner\n          expect(instance).not_to be_valid\n          expect(instance.errors[:owner]).to be_present\n        end\n\n        it \"Doorkeeper.configuration.confirm_application_owner? returns true\" do\n          expect(Doorkeeper.config.confirm_application_owner?).to be(true)\n        end\n      end\n    end\n  end\n\n  describe \"realm\" do\n    it \"is 'Doorkeeper' by default\" do\n      expect(Doorkeeper.config.realm).to eq(\"Doorkeeper\")\n    end\n\n    it \"can change the value\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        realm \"Example\"\n      end\n\n      expect(config.realm).to eq(\"Example\")\n    end\n  end\n\n  describe \"grant_flows\" do\n    it \"is set to all grant flows by default\" do\n      expect(Doorkeeper.config.grant_flows)\n        .to eq(%w[authorization_code client_credentials])\n    end\n\n    it \"can change the value\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        grant_flows %w[authorization_code implicit]\n      end\n\n      expect(config.grant_flows).to eq %w[authorization_code implicit]\n    end\n\n    context \"when including 'authorization_code'\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          grant_flows [\"authorization_code\"]\n        end\n      end\n\n      it \"includes 'authorization_code' in authorization_response_flows\" do\n        expect(config.authorization_response_flows).to include Doorkeeper::GrantFlow.get(\"authorization_code\")\n      end\n\n      it \"includes 'authorization_code' in token_grant_flows\" do\n        expect(config.token_grant_flows).to include Doorkeeper::GrantFlow.get(\"authorization_code\")\n      end\n    end\n\n    context \"when including 'implicit'\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          grant_flows [\"implicit\"]\n        end\n      end\n\n      it \"includes 'implicit' in authorization_response_flows\" do\n        expect(config.authorization_response_flows).to include Doorkeeper::GrantFlow.get(\"implicit\")\n      end\n    end\n\n    context \"when including 'password'\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          grant_flows [\"password\"]\n        end\n      end\n\n      it \"includes 'password' in token_grant_flows\" do\n        expect(config.token_grant_flows).to include Doorkeeper::GrantFlow.get(\"password\")\n      end\n    end\n\n    context \"when including 'client_credentials'\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          grant_flows [\"client_credentials\"]\n        end\n      end\n\n      it \"includes 'client_credentials' in token_grant_flows\" do\n        expect(config.token_grant_flows).to include Doorkeeper::GrantFlow.get(\"client_credentials\")\n      end\n    end\n  end\n\n  describe \"access_token_generator\" do\n    it \"is 'Doorkeeper::OAuth::Helpers::UniqueToken' by default\" do\n      expect(Doorkeeper.configuration.access_token_generator).to(\n        eq(\"Doorkeeper::OAuth::Helpers::UniqueToken\"),\n      )\n    end\n\n    it \"can change the value\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        access_token_generator \"Example\"\n      end\n      expect(config.access_token_generator).to eq(\"Example\")\n    end\n  end\n\n  describe \"custom_access_token_attributes\" do\n    it \"is '[]' by default\" do\n      expect(Doorkeeper.configuration.custom_access_token_attributes).to(eq([]))\n    end\n\n    it \"can change the value\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        custom_access_token_attributes [:tenant_name]\n      end\n      expect(config.custom_access_token_attributes).to eq([:tenant_name])\n    end\n  end\n\n  describe \"application_secret_generator\" do\n    it \"is 'Doorkeeper::OAuth::Helpers::UniqueToken' by default\" do\n      expect(Doorkeeper.configuration.application_secret_generator).to(\n        eq(\"Doorkeeper::OAuth::Helpers::UniqueToken\"),\n      )\n    end\n\n    it \"can change the value\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        application_secret_generator \"Example\"\n      end\n      expect(config.application_secret_generator).to eq(\"Example\")\n    end\n  end\n\n  describe \"default_generator_method\" do\n    it \"is :urlsafe_base64 by default\" do\n      expect(Doorkeeper.configuration.default_generator_method)\n        .to eq(:urlsafe_base64)\n    end\n\n    it \"can change the value\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        default_generator_method :hex\n      end\n\n      expect(config.default_generator_method).to eq(:hex)\n    end\n  end\n\n  describe \"base_controller\" do\n    context \"when default value set\" do\n      it { expect(Doorkeeper.configuration.base_controller).to be_an_instance_of(Proc) }\n\n      it \"resolves to a ApplicationController::Base in default mode\" do\n        expect(Doorkeeper.configuration.resolve_controller(:base))\n          .to eq(ActionController::Base)\n      end\n\n      it \"resolves to a ApplicationController::API in api_only mode\" do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          api_only\n        end\n\n        expect(Doorkeeper.configuration.resolve_controller(:base))\n          .to eq(ActionController::API)\n      end\n    end\n\n    context \"when custom value set\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          base_controller \"ApplicationController\"\n        end\n      end\n\n      it { expect(Doorkeeper.config.base_controller).to eq(\"ApplicationController\") }\n    end\n  end\n\n  describe \"base_metal_controller\" do\n    context \"when default value set\" do\n      it { expect(Doorkeeper.config.base_metal_controller).to eq(\"ActionController::API\") }\n    end\n\n    context \"when custom value set\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          base_metal_controller { \"ApplicationController\" }\n        end\n      end\n\n      it { expect(Doorkeeper.configuration.resolve_controller(:base_metal)).to eq(ApplicationController) }\n    end\n  end\n\n  if DOORKEEPER_ORM == :active_record\n    class FakeCustomModel < ::ActiveRecord::Base\n    end\n\n    describe \"access_token_class\" do\n      it \"uses default doorkeeper value\" do\n        expect(config.access_token_class).to eq(\"Doorkeeper::AccessToken\")\n        expect(config.access_token_model).to be(Doorkeeper::AccessToken)\n      end\n\n      it \"can change the value\" do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          access_token_class \"FakeCustomModel\"\n        end\n\n        expect(config.access_token_class).to eq(\"FakeCustomModel\")\n        expect(config.access_token_model).to be(FakeCustomModel)\n      end\n    end\n\n    describe \"access_grant_class\" do\n      it \"uses default doorkeeper value\" do\n        expect(config.access_grant_class).to eq(\"Doorkeeper::AccessGrant\")\n        expect(config.access_grant_model).to be(Doorkeeper::AccessGrant)\n      end\n\n      it \"can change the value\" do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          access_grant_class \"FakeCustomModel\"\n        end\n\n        expect(config.access_grant_class).to eq(\"FakeCustomModel\")\n        expect(config.access_grant_model).to be(FakeCustomModel)\n      end\n    end\n\n    describe \"application_class\" do\n      it \"uses default doorkeeper value\" do\n        expect(config.application_class).to eq(\"Doorkeeper::Application\")\n        expect(config.application_model).to be(Doorkeeper::Application)\n      end\n\n      it \"can change the value\" do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          application_class \"FakeCustomModel\"\n        end\n\n        expect(config.application_class).to eq(\"FakeCustomModel\")\n        expect(config.application_model).to be(FakeCustomModel)\n      end\n    end\n  end\n\n  describe \"api_only\" do\n    it \"is false by default\" do\n      expect(config.api_only).to eq(false)\n    end\n\n    it \"can change the value\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        api_only\n      end\n\n      expect(config.api_only).to eq(true)\n    end\n  end\n\n  describe \"token_lookup_batch_size\" do\n    it \"uses default doorkeeper value\" do\n      expect(config.token_lookup_batch_size).to eq(10_000)\n    end\n\n    it \"can change the value\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        token_lookup_batch_size 100_000\n      end\n\n      expect(config.token_lookup_batch_size).to eq(100_000)\n    end\n  end\n\n  describe \"strict_content_type\" do\n    it \"is false by default\" do\n      expect(config.enforce_content_type).to eq(false)\n    end\n\n    it \"can change the value\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        enforce_content_type\n      end\n\n      expect(config.enforce_content_type).to eq(true)\n    end\n  end\n\n  describe \"handle_auth_errors\" do\n    it \"is set to render by default\" do\n      expect(Doorkeeper.config.handle_auth_errors).to eq(:render)\n    end\n\n    it \"can change the value\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        handle_auth_errors :raise\n      end\n      expect(config.handle_auth_errors).to eq(:raise)\n    end\n  end\n\n  describe \"token_secret_strategy\" do\n    it \"is plain by default\" do\n      expect(config.token_secret_strategy).to eq(Doorkeeper::SecretStoring::Plain)\n      expect(config.token_secret_fallback_strategy).to eq(nil)\n    end\n\n    context \"when provided\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          hash_token_secrets\n        end\n      end\n\n      it \"will enable hashing for applications\" do\n        expect(config.token_secret_strategy).to eq(Doorkeeper::SecretStoring::Sha256Hash)\n        expect(config.token_secret_fallback_strategy).to eq(nil)\n      end\n    end\n\n    context \"when manually provided with invalid constant\" do\n      it \"raises an exception\" do\n        expect do\n          Doorkeeper.configure do\n            orm DOORKEEPER_ORM\n            hash_token_secrets using: \"does not exist\"\n          end\n        end.to raise_error(NameError)\n      end\n    end\n\n    context \"when manually provided with invalid option\" do\n      it \"raises an exception\" do\n        expect do\n          Doorkeeper.configure do\n            orm DOORKEEPER_ORM\n            hash_token_secrets using: \"Doorkeeper::SecretStoring::BCrypt\"\n          end\n        end.to raise_error(\n          ArgumentError,\n          /can only be used for storing application secrets/,\n        )\n      end\n    end\n\n    context \"when provided with fallback\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          hash_token_secrets fallback: :plain\n        end\n      end\n\n      it \"will enable hashing for applications\" do\n        expect(config.token_secret_strategy).to eq(Doorkeeper::SecretStoring::Sha256Hash)\n        expect(config.token_secret_fallback_strategy).to eq(Doorkeeper::SecretStoring::Plain)\n      end\n    end\n\n    describe \"hash_token_secrets together with reuse_access_token\" do\n      it \"will disable reuse_access_token\" do\n        expect(Rails.logger).to receive(:warn).with(/reuse_access_token will be disabled/)\n\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          reuse_access_token\n          hash_token_secrets\n        end\n\n        expect(config.reuse_access_token).to eq(false)\n      end\n    end\n  end\n\n  describe \"application_secret_strategy\" do\n    it \"is plain by default\" do\n      expect(config.application_secret_strategy).to eq(Doorkeeper::SecretStoring::Plain)\n      expect(config.application_secret_fallback_strategy).to eq(nil)\n    end\n\n    context \"when provided\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          hash_application_secrets\n        end\n      end\n\n      it \"will enable hashing for applications\" do\n        expect(config.application_secret_strategy).to eq(Doorkeeper::SecretStoring::Sha256Hash)\n        expect(config.application_secret_fallback_strategy).to eq(nil)\n      end\n    end\n\n    context \"when manually provided with invalid constant\" do\n      it \"raises an exception\" do\n        expect do\n          Doorkeeper.configure do\n            orm DOORKEEPER_ORM\n            hash_application_secrets using: \"does not exist\"\n          end\n        end.to raise_error(NameError)\n      end\n    end\n\n    context \"when provided with fallback\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          hash_application_secrets fallback: :plain\n        end\n      end\n\n      it \"will enable hashing for applications\" do\n        expect(config.application_secret_strategy).to eq(Doorkeeper::SecretStoring::Sha256Hash)\n        expect(config.application_secret_fallback_strategy).to eq(Doorkeeper::SecretStoring::Plain)\n      end\n    end\n  end\n\n  describe \"options deprecation\" do\n    it \"prints a warning message when an option is deprecated\" do\n      expect(Kernel).to receive(:warn).with(\n        /\\[DOORKEEPER\\] native_redirect_uri has been deprecated and will soon be removed/,\n      )\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        native_redirect_uri \"urn:ietf:wg:oauth:2.0:oob\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/doorkeeper/orm/active_record_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nif DOORKEEPER_ORM == :active_record\n  RSpec.describe Doorkeeper::Orm::ActiveRecord do\n    describe \".initialize_configured_associations\" do\n      it \"uses ActiveSupport.on_load(:active_record) to defer model loading\" do\n        expect(ActiveSupport).to receive(:on_load).with(:active_record)\n        described_class.initialize_configured_associations\n      end\n    end\n\n    # Reproduction test for https://github.com/doorkeeper-gem/doorkeeper/issues/1703\n    #\n    # Without the on_load wrapper, calling initialize_configured_associations\n    # eagerly triggers constantize on model class names (via access_token_model,\n    # access_grant_model, etc.), which forces ActiveRecord to load before\n    # config.active_record.* settings have been applied. This test verifies\n    # that the on_load block does NOT execute eagerly at registration time.\n    #\n    # See also: https://github.com/ngan/doorkeeper-activerecord-load-issue\n    describe \"deferral of model loading (issue #1703)\" do\n      it \"does not call config model accessors at registration time\" do\n        # Stub on_load to capture the block WITHOUT executing it,\n        # simulating the state before ActiveRecord is fully initialized.\n        allow(ActiveSupport).to receive(:on_load).with(:active_record)\n\n        expect(Doorkeeper.config).not_to receive(:application_model)\n        expect(Doorkeeper.config).not_to receive(:access_token_model)\n        expect(Doorkeeper.config).not_to receive(:access_grant_model)\n\n        described_class.initialize_configured_associations\n      end\n\n      it \"calls config model accessors only when the on_load hook fires\" do\n        deferred_block = nil\n\n        allow(ActiveSupport).to receive(:on_load).with(:active_record) do |*, &block|\n          deferred_block = block\n        end\n\n        described_class.initialize_configured_associations\n\n        # Block was captured but not yet executed\n        expect(deferred_block).not_to be_nil\n\n        # Now simulate ActiveRecord finishing initialization by executing the block\n        expect(Doorkeeper.config).to receive(:enable_application_owner?).and_return(false)\n        expect(Doorkeeper.config).to receive(:access_grant_model).and_return(Doorkeeper::AccessGrant)\n        expect(Doorkeeper.config).to receive(:access_token_model).and_return(Doorkeeper::AccessToken)\n\n        deferred_block.call\n      end\n    end\n\n    describe \"STI (Single Table Inheritance) support\" do\n      # Ensure STI subclasses work correctly with the ActiveSupport.on_load hook.\n      # See: https://github.com/doorkeeper-gem/doorkeeper/issues/1703\n      #      https://github.com/doorkeeper-gem/doorkeeper/issues/1513\n\n      context \"when application_class is a STI subclass of Doorkeeper::Application\" do\n        let!(:custom_application_class) do\n          Class.new(Doorkeeper::Application) do\n            def self.name\n              \"CustomStiApplication\"\n            end\n          end\n        end\n\n        before do\n          stub_const(\"CustomStiApplication\", custom_application_class)\n\n          Doorkeeper.configure do\n            orm DOORKEEPER_ORM\n            enable_application_owner\n            application_class \"CustomStiApplication\"\n          end\n\n          Doorkeeper.run_orm_hooks\n        end\n\n        it \"includes Ownership module in the STI subclass\" do\n          expect(CustomStiApplication.ancestors).to include(Doorkeeper::Models::Ownership)\n        end\n\n        it \"STI subclass responds to owner association\" do\n          instance = CustomStiApplication.new\n          expect(instance).to respond_to(:owner)\n        end\n      end\n\n      context \"when access_token_class is a STI subclass of Doorkeeper::AccessToken\" do\n        let!(:custom_token_class) do\n          Class.new(Doorkeeper::AccessToken) do\n            def self.name\n              \"CustomStiAccessToken\"\n            end\n          end\n        end\n\n        before do\n          stub_const(\"CustomStiAccessToken\", custom_token_class)\n\n          Doorkeeper.configure do\n            orm DOORKEEPER_ORM\n            access_token_class \"CustomStiAccessToken\"\n          end\n\n          Doorkeeper.run_orm_hooks\n        end\n\n        it \"includes PolymorphicResourceOwner::ForAccessToken in the STI subclass\" do\n          expect(CustomStiAccessToken.ancestors).to include(\n            Doorkeeper::Models::PolymorphicResourceOwner::ForAccessToken,\n          )\n        end\n      end\n\n      context \"when access_grant_class is a STI subclass of Doorkeeper::AccessGrant\" do\n        let!(:custom_grant_class) do\n          Class.new(Doorkeeper::AccessGrant) do\n            def self.name\n              \"CustomStiAccessGrant\"\n            end\n          end\n        end\n\n        before do\n          stub_const(\"CustomStiAccessGrant\", custom_grant_class)\n\n          Doorkeeper.configure do\n            orm DOORKEEPER_ORM\n            access_grant_class \"CustomStiAccessGrant\"\n          end\n\n          Doorkeeper.run_orm_hooks\n        end\n\n        it \"includes PolymorphicResourceOwner::ForAccessGrant in the STI subclass\" do\n          expect(CustomStiAccessGrant.ancestors).to include(\n            Doorkeeper::Models::PolymorphicResourceOwner::ForAccessGrant,\n          )\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/doorkeeper_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper do\n  describe \"#authenticate\" do\n    let(:request) { double }\n\n    it \"calls OAuth::Token#authenticate\" do\n      token_strategies = described_class.config.access_token_methods\n\n      expect(Doorkeeper::OAuth::Token).to receive(:authenticate)\n        .with(request, *token_strategies)\n\n      described_class.authenticate(request)\n    end\n\n    it \"accepts custom token strategies\" do\n      token_strategies = %i[first_way second_way]\n\n      expect(Doorkeeper::OAuth::Token).to receive(:authenticate)\n        .with(request, *token_strategies)\n\n      described_class.authenticate(request, token_strategies)\n    end\n  end\n\n  describe \"#setup_filter_parameters\" do\n    let(:original_filter_parameters) { Rails.application.config.filter_parameters.dup }\n\n    before { original_filter_parameters }\n\n    after do\n      Rails.application.config.filter_parameters.replace(original_filter_parameters)\n    end\n\n    it \"adds OAuth sensitive parameters to filter_parameters\" do\n      Rails.application.config.filter_parameters.clear\n\n      described_class.configure do\n        orm DOORKEEPER_ORM\n        resource_owner_authenticator { nil }\n      end\n      described_class.setup_filter_parameters\n\n      filter_params = Rails.application.config.filter_parameters\n      expect(filter_params).to include(\n        a_kind_of(Regexp).and(match(\"client_secret\"))\n      )\n      expect(filter_params).to include(\n        a_kind_of(Regexp).and(match(\"access_token\"))\n      )\n      expect(filter_params).to include(\n        a_kind_of(Regexp).and(match(\"refresh_token\"))\n      )\n      expect(filter_params).to include(\n        a_kind_of(Regexp).and(match(\"authentication_token\"))\n      )\n    end\n\n    it \"includes code parameter when authorization_code flow is enabled\" do\n      Rails.application.config.filter_parameters.clear\n\n      described_class.configure do\n        orm DOORKEEPER_ORM\n        resource_owner_authenticator { nil }\n        grant_flows %w[authorization_code]\n      end\n      described_class.setup_filter_parameters\n\n      filter_params = Rails.application.config.filter_parameters\n      expect(filter_params).to include(\n        a_kind_of(Regexp).and(match(\"code\"))\n      )\n    end\n\n    it \"does not include code parameter when authorization_code flow is not enabled\" do\n      Rails.application.config.filter_parameters.clear\n\n      described_class.configure do\n        orm DOORKEEPER_ORM\n        resource_owner_authenticator { nil }\n        grant_flows %w[client_credentials]\n      end\n      described_class.setup_filter_parameters\n\n      filter_params = Rails.application.config.filter_parameters\n      expect(filter_params.any? { |f| f.is_a?(Regexp) && f.match?(\"code\") }).to be(false)\n    end\n\n    it \"does not add duplicate filters when called multiple times\" do\n      Rails.application.config.filter_parameters.clear\n\n      described_class.configure do\n        orm DOORKEEPER_ORM\n        resource_owner_authenticator { nil }\n      end\n\n      2.times { described_class.setup_filter_parameters }\n\n      filters = Rails.application.config.filter_parameters.select { |f|\n        f.is_a?(Regexp) && f.match?(\"access_token\")\n      }\n      expect(filters.size).to eq(1)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/grant_flow/flow_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::GrantFlow::Flow do\n  subject(:flow) { described_class.new(name, **options) }\n\n  let(:name) { \"secret_handshake\" }\n  let(:options) { {} }\n\n  it \"reflects the given name\" do\n    expect(flow.name).to eq name\n  end\n\n  context \"with neither grant_type nor response_type\" do\n    it \"does not handle grant_type\" do\n      expect(flow.handles_grant_type?).to be false\n    end\n\n    it \"does not handle response_type\" do\n      expect(flow.handles_response_type?).to be false\n    end\n  end\n\n  context \"when given a grant_type to match\" do\n    let(:grant_type_matches) { \"secret_handshake\" }\n    let(:options) { { grant_type_matches: grant_type_matches } }\n\n    it \"handles grant_type\" do\n      expect(flow.handles_grant_type?).to be true\n    end\n\n    context \"when grant_type_matches is a string\" do\n      it \"matches grant_type values\" do\n        expect(flow.matches_grant_type?(grant_type_matches)).to be true\n      end\n    end\n\n    context \"when grant_type_matches is a regular expression\" do\n      let(:grant_type_matches) { /^secret_(.*)$/ }\n\n      it \"matches grant_type values\" do\n        expect(flow.matches_grant_type?(\"secret_boogie\")).to be true\n      end\n    end\n  end\n\n  context \"when given a response_type to match\" do\n    let(:response_type_matches) { \"secret_handshake\" }\n    let(:options) { { response_type_matches: response_type_matches } }\n\n    it \"handles response_type\" do\n      expect(flow.handles_response_type?).to be true\n    end\n\n    context \"when response_type_matches is a string\" do\n      it \"matches response_type values\" do\n        expect(flow.matches_response_type?(response_type_matches)).to be true\n      end\n    end\n\n    context \"when response_type_matches is a regular expression\" do\n      let(:response_type_matches) { /^secret_(.*)$/ }\n\n      it \"matches response_type values\" do\n        expect(flow.matches_response_type?(\"secret_boogie\")).to be true\n      end\n    end\n  end\n\n  context \"when given a response_mode to match\" do\n    let(:response_mode_matches) { %w[secret_handshake_1 secret_handshake_2] }\n    let(:options) { { response_mode_matches: response_mode_matches } }\n\n    it \"default response_mode value\" do\n      expect(flow.default_response_mode).to eq \"secret_handshake_1\"\n    end\n\n    it \"matches response_mode values\" do\n      expect(flow.matches_response_mode?(\"secret_handshake_2\")).to be true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/grant_flow_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::GrantFlow do\n  # Avoid global side effects\n  before do\n    @origin_grant_flows = Doorkeeper::GrantFlow::Registry.flows.deep_dup\n  end\n\n  after do\n    Doorkeeper::GrantFlow::Registry.flows = @origin_grant_flows\n  end\n\n  describe \"#register\" do\n    context \"with a name and options\" do\n      subject(:the_registered_flow) { described_class.get(name) }\n\n      let(:name) { \"puzzle_box\" }\n      let(:grant_type_matches) { \"tile_position\" }\n      let(:grant_type_strategy) { double }\n\n      before do\n        described_class.register(\n          name,\n          grant_type_matches: grant_type_matches,\n          grant_type_strategy: grant_type_strategy,\n        )\n      end\n\n      it \"creates a new Flow\" do\n        expect(the_registered_flow).to be_a(Doorkeeper::GrantFlow::Flow)\n      end\n\n      it \"passes on the given name\" do\n        expect(the_registered_flow.name).to eq name\n      end\n\n      it \"sets the options\" do\n        expect(the_registered_flow.grant_type_matches).to eq grant_type_matches\n        expect(the_registered_flow.grant_type_strategy).to eq grant_type_strategy\n      end\n\n      it \"shows a warning when trying to register already existing flow\" do\n        expect(::Kernel).to receive(:warn).with(/already registered/)\n\n        described_class.register(\n          name,\n          grant_type_matches: grant_type_matches,\n          grant_type_strategy: grant_type_strategy,\n        )\n      end\n    end\n\n    context \"with an existing flow\" do\n      let(:existing_flow) { Doorkeeper::GrantFlow::Flow.new(\"light\") }\n\n      before do\n        described_class.register(existing_flow)\n      end\n\n      it \"records the existing Flow using its name\" do\n        expect(described_class.get(existing_flow.name)).to eq existing_flow\n      end\n    end\n  end\n\n  describe \"#register_alias\" do\n    it \"allows to alias multiple grant flows\" do\n      described_class.register_alias(\"implicit_oidc\", as: [\"token\", \"id_token\", \"id_token token\"])\n\n      expect(described_class.aliases).to include(implicit_oidc: [\"token\", \"id_token\", \"id_token token\"])\n    end\n\n    it \"allows to alias a single grant flow\" do\n      described_class.register_alias(\"implicit_oidc\", as: :id_token)\n\n      expect(described_class.aliases).to include(implicit_oidc: [:id_token])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/models/concerns/write_to_primary_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::Models::Concerns::WriteToPrimary do\n  let(:test_class) do\n    Class.new do\n      include Doorkeeper::Models::Concerns::WriteToPrimary\n\n      def self.create_record\n        with_primary_role do\n          \"created\"\n        end\n      end\n    end\n  end\n\n  describe \".with_primary_role\" do\n    context \"when ActiveRecord is not defined\" do\n      before(:each) do\n        # Override the global before hook by skipping DatabaseCleaner for this context\n        # This is needed because removing ActiveRecord causes DatabaseCleaner to fail\n      end\n\n      after(:each) do\n        # Override the global after hook - don't try to clean database\n      end\n\n      around do |example|\n        # Save original ActiveRecord\n        original_active_record = Object.const_get(\"ActiveRecord\")\n        \n        begin\n          # Temporarily hide ActiveRecord constant\n          Object.send(:remove_const, \"ActiveRecord\")\n          \n          # Run the test\n          Doorkeeper.configure do\n            orm :active_record\n            enable_multiple_database_roles\n          end\n          \n          example.run\n        ensure\n          # Restore ActiveRecord for cleanup\n          Object.const_set(\"ActiveRecord\", original_active_record)\n        end\n      end\n\n      it \"executes block without connected_to when ActiveRecord is not available\" do\n        expect(test_class.create_record).to eq(\"created\")\n      end\n    end\n\n    context \"when enable_multiple_database_roles is disabled\" do\n      before do\n        Doorkeeper.configure do\n          orm :active_record\n          # enable_multiple_database_roles is disabled by default\n        end\n      end\n\n      it \"executes block without connected_to\" do\n        expect(ActiveRecord::Base).not_to receive(:connected_to)\n        expect(test_class.create_record).to eq(\"created\")\n      end\n    end\n\n    context \"when enable_multiple_database_roles is enabled\" do\n      before do\n        Doorkeeper.configure do\n          orm :active_record\n          enable_multiple_database_roles\n        end\n      end\n\n      context \"when ActiveRecord supports connected_to\" do\n        before do\n          allow(ActiveRecord::Base).to receive(:respond_to?)\n            .with(:connected_to)\n            .and_return(true)\n        end\n\n        it \"wraps block in connected_to with writing role\" do\n          expect(ActiveRecord::Base).to receive(:connected_to)\n            .with(role: :writing)\n            .and_yield\n\n          expect(test_class.create_record).to eq(\"created\")\n        end\n      end\n\n      context \"when ActiveRecord does not support connected_to\" do\n        before do\n          allow(ActiveRecord::Base).to receive(:respond_to?)\n            .with(:connected_to)\n            .and_return(false)\n        end\n\n        it \"executes block without connected_to\" do\n          expect(ActiveRecord::Base).not_to receive(:connected_to)\n          expect(test_class.create_record).to eq(\"created\")\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/models/expirable_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::Models::Expirable do\n  subject(:fake_object) do\n    Class.new do\n      include Doorkeeper::Models::Expirable\n    end.new\n  end\n\n  before do\n    allow(fake_object).to receive(:created_at).and_return(1.minute.ago)\n  end\n\n  describe \"#expired?\" do\n    it \"is not expired if time has not passed\" do\n      allow(fake_object).to receive(:expires_in).and_return(2.minutes)\n      expect(fake_object).not_to be_expired\n    end\n\n    it \"is expired if time has passed\" do\n      allow(fake_object).to receive(:expires_in).and_return(10.seconds)\n      expect(fake_object).to be_expired\n    end\n\n    it \"is not expired if expires_in is not set\" do\n      allow(fake_object).to receive(:expires_in).and_return(nil)\n      expect(fake_object).not_to be_expired\n    end\n  end\n\n  describe \"#expires_in_seconds\" do\n    it \"returns the amount of time remaining until the token is expired\" do\n      allow(fake_object).to receive(:expires_in).and_return(2.minutes)\n      expect(fake_object.expires_in_seconds).to eq(60)\n    end\n\n    it \"returns 0 when expired\" do\n      allow(fake_object).to receive(:expires_in).and_return(30.seconds)\n      expect(fake_object.expires_in_seconds).to eq(0)\n    end\n\n    it \"returns nil when expires_in is nil\" do\n      allow(fake_object).to receive(:expires_in).and_return(nil)\n      expect(fake_object.expires_in_seconds).to be_nil\n    end\n  end\n\n  describe \"#expires_at\" do\n    it \"returns the expiration time of the token\" do\n      allow(fake_object).to receive(:expires_in).and_return(2.minutes)\n      expect(fake_object.expires_at).to be_a(Time)\n    end\n\n    it \"returns nil when expires_in is nil\" do\n      allow(fake_object).to receive(:expires_in).and_return(nil)\n      expect(fake_object.expires_at).to be_nil\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/models/reusable_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::Models::Reusable do\n  subject(:fake_object) do\n    Class.new do\n      include Doorkeeper::Models::Reusable\n    end.new\n  end\n\n  describe \"#reusable?\" do\n    it \"is reusable if its expires_in is nil\" do\n      allow(fake_object).to receive(:expired?).and_return(false)\n      allow(fake_object).to receive(:expires_in).and_return(nil)\n      expect(fake_object).to be_reusable\n    end\n\n    it \"is reusable if its expiry has crossed reusable limit\" do\n      allow(fake_object).to receive(:expired?).and_return(false)\n      allow(Doorkeeper.configuration).to receive(:token_reuse_limit).and_return(90)\n      allow(fake_object).to receive(:expires_in).and_return(100.seconds)\n      allow(fake_object).to receive(:expires_in_seconds).and_return(20.seconds)\n      expect(fake_object).to be_reusable\n    end\n\n    it \"is not reusable if its expiry has crossed reusable limit\" do\n      allow(fake_object).to receive(:expired?).and_return(false)\n      allow(Doorkeeper.configuration).to receive(:token_reuse_limit).and_return(90)\n      allow(fake_object).to receive(:expires_in).and_return(100.seconds)\n      allow(fake_object).to receive(:expires_in_seconds).and_return(5.seconds)\n      expect(fake_object).not_to be_reusable\n    end\n\n    it \"is not reusable if it is already expired\" do\n      allow(fake_object).to receive(:expired?).and_return(true)\n      expect(fake_object).not_to be_reusable\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/models/revocable_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::Models::Revocable do\n  subject(:fake_object) do\n    Class.new do\n      include Doorkeeper::Models::Revocable\n    end.new\n  end\n\n  describe \"#revoke\" do\n    let(:revoked_at) { nil }\n\n    before do\n      allow(fake_object).to receive(:revoked_at).and_return(revoked_at)\n    end\n\n    it \"updates :revoked_at attribute with current time\" do\n      utc = double utc: double\n      clock = double now: utc\n      expect(fake_object).to receive(:update_attribute).with(:revoked_at, clock.now.utc)\n      fake_object.revoke(clock)\n    end\n\n    context \"when the object is already revoked\" do\n      let(:revoked_at) { Time.now.utc - 1000 }\n\n      it \"does not update :revoked_at attribute\" do\n        expect(fake_object).not_to receive(:update_attribute)\n      end\n    end\n  end\n\n  describe \"#revoked?\" do\n    it \"is revoked if :revoked_at has passed\" do\n      allow(fake_object).to receive(:revoked_at).and_return(Time.now.utc - 1000)\n      expect(fake_object).to be_revoked\n    end\n\n    it \"is not revoked if :revoked_at has not passed\" do\n      allow(fake_object).to receive(:revoked_at).and_return(Time.now.utc + 1000)\n      expect(fake_object).not_to be_revoked\n    end\n\n    it \"is not revoked if :revoked_at is not set\" do\n      allow(fake_object).to receive(:revoked_at).and_return(nil)\n      expect(fake_object).not_to be_revoked\n    end\n  end\n\n  describe \"#revoke_previous_refresh_token!\" do\n    it \"revokes the previous token if exists and resets the `previous_refresh_token` attribute\" do\n      previous_token = FactoryBot.create(\n        :access_token,\n        refresh_token: \"refresh_token\",\n      )\n      current_token = FactoryBot.create(\n        :access_token,\n        previous_refresh_token: previous_token.refresh_token,\n      )\n\n      current_token.revoke_previous_refresh_token!\n\n      expect(current_token.previous_refresh_token).to be_empty\n      expect(previous_token.reload).to be_revoked\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/models/scopes_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::Models::Scopes do\n  subject(:fake_object) do\n    Class.new(Struct.new(:scopes)) do\n      include Doorkeeper::Models::Scopes\n    end.new\n  end\n\n  before do\n    fake_object[:scopes] = \"public admin\"\n  end\n\n  describe \"#scopes\" do\n    it \"is a `Scopes` class\" do\n      expect(fake_object.scopes).to be_a(Doorkeeper::OAuth::Scopes)\n    end\n\n    it \"includes scopes\" do\n      expect(fake_object.scopes).to include(\"public\")\n    end\n  end\n\n  describe \"#scopes=\" do\n    it \"accepts String\" do\n      fake_object.scopes = \"private admin\"\n      expect(fake_object.scopes_string).to eq(\"private admin\")\n    end\n\n    it \"accepts Array\" do\n      fake_object.scopes = %w[private admin]\n      expect(fake_object.scopes_string).to eq(\"private admin\")\n    end\n\n    it \"ignores duplicated scopes\" do\n      fake_object.scopes = %w[private admin admin]\n      expect(fake_object.scopes_string).to eq(\"private admin\")\n\n      fake_object.scopes = \"private admin admin\"\n      expect(fake_object.scopes_string).to eq(\"private admin\")\n    end\n  end\n\n  describe \"#scopes_string\" do\n    it \"is a `Scopes` class\" do\n      expect(fake_object.scopes_string).to eq(\"public admin\")\n    end\n  end\n\n  describe \"#includes_scope?\" do\n    it \"returns true if at least one scope is included\" do\n      expect(fake_object.includes_scope?(\"public\", \"private\")).to be true\n    end\n\n    it \"returns false if no scopes are included\" do\n      expect(fake_object.includes_scope?(\"teacher\", \"student\")).to be false\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/models/secret_storable_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::Models::SecretStorable do\n  let(:clazz) do\n    Class.new do\n      include Doorkeeper::Models::SecretStorable\n\n      def self.find_by(*)\n        raise \"stub this\"\n      end\n\n      def update_column(*)\n        raise \"stub this\"\n      end\n\n      def token\n        raise \"stub this\"\n      end\n    end\n  end\n  let(:strategy) { clazz.secret_strategy }\n\n  describe \".find_by_plaintext_token\" do\n    subject(:result) { clazz.send(:find_by_plaintext_token, \"attr\", \"input\") }\n\n    it \"forwards to the secret_strategy\" do\n      expect(strategy)\n        .to receive(:transform_secret)\n        .with(\"input\")\n        .and_return \"found\"\n\n      expect(clazz)\n        .to receive(:find_by)\n        .with(\"attr\" => \"found\")\n        .and_return \"result\"\n\n      expect(result).to eq \"result\"\n    end\n\n    it \"calls find_by_fallback_token if not found\" do\n      expect(clazz)\n        .to receive(:find_by)\n        .with(\"attr\" => \"input\")\n        .and_return nil\n\n      expect(clazz)\n        .to receive(:find_by_fallback_token)\n        .with(\"attr\", \"input\")\n        .and_return \"fallback\"\n\n      expect(result).to eq \"fallback\"\n    end\n  end\n\n  describe \".find_by_fallback_token\" do\n    subject(:result) { clazz.send(:find_by_fallback_token, \"attr\", \"input\") }\n\n    let(:fallback) { double(::Doorkeeper::SecretStoring::Plain) }\n\n    it \"returns nil if none defined\" do\n      expect(clazz.fallback_secret_strategy).to eq nil\n      expect(result).to eq nil\n    end\n\n    context \"when a fallback strategy is defined\" do\n      before do\n        allow(clazz).to receive(:fallback_secret_strategy).and_return(fallback)\n      end\n\n      context \"when resource is defined\" do\n        let(:resource) { double(\"Token model\") }\n\n        it \"calls the strategy for lookup\" do\n          expect(clazz)\n            .to receive(:find_by)\n            .with(\"attr\" => \"fallback\")\n            .and_return(resource)\n\n          expect(fallback)\n            .to receive(:transform_secret)\n            .with(\"input\")\n            .and_return(\"fallback\")\n\n          # store_secret will call the resource\n          expect(resource)\n            .to receive(:attr=)\n            .with(\"new value\")\n\n          # It will upgrade the secret automatically using the current strategy\n          expect(strategy)\n            .to receive(:transform_secret)\n            .with(\"input\")\n            .and_return(\"new value\")\n\n          expect(resource).to receive(:update).with(\"attr\" => \"new value\")\n          expect(result).to eq resource\n        end\n      end\n\n      context \"when resource is not defined\" do\n        before do\n          allow(clazz).to receive(:fallback_secret_strategy).and_return(fallback)\n        end\n\n        it \"returns nil\" do\n          expect(clazz)\n            .to receive(:find_by)\n            .with(\"attr\" => \"fallback\")\n            .and_return(nil)\n\n          expect(fallback)\n            .to receive(:transform_secret)\n            .with(\"input\")\n            .and_return(\"fallback\")\n\n          # It does not find a token even with the fallback method\n          expect(result).to be_nil\n        end\n      end\n    end\n  end\n\n  describe \".secret_strategy\" do\n    it \"defaults to plain strategy\" do\n      expect(strategy).to eq Doorkeeper::SecretStoring::Plain\n    end\n  end\n\n  describe \".fallback_secret_strategy\" do\n    it \"defaults to nil\" do\n      expect(clazz.fallback_secret_strategy).to eq nil\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/authorization/code_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::Authorization::Code do\n  let(:pre_auth) do\n    double(\n      :pre_auth,\n      client: application,\n      redirect_uri: \"https://example.com/callback\",\n      scopes: Doorkeeper::OAuth::Scopes.from_string(\"public\"),\n      code_challenge: nil,\n      code_challenge_method: nil,\n      custom_access_token_attributes: {},\n    )\n  end\n  let(:resource_owner) { FactoryBot.create(:resource_owner) }\n  let(:application) { FactoryBot.create(:application) }\n  let(:authorization) { described_class.new(pre_auth, resource_owner) }\n\n  describe \"#issue_token! with read replica support\" do\n    context \"when enable_multiple_database_roles is enabled\" do\n      before do\n        Doorkeeper.configure do\n          orm :active_record\n          enable_multiple_database_roles\n        end\n      end\n\n      it \"creates access grant using primary database role\" do\n        expect(ActiveRecord::Base).to receive(:connected_to).with(role: :writing).and_call_original\n\n        token = authorization.issue_token!\n        expect(token).to be_persisted\n        expect(token.application_id).to eq(application.id)\n      end\n    end\n\n    context \"when enable_multiple_database_roles is disabled\" do\n      before do\n        Doorkeeper.configure do\n          orm :active_record\n          # enable_multiple_database_roles is disabled by default\n        end\n      end\n\n      it \"creates access grant without explicit role switching\" do\n        expect(ActiveRecord::Base).not_to receive(:connected_to)\n\n        token = authorization.issue_token!\n        expect(token).to be_persisted\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/authorization/uri_builder_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::Authorization::URIBuilder do\n  describe \".uri_with_query\" do\n    it \"returns the uri with query\" do\n      uri = described_class.uri_with_query \"http://example.com/\", parameter: \"value\"\n      expect(uri).to eq(\"http://example.com/?parameter=value\")\n    end\n\n    it \"rejects nil values\" do\n      uri = described_class.uri_with_query \"http://example.com/\", parameter: \"\"\n      expect(uri).to eq(\"http://example.com/?\")\n    end\n\n    it \"preserves original query parameters\" do\n      uri = described_class.uri_with_query \"http://example.com/?query1=value\", parameter: \"value\"\n      expect(uri).to match(/query1=value/)\n      expect(uri).to match(/parameter=value/)\n    end\n  end\n\n  describe \".uri_with_fragment\" do\n    it \"returns uri with parameters as fragments\" do\n      uri = described_class.uri_with_fragment \"http://example.com/\", parameter: \"value\"\n      expect(uri).to eq(\"http://example.com/#parameter=value\")\n    end\n\n    it \"preserves original query parameters\" do\n      uri = described_class.uri_with_fragment \"http://example.com/?query1=value1\", parameter: \"value\"\n      expect(uri).to eq(\"http://example.com/?query1=value1#parameter=value\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/authorization_code_request_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::AuthorizationCodeRequest do\n  subject(:request) do\n    described_class.new(server, grant, client, params)\n  end\n\n  let(:server) do\n    double :server,\n           access_token_expires_in: 2.days,\n           refresh_token_enabled?: false,\n           custom_access_token_expires_in: lambda { |context|\n             context.grant_type == Doorkeeper::OAuth::AUTHORIZATION_CODE ? 1234 : nil\n           }\n  end\n\n  let(:resource_owner) { FactoryBot.create :resource_owner }\n  let(:grant) do\n    FactoryBot.create :access_grant,\n                      resource_owner_id: resource_owner.id,\n                      resource_owner_type: resource_owner.class.name\n  end\n  let(:client) { grant.application }\n  let(:redirect_uri) { client.redirect_uri }\n  let(:params) { { redirect_uri: redirect_uri } }\n\n  before do\n    allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(true)\n  end\n\n  it \"issues a new token for the client\" do\n    expect do\n      request.authorize\n    end.to change { client.reload.access_tokens.count }.by(1)\n\n    expect(client.reload.access_tokens.max_by(&:created_at).expires_in).to eq(1234)\n  end\n\n  it \"issues the token with same grant's scopes\" do\n    request.authorize\n    expect(Doorkeeper::AccessToken.last.scopes).to eq(grant.scopes)\n  end\n\n  it \"revokes the grant\" do\n    expect { request.authorize }.to(change { grant.reload.accessible? })\n  end\n\n  it \"requires the grant to be accessible\" do\n    grant.revoke\n    request.validate\n    expect(request.error).to eq(Doorkeeper::Errors::InvalidGrant)\n  end\n\n  it \"requires the grant\" do\n    request = described_class.new(server, nil, client, params)\n    request.validate\n    expect(request.error).to eq(Doorkeeper::Errors::InvalidGrant)\n  end\n\n  it \"requires the client\" do\n    request = described_class.new(server, grant, nil, params)\n    request.validate\n    expect(request.error).to eq(Doorkeeper::Errors::InvalidClient)\n  end\n\n  it \"requires the redirect_uri\" do\n    request = described_class.new(server, grant, nil, params.except(:redirect_uri))\n    request.validate\n    expect(request.error).to eq(Doorkeeper::Errors::InvalidRequest)\n    expect(request.missing_param).to eq(:redirect_uri)\n  end\n\n  it \"matches the redirect_uri with grant's one\" do\n    request = described_class.new(server, grant, client, params.merge(redirect_uri: \"http://other.com\"))\n    request.validate\n    expect(request.error).to eq(Doorkeeper::Errors::InvalidGrant)\n  end\n\n  it \"matches the client with grant's one\" do\n    other_client = FactoryBot.create :application\n    request = described_class.new(server, grant, other_client, params)\n    request.validate\n    expect(request.error).to eq(Doorkeeper::Errors::InvalidGrant)\n  end\n\n  it \"skips token creation if there is a matching one reusable\" do\n    scopes = grant.scopes\n\n    Doorkeeper.configure do\n      orm DOORKEEPER_ORM\n      reuse_access_token\n      default_scopes(*scopes)\n    end\n\n    FactoryBot.create(\n      :access_token,\n      application_id: client.id,\n      resource_owner_id: grant.resource_owner_id,\n      resource_owner_type: grant.resource_owner_type,\n      scopes: grant.scopes.to_s,\n    )\n\n    expect { request.authorize }.not_to(change { Doorkeeper::AccessToken.count })\n  end\n\n  it \"creates token if there is a matching one but non reusable\" do\n    scopes = grant.scopes\n\n    Doorkeeper.configure do\n      orm DOORKEEPER_ORM\n      reuse_access_token\n      default_scopes(*scopes)\n    end\n\n    FactoryBot.create(\n      :access_token,\n      application_id: client.id,\n      resource_owner_id: grant.resource_owner_id,\n      resource_owner_type: grant.resource_owner_type,\n      scopes: grant.scopes.to_s,\n    )\n\n    allow_any_instance_of(Doorkeeper::AccessToken).to receive(:reusable?).and_return(false)\n\n    expect { request.authorize }.to change { Doorkeeper::AccessToken.count }.by(1)\n  end\n\n  it \"calls configured request callback methods\" do\n    expect(Doorkeeper.configuration.before_successful_strategy_response)\n      .to receive(:call).with(request).once\n    expect(Doorkeeper.configuration.after_successful_strategy_response)\n      .to receive(:call).with(request, instance_of(Doorkeeper::OAuth::TokenResponse)).once\n\n    request.authorize\n  end\n\n  context \"when redirect_uri contains some query params\" do\n    let(:redirect_uri) { \"#{client.redirect_uri}?query=q\" }\n\n    it \"allows query params\" do\n      request.validate\n      expect(request.error).to eq(nil)\n    end\n  end\n\n  context \"when redirect_uri is not an URI\" do\n    let(:redirect_uri) { \"123d#!s\" }\n\n    it \"responds with invalid_grant\" do\n      request.validate\n      expect(request.error).to eq(Doorkeeper::Errors::InvalidGrant)\n    end\n  end\n\n  context \"when redirect_uri is the native one\" do\n    let(:redirect_uri) { \"urn:ietf:wg:oauth:2.0:oob\" }\n\n    it \"invalidates when redirect_uri of the grant is not native\" do\n      request.validate\n      expect(request.error).to eq(Doorkeeper::Errors::InvalidGrant)\n    end\n\n    it \"validates when redirect_uri of the grant is also native\" do\n      allow(grant).to receive(:redirect_uri) { redirect_uri }\n      request.validate\n      expect(request.error).to eq(nil)\n    end\n  end\n\n  context \"when using PKCE params\" do\n    context \"when force_pkce is enabled\" do\n      before do\n        allow_any_instance_of(Doorkeeper::Config).to receive(:force_pkce?).and_return(true)\n      end\n\n      context \"when the app is confidential\" do\n        it \"issues a new token for the client\" do\n          expect do\n            request.authorize\n          end.to change { client.reload.access_tokens.count }.by(1)\n        end\n      end\n\n      context \"when the app is not confidential\" do\n        before do\n          client.update(confidential: false)\n        end\n\n        it \"does not issue a token\" do\n          expect do\n            request.authorize\n          end.not_to change { client.reload.access_tokens.count }\n        end\n      end\n\n      context \"when the app is missing\" do\n        it \"does not assume non-confidential and forcibly validate pkce params\" do\n          request = described_class.new(server, grant, nil, params)\n          request.validate\n          expect(request.error).to eq(Doorkeeper::Errors::InvalidClient)\n        end\n      end\n    end\n\n    context \"when PKCE is supported\" do\n      before do\n        allow(Doorkeeper::AccessGrant).to receive(:pkce_supported?).and_return(true)\n\n        grant.code_challenge = \"a45a9fea-0676-477e-95b1-a40f72ac3cfb\"\n        grant.code_challenge_method = \"plain\"\n      end\n\n      it \"validates when code_verifier is present\" do\n        params[:code_verifier] = grant.code_challenge\n        request.validate\n\n        expect(request.error).to eq(nil)\n      end\n\n      it \"validates when both code_verifier and code_challenge are blank\" do\n        params[:code_verifier] = grant.code_challenge = \"\"\n        request.validate\n\n        expect(request.error).to eq(nil)\n      end\n\n      it \"invalidates when code_verifier is missing\" do\n        request.validate\n\n        expect(request.error).to eq(Doorkeeper::Errors::InvalidRequest)\n        expect(request.missing_param).to eq(:code_verifier)\n      end\n\n      it \"invalidates when code_verifier is the wrong value\" do\n        params[:code_verifier] = \"foobar\"\n        request.validate\n\n        expect(request.error).to eq(Doorkeeper::Errors::InvalidGrant)\n      end\n\n      context \"when PKCE code challenge methods is set to only S256\" do\n        before do\n          Doorkeeper.configure do\n            pkce_code_challenge_methods [\"S256\"]\n          end\n        end\n\n        it \"validates when code_verifier is S256\" do\n          params[:code_verifier] = grant.code_challenge = \"S256\"\n          request.validate\n\n          expect(request.error).to eq(nil)\n        end\n\n        it \"invalidates when code_verifier is plain\" do\n          params[:code_verifier] = \"plain\"\n          request.validate\n\n          expect(request.error).to eq(Doorkeeper::Errors::InvalidGrant)\n        end\n      end\n    end\n\n    context \"when PKCE is not supported\" do\n      before do\n        allow(Doorkeeper::AccessGrant).to receive(:pkce_supported?).and_return(false)\n      end\n\n      it \"validates when code_verifier is present\" do\n        params[:code_verifier] = \"foobar\"\n        request.validate\n\n        expect(request.error).to be_nil\n      end\n    end\n  end\n\n  context \"when revoke_previous_authorization_code_token is false\" do\n    before do\n      allow(Doorkeeper.config).to receive(:revoke_previous_authorization_code_token?).and_return(false)\n    end\n\n    it \"does not revoke the previous token\" do\n      previous_token = FactoryBot.create(\n        :access_token,\n        application_id: client.id,\n        resource_owner_id: grant.resource_owner_id,\n        resource_owner_type: grant.resource_owner_type,\n        scopes: grant.scopes.to_s,\n      )\n\n      expect { request.authorize }.not_to(change { previous_token.reload.revoked_at })\n    end\n  end\n\n  context \"when revoke_previous_authorization_code_token is true\" do\n    before do\n      allow(Doorkeeper.config).to receive(:revoke_previous_authorization_code_token?).and_return(true)\n    end\n\n    it \"revokes the previous token\" do\n      previous_token = FactoryBot.create(\n        :access_token,\n        application_id: client.id,\n        resource_owner_id: grant.resource_owner_id,\n        resource_owner_type: grant.resource_owner_type,\n        scopes: grant.scopes.to_s,\n      )\n\n      expect { request.authorize }.to(change { previous_token.reload.revoked_at })\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/base_request_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::BaseRequest do\n  subject(:request) do\n    described_class.new\n  end\n\n  let(:access_token) do\n    double :access_token,\n           plaintext_token: \"some-token\",\n           expires_in: \"3600\",\n           expires_in_seconds: \"300\",\n           scopes_string: \"two scopes\",\n           plaintext_refresh_token: \"some-refresh-token\",\n           token_type: \"Bearer\",\n           created_at: 0\n  end\n\n  let(:client) { Doorkeeper::Application.new(id: \"1\") }\n\n  let(:scopes_array) { %w[public write] }\n\n  let(:server) do\n    double :server,\n           access_token_expires_in: 100,\n           custom_access_token_expires_in: ->(_context) { nil },\n           refresh_token_enabled?: false\n  end\n\n  before do\n    allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(true)\n  end\n\n  describe \"#authorize\" do\n    before do\n      allow(request).to receive(:access_token).and_return(access_token)\n    end\n\n    it \"validates itself\" do\n      expect(request).to receive(:validate).once\n      request.authorize\n    end\n\n    context \"when valid\" do\n      before do\n        allow(request).to receive(:valid?).and_return(true)\n      end\n\n      it \"calls callback methods\" do\n        expect(request).to receive(:before_successful_response).once\n        expect(request).to receive(:after_successful_response).once\n        request.authorize\n      end\n\n      it \"returns a TokenResponse object\" do\n        result = request.authorize\n\n        expect(result).to be_an_instance_of(Doorkeeper::OAuth::TokenResponse)\n        expect(result.body).to eq(\n          Doorkeeper::OAuth::TokenResponse.new(access_token).body,\n        )\n      end\n    end\n\n    context \"when invalid\" do\n      context \"with error other than invalid_request\" do\n        before do\n          allow(request).to receive(:valid?).and_return(false)\n          allow(request).to receive(:error).and_return(Doorkeeper::Errors::ServerError)\n          allow(request).to receive(:state).and_return(\"hello\")\n        end\n\n        it \"returns an ErrorResponse object\" do\n          result = request.authorize\n\n          expect(result).to be_an_instance_of(Doorkeeper::OAuth::ErrorResponse)\n\n          expect(result.body).to eq(\n            error: :server_error,\n            error_description: translated_error_message(:server_error),\n            state: \"hello\",\n          )\n        end\n      end\n\n      context \"with invalid_request error\" do\n        before do\n          allow(request).to receive(:valid?).and_return(false)\n          allow(request).to receive(:error).and_return(Doorkeeper::Errors::InvalidRequest)\n          allow(request).to receive(:state).and_return(\"hello\")\n        end\n\n        it \"returns an InvalidRequestResponse object\" do\n          result = request.authorize\n\n          expect(result).to be_an_instance_of(Doorkeeper::OAuth::InvalidRequestResponse)\n\n          expect(result.body).to eq(\n            error: :invalid_request,\n            error_description: translated_invalid_request_error_message(:unknown, :unknown),\n            state: \"hello\",\n          )\n        end\n      end\n    end\n  end\n\n  describe \"#default_scopes\" do\n    it \"delegates to the server\" do\n      expect(request).to receive(:server).and_return(server).once\n      expect(server).to receive(:default_scopes).once\n\n      request.default_scopes\n    end\n  end\n\n  describe \"#find_or_create_access_token\" do\n    let(:resource_owner) { FactoryBot.build_stubbed(:resource_owner) }\n\n    it \"returns an instance of AccessToken\" do\n      result = request.find_or_create_access_token(\n        client,\n        resource_owner,\n        \"public\",\n        {},\n        server,\n      )\n\n      expect(result).to be_an_instance_of(Doorkeeper::AccessToken)\n    end\n\n    it \"respects custom_access_token_expires_in\" do\n      server = double(\n        :server,\n        access_token_expires_in: 100,\n        custom_access_token_expires_in: ->(context) { context.scopes == \"public\" ? 500 : nil },\n        refresh_token_enabled?: false,\n      )\n\n      allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(true)\n\n      result = request.find_or_create_access_token(\n        client,\n        resource_owner,\n        \"public\",\n        {},\n        server,\n      )\n      expect(result.expires_in).to be(500)\n    end\n\n    it \"respects use_refresh_token with a block\" do\n      server = double(\n        :server,\n        access_token_expires_in: 100,\n        custom_access_token_expires_in: ->(_context) { nil },\n        refresh_token_enabled?: lambda { |context|\n          context.scopes == \"public\"\n        },\n      )\n\n      allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(true)\n\n      result = request.find_or_create_access_token(\n        client,\n        resource_owner,\n        \"public\",\n        {},\n        server,\n      )\n      expect(result.refresh_token).not_to be_nil\n\n      result = request.find_or_create_access_token(\n        client,\n        resource_owner,\n        \"private\",\n        {},\n        server,\n      )\n      expect(result.refresh_token).to be_nil\n    end\n  end\n\n  describe \"#scopes\" do\n    context \"when @original_scopes is present\" do\n      before do\n        request.instance_variable_set(:@original_scopes, \"public write\")\n      end\n\n      it \"returns array of @original_scopes\" do\n        result = request.scopes\n\n        expect(result).to eq(scopes_array)\n      end\n    end\n\n    context \"when @original_scopes is blank\" do\n      before do\n        request.instance_variable_set(:@original_scopes, \"\")\n      end\n\n      it \"calls #default_scopes\" do\n        allow(request).to receive(:server).and_return(server).once\n        allow(server).to receive(:default_scopes).and_return(scopes_array).once\n\n        result = request.scopes\n\n        expect(result).to eq(scopes_array)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/base_response_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::BaseResponse do\n  subject(:response) do\n    described_class.new\n  end\n\n  describe \"#body\" do\n    it \"returns an empty Hash\" do\n      expect(response.body).to eq({})\n    end\n  end\n\n  describe \"#description\" do\n    it \"returns an empty String\" do\n      expect(response.description).to eq(\"\")\n    end\n  end\n\n  describe \"#headers\" do\n    it \"returns an empty Hash\" do\n      expect(response.headers).to eq({})\n    end\n  end\n\n  describe \"#redirectable?\" do\n    it \"returns false\" do\n      expect(response.redirectable?).to eq(false)\n    end\n  end\n\n  describe \"#redirect_uri\" do\n    it \"returns an empty String\" do\n      expect(response.redirect_uri).to eq(\"\")\n    end\n  end\n\n  describe \"#status\" do\n    it \"returns :ok\" do\n      expect(response.status).to eq(:ok)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/client/credentials_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nclass Doorkeeper::OAuth::Client\n  describe Credentials do\n    let(:client_id) { \"some-uid\" }\n    let(:client_secret) { \"some-secret\" }\n\n    it \"is blank when the uid in credentials is blank\" do\n      expect(described_class.new(nil, nil)).to be_blank\n      expect(described_class.new(nil, \"something\")).to be_blank\n      expect(described_class.new(\"something\", nil)).to be_present\n      expect(described_class.new(\"something\", \"something\")).to be_present\n    end\n\n    describe \".from_request\" do\n      let(:request) { double.as_null_object }\n\n      let(:method) do\n        ->(_request) { %w[uid secret] }\n      end\n\n      it \"accepts anything that responds to #call\" do\n        expect(method).to receive(:call).with(request)\n        described_class.from_request request, method\n      end\n\n      it \"delegates methods received as symbols to Credentials class\" do\n        expect(described_class).to receive(:from_params).with(request)\n        described_class.from_request request, :from_params\n      end\n\n      it \"stops at the first credentials found\" do\n        not_called_method = double\n        expect(not_called_method).not_to receive(:call)\n        described_class.from_request request, ->(_) {}, method, not_called_method\n      end\n\n      it \"returns new Credentials\" do\n        credentials = described_class.from_request request, method\n        expect(credentials).to be_a(described_class)\n      end\n\n      it \"returns uid and secret from extractor method\" do\n        credentials = described_class.from_request request, method\n        expect(credentials.uid).to    eq(\"uid\")\n        expect(credentials.secret).to eq(\"secret\")\n      end\n    end\n\n    describe \".from_params\" do\n      it \"returns credentials from parameters when Authorization header is not available\" do\n        request = double parameters: { client_id: client_id, client_secret: client_secret }\n        uid, secret = described_class.from_params(request)\n\n        expect(uid).to eq(\"some-uid\")\n        expect(secret).to eq(\"some-secret\")\n      end\n\n      it \"is blank when there are no credentials\" do\n        request = double parameters: {}\n        uid, secret = described_class.from_params(request)\n\n        expect(uid).to be_blank\n        expect(secret).to be_blank\n      end\n    end\n\n    describe \".from_basic\" do\n      let(:credentials) { Base64.encode64(\"#{client_id}:#{client_secret}\") }\n\n      it \"decodes the credentials\" do\n        request = double authorization: \"Basic #{credentials}\"\n        uid, secret = described_class.from_basic(request)\n\n        expect(uid).to eq(\"some-uid\")\n        expect(secret).to eq(\"some-secret\")\n      end\n\n      it \"is blank if Authorization is not Basic\" do\n        request = double authorization: credentials.to_s\n        uid, secret = described_class.from_basic(request)\n\n        expect(uid).to be_blank\n        expect(secret).to be_blank\n      end\n\n      it \"decodes credentials with lowercase 'basic' prefix\" do\n        request = double authorization: \"basic #{credentials}\"\n        uid, secret = described_class.from_basic(request)\n\n        expect(uid).to eq(\"some-uid\")\n        expect(secret).to eq(\"some-secret\")\n      end\n\n      it \"decodes credentials with mixed case 'BaSiC' prefix\" do\n        request = double authorization: \"BaSiC #{credentials}\"\n        uid, secret = described_class.from_basic(request)\n\n        expect(uid).to eq(\"some-uid\")\n        expect(secret).to eq(\"some-secret\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/client_credentials/creator_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::ClientCredentials::Creator do\n  subject(:creator) { described_class.new }\n\n  let(:client) { FactoryBot.create :application }\n  let(:scopes) { Doorkeeper::OAuth::Scopes.from_string(\"public\") }\n\n  before do\n    default_scopes_exist :public\n  end\n\n  it \"creates a new token\" do\n    expect do\n      creator.call(client, scopes)\n    end.to change { Doorkeeper::AccessToken.count }.by(1)\n  end\n\n  context \"when reuse_access_token is true\" do\n    before do\n      allow(Doorkeeper.config).to receive(:reuse_access_token).and_return(true)\n    end\n\n    context \"when expiration is disabled\" do\n      it \"returns the existing valid token\" do\n        existing_token = creator.call(client, scopes)\n\n        result = creator.call(client, scopes)\n\n        expect(Doorkeeper::AccessToken.count).to eq(1)\n        expect(result).to eq(existing_token)\n      end\n    end\n\n    context \"when existing token has not crossed token_reuse_limit\" do\n      let!(:existing_token) { creator.call(client, scopes, expires_in: 1000) }\n\n      before do\n        allow(Doorkeeper.config).to receive(:token_reuse_limit).and_return(50)\n        allow_any_instance_of(Doorkeeper::AccessToken).to receive(:expires_in_seconds).and_return(600)\n      end\n\n      it \"returns the existing valid token\" do\n        result = creator.call(client, scopes, expires_in: 1000)\n\n        expect(Doorkeeper::AccessToken.count).to eq(1)\n        expect(result).to eq(existing_token)\n      end\n\n      context \"when revoke_previous_client_credentials_token is false\" do\n        before do\n          allow(Doorkeeper.config).to receive(:revoke_previous_client_credentials_token).and_return(false)\n        end\n\n        it \"does not revoke the existing valid token\" do\n          creator.call(client, scopes, expires_in: 1000)\n          expect(existing_token.reload).not_to be_revoked\n        end\n      end\n    end\n\n    context \"when existing token has crossed token_reuse_limit\" do\n      it \"returns a new token\" do\n        allow(Doorkeeper.config).to receive(:token_reuse_limit).and_return(50)\n        existing_token = creator.call(client, scopes, expires_in: 1000)\n\n        allow_any_instance_of(Doorkeeper::AccessToken).to receive(:expires_in_seconds).and_return(400)\n        result = creator.call(client, scopes, expires_in: 1000)\n\n        expect(Doorkeeper::AccessToken.count).to eq(2)\n        expect(result).not_to eq(existing_token)\n      end\n    end\n\n    context \"when existing token has been expired\" do\n      it \"returns a new token\" do\n        allow(Doorkeeper.configuration).to receive(:token_reuse_limit).and_return(50)\n        existing_token = creator.call(client, scopes, expires_in: 1000)\n\n        allow_any_instance_of(Doorkeeper::AccessToken).to receive(:expired?).and_return(true)\n        result = creator.call(client, scopes, expires_in: 1000)\n\n        expect(Doorkeeper::AccessToken.count).to eq(2)\n        expect(result).not_to eq(existing_token)\n      end\n    end\n  end\n\n  context \"when reuse_access_token is false\" do\n    before do\n      allow(Doorkeeper.config).to receive(:reuse_access_token).and_return(false)\n    end\n\n    it \"returns a new token\" do\n      existing_token = creator.call(client, scopes)\n\n      result = creator.call(client, scopes)\n\n      expect(Doorkeeper::AccessToken.count).to eq(2)\n      expect(result).not_to eq(existing_token)\n    end\n  end\n\n  context \"when revoke_previous_client_credentials_token is true\" do\n    let!(:existing_token) { creator.call(client, scopes, expires_in: 1000) }\n\n    before do\n      allow(Doorkeeper.configuration).to receive(:revoke_previous_client_credentials_token?).and_return(true)\n    end\n\n    it \"revokes the existing token\" do\n      creator.call(client, scopes, expires_in: 1000)\n      expect(existing_token.reload).to be_revoked\n    end\n  end\n\n  context \"when revoke_previous_client_credentials_token is false\" do\n    let!(:existing_token) { creator.call(client, scopes, expires_in: 1000) }\n\n    before do\n      allow(Doorkeeper.configuration).to receive(:revoke_previous_client_credentials_token?).and_return(false)\n    end\n\n    it \"does not revoke the existing token\" do\n      creator.call(client, scopes, expires_in: 1000)\n      expect(existing_token.reload).not_to be_revoked\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/client_credentials/issuer_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::ClientCredentials::Issuer do\n  subject(:issuer) { described_class.new(server, validator) }\n\n  let(:creator) { double :access_token_creator }\n  let(:server) do\n    double(\n      :server,\n      access_token_expires_in: 100,\n    )\n  end\n  let(:validator) { double :validator, valid?: true }\n\n  before do\n    allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(false)\n  end\n\n  describe \"#create\" do\n    let(:client) { double :client, id: \"some-id\" }\n    let(:scopes) { \"some scope\" }\n\n    it \"creates and sets the token\" do\n      expect(creator).to receive(:call).and_return(\"token\")\n      issuer.create client, scopes, {}, creator\n\n      expect(issuer.token).to eq(\"token\")\n    end\n\n    it \"creates with correct token parameters\" do\n      expect(creator).to receive(:call).with(\n        client,\n        scopes,\n        expires_in: 100,\n        use_refresh_token: false,\n      )\n\n      issuer.create client, scopes, {}, creator\n    end\n\n    it \"creates with custom token parameters\" do\n      expect(creator).to receive(:call).with(\n        client,\n        scopes,\n        expires_in: 100,\n        use_refresh_token: false,\n        tenant_id: 9000\n      )\n\n      issuer.create client, scopes, { tenant_id: 9000 }, creator\n    end\n\n    it \"has error set to :server_error if creator fails\" do\n      expect(creator).to receive(:call).and_return(false)\n      issuer.create client, scopes, {}, creator\n\n      expect(issuer.error).to eq(Doorkeeper::Errors::ServerError)\n    end\n\n    context \"when validator fails\" do\n      before do\n        allow(validator).to receive(:valid?).and_return(false)\n        allow(validator).to receive(:error).and_return(:validation_error)\n      end\n\n      it \"has error set from validator\" do\n        expect(creator).not_to receive(:create)\n        issuer.create client, scopes, {}, creator\n        expect(issuer.error).to eq(:validation_error)\n      end\n\n      it \"returns false\" do\n        expect(issuer.create(client, scopes, {}, creator)).to be_falsey\n      end\n    end\n\n    context \"with custom expiration\" do\n      let(:custom_ttl_grant) { 1234 }\n      let(:custom_ttl_scope) { 1235 }\n      let(:custom_scope) { \"special\" }\n      let(:server) do\n        double(\n          :server,\n          custom_access_token_expires_in: lambda { |context|\n            # scopes is normally an object but is a string in this test\n            if context.scopes == custom_scope\n              custom_ttl_scope\n            elsif context.grant_type == Doorkeeper::OAuth::CLIENT_CREDENTIALS\n              custom_ttl_grant\n            end\n          },\n        )\n      end\n\n      before do\n        allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(true)\n      end\n\n      it \"respects grant based rules\" do\n        expect(creator).to receive(:call).with(\n          client,\n          scopes,\n          expires_in: custom_ttl_grant,\n          use_refresh_token: false,\n        )\n        issuer.create client, scopes, {}, creator\n      end\n\n      it \"respects scope based rules\" do\n        expect(creator).to receive(:call).with(\n          client,\n          custom_scope,\n          expires_in: custom_ttl_scope,\n          use_refresh_token: false,\n        )\n        issuer.create client, custom_scope, {}, creator\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/client_credentials/validation_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::ClientCredentials::Validator do\n  subject(:validator) { described_class.new(server, request) }\n\n  let(:server)      { double :server, scopes: nil }\n  let(:application) { double scopes: nil }\n  let(:client)      { double application: application }\n  let(:request)     { double :request, client: client, scopes: nil }\n\n  it \"is valid with valid request\" do\n    expect(validator).to be_valid\n  end\n\n  it \"is invalid when client is not present\" do\n    allow(request).to receive(:client).and_return(nil)\n    expect(validator).not_to be_valid\n  end\n\n  context \"when a grant flow check is configured\" do\n    let(:callback) { double(\"callback\") }\n\n    before do\n      allow(Doorkeeper.config).to receive(:option_defined?).with(:allow_grant_flow_for_client).and_return(true)\n      allow(Doorkeeper.config).to receive(:allow_grant_flow_for_client).and_return(callback)\n    end\n\n    context \"when the callback rejects the grant flow\" do\n      let(:callback_response) { false }\n\n      it \"is invalid\" do\n        expect(callback).to receive(:call).twice.with(\n          Doorkeeper::OAuth::CLIENT_CREDENTIALS,\n          application,\n        ).and_return(callback_response)\n\n        expect(validator).not_to be_valid\n      end\n    end\n\n    context \"when the callback allows the grant flow\" do\n      let(:callback_response) { true }\n\n      it \"is invalid\" do\n        expect(callback).to receive(:call).twice.with(\n          Doorkeeper::OAuth::CLIENT_CREDENTIALS,\n          application,\n        ).and_return(callback_response)\n\n        expect(validator).to be_valid\n      end\n    end\n  end\n\n  context \"with scopes\" do\n    it \"is invalid when scopes are not included in the server\" do\n      server_scopes = Doorkeeper::OAuth::Scopes.from_string \"email\"\n      allow(request).to receive(:grant_type).and_return(Doorkeeper::OAuth::CLIENT_CREDENTIALS)\n      allow(server).to receive(:scopes).and_return(server_scopes)\n      allow(request).to receive(:scopes).and_return(\n        Doorkeeper::OAuth::Scopes.from_string(\"invalid\"),\n      )\n      expect(validator).not_to be_valid\n    end\n\n    context \"with application scopes\" do\n      it \"is valid when scopes are included in the application\" do\n        application_scopes = Doorkeeper::OAuth::Scopes.from_string \"app\"\n        server_scopes = Doorkeeper::OAuth::Scopes.from_string \"email app\"\n        allow(application).to receive(:scopes).and_return(application_scopes)\n        allow(server).to receive(:scopes).and_return(server_scopes)\n        allow(request).to receive(:grant_type).and_return(Doorkeeper::OAuth::CLIENT_CREDENTIALS)\n        allow(request).to receive(:scopes).and_return(application_scopes)\n        expect(validator).to be_valid\n      end\n\n      it \"is invalid when scopes are not included in the application\" do\n        application_scopes = Doorkeeper::OAuth::Scopes.from_string \"app\"\n        server_scopes = Doorkeeper::OAuth::Scopes.from_string \"email app\"\n        allow(application).to receive(:scopes).and_return(application_scopes)\n        allow(request).to receive(:grant_type).and_return(Doorkeeper::OAuth::CLIENT_CREDENTIALS)\n        allow(server).to receive(:scopes).and_return(server_scopes)\n        allow(request).to receive(:scopes).and_return(\n          Doorkeeper::OAuth::Scopes.from_string(\"email\"),\n        )\n        expect(validator).not_to be_valid\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/client_credentials_integration_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::ClientCredentialsRequest do\n  let(:server) { Doorkeeper.configuration }\n\n  context \"with a valid request\" do\n    let(:client) { Doorkeeper::OAuth::Client.new(FactoryBot.build_stubbed(:application)) }\n\n    it \"issues an access token\" do\n      request = described_class.new(server, client, {})\n      expect do\n        request.authorize\n      end.to change { Doorkeeper::AccessToken.count }.by(1)\n    end\n  end\n\n  describe \"with an invalid request\" do\n    it \"does not issue an access token\" do\n      request = described_class.new(server, nil, {})\n      expect do\n        request.authorize\n      end.not_to(change { Doorkeeper::AccessToken.count })\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/client_credentials_request_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::ClientCredentialsRequest do\n  subject(:request) { described_class.new(server, client) }\n\n  let(:server) do\n    double(\n      default_scopes: nil,\n      access_token_expires_in: 2.hours,\n      custom_access_token_expires_in: ->(_context) { nil },\n    )\n  end\n\n  let(:application)   { FactoryBot.create(:application, scopes: \"\") }\n  let(:client)        { double :client, application: application, scopes: \"\" }\n  let(:token_creator) { double :issuer, create: true, token: double }\n\n  before do\n    allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(true)\n    allow(request).to receive(:issuer).and_return(token_creator)\n  end\n\n  it \"returns :grant_type as client_credentials\" do\n    expect(request.grant_type).to eq(Doorkeeper::OAuth::CLIENT_CREDENTIALS)\n  end\n\n  it \"issues an access token for the current client\" do\n    expect(token_creator).to receive(:create).with(client, nil, {})\n    request.authorize\n  end\n\n  it \"has successful response when issue was created\" do\n    request.authorize\n    expect(request.response).to be_a(Doorkeeper::OAuth::TokenResponse)\n  end\n\n  context \"when issue was not created\" do\n    before do\n      issuer = double create: false, error: Doorkeeper::Errors::UnsupportedResponseType\n      allow(request).to receive(:issuer).and_return(issuer)\n    end\n\n    it \"has an error response\" do\n      request.authorize\n      expect(request.response).to be_a(Doorkeeper::OAuth::ErrorResponse)\n    end\n\n    it \"delegates the error to issuer\" do\n      request.authorize\n      expect(request.error).to eq(Doorkeeper::Errors::UnsupportedResponseType)\n    end\n  end\n\n  context \"with scopes\" do\n    let(:default_scopes) { Doorkeeper::OAuth::Scopes.from_string(\"public email\") }\n\n    before do\n      allow(server).to receive(:default_scopes).and_return(default_scopes)\n    end\n\n    it \"issues an access token with default scopes if none was requested\" do\n      expect(token_creator).to receive(:create).with(client, default_scopes, {})\n      request.authorize\n    end\n\n    it \"issues an access token with requested scopes\" do\n      request = described_class.new(server, client, scope: \"email\")\n      allow(request).to receive(:issuer).and_return(token_creator)\n      expect(token_creator).to receive(:create).with(client, Doorkeeper::OAuth::Scopes.from_string(\"email\"), {})\n      request.authorize\n    end\n  end\n\n  context \"with custom_access_token_attributes configured\" do\n    before do\n      Doorkeeper.configure do\n        custom_access_token_attributes [:tenant_id]\n      end\n    end\n\n    it \"issues an access token with the custom access token attributes\" do\n      request = described_class.new(server, client, scope: \"email\", tenant_id: 9000)\n      allow(request).to receive(:issuer).and_return(token_creator)\n      expect(token_creator).to receive(:create).with(client, Doorkeeper::OAuth::Scopes.from_string(\"email\"), { tenant_id: 9000 })\n      request.authorize\n    end\n  end\n\n  context \"with restricted client\" do\n    let(:default_scopes) do\n      Doorkeeper::OAuth::Scopes.from_string(\"public email\")\n    end\n    let(:server_scopes) do\n      Doorkeeper::OAuth::Scopes.from_string(\"public email phone\")\n    end\n    let(:client_scopes) do\n      Doorkeeper::OAuth::Scopes.from_string(\"public phone\")\n    end\n\n    before do\n      allow(server).to receive(:default_scopes).and_return(default_scopes)\n      allow(server).to receive(:scopes).and_return(server_scopes)\n      allow(server).to receive(:access_token_expires_in).and_return(100)\n      allow(application).to receive(:scopes).and_return(client_scopes)\n      allow(client).to receive(:id).and_return(nil)\n    end\n\n    it \"delegates the error to issuer if no scope was requested\" do\n      request = described_class.new(server, client)\n      request.authorize\n      expect(request.response).to be_a(Doorkeeper::OAuth::ErrorResponse)\n      expect(request.error).to eq(Doorkeeper::Errors::InvalidScope)\n    end\n\n    it \"issues an access token with requested scopes\" do\n      request = described_class.new(server, client, scope: \"phone\")\n      request.authorize\n      expect(request.response).to be_a(Doorkeeper::OAuth::TokenResponse)\n      expect(request.response.token.scopes_string).to eq(\"phone\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/client_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::Client do\n  describe \".find\" do\n    let(:method) { double }\n\n    it \"finds the client via uid\" do\n      client = double\n      expect(method).to receive(:call).with(\"uid\").and_return(client)\n      expect(described_class.find(\"uid\", method))\n        .to be_a(described_class)\n    end\n\n    it \"returns nil if client was not found\" do\n      expect(method).to receive(:call).with(\"uid\").and_return(nil)\n      expect(described_class.find(\"uid\", method)).to be_nil\n    end\n  end\n\n  describe \".authenticate\" do\n    it \"returns the authenticated client via credentials\" do\n      credentials = Doorkeeper::OAuth::Client::Credentials.new(\"some-uid\", \"some-secret\")\n      authenticator = double\n      expect(authenticator).to receive(:call).with(\"some-uid\", \"some-secret\").and_return(double)\n      expect(described_class.authenticate(credentials, authenticator))\n        .to be_a(described_class)\n    end\n\n    it \"returns nil if client was not authenticated\" do\n      credentials = Doorkeeper::OAuth::Client::Credentials.new(\"some-uid\", \"some-secret\")\n      authenticator = double\n      expect(authenticator).to receive(:call).with(\"some-uid\", \"some-secret\").and_return(nil)\n      expect(described_class.authenticate(credentials, authenticator)).to be_nil\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/code_request_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::CodeRequest do\n  subject(:request) do\n    described_class.new(pre_auth, owner)\n  end\n\n  let(:pre_auth) do\n    allow(Doorkeeper.config)\n      .to receive(:default_scopes).and_return(Doorkeeper::OAuth::Scopes.from_string(\"public\"))\n    allow(Doorkeeper.config)\n      .to receive(:grant_flows).and_return(Doorkeeper::OAuth::Scopes.from_string(\"authorization_code\"))\n\n    application = FactoryBot.create(:application, scopes: \"public\")\n    client = Doorkeeper::OAuth::Client.new(application)\n\n    attributes = {\n      client_id: client.uid,\n      response_type: \"code\",\n      redirect_uri: \"https://app.com/callback\",\n      response_mode: response_mode,\n    }.compact\n\n    pre_auth = Doorkeeper::OAuth::PreAuthorization.new(Doorkeeper.config, attributes)\n    pre_auth.authorizable?\n    pre_auth\n  end\n\n  let(:response_mode) { nil }\n  let(:owner) { FactoryBot.create(:resource_owner) }\n\n  context \"when pre_auth is authorized\" do\n    it \"creates an access grant and returns a code response\" do\n      expect { request.authorize }.to change { Doorkeeper::AccessGrant.count }.by(1)\n      expect(request.authorize).to be_a(Doorkeeper::OAuth::CodeResponse)\n      expect(request.authorize.response_on_fragment).to be false\n    end\n\n    context \"with 'fragment' as response_mode\" do\n      let(:response_mode) { \"fragment\" }\n\n      it \"returns a code response with response_on_fragment set to true\" do\n        expect(request.authorize.response_on_fragment).to be true\n      end\n    end\n  end\n\n  context \"when pre_auth is denied\" do\n    it \"does not create access grant and returns a error response\" do\n      expect { request.deny }.not_to(change { Doorkeeper::AccessGrant.count })\n      expect(request.deny).to be_a(Doorkeeper::OAuth::ErrorResponse)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/code_response_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::CodeResponse do\n  let(:application) { FactoryBot.create(:application, scopes: \"\") }\n  let(:owner) { FactoryBot.build_stubbed(:resource_owner) }\n  let(:pre_auth) do\n    double(\n      :pre_auth,\n      client: application,\n      redirect_uri: \"http://tst.com/cb\",\n      state: \"state\",\n      scopes: Doorkeeper::OAuth::Scopes.from_string(\"public\"),\n      custom_access_token_attributes: {},\n    )\n  end\n\n  describe \"#body\" do\n    subject(:body) { described_class.new(pre_auth, auth).body }\n\n    context \"when auth object is for authorization code flow\" do\n      let(:auth) do\n        Doorkeeper::OAuth::Authorization::Code.new(pre_auth, owner).tap(&:issue_token!)\n      end\n\n      before do\n        allow(pre_auth).to receive(:code_challenge).and_return(\"code_challenge\")\n        allow(pre_auth).to receive(:code_challenge_method).and_return(\"plain\")\n      end\n\n      it \"return body response for authorization code\" do\n        expect(body).to eq({ code: auth.token.plaintext_token, state: pre_auth.state })\n      end\n    end\n\n    context \"when auth object is for implicit grant flow\" do\n      let(:auth) do\n        Doorkeeper::OAuth::Authorization::Token.new(pre_auth, owner).tap do |c|\n          c.issue_token!\n          allow(c.token).to receive(:expires_in_seconds).and_return(3600)\n        end\n      end\n\n      it \"return body response for access token\" do\n        expect(body).to eq(\n          {\n            access_token: auth.token.plaintext_token,\n            token_type: auth.token.token_type,\n            expires_in: auth.token.expires_in_seconds,\n            state: pre_auth.state,\n          },\n        )\n      end\n    end\n  end\n\n  describe \"#redirect_uri\" do\n    subject(:redirect_uri) do\n      described_class.new(pre_auth, auth, response_on_fragment: response_on_fragment).redirect_uri\n    end\n\n    context \"when generating the redirect URI for an authorization code grant\" do\n      let(:response_on_fragment) { false }\n      let(:auth) do\n        Doorkeeper::OAuth::Authorization::Code.new(pre_auth, owner).tap(&:issue_token!)\n      end\n\n      before do\n        allow(pre_auth).to receive(:code_challenge).and_return(\"code_challenge\")\n        allow(pre_auth).to receive(:code_challenge_method).and_return(\"plain\")\n      end\n\n      it \"includes the authorization code was generated and state\" do\n        expect(redirect_uri).to eq(\"#{pre_auth.redirect_uri}?code=#{auth.token.plaintext_token}&state=#{pre_auth.state}\")\n      end\n    end\n\n    context \"when generating the redirect URI for an implicit grant\" do\n      let(:response_on_fragment) { true }\n      let(:auth) do\n        Doorkeeper::OAuth::Authorization::Token.new(pre_auth, owner).tap do |c|\n          c.issue_token!\n          allow(c.token).to receive(:expires_in_seconds).and_return(3600)\n        end\n      end\n\n      it \"includes info of the token was generated and state\" do\n        expect(redirect_uri).to include(\"#{pre_auth.redirect_uri}#access_token=#{auth.token.plaintext_token}&\" \\\n          \"token_type=#{auth.token.token_type}&expires_in=3600&state=#{pre_auth.state}\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/error_response_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::ErrorResponse do\n  describe \"#status\" do\n    it \"has a status of bad_request\" do\n      expect(described_class.new.status).to eq(:bad_request)\n    end\n\n    it \"has a status of unauthorized for an invalid_client error\" do\n      subject = described_class.new(name: :invalid_client)\n\n      expect(subject.status).to eq(:unauthorized)\n    end\n\n    it \"has a status of unauthorized for an unauthorized_client error\" do\n      subject = described_class.new(name: :unauthorized_client)\n\n      expect(subject.status).to eq(:unauthorized)\n    end\n  end\n\n  describe \".from_request\" do\n    it \"has the error from request\" do\n      error = described_class.from_request double(error: Doorkeeper::Errors::InvalidClient)\n      expect(error.name).to eq(:invalid_client)\n    end\n\n    it \"ignores state if request does not respond to state\" do\n      error = described_class.from_request double(error: Doorkeeper::Errors::InvalidClient)\n      expect(error.state).to be_nil\n    end\n\n    it \"has state if request responds to state\" do\n      error = described_class.from_request double(error: Doorkeeper::Errors::InvalidClient, state: :hello)\n      expect(error.state).to eq(:hello)\n    end\n\n    it \"supports old extensions\" do\n      error = described_class.from_request double(error: :invalid_client)\n      expect(error.name).to eq(:invalid_client)\n\n      expect { error.raise_exception! }.to raise_error(Doorkeeper::Errors::InvalidClient)\n    end\n  end\n\n  it \"ignores empty error values\" do\n    subject = described_class.new(error: Doorkeeper::Errors::InvalidClient, state: nil)\n    expect(subject.body).not_to have_key(:state)\n  end\n\n  describe \".body\" do\n    subject(:body) { described_class.new(name: Doorkeeper::Errors::InvalidClient, state: :some_state).body }\n\n    describe \"#body\" do\n      it { expect(body).to have_key(:error) }\n      it { expect(body).to have_key(:error_description) }\n      it { expect(body).to have_key(:state) }\n    end\n  end\n\n  describe \".headers\" do\n    subject(:headers) { error_response.headers }\n\n    let(:error_response) { described_class.new(name: Doorkeeper::Errors::InvalidClient, state: :some_state) }\n\n    it { expect(headers).to include \"WWW-Authenticate\" }\n\n    describe \"WWW-Authenticate header\" do\n      subject(:headers) { error_response.headers[\"WWW-Authenticate\"] }\n\n      it { expect(headers).to include(\"realm=\\\"#{error_response.send(:realm)}\\\"\") }\n      it { expect(headers).to include(\"error=\\\"#{error_response.name}\\\"\") }\n      it { expect(headers).to include(\"error_description=\\\"#{error_response.description}\\\"\") }\n\n      context \"with error description containing forbidden characters (\\\\ or \\\")\" do\n        it \"sanitize the value per RFC 6750 Section 3.1\" do\n          error = double(:error, name: \"backslash\\\\\", description:\"\\\"quotes\\\"\")\n          allow(Doorkeeper::OAuth::Error).to receive(:new).and_return(error)\n          expect(headers).to include(\"error=\\\"backslash_\\\"\")\n          expect(headers).to include(\"error_description=\\\"_quotes_\\\"\")\n        end\n      end\n    end\n  end\n\n  describe \".redirectable?\" do\n    it \"not redirectable when error name is invalid_redirect_uri\" do\n      subject = described_class.new(name: :invalid_redirect_uri, redirect_uri: \"https://example.com\")\n\n      expect(subject.redirectable?).to be false\n    end\n\n    it \"not redirectable when error name is invalid_client\" do\n      subject = described_class.new(name: :invalid_client, redirect_uri: \"https://example.com\")\n\n      expect(subject.redirectable?).to be false\n    end\n\n    it \"not redirectable when error name is unauthorized_client\" do\n      subject = described_class.new(name: :unauthorized_client, redirect_uri: \"https://example.com\")\n\n      expect(subject.redirectable?).to be false\n    end\n\n    it \"not redirectable when redirect_uri is oob uri\" do\n      subject = described_class.new(name: :other_error, redirect_uri: Doorkeeper::OAuth::NonStandard::IETF_WG_OAUTH2_OOB)\n\n      expect(subject.redirectable?).to be false\n    end\n\n    it \"is redirectable when error is not related to client or redirect_uri, and redirect_uri is not oob uri\" do\n      subject = described_class.new(name: :other_error, redirect_uri: \"https://example.com\")\n\n      expect(subject.redirectable?).to be true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/error_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::Error do\n  subject(:error) { described_class.new(:some_error, :some_state, nil) }\n\n  it { expect(error).to respond_to(:name) }\n  it { expect(error).to respond_to(:state) }\n  it { expect(error).to respond_to(:translate_options) }\n\n  describe \"#description\" do\n    it \"is translated from translation messages\" do\n      expect(I18n).to receive(:translate).with(\n        :some_error,\n        scope: %i[doorkeeper errors messages],\n        default: :server_error,\n      )\n      error.description\n    end\n\n    context \"when there are variables\" do\n      subject(:error) do\n        described_class.new(\n          :invalid_code_challenge_method,\n          :some_state,\n          {\n            challenge_methods: \"foo, bar\",\n            count: 2,\n          }\n        )\n      end\n\n      it \"is translated from translation messages with variables\" do\n        expect(error.description).to eq(\"The code_challenge_method must be one of foo, bar.\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/forbidden_token_response_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::ForbiddenTokenResponse do\n  subject(:response) { described_class.new }\n\n  describe \"#name\" do\n    it { expect(response.name).to eq(:insufficient_scope) }\n  end\n\n  describe \"#status\" do\n    it { expect(response.status).to eq(:forbidden) }\n  end\n\n  describe \"#headers\" do\n    subject(:response) { described_class.from_scopes([\"public\"]) }\n\n    it \"includes a WWW-Authenticate header per RFC 6750 Section 3.1\" do\n      www_authenticate = response.headers[\"WWW-Authenticate\"]\n      expect(www_authenticate).to include('error=\"insufficient_scope\"')\n      expect(www_authenticate).to include('error_description=\"Access to this resource requires scope _public_.\"')\n    end\n  end\n\n  describe \".from_scopes\" do\n    subject(:response) { described_class.from_scopes([\"public\"]) }\n\n    it \"includes a list of acceptable scopes\" do\n      expect(response.description).to include(\"public\")\n    end\n\n    it \"explains that the problem is due to a missing scope\" do\n      expect(response.description).to match(/requires scope/i)\n    end\n\n    it \"does not use the scope description from authorize page\" do\n      expect(response.description).not_to eql(\"Access your public data\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/helpers/scope_checker_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::Helpers::ScopeChecker do\n  describe \".valid?\" do\n    let(:server_scopes) { Doorkeeper::OAuth::Scopes.new }\n\n    it \"is valid if scope is present\" do\n      server_scopes.add :scope\n      expect(described_class.valid?(scope_str: \"scope\", server_scopes: server_scopes)).to be(true)\n    end\n\n    it \"is invalid if includes tabs space\" do\n      expect(described_class.valid?(scope_str: \"\\tsomething\", server_scopes: server_scopes)).to be(false)\n    end\n\n    it \"is invalid if scope is not present\" do\n      expect(described_class.valid?(scope_str: nil, server_scopes: server_scopes)).to be(false)\n    end\n\n    it \"is invalid if scope is blank\" do\n      expect(described_class.valid?(scope_str: \" \", server_scopes: server_scopes)).to be(false)\n    end\n\n    it \"is invalid if includes return space\" do\n      expect(described_class.valid?(scope_str: \"scope\\r\", server_scopes: server_scopes)).to be(false)\n    end\n\n    it \"is invalid if includes new lines\" do\n      expect(described_class.valid?(scope_str: \"scope\\nanother\", server_scopes: server_scopes)).to be(false)\n    end\n\n    it \"is invalid if any scope is not included in server scopes\" do\n      expect(described_class.valid?(scope_str: \"scope another\", server_scopes: server_scopes)).to be(false)\n    end\n\n    context \"with application_scopes\" do\n      let(:server_scopes) { Doorkeeper::OAuth::Scopes.from_string \"common svr\" }\n      let(:application_scopes) { Doorkeeper::OAuth::Scopes.from_string \"app123\" }\n\n      it \"is valid if scope is included in the application scope list\" do\n        expect(described_class.valid?(scope_str: \"app123\", server_scopes: server_scopes, app_scopes: application_scopes))\n          .to be(true)\n      end\n\n      it \"is invalid if any scope is not included in the application\" do\n        expect(described_class.valid?(scope_str: \"svr\", server_scopes: server_scopes, app_scopes: application_scopes))\n          .to be(false)\n      end\n    end\n\n    context \"with grant_type\" do\n      let(:server_scopes) { Doorkeeper::OAuth::Scopes.from_string \"scope1 scope2\" }\n\n      context \"with scopes_by_grant_type not configured for grant_type\" do\n        it \"is valid if the scope is in server scopes\" do\n          expect(described_class.valid?(scope_str: \"scope1\", server_scopes: server_scopes, grant_type: Doorkeeper::OAuth::PASSWORD))\n            .to be(true)\n        end\n\n        it \"is invalid if the scope is not in server scopes\" do\n          expect(described_class.valid?(scope_str: \"unknown\", server_scopes: server_scopes, grant_type: Doorkeeper::OAuth::PASSWORD))\n            .to be(false)\n        end\n      end\n\n      context \"when scopes_by_grant_type configured for grant_type\" do\n        before do\n          allow(Doorkeeper.configuration).to receive(:scopes_by_grant_type)\n            .and_return(password: [:scope1])\n        end\n\n        it \"is valid if the scope is permitted for grant_type\" do\n          expect(described_class.valid?(scope_str: \"scope1\", server_scopes: server_scopes, grant_type: Doorkeeper::OAuth::PASSWORD))\n            .to be(true)\n        end\n\n        it \"is invalid if the scope is permitted for grant_type\" do\n          expect(described_class.valid?(scope_str: \"scope2\", server_scopes: server_scopes, grant_type: Doorkeeper::OAuth::PASSWORD))\n            .to be(false)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/helpers/unique_token_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nmodule Doorkeeper::OAuth::Helpers\n  describe UniqueToken do\n    let :generator do\n      ->(size) { \"a\" * size }\n    end\n\n    it \"is able to customize the generator method\" do\n      token = described_class.generate(generator: generator)\n      expect(token).to eq(\"a\" * 32)\n    end\n\n    it \"is able to customize the size of the token\" do\n      token = described_class.generate(generator: generator, size: 2)\n      expect(token).to eq(\"aa\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/helpers/uri_checker_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\ndescribe Doorkeeper::OAuth::Helpers::URIChecker do\n  describe \".valid?\" do\n    it \"is valid for valid uris\" do\n      uri = \"http://app.co\"\n      expect(described_class).to be_valid(uri)\n    end\n\n    it \"is valid if include path param\" do\n      uri = \"http://app.co/path\"\n      expect(described_class).to be_valid(uri)\n    end\n\n    it \"is valid if include query param\" do\n      uri = \"http://app.co/?query=1\"\n      expect(described_class).to be_valid(uri)\n    end\n\n    it \"is invalid if uri includes fragment\" do\n      uri = \"http://app.co/test#fragment\"\n      expect(described_class).not_to be_valid(uri)\n    end\n\n    it \"is invalid if scheme is missing\" do\n      uri = \"app.co\"\n      expect(described_class).not_to be_valid(uri)\n    end\n\n    it \"is invalid if is a relative uri\" do\n      uri = \"/abc/123\"\n      expect(described_class).not_to be_valid(uri)\n    end\n\n    it \"is invalid if is not a url\" do\n      uri = \"http://\"\n      expect(described_class).not_to be_valid(uri)\n    end\n\n    it \"is invalid if localhost is resolved as as scheme (no scheme specified)\" do\n      uri = \"localhost:8080\"\n      expect(described_class).not_to be_valid(uri)\n    end\n\n    it \"is invalid if scheme is missing #2\" do\n      uri = \"app.co:80\"\n      expect(described_class).not_to be_valid(uri)\n    end\n\n    it \"is invalid if is not an uri\" do\n      uri = \"   \"\n      expect(described_class).not_to be_valid(uri)\n    end\n\n    it \"is valid for custom schemes\" do\n      uri = \"com.example.app:/test\"\n      expect(described_class).to be_valid(uri)\n    end\n\n    it \"is valid for custom schemes with authority marker (common misconfiguration)\" do\n      uri = \"com.example.app://test\"\n      expect(described_class).to be_valid(uri)\n    end\n  end\n\n  describe \".matches?\" do\n    it \"is true if both url matches\" do\n      uri = client_uri = \"http://app.co/aaa\"\n      expect(described_class).to be_matches(uri, client_uri)\n    end\n\n    it \"allows additional query parameters\" do\n      uri = \"http://app.co/?query=hello\"\n      client_uri = \"http://app.co\"\n      expect(described_class).to be_matches(uri, client_uri)\n    end\n\n    it \"doesn't allow non-matching domains through\" do\n      uri = \"http://app.abc/?query=hello\"\n      client_uri = \"http://app.co\"\n      expect(described_class).not_to be_matches(uri, client_uri)\n    end\n\n    it \"doesn't allow non-matching domains that don't start at the beginning\" do\n      uri = \"http://app.co/?query=hello\"\n      client_uri = \"http://example.com?app.co=test\"\n      expect(described_class).not_to be_matches(uri, client_uri)\n    end\n\n    context \"when loopback IP redirect URIs\" do\n      it \"ignores port for same URIs\" do\n        uri = \"http://127.0.0.1:5555/auth/callback\"\n        client_uri = \"http://127.0.0.1:48599/auth/callback\"\n        expect(described_class).to be_matches(uri, client_uri)\n\n        uri = \"http://[::1]:5555/auth/callback\"\n        client_uri = \"http://[::1]:5555/auth/callback\"\n        expect(described_class).to be_matches(uri, client_uri)\n      end\n\n      it \"doesn't ignore port for URIs with different queries\" do\n        uri = \"http://127.0.0.1:5555/auth/callback\"\n        client_uri = \"http://127.0.0.1:48599/auth/callback2\"\n        expect(described_class).not_to be_matches(uri, client_uri)\n      end\n    end\n\n    context \"when client registered query params\" do\n      it \"doesn't allow query being absent\" do\n        uri = \"http://app.co\"\n        client_uri = \"http://app.co/?vendorId=AJ4L7XXW9\"\n        expect(described_class).not_to be_matches(uri, client_uri)\n      end\n\n      it \"is false if query values differ but key same\" do\n        uri = \"http://app.co/?vendorId=pancakes\"\n        client_uri = \"http://app.co/?vendorId=waffles\"\n        expect(described_class).not_to be_matches(uri, client_uri)\n      end\n\n      it \"is false if query values same but key differs\" do\n        uri = \"http://app.co/?foo=pancakes\"\n        client_uri = \"http://app.co/?bar=pancakes\"\n        expect(described_class).not_to be_matches(uri, client_uri)\n      end\n\n      it \"is false if query present and match, but unknown queries present\" do\n        uri = \"http://app.co/?vendorId=pancakes&unknown=query\"\n        client_uri = \"http://app.co/?vendorId=waffles\"\n        expect(described_class).not_to be_matches(uri, client_uri)\n      end\n\n      it \"is true if queries are present and match\" do\n        uri = \"http://app.co/?vendorId=AJ4L7XXW9&foo=bar\"\n        client_uri = \"http://app.co/?vendorId=AJ4L7XXW9&foo=bar\"\n        expect(described_class).to be_matches(uri, client_uri)\n      end\n\n      it \"is true if queries are present, match and in different order\" do\n        uri = \"http://app.co/?bing=bang&foo=bar\"\n        client_uri = \"http://app.co/?foo=bar&bing=bang\"\n        expect(described_class).to be_matches(uri, client_uri)\n      end\n    end\n  end\n\n  describe \".valid_for_authorization?\" do\n    it \"is true if valid and matches\" do\n      uri = client_uri = \"http://app.co/aaa\"\n      expect(described_class).to be_valid_for_authorization(uri, client_uri)\n\n      uri = client_uri = \"http://app.co/aaa?b=c\"\n      expect(described_class).to be_valid_for_authorization(uri, client_uri)\n    end\n\n    it \"is true if uri includes blank query\" do\n      uri = client_uri = \"http://app.co/aaa?\"\n      expect(described_class).to be_valid_for_authorization(uri, client_uri)\n\n      uri = \"http://app.co/aaa?\"\n      client_uri = \"http://app.co/aaa\"\n      expect(described_class).to be_valid_for_authorization(uri, client_uri)\n\n      uri = \"http://app.co/aaa\"\n      client_uri = \"http://app.co/aaa?\"\n      expect(described_class).to be_valid_for_authorization(uri, client_uri)\n    end\n\n    it \"is false if valid and mismatches\" do\n      uri = \"http://app.co/aaa\"\n      client_uri = \"http://app.co/bbb\"\n      expect(described_class).not_to be_valid_for_authorization(uri, client_uri)\n    end\n\n    it \"is true if valid and included in array\" do\n      uri = \"http://app.co/aaa\"\n      client_uri = \"http://example.com/bbb\\nhttp://app.co/aaa\"\n      expect(described_class).to be_valid_for_authorization(uri, client_uri)\n    end\n\n    it \"is false if valid and not included in array\" do\n      uri = \"http://app.co/aaa\"\n      client_uri = \"http://example.com/bbb\\nhttp://app.co/cc\"\n      expect(described_class).not_to be_valid_for_authorization(uri, client_uri)\n    end\n\n    it \"is false if queries does not match\" do\n      uri = \"http://app.co/aaa?pankcakes=abc\"\n      client_uri = \"http://app.co/aaa?waffles=abc\"\n      expect(described_class.valid_for_authorization?(uri, client_uri)).to be false\n    end\n\n    it \"calls .matches?\" do\n      uri = \"http://app.co/aaa?pankcakes=abc\"\n      client_uri = \"http://app.co/aaa?waffles=abc\"\n      expect(described_class).to receive(:matches?).with(uri, client_uri).once\n      described_class.valid_for_authorization?(uri, client_uri)\n    end\n\n    it \"calls .valid?\" do\n      uri = \"http://app.co/aaa?pankcakes=abc\"\n      client_uri = \"http://app.co/aaa?waffles=abc\"\n      expect(described_class).to receive(:valid?).with(uri).once\n      described_class.valid_for_authorization?(uri, client_uri)\n    end\n  end\n\n  describe \".query_matches?\" do\n    it \"is true if no queries\" do\n      expect(described_class).to be_query_matches(\"\", \"\")\n      expect(described_class).to be_query_matches(nil, nil)\n    end\n\n    it \"is true if same query\" do\n      expect(described_class).to be_query_matches(\"foo\", \"foo\")\n    end\n\n    it \"is false if different query\" do\n      expect(described_class).not_to be_query_matches(\"foo\", \"bar\")\n    end\n\n    it \"is true if same queries\" do\n      expect(described_class).to be_query_matches(\"foo&bar\", \"foo&bar\")\n    end\n\n    it \"is true if same queries, different order\" do\n      expect(described_class).to be_query_matches(\"foo&bar\", \"bar&foo\")\n    end\n\n    it \"is false if one different query\" do\n      expect(described_class).not_to be_query_matches(\"foo&bang\", \"foo&bing\")\n    end\n\n    it \"is true if same query with same value\" do\n      expect(described_class).to be_query_matches(\"foo=bar\", \"foo=bar\")\n    end\n\n    it \"is true if same queries with same values\" do\n      expect(described_class).to be_query_matches(\"foo=bar&bing=bang\", \"foo=bar&bing=bang\")\n    end\n\n    it \"is true if same queries with same values, different order\" do\n      expect(described_class).to be_query_matches(\"foo=bar&bing=bang\", \"bing=bang&foo=bar\")\n    end\n\n    it \"is false if same query with different value\" do\n      expect(described_class).not_to be_query_matches(\"foo=bar\", \"foo=bang\")\n    end\n\n    it \"is false if some queries missing\" do\n      expect(described_class).not_to be_query_matches(\"foo=bar\", \"foo=bar&bing=bang\")\n    end\n\n    it \"is false if some queries different value\" do\n      expect(described_class).not_to be_query_matches(\"foo=bar&bing=bang\", \"foo=bar&bing=banana\")\n    end\n  end\n\n  describe \".loopback_uri?\" do\n    it \"is true if loopback IP\" do\n      expect(described_class).to be_loopback_uri(URI.parse(\"http://127.0.0.1\"))\n    end\n\n    it 'is false if not loopback IP' do\n      expect(described_class).not_to be_loopback_uri(URI.parse(\"http://example.com\"))\n    end\n\n    it 'is false for non URL' do\n      expect(described_class).not_to be_loopback_uri(URI.parse(\"vscode://file/home/user/.vimrc\"))\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/invalid_request_response_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::InvalidRequestResponse do\n  subject(:response) { described_class.new }\n\n  describe \"#name\" do\n    it { expect(response.name).to eq(:invalid_request) }\n  end\n\n  describe \"#status\" do\n    it { expect(response.status).to eq(:bad_request) }\n  end\n\n  describe \".from_request\" do\n    let(:response) { described_class.from_request(request) }\n\n    context \"when param missed\" do\n      let(:request) { double(missing_param: \"some_param\") }\n\n      it \"sets a description\" do\n        expect(response.description).to eq(\n          I18n.t(:missing_param, scope: %i[doorkeeper errors messages invalid_request], value: \"some_param\"),\n        )\n      end\n\n      it \"sets the reason\" do\n        expect(response.reason).to eq(:missing_param)\n      end\n    end\n\n    context \"when request is not authorized\" do\n      let(:request) { double(invalid_request_reason: :request_not_authorized) }\n\n      it \"sets a description\" do\n        expect(response.description).to eq(\n          I18n.t(:request_not_authorized, scope: %i[doorkeeper errors messages invalid_request]),\n        )\n      end\n\n      it \"sets the reason\" do\n        expect(response.reason).to eq(:request_not_authorized)\n      end\n    end\n\n    context \"when unknown reason\" do\n      let(:request) { double(invalid_request_reason: :unknown_reason) }\n\n      it \"sets a description\" do\n        expect(response.description).to eq(\n          I18n.t(:unknown, scope: %i[doorkeeper errors messages invalid_request]),\n        )\n      end\n\n      it \"sets the reason to unknown\" do\n        expect(response.reason).to eq(:unknown_reason)\n      end\n    end\n  end\n\n  describe \".redirectable?\" do\n    it \"not redirectable when missing_param is client_id\" do\n      subject = described_class.new(missing_param: :client_id)\n\n      expect(subject.redirectable?).to be false\n    end\n\n    it \"is redirectable when missing_param is other than client_id\" do\n      subject = described_class.new(missing_param: :code_verifier)\n\n      expect(subject.redirectable?).to be true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/invalid_token_response_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::InvalidTokenResponse do\n  let(:response) { described_class.new }\n\n  describe \"#name\" do\n    it { expect(response.name).to eq(:invalid_token) }\n  end\n\n  describe \"#status\" do\n    it { expect(response.status).to eq(:unauthorized) }\n  end\n\n  describe \".from_access_token\" do\n    let(:response) { described_class.from_access_token(access_token) }\n\n    context \"when token revoked\" do\n      let(:access_token) { double(revoked?: true, expired?: true) }\n\n      it \"sets a description\" do\n        expect(response.description).to include(\"revoked\")\n      end\n\n      it \"sets the reason\" do\n        expect(response.reason).to eq(:revoked)\n      end\n    end\n\n    context \"when token expired\" do\n      let(:access_token) { double(revoked?: false, expired?: true) }\n\n      it \"sets a description\" do\n        expect(response.description).to include(\"expired\")\n      end\n\n      it \"sets the reason\" do\n        expect(response.reason).to eq(:expired)\n      end\n    end\n\n    context \"when unknown\" do\n      let(:access_token) { double(revoked?: false, expired?: false) }\n\n      it \"sets a description\" do\n        expect(response.description).to include(\"invalid\")\n      end\n\n      it \"sets the reason\" do\n        expect(response.reason).to eq(:unknown)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/password_access_token_request_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::PasswordAccessTokenRequest do\n  subject(:request) do\n    described_class.new(server, client, credentials, owner)\n  end\n\n  let(:server) do\n    double(\n      :server,\n      default_scopes: Doorkeeper::OAuth::Scopes.new,\n      access_token_expires_in: 2.hours,\n      refresh_token_enabled?: false,\n      custom_access_token_expires_in: lambda { |context|\n        context.grant_type == Doorkeeper::OAuth::PASSWORD ? 1234 : nil\n      },\n    )\n  end\n  let(:client) { Doorkeeper::OAuth::Client.new(FactoryBot.create(:application)) }\n  let(:credentials) { Doorkeeper::OAuth::Client::Credentials.new(\"uid\", \"secret\") }\n  let(:application) { client.application }\n  let(:owner) { FactoryBot.build_stubbed(:resource_owner) }\n\n  before do\n    allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(true)\n  end\n\n  it \"issues a new token for the client\" do\n    expect do\n      request.authorize\n    end.to change { application.reload.access_tokens.count }.by(1)\n\n    expect(application.reload.access_tokens.max_by(&:created_at).expires_in).to eq(1234)\n  end\n\n  it \"doesn't issue a new token without client authentication\" do\n    request = described_class.new(server, nil, nil, owner)\n    expect do\n      request.authorize\n    end.not_to(change { Doorkeeper::AccessToken.count })\n\n    expect(request.error).to eq(Doorkeeper::Errors::InvalidClient)\n  end\n\n  context \"when skip_client_authentication_for_password_grant is true\" do\n    before do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        skip_client_authentication_for_password_grant true\n      end\n    end\n\n    it \"issues a new token for the client without client authentication\" do\n      request = described_class.new(server, nil, nil, owner)\n      expect do\n        request.authorize\n      end.to change { Doorkeeper::AccessToken.count }.by(1)\n\n      expect(Doorkeeper::AccessToken.all.max_by(&:created_at).expires_in).to eq(1234)\n    end\n  end\n\n  it \"doesn't issue a new token with an invalid client\" do\n    request = described_class.new(server, nil, credentials, owner, { client_id: \"bad_id\" })\n    expect do\n      request.authorize\n    end.not_to(change { Doorkeeper::AccessToken.count })\n\n    expect(request.error).to eq(Doorkeeper::Errors::InvalidClient)\n  end\n\n  it \"requires the owner\" do\n    request = described_class.new(server, client, credentials, nil)\n    request.validate\n    expect(request.error).to eq(Doorkeeper::Errors::InvalidGrant)\n  end\n\n  it \"creates token even when there is already one (default)\" do\n    FactoryBot.create(\n      :access_token,\n      application_id: client.id,\n      resource_owner_id: owner.id,\n      resource_owner_type: owner.class.name,\n    )\n\n    expect do\n      request.authorize\n    end.to change { Doorkeeper::AccessToken.count }.by(1)\n  end\n\n  it \"skips token creation if there is already one reusable\" do\n    allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)\n    FactoryBot.create(\n      :access_token,\n      application_id: client.id,\n      resource_owner_id: owner.id,\n      resource_owner_type: owner.class.name,\n    )\n\n    expect do\n      request.authorize\n    end.not_to(change { Doorkeeper::AccessToken.count })\n  end\n\n  it \"creates token when there is already one but non reusable\" do\n    allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)\n    FactoryBot.create(\n      :access_token,\n      application_id: client.id,\n      resource_owner_id: owner.id,\n      resource_owner_type: owner.class.name,\n    )\n    allow_any_instance_of(Doorkeeper::AccessToken).to receive(:reusable?).and_return(false)\n\n    expect do\n      request.authorize\n    end.to change { Doorkeeper::AccessToken.count }.by(1)\n  end\n\n  it \"calls configured request callback methods\" do\n    expect(Doorkeeper.configuration.before_successful_strategy_response)\n      .to receive(:call).with(request).once\n\n    expect(Doorkeeper.configuration.after_successful_strategy_response)\n      .to receive(:call).with(request, instance_of(Doorkeeper::OAuth::TokenResponse)).once\n\n    request.authorize\n  end\n\n  describe \"with scopes\" do\n    subject(:request) do\n      described_class.new(server, client, credentials, owner, scope: \"public\")\n    end\n\n    context \"when scopes_by_grant_type is not configured for grant_type\" do\n      it \"returns error when scopes are invalid\" do\n        allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string(\"another\"))\n        request.validate\n        expect(request.error).to eq(Doorkeeper::Errors::InvalidScope)\n      end\n\n      it \"creates the token with scopes if scopes are valid\" do\n        allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string(\"public\"))\n        expect do\n          request.authorize\n        end.to change { Doorkeeper::AccessToken.count }.by(1)\n\n        expect(Doorkeeper::AccessToken.last.scopes).to include(\"public\")\n      end\n    end\n\n    context \"when scopes_by_grant_type is configured for grant_type\" do\n      it \"returns error when scopes are valid but not permitted for grant_type\" do\n        allow(server)\n          .to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string(\"public\"))\n        allow(Doorkeeper.configuration)\n          .to receive(:scopes_by_grant_type).and_return(password: \"another\")\n        request.validate\n        expect(request.error).to eq(Doorkeeper::Errors::InvalidScope)\n      end\n\n      it \"creates the token with scopes if scopes are valid and permitted for grant_type\" do\n        allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string(\"public\"))\n        allow(Doorkeeper.configuration)\n          .to receive(:scopes_by_grant_type).and_return(password: [:public])\n\n        expect do\n          request.authorize\n        end.to change { Doorkeeper::AccessToken.count }.by(1)\n\n        expect(Doorkeeper::AccessToken.last.scopes).to include(\"public\")\n      end\n    end\n  end\n\n  describe \"with custom expiry\" do\n    let(:server) do\n      double(\n        :server,\n        default_scopes: Doorkeeper::OAuth::Scopes.new,\n        access_token_expires_in: 2.hours,\n        refresh_token_enabled?: false,\n        custom_access_token_expires_in: lambda { |context|\n          if context.scopes.exists?(\"public\")\n            222\n          elsif context.scopes.exists?(\"magic\")\n            Float::INFINITY\n          end\n        },\n      )\n    end\n\n    before do\n      allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(true)\n    end\n\n    it \"checks scopes\" do\n      request = described_class.new(server, client, credentials, owner, scope: \"public\")\n      allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string(\"public\"))\n\n      expect do\n        request.authorize\n      end.to change { Doorkeeper::AccessToken.count }.by(1)\n\n      expect(Doorkeeper::AccessToken.last.expires_in).to eq(222)\n    end\n\n    it \"falls back to the default otherwise\" do\n      request = described_class.new(server, client, credentials, owner, scope: \"private\")\n      allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string(\"private\"))\n\n      expect do\n        request.authorize\n      end.to change { Doorkeeper::AccessToken.count }.by(1)\n\n      expect(Doorkeeper::AccessToken.last.expires_in).to eq(2.hours)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/pre_authorization_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::PreAuthorization do\n  subject(:pre_auth) do\n    described_class.new(server, attributes)\n  end\n\n  let(:server) do\n    server = Doorkeeper.configuration\n    allow(server).to receive(:default_scopes).and_return(Doorkeeper::OAuth::Scopes.from_string(\"default\"))\n    allow(server).to receive(:optional_scopes).and_return(Doorkeeper::OAuth::Scopes.from_string(\"public profile\"))\n    server\n  end\n\n  let(:application) { FactoryBot.create(:application, redirect_uri: \"https://app.com/callback\") }\n  let(:client) { Doorkeeper::OAuth::Client.find(application.uid) }\n\n  let(:attributes) do\n    {\n      client_id: client.uid,\n      response_type: \"code\",\n      redirect_uri: \"https://app.com/callback\",\n      state: \"save-this\",\n      current_resource_owner: Object.new,\n    }\n  end\n\n  it \"must call the validations on client and redirect_uri before other validations because they are not redirectable\" do\n    validation_attributes = described_class.validations.map { |validation| validation[:attribute] }\n\n    expect(validation_attributes).to eq(%i[\n      client_id\n      client\n      client_supports_grant_flow\n      resource_owner_authorize_for_client\n      redirect_uri\n      params\n      response_type\n      response_mode\n      scopes\n      code_challenge\n      code_challenge_method\n    ])\n  end\n\n  it \"is authorizable when request is valid\" do\n    expect(pre_auth).to be_authorizable\n  end\n\n  context \"when using default grant flows\" do\n    it 'accepts \"code\" as response type' do\n      attributes[:response_type] = \"code\"\n      expect(pre_auth).to be_authorizable\n    end\n\n    it 'accepts \"token\" as response type' do\n      allow(server).to receive(:grant_flows).and_return([\"implicit\"])\n      attributes[:response_type] = \"token\"\n      expect(pre_auth).to be_authorizable\n    end\n  end\n\n  context \"when authorization code grant flow is disabled\" do\n    before do\n      allow(server).to receive(:grant_flows).and_return([\"implicit\"])\n    end\n\n    it 'does not accept \"code\" as response type' do\n      attributes[:response_type] = \"code\"\n      expect(pre_auth).not_to be_authorizable\n    end\n  end\n\n  context \"when implicit grant flow is disabled\" do\n    before do\n      allow(server).to receive(:grant_flows).and_return([\"authorization_code\"])\n    end\n\n    it 'does not accept \"token\" as response type' do\n      attributes[:response_type] = \"token\"\n      expect(pre_auth).not_to be_authorizable\n    end\n  end\n\n  context \"with response_mode parameter is provided\" do\n    context \"when response_type is 'code'\" do\n      before { attributes[:response_type] = \"code\" }\n\n      it \"sets response_mode as 'query' when it is not provided\" do\n        attributes[:response_mode] = \"\"\n\n        expect(pre_auth).to be_authorizable\n        expect(pre_auth.response_mode).to eq(\"query\")\n      end\n\n      it 'accepts \"query\" as response_mode' do\n        attributes[:response_mode] = \"query\"\n        expect(pre_auth).to be_authorizable\n      end\n\n      it 'accepts \"fragment\" as response_mode' do\n        attributes[:response_mode] = \"fragment\"\n        expect(pre_auth).to be_authorizable\n      end\n\n      it 'accepts \"form_post\" as response_mode' do\n        attributes[:response_mode] = \"form_post\"\n        expect(pre_auth).to be_authorizable\n      end\n\n      it \"does not accept response_mode other than query, fragment, form_post\" do\n        attributes[:response_mode] = \"other response_mode\"\n\n        expect(pre_auth).not_to be_authorizable\n      end\n    end\n\n    context \"when response_type is 'token'\" do\n      before do\n        allow(server).to receive(:grant_flows).and_return([\"implicit\"])\n        attributes[:response_type] = \"token\"\n      end\n\n      it \"sets response_mode as 'fragment' when it is not provided\" do\n        attributes[:response_mode] = \"\"\n\n        expect(pre_auth).to be_authorizable\n        expect(pre_auth.response_mode).to eq(\"fragment\")\n      end\n\n      it 'accepts \"fragment\" as response_mode' do\n        attributes[:response_mode] = \"fragment\"\n        expect(pre_auth).to be_authorizable\n      end\n\n      it 'accepts \"form_post\" as response_mode' do\n        attributes[:response_mode] = \"form_post\"\n        expect(pre_auth).to be_authorizable\n      end\n\n      it 'does not accept \"query\" response_mode when response_type is \"token\"' do\n        attributes[:response_mode] = \"query\"\n\n        expect(pre_auth).not_to be_authorizable\n      end\n    end\n  end\n\n  context \"when client application does not restrict valid scopes\" do\n    it \"accepts valid scopes\" do\n      attributes[:scope] = \"public\"\n      expect(pre_auth).to be_authorizable\n    end\n\n    it \"rejects (globally) non-valid scopes\" do\n      attributes[:scope] = \"invalid\"\n      expect(pre_auth).not_to be_authorizable\n    end\n\n    it \"accepts scopes which are permitted for grant_type\" do\n      allow(server).to receive(:scopes_by_grant_type).and_return(authorization_code: [:public])\n      attributes[:scope] = \"public\"\n      expect(pre_auth).to be_authorizable\n    end\n\n    it \"rejects scopes which are not permitted for grant_type\" do\n      allow(server).to receive(:scopes_by_grant_type).and_return(authorization_code: [:profile])\n      attributes[:scope] = \"public\"\n      expect(pre_auth).not_to be_authorizable\n    end\n  end\n\n  context \"when client application restricts valid scopes\" do\n    let(:application) do\n      FactoryBot.create(:application, scopes: Doorkeeper::OAuth::Scopes.from_string(\"public nonsense\"))\n    end\n\n    it \"accepts valid scopes\" do\n      attributes[:scope] = \"public\"\n      expect(pre_auth).to be_authorizable\n    end\n\n    it \"rejects (globally) non-valid scopes\" do\n      attributes[:scope] = \"invalid\"\n      expect(pre_auth).not_to be_authorizable\n    end\n\n    it \"rejects (application level) non-valid scopes\" do\n      attributes[:scope] = \"profile\"\n      expect(pre_auth).not_to be_authorizable\n    end\n\n    it \"accepts scopes which are permitted for grant_type\" do\n      allow(server).to receive(:scopes_by_grant_type).and_return(authorization_code: [:public])\n      attributes[:scope] = \"public\"\n      expect(pre_auth).to be_authorizable\n    end\n\n    it \"rejects scopes which are not permitted for grant_type\" do\n      allow(server).to receive(:scopes_by_grant_type).and_return(authorization_code: [:profile])\n      attributes[:scope] = \"public\"\n      expect(pre_auth).not_to be_authorizable\n    end\n  end\n\n  context \"when scope is not provided to pre_authorization\" do\n    before { attributes[:scope] = nil }\n\n    context \"when default scopes is provided\" do\n      it \"uses default scopes\" do\n        allow(server).to receive(:default_scopes).and_return(Doorkeeper::OAuth::Scopes.from_string(\"default_scope\"))\n        expect(pre_auth).to be_authorizable\n        expect(pre_auth.scope).to eq(\"default_scope\")\n        expect(pre_auth.scopes).to eq(Doorkeeper::OAuth::Scopes.from_string(\"default_scope\"))\n      end\n    end\n\n    context \"when default scopes is none\" do\n      it \"not be authorizable when none default scope\" do\n        allow(server).to receive(:default_scopes).and_return(Doorkeeper::OAuth::Scopes.new)\n        expect(pre_auth).not_to be_authorizable\n      end\n    end\n  end\n\n  it \"matches the redirect uri against client's one\" do\n    attributes[:redirect_uri] = \"http://nothesame.com\"\n    expect(pre_auth).not_to be_authorizable\n  end\n\n  it \"stores the state\" do\n    expect(pre_auth.state).to eq(\"save-this\")\n  end\n\n  it \"rejects if response type is not allowed\" do\n    attributes[:response_type] = \"whops\"\n    expect(pre_auth).not_to be_authorizable\n  end\n\n  it \"requires an existing client\" do\n    attributes[:client_id] = nil\n    expect(pre_auth).not_to be_authorizable\n  end\n\n  it \"requires a redirect uri\" do\n    attributes[:redirect_uri] = nil\n    expect(pre_auth).not_to be_authorizable\n  end\n\n  context \"when resource_owner cannot access client application\" do\n    before { allow(Doorkeeper.configuration).to receive(:authorize_resource_owner_for_client).and_return(->(*_) { false }) }\n\n    it \"is not authorizable\" do\n      expect(pre_auth).not_to be_authorizable\n    end\n  end\n\n  describe \"as_json\" do\n    before { pre_auth.authorizable? }\n\n    it { is_expected.to respond_to :as_json }\n\n    shared_examples \"returns the pre authorization\" do\n      it \"returns the pre authorization\" do\n        expect(json[:client_id]).to eq client.uid\n        expect(json[:redirect_uri]).to eq pre_auth.redirect_uri\n        expect(json[:state]).to eq pre_auth.state\n        expect(json[:response_type]).to eq pre_auth.response_type\n        expect(json[:scope]).to eq pre_auth.scope\n        expect(json[:client_name]).to eq client.name\n        expect(json[:status]).to eq I18n.t(\"doorkeeper.pre_authorization.status\")\n      end\n    end\n\n    context \"when called without params\" do\n      let(:json) { pre_auth.as_json }\n\n      include_examples \"returns the pre authorization\"\n    end\n\n    context \"when called with params\" do\n      let(:json) { pre_auth.as_json(foo: \"bar\") }\n\n      include_examples \"returns the pre authorization\"\n    end\n  end\n\n  describe \"#form_post_response?\" do\n    it { is_expected.to respond_to(:form_post_response?) }\n\n    it \"return true when response_mode is form_post\" do\n      attributes[:response_mode] = \"form_post\"\n      expect(pre_auth.form_post_response?).to be true\n    end\n\n    it \"when response_mode is other than form_post\" do\n      attributes[:response_mode] = \"fragment\"\n      expect(pre_auth.form_post_response?).to be false\n    end\n  end\n\n  context \"when using PKCE params\" do\n    context \"when PKCE is supported\" do\n      before do\n        allow(Doorkeeper::AccessGrant).to receive(:pkce_supported?).and_return(true)\n      end\n\n      it \"accepts a blank code_challenge\" do\n        attributes[:code_challenge] = \" \"\n\n        expect(pre_auth).to be_authorizable\n      end\n\n      it \"accepts a code_challenge with a known code_challenge_method\" do\n        attributes[:code_challenge] = \"a45a9fea-0676-477e-95b1-a40f72ac3cfb\"\n        attributes[:code_challenge_method] = \"plain\"\n\n        expect(pre_auth).to be_authorizable\n\n        attributes[:code_challenge_method] = \"S256\"\n\n        expect(pre_auth).to be_authorizable\n      end\n\n      it \"rejects unknown values for code_challenge_method\" do\n        attributes[:code_challenge] = \"a45a9fea-0676-477e-95b1-a40f72ac3cfb\"\n        attributes[:code_challenge_method] = \"unknown\"\n\n        expect(pre_auth).not_to be_authorizable\n      end\n\n      context \"when pkce_code_challenge_methods is set to none\" do\n        before do\n          Doorkeeper.configure do\n            pkce_code_challenge_methods []\n          end\n        end\n\n        it \"rejects plain as a code_challenge_method\" do\n          attributes[:code_challenge] = \"a45a9fea-0676-477e-95b1-a40f72ac3cfb\"\n          attributes[:code_challenge_method] = \"plain\"\n\n          expect(pre_auth).to_not be_authorizable\n          expect(pre_auth.error_response.description).to eq(\n            \"The authorization server does not support PKCE as there are no accepted code_challenge_method values.\"\n          )\n        end\n      end\n\n      context \"when pkce_code_challenge_methods is set to only S256\" do\n        before do\n          Doorkeeper.configure do\n            pkce_code_challenge_methods [\"S256\"]\n          end\n        end\n\n        it \"accepts S256 as a code_challenge_method\" do\n          attributes[:code_challenge] = \"a45a9fea-0676-477e-95b1-a40f72ac3cfb\"\n          attributes[:code_challenge_method] = \"S256\"\n\n          expect(pre_auth).to be_authorizable\n        end\n\n        it \"rejects plain as a code_challenge_method\" do\n          attributes[:code_challenge] = \"a45a9fea-0676-477e-95b1-a40f72ac3cfb\"\n          attributes[:code_challenge_method] = \"plain\"\n\n          expect(pre_auth).to_not be_authorizable\n          expect(pre_auth.error_response.description).to eq(\"The code_challenge_method must be S256.\")\n        end\n      end\n\n      it \"rejects unknown as a code_challenge_method\" do\n        attributes[:code_challenge] = \"a45a9fea-0676-477e-95b1-a40f72ac3cfb\"\n        attributes[:code_challenge_method] = \"unknown\"\n\n        expect(pre_auth).to_not be_authorizable\n        expect(pre_auth.error_response.description).to eq(\"The code_challenge_method must be one of plain, S256.\")\n      end\n    end\n\n    context \"when PKCE is not supported\" do\n      before do\n        allow(Doorkeeper::AccessGrant).to receive(:pkce_supported?).and_return(false)\n      end\n\n      it \"accepts unknown values for code_challenge_method\" do\n        attributes[:code_challenge] = \"a45a9fea-0676-477e-95b1-a40f72ac3cfb\"\n        attributes[:code_challenge_method] = \"unknown\"\n\n        expect(pre_auth).to be_authorizable\n      end\n    end\n\n    context \"when force_pkce is enabled\" do\n      before do\n        allow_any_instance_of(Doorkeeper::Config).to receive(:force_pkce?).and_return(true)\n      end\n\n      context \"when the app is confidential\" do\n        before do\n          application.update(confidential: true)\n        end\n\n        it \"accepts a blank code_challenge\" do\n          attributes[:code_challenge] = \" \"\n\n          expect(pre_auth).to be_authorizable\n        end\n\n        it \"accepts a code challenge\" do\n          attributes[:code_challenge] = \"a45a9fea-0676-477e-95b1-a40f72ac3cfb\"\n          attributes[:code_challenge_method] = \"plain\"\n\n          expect(pre_auth).to be_authorizable\n        end\n      end\n\n      context \"when the app is not confidential\" do\n        before do\n          application.update(confidential: false)\n        end\n\n        it \"does not accept a blank code_challenge\" do\n          attributes[:code_challenge] = \" \"\n\n          expect(pre_auth).not_to be_authorizable\n          expect(pre_auth.error_response.description).to eq(translated_invalid_request_error_message(:invalid_code_challenge, nil))\n        end\n\n        it \"accepts a code challenge\" do\n          attributes[:code_challenge] = \"a45a9fea-0676-477e-95b1-a40f72ac3cfb\"\n          attributes[:code_challenge_method] = \"plain\"\n\n          expect(pre_auth).to be_authorizable\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/refresh_token_request_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::RefreshTokenRequest do\n  subject(:request) { described_class.new(server, refresh_token, credentials) }\n\n  let(:server) do\n    double :server, access_token_expires_in: 2.minutes\n  end\n\n  let(:refresh_token) do\n    FactoryBot.create(:access_token, use_refresh_token: true)\n  end\n\n  let(:client) { refresh_token.application }\n  let(:credentials) { Doorkeeper::OAuth::Client::Credentials.new(client.uid, client.secret) }\n\n  before do\n    allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)\n    allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(false)\n  end\n\n  it \"returns :grant_type as refresh_token\" do\n    expect(request.grant_type).to eq(Doorkeeper::OAuth::REFRESH_TOKEN)\n  end\n\n  it \"issues a new token for the client\" do\n    expect { request.authorize }.to change { client.reload.access_tokens.count }.by(1)\n    # #sort_by used for MongoDB ORM extensions for valid ordering\n    expect(client.reload.access_tokens.max_by(&:created_at).expires_in).to eq(refresh_token.expires_in)\n  end\n\n  it \"issues a new token for the client with the same expiry as of original token\" do\n    allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(true)\n    allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)\n\n    described_class.new(server, refresh_token, credentials).authorize\n\n    # #sort_by used for MongoDB ORM extensions for valid ordering\n    expect(client.reload.access_tokens.max_by(&:created_at).expires_in).to eq(refresh_token.expires_in)\n  end\n\n  it \"revokes the previous token\" do\n    expect { request.authorize }.to change(refresh_token, :revoked?).from(false).to(true)\n  end\n\n  it \"calls configured request callback methods\" do\n    expect(Doorkeeper.configuration.before_successful_strategy_response)\n      .to receive(:call).with(request).once\n\n    expect(Doorkeeper.configuration.after_successful_strategy_response)\n      .to receive(:call).with(request, instance_of(Doorkeeper::OAuth::TokenResponse)).once\n\n    request.authorize\n  end\n\n  it \"requires the refresh token\" do\n    request = described_class.new(server, nil, credentials)\n    request.validate\n    expect(request.error).to eq(Doorkeeper::Errors::InvalidRequest)\n    expect(request.missing_param).to eq(:refresh_token)\n  end\n\n  it \"requires credentials to be valid if provided\" do\n    credentials = Doorkeeper::OAuth::Client::Credentials.new(\"invalid\", \"invalid\")\n    request = described_class.new(server, refresh_token, credentials)\n    request.validate\n    expect(request.error).to eq(Doorkeeper::Errors::InvalidClient)\n  end\n\n  it \"requires the token's client and current client to match\" do\n    other_app = FactoryBot.create(:application)\n    credentials = Doorkeeper::OAuth::Client::Credentials.new(other_app.uid, other_app.secret)\n\n    request = described_class.new(server, refresh_token, credentials)\n    request.validate\n    expect(request.error).to eq(Doorkeeper::Errors::InvalidGrant)\n  end\n\n  it \"rejects revoked tokens\" do\n    refresh_token.revoke\n    request.validate\n    expect(request.error).to eq(Doorkeeper::Errors::InvalidGrant)\n  end\n\n  it \"accepts expired tokens\" do\n    refresh_token.expires_in = -1\n    refresh_token.save\n    request.validate\n    expect(request).to be_valid\n  end\n\n  context \"when refresh token gets revoked between validation and authorization\" do\n    before do\n      allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)\n    end\n\n    it \"raises InvalidGrantReuse error inside the lock block to prevent race condition\" do\n      # This test verifies that the InvalidGrantReuse check inside the lock block\n      # properly detects when a token has been revoked by a concurrent request.\n      \n      # Set up the token to be revoked inside the lock\n      allow(refresh_token).to receive(:with_lock) do |&block|\n        # Mark token as revoked before executing the block\n        allow(refresh_token).to receive(:revoked?).and_return(true)\n        block.call\n      end\n      \n      # Validation should pass (we haven't set up the mock yet)\n      expect(request).to be_valid\n      \n      # Authorization should raise error when it checks revoked status inside lock\n      expect { request.authorize }.to raise_error(Doorkeeper::Errors::InvalidGrantReuse)\n    end\n  end\n\n  context \"when refresh tokens expire on access token use\" do\n    before do\n      allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(true)\n    end\n\n    it \"issues a new token for the client\" do\n      expect { request.authorize }.to change { client.reload.access_tokens.count }.by(1)\n    end\n\n    it \"does not revoke the previous token\" do\n      request.authorize\n      expect(refresh_token).not_to be_revoked\n    end\n\n    it \"sets the previous refresh token in the new access token\" do\n      request.authorize\n      expect(\n        # #sort_by used for MongoDB ORM extensions for valid ordering\n        client.access_tokens.max_by(&:created_at).previous_refresh_token,\n      ).to eq(refresh_token.refresh_token)\n    end\n\n    it \"does not lock the previous token model\" do\n      expect(refresh_token).not_to receive(:lock!)\n      request.authorize\n    end\n  end\n\n  context \"with clientless access tokens\" do\n    subject(:request) { described_class.new(server, refresh_token, nil) }\n\n    let!(:refresh_token) { FactoryBot.create(:clientless_access_token, use_refresh_token: true) }\n\n    it \"issues a new token without a client\" do\n      expect { request.authorize }.to change { Doorkeeper::AccessToken.count }.by(1)\n    end\n  end\n\n  context \"with scopes\" do\n    subject(:request) { described_class.new(server, refresh_token, credentials, parameters) }\n\n    let(:refresh_token) do\n      FactoryBot.create :access_token,\n                        use_refresh_token: true,\n                        scopes: \"public write\"\n    end\n    let(:parameters) { {} }\n\n    it \"transfers scopes from the old token to the new token\" do\n      request.authorize\n      expect(Doorkeeper::AccessToken.last.scopes).to eq(%i[public write])\n    end\n\n    it \"reduces scopes to the provided scopes\" do\n      parameters[:scopes] = \"public\"\n      request.authorize\n      expect(Doorkeeper::AccessToken.last.scopes).to eq(%i[public])\n    end\n\n    it \"validates that scopes are included in the original access token\" do\n      parameters[:scopes] = \"public update\"\n\n      request.validate\n      expect(request.error).to eq(Doorkeeper::Errors::InvalidScope)\n    end\n\n    it \"uses params[:scope] in favor of scopes if present (valid)\" do\n      parameters[:scopes] = \"public update\"\n      parameters[:scope] = \"public\"\n      request.authorize\n      expect(Doorkeeper::AccessToken.last.scopes).to eq(%i[public])\n    end\n\n    it \"uses params[:scope] in favor of scopes if present (invalid)\" do\n      parameters[:scopes] = \"public\"\n      parameters[:scope] = \"public update\"\n\n      request.validate\n      expect(request.error).to eq(Doorkeeper::Errors::InvalidScope)\n    end\n  end\n\n  context \"with dynamic scopes enabled\" do\n    subject(:request) { described_class.new(server, refresh_token, credentials, parameters) }\n\n    let(:application_scopes) { \"public write user:*\" }\n    let(:application) { FactoryBot.create(:application, scopes: application_scopes) }\n    let(:token_scopes) { \"public write user:1\" }\n\n    let(:refresh_token) do\n      FactoryBot.create :access_token,\n                        use_refresh_token: true,\n                        scopes: token_scopes,\n                        application: application\n    end\n\n    let(:parameters) { {} }\n\n    before do\n      Doorkeeper.configure do\n        enable_dynamic_scopes\n      end\n    end\n\n    it \"transfers scopes from the old token to the new token\" do\n      request.authorize\n      expect(Doorkeeper::AccessToken.last.scopes).to eq(%i[public write user:1])\n    end\n\n    it \"returns an error with invalid scope\" do\n      parameters[:scopes] = \"public garbage:*\"\n\n      response = request.authorize\n\n      expect(response).to be_a(Doorkeeper::OAuth::ErrorResponse)\n      expect(response.status).to eq(:bad_request)\n    end\n\n    it \"reduces scopes to the dynamic scope\" do\n      parameters[:scopes] = \"user:1\"\n      request.authorize\n      expect(Doorkeeper::AccessToken.last.scopes).to eq(%i[user:1])\n    end\n\n    it \"reduces scopes to the public scope\" do\n      parameters[:scopes] = \"public\"\n      request.authorize\n      expect(Doorkeeper::AccessToken.last.scopes).to eq(%i[public])\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/scopes_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::Scopes do\n  subject(:scopes) { described_class.new }\n\n  describe \"#add\" do\n    it \"allows you to add scopes with symbols\" do\n      scopes.add :public\n      expect(scopes.all).to eq([\"public\"])\n    end\n\n    it \"allows you to add scopes with strings\" do\n      scopes.add \"public\"\n      expect(scopes.all).to eq([\"public\"])\n    end\n\n    it \"do not add already included scopes\" do\n      scopes.add :public\n      scopes.add :public\n      expect(scopes.all).to eq([\"public\"])\n    end\n  end\n\n  describe \"#exists\" do\n    before do\n      scopes.add :public\n    end\n\n    it \"returns true if scope with given name is present\" do\n      expect(scopes).to exist(\"public\")\n    end\n\n    it \"returns false if scope with given name does not exist\" do\n      expect(scopes).not_to exist(\"other\")\n    end\n\n    it \"handles symbols\" do\n      expect(scopes).to exist(:public)\n      expect(scopes).not_to exist(:other)\n    end\n  end\n\n  describe \".from_string\" do\n    subject(:scopes) { described_class.from_string(string) }\n\n    let(:string) { \"public write\" }\n\n    it { expect(scopes).to be_a(described_class) }\n\n    describe \"#all\" do\n      it \"is an array of the expected scopes\" do\n        scopes_array = scopes.all\n        expect(scopes_array.size).to eq(2)\n        expect(scopes_array).to include(\"public\")\n        expect(scopes_array).to include(\"write\")\n      end\n    end\n  end\n\n  describe \"#+\" do\n    it \"can add to another scope object\" do\n      scopes = described_class.from_string(\"public\") + described_class.from_string(\"admin\")\n      expect(scopes.all).to eq(%w[public admin])\n    end\n\n    it \"does not change the existing object\" do\n      origin = described_class.from_string(\"public\")\n      expect(origin.to_s).to eq(\"public\")\n    end\n\n    it \"can add an array to a scope object\" do\n      scopes = described_class.from_string(\"public\") + [\"admin\"]\n      expect(scopes.all).to eq(%w[public admin])\n    end\n\n    it \"raises an error if cannot handle addition\" do\n      expect do\n        described_class.from_string(\"public\") + \"admin\"\n      end.to raise_error(NoMethodError)\n    end\n  end\n\n  describe \"#&\" do\n    it \"can get intersection with another scope object\" do\n      scopes = described_class.from_string(\"public admin\") & described_class.from_string(\"write admin\")\n      expect(scopes.all).to eq(%w[admin])\n    end\n\n    it \"does not change the existing object\" do\n      origin = described_class.from_string(\"public admin\")\n      origin & described_class.from_string(\"write admin\")\n      expect(origin.to_s).to eq(\"public admin\")\n    end\n\n    it \"can get intersection with an array\" do\n      scopes = described_class.from_string(\"public admin\") & %w[write admin]\n      expect(scopes.all).to eq(%w[admin])\n    end\n  end\n\n  describe \"#==\" do\n    it \"is equal to another set of scopes\" do\n      expect(described_class.from_string(\"public\")).to eq(described_class.from_string(\"public\"))\n    end\n\n    it \"is equal to another set of scopes with no particular order\" do\n      expect(described_class.from_string(\"public write\")).to eq(described_class.from_string(\"write public\"))\n    end\n\n    it \"differs from another set of scopes when scopes are not the same\" do\n      expect(described_class.from_string(\"public write\")).not_to eq(described_class.from_string(\"write\"))\n    end\n\n    it \"does not raise an error when compared to a non-enumerable object\" do\n      expect { described_class.from_string(\"public\") == false }.not_to raise_error\n    end\n  end\n\n  describe \"#allowed\" do\n    it \"can get intersection with another scope object\" do\n      scopes = described_class.from_string(\"public admin\").allowed(described_class.from_string(\"write admin\"))\n      expect(scopes.all).to eq(%w[admin])\n    end\n  end\n\n  describe \"#has_scopes?\" do\n    subject(:scopes) { described_class.from_string(\"public admin\") }\n\n    it \"returns true when at least one scope is included\" do\n      expect(scopes).to have_scopes(described_class.from_string(\"public\"))\n    end\n\n    it \"returns true when all scopes are included\" do\n      expect(scopes).to have_scopes(described_class.from_string(\"public admin\"))\n    end\n\n    it \"is true if all scopes are included in any order\" do\n      expect(scopes).to have_scopes(described_class.from_string(\"admin public\"))\n    end\n\n    it \"is false if no scopes are included\" do\n      expect(scopes).not_to have_scopes(described_class.from_string(\"notexistent\"))\n    end\n\n    it \"returns false when any scope is not included\" do\n      expect(scopes).not_to have_scopes(described_class.from_string(\"public nope\"))\n    end\n\n    it \"is false if no scopes are included even for existing ones\" do\n      expect(scopes).not_to have_scopes(described_class.from_string(\"public admin notexistent\"))\n    end\n\n    context \"with dynamic scopes disabled\" do\n      context \"with wildcard dynamic scope\" do\n        before do\n          scopes.add \"user:*\"\n        end\n\n        it \"returns false with specific user\" do\n          expect(scopes).not_to have_scopes(described_class.from_string(\"public user:1\"))\n        end\n\n        it \"returns true with wildcard user\" do\n          expect(scopes).to have_scopes(described_class.from_string(\"public user:*\"))\n        end\n\n        it \"returns false if requested scope missing parameter\" do\n          expect(scopes).not_to have_scopes(described_class.from_string(\"public user:\"))\n        end\n      end\n    end\n\n    context \"with dynamic scopes enabled\" do\n      before do\n        Doorkeeper.configure do\n          enable_dynamic_scopes\n        end\n      end\n\n      context \"with wildcard dynamic scope\" do\n        before do\n          scopes.add \"user:*\"\n        end\n\n        it \"returns true with specific user\" do\n          expect(scopes).to have_scopes(described_class.from_string(\"public user:1\"))\n        end\n\n        it \"returns true with wildcard user\" do\n          expect(scopes).to have_scopes(described_class.from_string(\"public user:*\"))\n        end\n\n        it \"returns false if requested scope missing parameter\" do\n          expect(scopes).not_to have_scopes(described_class.from_string(\"public user:\"))\n        end\n\n        it \"returns false if dynamic scope does not match\" do\n          expect(scopes).not_to have_scopes(described_class.from_string(\"public userA:1\"))\n        end\n\n        describe \"#&\" do\n          it \"allows user:1 scope\" do\n            scopes = described_class.from_string(\"public user:*\") & (described_class.from_string(\"public user:1\"))\n            expect(scopes.all).to eq(%w[public user:1])\n          end\n\n          it \"does not allow user:2 scope\" do\n            scopes = described_class.from_string(\"public user:1\") & (described_class.from_string(\"public user:2\"))\n            expect(scopes.all).to eq(%w[public])\n          end\n\n          it \"does not allow user:* scope\" do\n            scopes = described_class.from_string(\"public user:1\") & (described_class.from_string(\"public user:*\"))\n            expect(scopes.all).to eq(%w[public])\n          end\n        end\n\n        describe \"#allowed\" do\n          it \"allows user:1 scope\" do\n            scopes = described_class.from_string(\"public user:*\").allowed(described_class.from_string(\"public user:1\"))\n            expect(scopes.all).to eq(%w[public user:1])\n          end\n\n          it \"does not allow user:2 scope\" do\n            scopes = described_class.from_string(\"public user:1\").allowed(described_class.from_string(\"public user:2\"))\n            expect(scopes.all).to eq(%w[public])\n          end\n\n          it \"does not allow user:* scope\" do\n            scopes = described_class.from_string(\"public user:1\").allowed(described_class.from_string(\"public user:*\"))\n            expect(scopes.all).to eq(%w[public])\n          end\n        end\n      end\n\n      context \"with specific dynamic scope\" do\n        before do\n          scopes.add \"user:1\"\n        end\n\n        it \"returns true with specific user\" do\n          expect(scopes).to have_scopes(described_class.from_string(\"public user:1\"))\n        end\n\n        it \"returns false with wildcard user\" do\n          expect(scopes).not_to have_scopes(described_class.from_string(\"public user:*\"))\n        end\n\n        it \"returns false for disallowed user\" do\n          expect(scopes).not_to have_scopes(described_class.from_string(\"public user:2\"))\n        end\n\n        context \"with custom delimiter\" do\n          before do\n            Doorkeeper.configure do\n              enable_dynamic_scopes(delimiter: \"-\")\n            end\n\n            scopes.add \"user-1\"\n          end\n\n          it \"returns true with specific user\" do\n            expect(scopes).to have_scopes(described_class.from_string(\"public user-1\"))\n          end\n\n          it \"returns false with wildcard user\" do\n            expect(scopes).not_to have_scopes(described_class.from_string(\"public user-*\"))\n          end\n\n          it \"returns false for disallowed user\" do\n            expect(scopes).not_to have_scopes(described_class.from_string(\"public user-2\"))\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/token_request_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::TokenRequest do\n  subject(:request) do\n    described_class.new(pre_auth, owner)\n  end\n\n  let(:application) do\n    FactoryBot.create(:application, scopes: \"public\")\n  end\n\n  let(:pre_auth) do\n    server = Doorkeeper.config\n    allow(server).to receive(:default_scopes).and_return(Doorkeeper::OAuth::Scopes.from_string(\"public\"))\n    allow(server).to receive(:grant_flows).and_return(Doorkeeper::OAuth::Scopes.from_string(\"implicit\"))\n\n    client = Doorkeeper::OAuth::Client.new(application)\n\n    attributes = {\n      client_id: client.uid,\n      response_type: \"token\",\n      redirect_uri: \"https://app.com/callback\",\n    }\n\n    pre_auth = Doorkeeper::OAuth::PreAuthorization.new(server, attributes)\n    pre_auth.authorizable?\n    pre_auth\n  end\n\n  let(:owner) do\n    FactoryBot.create(:doorkeeper_testing_user, name: \"John\")\n  end\n\n  it \"creates an access token\" do\n    expect do\n      request.authorize\n    end.to change { Doorkeeper::AccessToken.count }.by(1)\n  end\n\n  it \"returns a code response\" do\n    expect(request.authorize).to be_a(Doorkeeper::OAuth::CodeResponse)\n  end\n\n  context \"when pre_auth is denied\" do\n    it \"does not create token and returns a error response\" do\n      expect { request.deny }.not_to(change { Doorkeeper::AccessToken.count })\n      expect(request.deny).to be_a(Doorkeeper::OAuth::ErrorResponse)\n    end\n  end\n\n  describe \"with custom expiration\" do\n    context \"when proper TTL returned\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          custom_access_token_expires_in do |context|\n            context.grant_type == Doorkeeper::OAuth::IMPLICIT ? 1234 : nil\n          end\n        end\n      end\n\n      it \"uses the custom ttl\" do\n        request.authorize\n        token = Doorkeeper::AccessToken.first\n        expect(token.expires_in).to eq(1234)\n      end\n    end\n\n    context \"when nil TTL returned\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          access_token_expires_in 654\n          custom_access_token_expires_in do |_context|\n            nil\n          end\n        end\n      end\n\n      it \"fallbacks to access_token_expires_in\" do\n        request.authorize\n        token = Doorkeeper::AccessToken.first\n        expect(token.expires_in).to eq(654)\n      end\n    end\n\n    context \"when infinite TTL returned\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          access_token_expires_in 654\n          custom_access_token_expires_in do |_context|\n            Float::INFINITY\n          end\n        end\n      end\n\n      it \"fallbacks to access_token_expires_in\" do\n        request.authorize\n        token = Doorkeeper::AccessToken.first\n        expect(token.expires_in).to be_nil\n      end\n    end\n\n    context \"when custom_access_token_expires_in uses resource_owner condition\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          custom_access_token_expires_in do |context|\n            if context.resource_owner&.name == \"John\"\n              10_000\n            else\n              500\n            end\n          end\n        end\n      end\n\n      it \"uses configured values for TTL\" do\n        request = described_class.new(pre_auth, owner)\n        request.authorize\n        token = Doorkeeper::AccessToken.last\n        expect(token.expires_in).to eq(10_000)\n\n        request = described_class.new(pre_auth, nil)\n        request.authorize\n        token = Doorkeeper::AccessToken.last\n        expect(token.expires_in).to eq(500)\n      end\n    end\n  end\n\n  context \"when reuse_access_token enabled\" do\n    it \"creates a new token if there are no matching tokens\" do\n      allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)\n      expect do\n        request.authorize\n      end.to change { Doorkeeper::AccessToken.count }.by(1)\n    end\n\n    it \"creates a new token if scopes do not match\" do\n      allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)\n      FactoryBot.create(\n        :access_token,\n        application_id: pre_auth.client.id,\n        resource_owner_id: owner.id,\n        resource_owner_type: owner.class.name,\n        scopes: \"\",\n      )\n\n      expect do\n        request.authorize\n      end.to change { Doorkeeper::AccessToken.count }.by(1)\n    end\n\n    it \"skips token creation if there is a matching one reusable\" do\n      allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)\n      allow(application.scopes).to receive(:has_scopes?).and_return(true)\n      allow(application.scopes).to receive(:all?).and_return(true)\n\n      FactoryBot.create(\n        :access_token, application_id: pre_auth.client.id,\n                       resource_owner_id: owner.id, resource_owner_type: owner.class.name, scopes: \"public\",\n      )\n\n      expect { request.authorize }.not_to(change { Doorkeeper::AccessToken.count })\n    end\n\n    it \"creates new token if there is a matching one but non reusable\" do\n      allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)\n      allow(application.scopes).to receive(:has_scopes?).and_return(true)\n      allow(application.scopes).to receive(:all?).and_return(true)\n\n      FactoryBot.create(\n        :access_token,\n        application_id: pre_auth.client.id,\n        resource_owner_id: owner.id,\n        resource_owner_type: owner.class.name,\n        scopes: \"public\",\n      )\n\n      allow_any_instance_of(Doorkeeper::AccessToken).to receive(:reusable?).and_return(false)\n\n      expect do\n        request.authorize\n      end.to change { Doorkeeper::AccessToken.count }.by(1)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/token_response_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::OAuth::TokenResponse do\n  subject(:response) { described_class.new(double.as_null_object) }\n\n  it \"includes access token response headers\" do\n    headers = response.headers\n    expect(headers.fetch(\"Cache-Control\")).to eq(\"no-store, no-cache\")\n    expect(headers.fetch(\"Pragma\")).to eq(\"no-cache\")\n  end\n\n  it \"status is ok\" do\n    expect(response.status).to eq(:ok)\n  end\n\n  describe \".body\" do\n    subject(:body) { described_class.new(access_token).body }\n\n    let(:access_token) do\n      double :access_token,\n             plaintext_token: \"some-token\",\n             expires_in: \"3600\",\n             expires_in_seconds: \"300\",\n             scopes_string: \"two scopes\",\n             plaintext_refresh_token: \"some-refresh-token\",\n             token_type: \"Bearer\",\n             created_at: 0\n    end\n\n    it \"includes :access_token\" do\n      expect(body[\"access_token\"]).to eq(\"some-token\")\n    end\n\n    it \"includes :token_type\" do\n      expect(body[\"token_type\"]).to eq(\"Bearer\")\n    end\n\n    # expires_in_seconds is returned as `expires_in` in order to match\n    # the OAuth spec (section 4.2.2)\n    it \"includes :expires_in\" do\n      expect(body[\"expires_in\"]).to eq(\"300\")\n    end\n\n    it \"includes :scope\" do\n      expect(body[\"scope\"]).to eq(\"two scopes\")\n    end\n\n    it \"includes :refresh_token\" do\n      expect(body[\"refresh_token\"]).to eq(\"some-refresh-token\")\n    end\n\n    it \"includes :created_at\" do\n      expect(body[\"created_at\"]).to eq(0)\n    end\n  end\n\n  describe \".body attributes\" do\n    subject(:token_response) { described_class.new(access_token) }\n\n    let(:access_token) do\n      double :access_token,\n             plaintext_token: \"some-token\",\n             expires_in: \"3600\",\n             expires_in_seconds: \"300\",\n             scopes_string: \"two scopes\",\n             plaintext_refresh_token: \"some-refresh-token\",\n             token_type: \"Bearer\",\n             custom_parameter: \"custom_value\",\n             created_at: 0\n    end\n\n    it \"can be augmented\" do\n      token_response.body[\"custom_parameter\"] = access_token.custom_parameter\n\n      expect(token_response.body[\"custom_parameter\"]).to eq(\"custom_value\")\n    end\n  end\n\n  describe \".body filters out empty values\" do\n    subject(:body) { described_class.new(access_token).body }\n\n    let(:access_token) do\n      double :access_token,\n             plaintext_token: \"some-token\",\n             expires_in_seconds: \"\",\n             scopes_string: \"\",\n             plaintext_refresh_token: \"\",\n             token_type: \"Bearer\",\n             created_at: 0\n    end\n\n    it \"includes :expires_in\" do\n      expect(body[\"expires_in\"]).to be_nil\n    end\n\n    it \"includes :scope\" do\n      expect(body[\"scope\"]).to be_nil\n    end\n\n    it \"includes :refresh_token\" do\n      expect(body[\"refresh_token\"]).to be_nil\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/oauth/token_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nmodule Doorkeeper\n  unless defined?(AccessToken)\n    class AccessToken\n    end\n  end\nend\n\nRSpec.describe Doorkeeper::OAuth::Token do\n  describe \".from_request\" do\n    let(:request) { double.as_null_object }\n\n    let(:method) do\n      ->(*) { \"token-value\" }\n    end\n\n    it \"accepts anything that responds to #call\" do\n      expect(method).to receive(:call).with(request)\n      described_class.from_request request, method\n    end\n\n    it \"delegates methods received as symbols to described_class class\" do\n      expect(described_class).to receive(:from_params).with(request)\n      described_class.from_request request, :from_params\n    end\n\n    it \"stops at the first credentials found\" do\n      not_called_method = double\n      expect(not_called_method).not_to receive(:call)\n      described_class.from_request request, ->(_r) {}, method, not_called_method\n    end\n\n    it \"returns the credential from extractor method\" do\n      credentials = described_class.from_request request, method\n      expect(credentials).to eq(\"token-value\")\n    end\n  end\n\n  describe \".from_access_token_param\" do\n    it \"returns token from access_token parameter\" do\n      request = double parameters: { access_token: \"some-token\" }\n      token   = described_class.from_access_token_param(request)\n      expect(token).to eq(\"some-token\")\n    end\n  end\n\n  describe \".from_bearer_param\" do\n    it \"returns token from bearer_token parameter\" do\n      request = double parameters: { bearer_token: \"some-token\" }\n      token   = described_class.from_bearer_param(request)\n      expect(token).to eq(\"some-token\")\n    end\n  end\n\n  describe \".from_bearer_authorization\" do\n    it \"returns token from capitalized authorization bearer\" do\n      request = double authorization: \"Bearer SomeToken\"\n      token   = described_class.from_bearer_authorization(request)\n      expect(token).to eq(\"SomeToken\")\n    end\n\n    it \"returns token from lowercased authorization bearer\" do\n      request = double authorization: \"bearer SomeToken\"\n      token   = described_class.from_bearer_authorization(request)\n      expect(token).to eq(\"SomeToken\")\n    end\n\n    it \"does not return token if authorization is not bearer\" do\n      request = double authorization: \"MAC SomeToken\"\n      token   = described_class.from_bearer_authorization(request)\n      expect(token).to be_blank\n    end\n  end\n\n  describe \".from_basic_authorization\" do\n    it \"returns token from capitalized authorization basic\" do\n      request = double authorization: \"Basic #{Base64.encode64 \"SomeToken:\"}\"\n      token   = described_class.from_basic_authorization(request)\n      expect(token).to eq(\"SomeToken\")\n    end\n\n    it \"returns token from lowercased authorization basic\" do\n      request = double authorization: \"basic #{Base64.encode64 \"SomeToken:\"}\"\n      token   = described_class.from_basic_authorization(request)\n      expect(token).to eq(\"SomeToken\")\n    end\n\n    it \"does not return token if authorization is not basic\" do\n      request = double authorization: \"MAC #{Base64.encode64 \"SomeToken:\"}\"\n      token   = described_class.from_basic_authorization(request)\n      expect(token).to be_blank\n    end\n  end\n\n  describe \".authenticate\" do\n    context \"when refresh tokens are disabled (default)\" do\n      context \"when refresh tokens are enabled\" do\n        it \"does not revoke previous refresh_token if token was found\" do\n          token = ->(_r) { \"token\" }\n          expect(\n            Doorkeeper::AccessToken,\n          ).to receive(:by_token).with(\"token\").and_return(token)\n          expect(token).not_to receive(:revoke_previous_refresh_token!)\n          described_class.authenticate double, token\n        end\n      end\n\n      it \"calls the finder if token was returned\" do\n        token = ->(_r) { \"token\" }\n        expect(Doorkeeper::AccessToken).to receive(:by_token).with(\"token\")\n        described_class.authenticate double, token\n      end\n    end\n\n    context \"when token hashing is enabled\" do\n      include_context \"with token hashing enabled\"\n\n      let(:hashed_token) { hashed_or_plain_token_func.call(\"token\") }\n      let(:token) { ->(_r) { \"token\" } }\n\n      it \"searches with the hashed token\" do\n        expect(\n          Doorkeeper::AccessToken,\n        ).to receive(:find_by).with(token: hashed_token).and_return(token)\n        described_class.authenticate double, token\n      end\n    end\n\n    context \"when refresh tokens are enabled\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          use_refresh_token\n        end\n      end\n\n      it \"revokes previous refresh_token if token was found\" do\n        token = ->(_r) { \"token\" }\n        expect(\n          Doorkeeper::AccessToken,\n        ).to receive(:by_token).with(\"token\").and_return(token)\n        expect(token).to receive(:revoke_previous_refresh_token!)\n        described_class.authenticate double, token\n      end\n\n      it \"calls the finder if token was returned\" do\n        token = ->(_r) { \"token\" }\n        expect(Doorkeeper::AccessToken).to receive(:by_token).with(\"token\")\n        described_class.authenticate double, token\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/option_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::Config::Option do\n  class Extension\n    def self.configure(&block)\n      @config = Config::Builder.new(Config.new, &block).build\n    end\n\n    def self.configuration\n      @config || (raise Errors::MissingConfiguration)\n    end\n\n    class Config\n      class Builder < Doorkeeper::Config::AbstractBuilder\n        def enforce_something\n          @config.instance_variable_set(:@enforce_something, true)\n        end\n      end\n\n      def enforce_something?\n        if defined?(@enforce_something)\n          @enforce_something\n        else\n          false\n        end\n      end\n\n      def self.builder_class\n        Config::Builder\n      end\n\n      extend Doorkeeper::Config::Option\n    end\n  end\n\n  it \"allows to define custom options in extensions\" do\n    expect do\n      Extension::Config.option(:some_option, default: 1)\n    end.not_to raise_error\n\n    Extension.configure do\n      some_option 20\n      enforce_something\n    end\n\n    expect(Extension.configuration.some_option).to eq(20)\n    expect(Extension.configuration.enforce_something?).to be(true)\n  end\nend\n"
  },
  {
    "path": "spec/lib/request/strategy_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::Request::Strategy do\n  subject(:strategy) { described_class.new(server) }\n\n  let(:server) { double }\n\n  describe \"#initialize\" do\n    it \"sets the server attribute\" do\n      expect(strategy.server).to eq server\n    end\n  end\n\n  describe \"#request\" do\n    it \"requires an implementation\" do\n      expect { strategy.request }.to raise_exception NotImplementedError\n    end\n  end\n\n  describe \"a sample Strategy subclass\" do\n    subject(:strategy) { strategy_class.new(server) }\n\n    let(:fake_request) { double }\n\n    let(:strategy_class) do\n      subclass = Class.new(described_class) do\n        class << self\n          attr_accessor :fake_request\n        end\n\n        def request\n          self.class.fake_request\n        end\n      end\n\n      subclass.fake_request = fake_request\n      subclass\n    end\n\n    it \"provides a request implementation\" do\n      expect(strategy.request).to eq fake_request\n    end\n\n    it \"authorizes the request\" do\n      expect(fake_request).to receive :authorize\n      strategy.authorize\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/secret_storing/base_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe ::Doorkeeper::SecretStoring::Base do\n  let(:instance) { double(\"instance\", token: \"foo\") }\n\n  describe \"#transform_secret\" do\n    it \"raises\" do\n      expect { described_class.transform_secret(\"foo\") }\n        .to raise_error(NotImplementedError)\n    end\n  end\n\n  describe \"#store_secret\" do\n    it \"sends to response of #transform_secret to the instance\" do\n      expect(described_class)\n        .to receive(:transform_secret).with(\"bar\")\n        .and_return \"bar+transform\"\n\n      expect(instance).to receive(:token=).with \"bar+transform\"\n      result = described_class.store_secret instance, :token, \"bar\"\n      expect(result).to eq \"bar+transform\"\n    end\n  end\n\n  describe \"#restore_secret\" do\n    it \"raises\" do\n      expect { described_class.restore_secret(described_class, :token) }\n        .to raise_error(NotImplementedError)\n    end\n  end\n\n  describe \"#allows_restoring_secrets?\" do\n    it \"does not allow it\" do\n      expect(described_class.allows_restoring_secrets?).to eq false\n    end\n  end\n\n  describe \"validate_for\" do\n    it \"allows for valid model\" do\n      expect(described_class.validate_for(:application)).to eq true\n      expect(described_class.validate_for(:token)).to eq true\n    end\n\n    it \"raises for invalid model\" do\n      expect { described_class.validate_for(:wat) }\n        .to raise_error(ArgumentError, /can not be used for wat/)\n    end\n  end\n\n  describe \"secret_matches?\" do\n    before do\n      allow(described_class).to receive(:transform_secret) { |input| \"transformed: #{input}\" }\n    end\n\n    it \"compares input with #transform_secret\" do\n      expect(described_class.secret_matches?(\"input\", \"input\")).to eq false\n      expect(described_class.secret_matches?(\"a\", \"transformed: a\")).to eq true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/secret_storing/bcrypt_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\nrequire \"bcrypt\"\n\nRSpec.describe ::Doorkeeper::SecretStoring::BCrypt do\n  let(:instance) { double(\"instance\", token: \"foo\") }\n\n  describe \"#transform_secret\" do\n    it \"creates a bcrypt password\" do\n      expect(described_class.transform_secret(\"foo\")).to be_a BCrypt::Password\n    end\n  end\n\n  describe \"#restore_secret\" do\n    it \"raises\" do\n      expect { described_class.restore_secret(instance, :token) }\n        .to raise_error(NotImplementedError)\n    end\n  end\n\n  describe \"#allows_restoring_secrets?\" do\n    it \"does not allow it\" do\n      expect(described_class.allows_restoring_secrets?).to be(false)\n    end\n  end\n\n  describe \"validate_for\" do\n    it \"allows for valid model\" do\n      expect(described_class.validate_for(:application)).to eq(true)\n    end\n\n    it \"raises for invalid model\" do\n      expect { described_class.validate_for(:wat) }\n        .to raise_error(ArgumentError, /can only be used for storing application secrets/)\n      expect { described_class.validate_for(:token) }\n        .to raise_error(ArgumentError, /can only be used for storing application secrets/)\n    end\n  end\n\n  describe \"secret_matches?\" do\n    it \"compares input with #transform_secret\" do\n      expect(described_class.secret_matches?(\"input\", \"input\")).to eq(false)\n\n      password = BCrypt::Password.create(\"foobar\")\n      expect(described_class.secret_matches?(\"foobar\", password.to_s)).to eq(true)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/secret_storing/plain_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe ::Doorkeeper::SecretStoring::Plain do\n  let(:instance) { double(\"instance\", token: \"foo\") }\n\n  describe \"#transform_secret\" do\n    it \"raises\" do\n      expect(described_class.transform_secret(\"foo\")).to eq \"foo\"\n    end\n  end\n\n  describe \"#restore_secret\" do\n    it \"raises\" do\n      expect(described_class.restore_secret(instance, :token)).to eq \"foo\"\n    end\n  end\n\n  describe \"#allows_restoring_secrets?\" do\n    it \"does allow it\" do\n      expect(described_class.allows_restoring_secrets?).to eq true\n    end\n  end\n\n  describe \"validate_for\" do\n    it \"allows for valid model\" do\n      expect(described_class.validate_for(:application)).to eq true\n      expect(described_class.validate_for(:token)).to eq true\n    end\n\n    it \"raises for invalid model\" do\n      expect { described_class.validate_for(:wat) }\n        .to raise_error(ArgumentError, /can not be used for wat/)\n    end\n  end\n\n  describe \"secret_matches?\" do\n    it \"compares input with #transform_secret\" do\n      expect(described_class.secret_matches?(\"input\", \"input\")).to eq true\n      expect(described_class.secret_matches?(\"a\", \"b\")).to eq false\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/secret_storing/sha256_hash_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe ::Doorkeeper::SecretStoring::Sha256Hash do\n  let(:instance) { double(\"instance\") }\n\n  let(:hash_function) do\n    ->(input) { ::Digest::SHA256.hexdigest(input) }\n  end\n\n  describe \"#transform_secret\" do\n    it \"raises\" do\n      expect(described_class.transform_secret(\"foo\")).to eq hash_function.call(\"foo\")\n    end\n  end\n\n  describe \"#restore_secret\" do\n    it \"raises\" do\n      expect { described_class.restore_secret(instance, :token) }.to raise_error(NotImplementedError)\n    end\n  end\n\n  describe \"#allows_restoring_secrets?\" do\n    it \"does not allow it\" do\n      expect(described_class.allows_restoring_secrets?).to eq false\n    end\n  end\n\n  describe \"validate_for\" do\n    it \"allows for valid model\" do\n      expect(described_class.validate_for(:application)).to eq true\n      expect(described_class.validate_for(:token)).to eq true\n    end\n\n    it \"raises for invalid model\" do\n      expect { described_class.validate_for(:wat) }.to raise_error(ArgumentError, /can not be used for wat/)\n    end\n  end\n\n  describe \"secret_matches?\" do\n    it \"compares input with #transform_secret\" do\n      expect(described_class.secret_matches?(\"input\", \"input\")).to eq false\n      expect(described_class.secret_matches?(\"a\", hash_function.call(\"a\"))).to eq true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/models/doorkeeper/access_grant_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::AccessGrant do\n  subject(:access_grant) do\n    FactoryBot.build(\n      :access_grant,\n      application: client,\n      resource_owner_id: resource_owner.id,\n      resource_owner_type: resource_owner.class.name,\n    )\n  end\n\n  let(:resource_owner) { FactoryBot.build_stubbed(:resource_owner) }\n  let(:client) { FactoryBot.build_stubbed(:application) }\n\n  it { expect(access_grant).to be_valid }\n\n  it_behaves_like \"an accessible token\"\n  it_behaves_like \"a revocable token\"\n  it_behaves_like \"a unique token\" do\n    let(:factory_name) { :access_grant }\n  end\n\n  context \"with hashing enabled\" do\n    let(:grant) do\n      FactoryBot.create :access_grant,\n                        resource_owner_id: resource_owner.id,\n                        resource_owner_type: resource_owner.class.name\n    end\n\n    include_context \"with token hashing enabled\"\n\n    it \"holds a volatile plaintext token when created\" do\n      expect(grant.plaintext_token).to be_a(String)\n      expect(grant.token)\n        .to eq(hashed_or_plain_token_func.call(grant.plaintext_token))\n\n      # Finder method only finds the hashed token\n      loaded = described_class.find_by(token: grant.token)\n      expect(loaded).to eq(grant)\n      expect(loaded.plaintext_token).to be_nil\n      expect(loaded.token).to eq(grant.token)\n    end\n\n    it \"does not find_by plain text tokens\" do\n      expect(described_class.find_by(token: grant.plaintext_token)).to be_nil\n    end\n\n    describe \"with having a plain text token\" do\n      let(:plain_text_token) { \"plain text token\" }\n\n      before do\n        # Assume we have a plain text token from before activating the option\n        grant.update_column(:token, plain_text_token)\n      end\n\n      context \"without fallback lookup\" do\n        it \"does not provide lookups with either through by_token\" do\n          expect(described_class.by_token(plain_text_token)).to eq(nil)\n          expect(described_class.by_token(grant.token)).to eq(nil)\n\n          # And it does not touch the token\n          grant.reload\n          expect(grant.token).to eq(plain_text_token)\n        end\n      end\n\n      context \"with fallback lookup\" do\n        include_context \"with token hashing and fallback lookup enabled\"\n\n        it \"upgrades a plain token when falling back to it\" do\n          # Side-effect: This will automatically upgrade the token\n          expect(described_class).to receive(:upgrade_fallback_value).and_call_original\n          expect(described_class.by_token(plain_text_token))\n            .to have_attributes(\n              resource_owner_id: grant.resource_owner_id,\n              application_id: grant.application_id,\n              redirect_uri: grant.redirect_uri,\n              expires_in: grant.expires_in,\n              scopes: grant.scopes,\n            )\n\n          # Will find subsequently by hashing the token\n          expect(described_class.by_token(plain_text_token))\n            .to have_attributes(\n              resource_owner_id: grant.resource_owner_id,\n              application_id: grant.application_id,\n              redirect_uri: grant.redirect_uri,\n              expires_in: grant.expires_in,\n              scopes: grant.scopes,\n            )\n\n          # Not all the ORM support :id PK\n          if grant.respond_to?(:id)\n            expect(described_class.by_token(plain_text_token).id).to eq(grant.id)\n          end\n\n          # And it modifies the token value\n          grant.reload\n          expect(grant.token).not_to eq(plain_text_token)\n          expect(described_class.find_by(token: plain_text_token)).to eq(nil)\n          expect(described_class.find_by(token: grant.token)).not_to be_nil\n        end\n      end\n    end\n  end\n\n  describe \"validations\" do\n    it \"is invalid without resource_owner_id\" do\n      access_grant.resource_owner_id = nil\n      expect(access_grant).not_to be_valid\n    end\n\n    it \"is invalid without application_id\" do\n      access_grant.application_id = nil\n      expect(access_grant).not_to be_valid\n    end\n\n    it \"is invalid without token\" do\n      access_grant.save\n      access_grant.token = nil\n      expect(access_grant).not_to be_valid\n    end\n\n    it \"is invalid without expires_in\" do\n      access_grant.expires_in = nil\n      expect(access_grant).not_to be_valid\n    end\n  end\n\n  describe \".revoke_all_for\" do\n    let(:application) { FactoryBot.create :application }\n    let(:default_attributes) do\n      {\n        application: application,\n        resource_owner_id: resource_owner.id,\n        resource_owner_type: resource_owner.class.name,\n      }\n    end\n\n    it \"revokes all tokens for given application and resource owner\" do\n      FactoryBot.create :access_grant, default_attributes\n\n      described_class.revoke_all_for(application.id, resource_owner)\n      expect(described_class.all).to all(be_revoked)\n    end\n\n    it \"matches application\" do\n      access_grant_for_different_app = FactoryBot.create(\n        :access_grant,\n        default_attributes.merge(application: FactoryBot.create(:application)),\n      )\n\n      described_class.revoke_all_for(application.id, resource_owner)\n\n      expect(access_grant_for_different_app.reload).not_to be_revoked\n    end\n\n    it \"matches resource owner\" do\n      other_resource_owner = FactoryBot.create(:resource_owner)\n      access_grant_for_different_owner = FactoryBot.create(\n        :access_grant,\n        default_attributes.merge(resource_owner_id: other_resource_owner.id),\n      )\n\n      described_class.revoke_all_for(application.id, resource_owner)\n\n      expect(access_grant_for_different_owner.reload).not_to be_revoked\n    end\n  end\n\n  describe \".revoke_all_for with read replica support\" do\n    let(:application) { FactoryBot.create(:application) }\n    let(:resource_owner) { FactoryBot.create(:resource_owner) }\n\n    before do\n      FactoryBot.create(:access_grant, application: application, resource_owner_id: resource_owner.id)\n    end\n\n    context \"when enable_multiple_database_roles is enabled\" do\n      before do\n        Doorkeeper.configure do\n          orm :active_record\n          enable_multiple_database_roles\n        end\n      end\n\n      it \"revokes grants using primary database role\" do\n        expect(ActiveRecord::Base).to receive(:connected_to).with(role: :writing).and_call_original\n\n        described_class.revoke_all_for(application.id, resource_owner)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/models/doorkeeper/access_token_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Doorkeeper::AccessToken do\n  subject(:access_token) { FactoryBot.build(:access_token) }\n\n  it { expect(access_token).to be_valid }\n\n  it_behaves_like \"an accessible token\"\n  it_behaves_like \"a revocable token\"\n  it_behaves_like \"a unique token\" do\n    let(:factory_name) { :access_token }\n  end\n\n  module CustomGeneratorArgs\n    def self.generate; end\n  end\n\n  describe \"#generate_token\" do\n    it \"generates a token using the default method\" do\n      FactoryBot.create :access_token\n\n      token = FactoryBot.create :access_token\n      expect(token.token).to be_a(String)\n    end\n\n    context \"with hashing enabled\" do\n      let(:token) { FactoryBot.create :access_token }\n\n      include_context \"with token hashing enabled\"\n\n      it \"holds a volatile plaintext token when created\" do\n        expect(token.plaintext_token).to be_a(String)\n        expect(token.token)\n          .to eq(hashed_or_plain_token_func.call(token.plaintext_token))\n\n        # Finder method only finds the hashed token\n        loaded = described_class.find_by(token: token.token)\n        expect(loaded).to eq(token)\n        expect(loaded.plaintext_token).to be_nil\n        expect(loaded.token).to eq(token.token)\n      end\n\n      it \"does not find_by plain text tokens\" do\n        expect(described_class.find_by(token: token.plaintext_token)).to be_nil\n      end\n\n      describe \"with having a plain text token\" do\n        let(:plain_text_token) { \"plain text token\" }\n        let(:access_token) { FactoryBot.create :access_token }\n\n        before do\n          # Assume we have a plain text token from before activating the option\n          access_token.update_column(:token, plain_text_token)\n        end\n\n        context \"without fallback lookup\" do\n          it \"does not provide lookups with either through by_token\" do\n            expect(described_class.by_token(plain_text_token)).to eq(nil)\n            expect(described_class.by_token(access_token.token)).to eq(nil)\n\n            # And it does not touch the token\n            access_token.reload\n            expect(access_token.token).to eq(plain_text_token)\n          end\n        end\n\n        context \"with fallback lookup\" do\n          include_context \"with token hashing and fallback lookup enabled\"\n\n          it \"upgrades a plain token when falling back to it\" do\n            # Side-effect: This will automatically upgrade the token\n            expect(described_class).to receive(:upgrade_fallback_value).and_call_original\n            expect(described_class.by_token(plain_text_token))\n              .to have_attributes(\n                resource_owner_id: access_token.resource_owner_id,\n                application_id: access_token.application_id,\n                scopes: access_token.scopes,\n              )\n\n            # Will find subsequently by hashing the token\n            expect(described_class.by_token(plain_text_token))\n              .to have_attributes(\n                resource_owner_id: access_token.resource_owner_id,\n                application_id: access_token.application_id,\n                scopes: access_token.scopes,\n              )\n\n            # Not all the ORM support :id PK\n            if access_token.respond_to?(:id)\n              expect(described_class.by_token(plain_text_token).id).to eq(access_token.id)\n            end\n\n            # And it modifies the token value\n            access_token.reload\n            expect(access_token.token).not_to eq(plain_text_token)\n            expect(described_class.find_by(token: plain_text_token)).to eq(nil)\n            expect(described_class.find_by(token: access_token.token)).not_to be_nil\n          end\n        end\n      end\n    end\n\n    it \"generates a token using a custom object\" do\n      eigenclass = class << CustomGeneratorArgs; self; end\n      eigenclass.class_eval do\n        remove_method :generate\n      end\n      module CustomGeneratorArgs\n        def self.generate(opts = {})\n          id = opts[:resource_owner_id] || opts[:resource_owner]&.id\n          \"custom_generator_token_#{id}\"\n        end\n      end\n\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        access_token_generator \"CustomGeneratorArgs\"\n      end\n\n      owner = FactoryBot.create :resource_owner\n      token = FactoryBot.create :access_token,\n                                resource_owner_id: owner.id,\n                                resource_owner_type: owner.class.name\n\n      expect(token.token).to match(/custom_generator_token_\\d+/)\n    end\n\n    it \"allows the custom generator to access the application details\" do\n      eigenclass = class << CustomGeneratorArgs; self; end\n      eigenclass.class_eval do\n        remove_method :generate\n      end\n\n      module CustomGeneratorArgs\n        def self.generate(opts = {})\n          \"custom_generator_token_#{opts[:application].name}\"\n        end\n      end\n\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        access_token_generator \"CustomGeneratorArgs\"\n      end\n\n      token = FactoryBot.create :access_token\n      expect(token.token).to match(/custom_generator_token_Application \\d+/)\n    end\n\n    it \"allows the custom generator to access the scopes\" do\n      eigenclass = class << CustomGeneratorArgs; self; end\n      eigenclass.class_eval do\n        remove_method :generate\n      end\n      module CustomGeneratorArgs\n        def self.generate(opts = {})\n          \"custom_generator_token_#{opts[:scopes].count}_#{opts[:scopes]}\"\n        end\n      end\n\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        access_token_generator \"CustomGeneratorArgs\"\n      end\n\n      token = FactoryBot.create :access_token, scopes: \"public write\"\n\n      expect(token.token).to eq \"custom_generator_token_2_public write\"\n    end\n\n    it \"allows the custom generator to access the expiry length\" do\n      eigenclass = class << CustomGeneratorArgs; self; end\n      eigenclass.class_eval do\n        remove_method :generate\n      end\n      module CustomGeneratorArgs\n        def self.generate(opts = {})\n          \"custom_generator_token_#{opts[:expires_in]}\"\n        end\n      end\n\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        access_token_generator \"CustomGeneratorArgs\"\n      end\n\n      token = FactoryBot.create :access_token\n      expect(token.token).to eq \"custom_generator_token_7200\"\n    end\n\n    it \"allows the custom generator to access the created time\" do\n      module CustomGeneratorArgs\n        def self.generate(opts = {})\n          \"custom_generator_token_#{opts[:created_at].to_i}\"\n        end\n      end\n\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        access_token_generator \"CustomGeneratorArgs\"\n      end\n\n      token = FactoryBot.create :access_token\n      created_at = token.created_at\n      expect(token.token).to eq \"custom_generator_token_#{created_at.to_i}\"\n    end\n\n    it \"allows the custom generator to access the custom attributes\" do\n      module CustomGeneratorArgs\n        def self.generate(opts = {})\n          \"custom_generator_token_#{opts[:tenant_name]}\"\n        end\n      end\n\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        access_token_generator \"CustomGeneratorArgs\"\n        custom_access_token_attributes [:tenant_name]\n      end\n\n      token = FactoryBot.create :access_token, tenant_name: \"Tenant 1\"\n      expect(token.token).to eq \"custom_generator_token_Tenant 1\"\n    end\n\n    it \"raises an error if the custom object does not support generate\" do\n      module NoGenerate\n      end\n\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        access_token_generator \"NoGenerate\"\n      end\n\n      expect { FactoryBot.create :access_token }.to(\n        raise_error(Doorkeeper::Errors::UnableToGenerateToken),\n      )\n    end\n\n    it \"raises original error if something went wrong in custom generator\" do\n      eigenclass = class << CustomGeneratorArgs; self; end\n      eigenclass.class_eval do\n        remove_method :generate\n      end\n\n      module CustomGeneratorArgs\n        def self.generate(_opts = {})\n          raise LoadError, \"custom behaviour\"\n        end\n      end\n\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        access_token_generator \"CustomGeneratorArgs\"\n      end\n\n      expect { FactoryBot.create :access_token }.to(\n        raise_error(LoadError),\n      )\n    end\n\n    it \"raises an error if the custom object does not exist\" do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        access_token_generator \"Doorkeeper::NotReal\"\n      end\n\n      expect { FactoryBot.create :access_token }.to(\n        raise_error(Doorkeeper::Errors::TokenGeneratorNotFound, /NotReal/),\n      )\n    end\n  end\n\n  describe \"refresh_token\" do\n    it \"has empty refresh token if it was not required\" do\n      token = FactoryBot.create :access_token\n      expect(token.refresh_token).to be_nil\n    end\n\n    it \"generates a refresh token if it was requested\" do\n      token = FactoryBot.create :access_token, use_refresh_token: true\n      expect(token.refresh_token).not_to be_nil\n    end\n\n    it \"is not valid if token exists\" do\n      token1 = FactoryBot.create :access_token, use_refresh_token: true\n      token2 = FactoryBot.create :access_token, use_refresh_token: true\n      token2.refresh_token = token1.refresh_token\n      expect(token2).not_to be_valid\n    end\n\n    it \"expects database to raise an error if refresh tokens are the same\" do\n      token1 = FactoryBot.create :access_token, use_refresh_token: true\n      token2 = FactoryBot.create :access_token, use_refresh_token: true\n      expect do\n        token2.refresh_token = token1.refresh_token\n        token2.save(validate: false)\n      end.to raise_error(uniqueness_error)\n    end\n\n    context \"with hashing enabled\" do\n      include_context \"with token hashing enabled\"\n      let(:token) { FactoryBot.create :access_token, use_refresh_token: true }\n\n      it \"holds a volatile refresh token when created\" do\n        expect(token.plaintext_refresh_token).to be_a(String)\n        expect(token.refresh_token)\n          .to eq(hashed_or_plain_token_func.call(token.plaintext_refresh_token))\n\n        # Finder method only finds the hashed token\n        loaded = described_class.find_by(refresh_token: token.refresh_token)\n        expect(loaded).to eq(token)\n        expect(loaded.plaintext_refresh_token).to be_nil\n        expect(loaded.refresh_token).to eq(token.refresh_token)\n      end\n\n      it \"does not find_by plain text refresh tokens\" do\n        expect(described_class.find_by(refresh_token: token.plaintext_refresh_token)).to be_nil\n      end\n\n      describe \"with having a plain text token\" do\n        let(:plain_refresh_token) { \"plain refresh token\" }\n        let(:access_token) { FactoryBot.create :access_token }\n\n        before do\n          # Assume we have a plain text token from before activating the option\n          access_token.update_column(:refresh_token, plain_refresh_token)\n        end\n\n        context \"without fallback lookup\" do\n          it \"does not provide lookups with either through by_token\" do\n            expect(described_class.by_refresh_token(plain_refresh_token)).to eq(nil)\n            expect(described_class.by_refresh_token(access_token.refresh_token)).to eq(nil)\n\n            # And it does not touch the token\n            access_token.reload\n            expect(access_token.refresh_token).to eq(plain_refresh_token)\n          end\n        end\n\n        context \"with fallback lookup\" do\n          include_context \"with token hashing and fallback lookup enabled\"\n\n          it \"upgrades a plain token when falling back to it\" do\n            # Side-effect: This will automatically upgrade the token\n            expect(described_class).to receive(:upgrade_fallback_value).and_call_original\n            expect(described_class.by_refresh_token(plain_refresh_token))\n              .to have_attributes(\n                token: access_token.token,\n                resource_owner_id: access_token.resource_owner_id,\n                application_id: access_token.application_id,\n              )\n\n            # Will find subsequently by hashing the token\n            expect(described_class.by_refresh_token(plain_refresh_token))\n              .to have_attributes(\n                token: access_token.token,\n                resource_owner_id: access_token.resource_owner_id,\n                application_id: access_token.application_id,\n              )\n\n            # Not all the ORM support :id PK\n            if access_token.respond_to?(:id)\n              expect(described_class.by_refresh_token(plain_refresh_token).id).to eq(access_token.id)\n            end\n\n            # And it modifies the token value\n            access_token.reload\n            expect(access_token.refresh_token).not_to eq(plain_refresh_token)\n            expect(described_class.find_by(refresh_token: plain_refresh_token)).to eq(nil)\n            expect(described_class.find_by(refresh_token: access_token.refresh_token)).not_to be_nil\n          end\n        end\n      end\n    end\n  end\n\n  describe \"validations\" do\n    it \"is valid without resource_owner_id\" do\n      # For client credentials flow\n      access_token.resource_owner_id = nil\n      expect(access_token).to be_valid\n    end\n\n    it \"is valid without application_id\" do\n      # For resource owner credentials flow\n      access_token.application_id = nil\n      expect(access_token).to be_valid\n    end\n  end\n\n  describe \"#same_credential?\" do\n    context \"with default parameters\" do\n      let(:resource_owner) { FactoryBot.create(:resource_owner) }\n      let(:resource_owner_id) { resource_owner.id }\n      let(:application) { FactoryBot.create :application }\n      let(:default_attributes) do\n        {\n          application: application,\n          resource_owner_id: resource_owner_id,\n          resource_owner_type: resource_owner.class.name,\n        }\n      end\n      let(:access_token1) { FactoryBot.create :access_token, default_attributes }\n\n      context \"when the second token has the same owner and same app\" do\n        let(:access_token2) { FactoryBot.create :access_token, default_attributes }\n\n        it \"success\" do\n          expect(access_token1).to be_same_credential(access_token2)\n        end\n      end\n\n      context \"when the second token has same owner and different app\" do\n        let(:other_application) { FactoryBot.create :application }\n        let(:access_token2) do\n          FactoryBot.create :access_token,\n                            application: other_application,\n                            resource_owner_id: resource_owner_id,\n                            resource_owner_type: resource_owner.class.name\n        end\n\n        it \"fails\" do\n          expect(access_token1).not_to be_same_credential(access_token2)\n        end\n      end\n\n      context \"when the second token has different owner and different app\" do\n        let(:other_application) { FactoryBot.create :application }\n        let(:access_token2) do\n          FactoryBot.create :access_token,\n                            application: other_application,\n                            resource_owner_id: resource_owner.id + 1\n        end\n\n        it \"fails\" do\n          expect(access_token1).not_to be_same_credential(access_token2)\n        end\n      end\n\n      context \"when the second token has different owner and same app\" do\n        let(:access_token2) do\n          FactoryBot.create :access_token,\n                            application: application,\n                            resource_owner_id: resource_owner.id + 1\n        end\n\n        it \"fails\" do\n          expect(access_token1).not_to be_same_credential(access_token2)\n        end\n      end\n    end\n  end\n\n  describe \"#acceptable?\" do\n    context \"when token is not accessible\" do\n      let(:token) { FactoryBot.create(:access_token, created_at: 6.hours.ago) }\n\n      it \"returns false\" do\n        expect(token.acceptable?(nil)).to be false\n      end\n    end\n\n    context \"when token has the incorrect scopes\" do\n      let(:token) { FactoryBot.create(:access_token) }\n\n      it \"returns false\" do\n        expect(token.acceptable?([\"public\"])).to be false\n      end\n    end\n\n    context \"when token is acceptable with the correct scopes\" do\n      let(:token) do\n        token = FactoryBot.create(:access_token)\n        token[:scopes] = \"public\"\n        token\n      end\n\n      it \"returns true\" do\n        expect(token.acceptable?([\"public\"])).to be true\n      end\n    end\n  end\n\n  describe \".revoke_all_for\" do\n    let(:resource_owner) { FactoryBot.create :resource_owner }\n    let(:application)    { FactoryBot.create :application }\n    let(:default_attributes) do\n      {\n        application: application,\n        resource_owner_id: resource_owner.id,\n        resource_owner_type: resource_owner.class.name,\n      }\n    end\n\n    it \"revokes all tokens for given application and resource owner\" do\n      FactoryBot.create :access_token, default_attributes\n      described_class.revoke_all_for(application.id, resource_owner)\n      expect(described_class.all).to all(be_revoked)\n    end\n\n    it \"matches application\" do\n      access_token_for_different_app = FactoryBot.create(\n        :access_token,\n        default_attributes.merge(application: FactoryBot.create(:application)),\n      )\n\n      described_class.revoke_all_for(application.id, resource_owner)\n\n      expect(access_token_for_different_app.reload).not_to be_revoked\n    end\n\n    it \"matches resource owner\" do\n      access_token_for_different_owner = FactoryBot.create(\n        :access_token,\n        default_attributes.merge(resource_owner_id: resource_owner.id + 1),\n      )\n\n      described_class.revoke_all_for(application.id, resource_owner)\n\n      expect(access_token_for_different_owner.reload).not_to be_revoked\n    end\n  end\n\n  describe \".matching_token_for\" do\n    let(:resource_owner)    { FactoryBot.create :resource_owner }\n    let(:resource_owner_id) { resource_owner.id }\n    let(:application)       { FactoryBot.create :application }\n    let(:scopes) { Doorkeeper::OAuth::Scopes.from_string(\"public write\") }\n    let(:default_attributes) do\n      {\n        application: application,\n        resource_owner_id: resource_owner_id,\n        resource_owner_type: resource_owner.class.name,\n        scopes: scopes.to_s,\n      }\n    end\n\n    before do\n      default_scopes_exist(*scopes.all)\n    end\n\n    it \"returns only one token\" do\n      token = FactoryBot.create :access_token, default_attributes\n      last_token = described_class.matching_token_for(application, resource_owner, scopes)\n      expect(last_token).to eq(token)\n    end\n\n    it \"accepts nil as resource owner\" do\n      token = FactoryBot.create :access_token,\n                                default_attributes.merge(resource_owner_id: nil, resource_owner_type: nil)\n      last_token = described_class.matching_token_for(application, nil, scopes)\n      expect(last_token).to eq(token)\n    end\n\n    it \"excludes revoked tokens\" do\n      FactoryBot.create :access_token, default_attributes.merge(revoked_at: 1.day.ago)\n      last_token = described_class.matching_token_for(application, resource_owner_id, scopes)\n      expect(last_token).to be_nil\n    end\n\n    it \"excludes tokens with a different application\" do\n      FactoryBot.create :access_token, default_attributes.merge(application: FactoryBot.create(:application))\n      last_token = described_class.matching_token_for(application, resource_owner_id, scopes)\n      expect(last_token).to be_nil\n    end\n\n    it \"excludes tokens with a different resource owner\" do\n      FactoryBot.create :access_token, default_attributes.merge(resource_owner_id: resource_owner.id + 1)\n      last_token = described_class.matching_token_for(application, resource_owner_id, scopes)\n      expect(last_token).to be_nil\n    end\n\n    it \"excludes tokens with fewer scopes\" do\n      FactoryBot.create :access_token, default_attributes.merge(scopes: \"public\")\n      last_token = described_class.matching_token_for(application, resource_owner_id, scopes)\n      expect(last_token).to be_nil\n    end\n\n    it \"excludes tokens with different scopes\" do\n      FactoryBot.create :access_token, default_attributes.merge(scopes: \"public email\")\n      last_token = described_class.matching_token_for(application, resource_owner, scopes)\n      expect(last_token).to be_nil\n    end\n\n    it \"excludes tokens with additional scopes\" do\n      FactoryBot.create :access_token, default_attributes.merge(scopes: \"public write email\")\n      last_token = described_class.matching_token_for(application, resource_owner, scopes)\n      expect(last_token).to be_nil\n    end\n\n    it \"excludes tokens with scopes that are not present in server scopes\" do\n      FactoryBot.create :access_token, default_attributes.merge(\n        application: application, scopes: \"public read\",\n      )\n      last_token = described_class.matching_token_for(application, resource_owner, scopes)\n      expect(last_token).to be_nil\n    end\n\n    it \"excludes tokens with scopes that are not present in application scopes\" do\n      application = FactoryBot.create :application, scopes: \"private read\"\n      FactoryBot.create :access_token, default_attributes.merge(\n        application: application,\n      )\n      last_token = described_class.matching_token_for(application, resource_owner, scopes)\n      expect(last_token).to be_nil\n    end\n\n    it \"does not match token if empty scope requested and token/app scopes present\" do\n      application = FactoryBot.create :application, scopes: \"sample:scope\"\n      app_params = {\n        application_id: application.id, scopes: \"sample:scope\",\n        resource_owner_id: resource_owner.id,\n        resource_owner_type: resource_owner.class.name,\n      }\n      FactoryBot.create :access_token, app_params\n      empty_scopes = Doorkeeper::OAuth::Scopes.from_string(\"\")\n      last_token = described_class.matching_token_for(application, resource_owner.id, empty_scopes)\n      expect(last_token).to be_nil\n    end\n\n    it \"matches token if empty scope requested and no token scopes present\" do\n      empty_scopes = Doorkeeper::OAuth::Scopes.from_string(\"\")\n      token = FactoryBot.create :access_token, default_attributes.merge(scopes: empty_scopes)\n      last_token = described_class.matching_token_for(application, resource_owner.id, empty_scopes)\n      expect(last_token).to eq(token)\n    end\n\n    it \"returns the last matching token\" do\n      FactoryBot.create :access_token, default_attributes.merge(created_at: 1.day.ago)\n      matching_token = FactoryBot.create :access_token, default_attributes\n      FactoryBot.create :access_token, default_attributes.merge(scopes: \"public\")\n\n      last_token = described_class.matching_token_for(application, resource_owner_id, scopes)\n      expect(last_token).to eq(matching_token)\n    end\n\n    context \"when custom access token attributes are used\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          custom_access_token_attributes [:tenant_name]\n        end\n        default_scopes_exist(*scopes.all)\n      end\n      let(:custom_attributes) { { tenant_name: \"Me\" } }\n\n      it \"returns a token when attributes match\" do\n        token = FactoryBot.create :access_token, default_attributes.merge(custom_attributes)\n        last_token = described_class.matching_token_for(\n          application, resource_owner, scopes, custom_attributes: custom_attributes)\n        expect(last_token).to eq(token)\n      end\n\n      it \"does not return a token if attributes don't match\" do\n        token = FactoryBot.create :access_token, default_attributes.merge(custom_attributes)\n        last_token = described_class.matching_token_for(application, resource_owner, scopes, custom_attributes: { tenant_id: 'different' })\n        expect(last_token).to eq(nil)\n      end\n\n      it \"ignores custom attributes if a nil value is passed\" do\n        token = FactoryBot.create :access_token, default_attributes.merge(custom_attributes)\n        last_token = described_class.matching_token_for(application, resource_owner, scopes, custom_attributes: nil)\n        expect(last_token).to eq(token)\n      end\n    end\n  end\n\n  describe \"#as_json\" do\n    let(:token) { FactoryBot.create(:access_token) }\n    let(:token_hash) do\n      {\n        resource_owner_id: token.resource_owner_id,\n        scope: token.scopes,\n        expires_in: token.expires_in_seconds,\n        application: { uid: token.application.uid },\n        created_at: token.created_at.to_i,\n      }\n    end\n\n    it \"returns as_json hash\" do\n      hash = token_hash\n\n      if Doorkeeper.configuration.polymorphic_resource_owner?\n        hash[:resource_owner_type] = token.resource_owner_type\n      end\n\n      expect(token.as_json).to match(hash)\n    end\n\n    describe \"#not_expired\" do\n      let(:resource_owner) { FactoryBot.create(:doorkeeper_testing_user) }\n      let(:application) { FactoryBot.create(:application) }\n\n      let(:attrs) { { resource_owner_id: resource_owner.id, application_id: application.id } }\n\n      let!(:active_token1) { FactoryBot.create(:access_token, attrs.merge(expires_in: 2000)) }\n      let!(:active_token2) { FactoryBot.create(:access_token, attrs.merge(expires_in: 2)) }\n      let!(:active_token3) { FactoryBot.create(:access_token, attrs.merge(expires_in: 10, created_at: Time.current - 5.seconds)) }\n      let!(:active_token4) { FactoryBot.create(:access_token, attrs.merge(expires_in: nil)) }\n      let!(:not_active_token1) { FactoryBot.create(:access_token, attrs.merge(expires_in: 2, created_at: Time.current - 2.seconds)) }\n      let!(:not_active_token2) { FactoryBot.create(:access_token, attrs.merge(expires_in: 10, created_at: Time.current - 12.seconds)) }\n      let!(:not_active_token3) { FactoryBot.create(:access_token, attrs.merge(expires_in: 10_000, revoked_at: Time.current)) }\n\n      before do\n        Timecop.freeze(Time.current)\n      end\n\n      after do\n        Timecop.return\n      end\n\n      it \"returns only non expired tokens\" do\n        expired_tokens = described_class.not_expired\n        expect(expired_tokens.size).to be(4)\n        expect(expired_tokens).to match_array([active_token1, active_token2, active_token3, active_token4])\n      end\n    end\n  end\n\n  describe \".create_for with read replica support\" do\n    let(:application) { FactoryBot.create(:application) }\n    let(:resource_owner) { FactoryBot.create(:resource_owner) }\n    let(:scopes) { Doorkeeper::OAuth::Scopes.from_string(\"public\") }\n\n    context \"when handle_read_write_roles is enabled\" do\n      before do\n        Doorkeeper.configure do\n          orm :active_record\n          enable_multiple_database_roles\n        end\n      end\n\n      it \"creates token using primary database role\" do\n        expect(ActiveRecord::Base).to receive(:connected_to).with(role: :writing).and_call_original\n\n        token = described_class.create_for(\n          application: application,\n          resource_owner: resource_owner,\n          scopes: scopes,\n        )\n\n        expect(token).to be_persisted\n        expect(token.application).to eq(application)\n        expect(token.resource_owner_id).to eq(resource_owner.id)\n      end\n    end\n\n    context \"when enable_multiple_database_roles is disabled\" do\n      before do\n        Doorkeeper.configure do\n          orm :active_record\n          # enable_multiple_database_roles is disabled by default\n        end\n      end\n\n      it \"creates token without explicit role switching\" do\n        expect(ActiveRecord::Base).not_to receive(:connected_to)\n\n        token = described_class.create_for(\n          application: application,\n          resource_owner: resource_owner,\n          scopes: scopes,\n        )\n\n        expect(token).to be_persisted\n      end\n    end\n  end\n\n  describe \".revoke_all_for with read replica support\" do\n    let(:application) { FactoryBot.create(:application) }\n    let(:resource_owner) { FactoryBot.create(:resource_owner) }\n\n    before do\n      FactoryBot.create(:access_token, application: application, resource_owner_id: resource_owner.id)\n    end\n\n    context \"when handle_read_write_roles is enabled\" do\n      before do\n        Doorkeeper.configure do\n          orm :active_record\n          enable_multiple_database_roles\n        end\n      end\n\n      it \"revokes tokens using primary database role\" do\n        expect(ActiveRecord::Base).to receive(:connected_to).with(role: :writing).and_call_original\n\n        described_class.revoke_all_for(application.id, resource_owner)\n      end\n    end\n  end\n\n  describe \"#revoke with read replica support\" do\n    let(:token) { FactoryBot.create(:access_token) }\n\n    context \"when handle_read_write_roles is enabled\" do\n      before do\n        Doorkeeper.configure do\n          orm :active_record\n          enable_multiple_database_roles\n        end\n      end\n\n      it \"revokes token using primary database role\" do\n        expect(ActiveRecord::Base).to receive(:connected_to).with(role: :writing).and_call_original\n\n        token.revoke\n        expect(token).to be_revoked\n      end\n    end\n\n    context \"when enable_multiple_database_roles is disabled\" do\n      before do\n        Doorkeeper.configure do\n          orm :active_record\n          # enable_multiple_database_roles is disabled by default\n        end\n      end\n\n      it \"revokes token without explicit role switching\" do\n        expect(ActiveRecord::Base).not_to receive(:connected_to)\n\n        token.revoke\n        expect(token).to be_revoked\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/models/doorkeeper/application_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\nrequire \"bcrypt\"\n\nRSpec.describe Doorkeeper::Application do\n  let(:new_application) { FactoryBot.build(:application) }\n  let(:owner) { FactoryBot.build_stubbed(:doorkeeper_testing_user) }\n\n  let(:uid) { SecureRandom.hex(8) }\n  let(:secret) { SecureRandom.hex(8) }\n\n  it \"is invalid without a name\" do\n    new_application.name = nil\n    expect(new_application).not_to be_valid\n  end\n\n  it \"is invalid without determining confidentiality\" do\n    new_application.confidential = nil\n    expect(new_application).not_to be_valid\n  end\n\n  it \"generates uid on create\" do\n    expect(new_application.uid).to be_nil\n    new_application.save\n    expect(new_application.uid).not_to be_nil\n  end\n\n  it \"generates uid on create if an empty string\" do\n    new_application.uid = \"\"\n    new_application.save\n    expect(new_application.uid).not_to be_blank\n  end\n\n  it \"generates uid on create unless one is set\" do\n    new_application.uid = uid\n    new_application.save\n    expect(new_application.uid).to eq(uid)\n  end\n\n  it \"is invalid without uid\" do\n    new_application.save\n    new_application.uid = nil\n    expect(new_application).not_to be_valid\n  end\n\n  it \"checks uniqueness of uid\" do\n    app1 = FactoryBot.create(:application)\n    app2 = FactoryBot.create(:application)\n    app2.uid = app1.uid\n    expect(app2).not_to be_valid\n  end\n\n  it \"expects database to throw an error when uids are the same\" do\n    app1 = FactoryBot.create(:application)\n    app2 = FactoryBot.create(:application)\n    app2.uid = app1.uid\n    expect { app2.save!(validate: false) }.to raise_error(uniqueness_error)\n  end\n\n  it \"generate secret on create\" do\n    expect(new_application.secret).to be_nil\n    new_application.save\n    expect(new_application.secret).not_to be_nil\n  end\n\n  it \"generate secret on create if is blank string\" do\n    new_application.secret = \"\"\n    new_application.save\n    expect(new_application.secret).not_to be_blank\n  end\n\n  it \"generate secret on create unless one is set\" do\n    new_application.secret = secret\n    new_application.save\n    expect(new_application.secret).to eq(secret)\n  end\n\n  it \"is invalid without secret\" do\n    new_application.save\n    new_application.secret = nil\n    expect(new_application).not_to be_valid\n  end\n\n  it \"is valid without secret if client is public\" do\n    new_application.confidential = false\n    new_application.secret = nil\n    expect(new_application).to be_valid\n  end\n\n  it \"generates a secret using a custom object\" do\n    module CustomGeneratorArgs\n      def self.generate\n        \"custom_application_secret\"\n      end\n    end\n\n    Doorkeeper.configure do\n      orm DOORKEEPER_ORM\n      application_secret_generator \"CustomGeneratorArgs\"\n    end\n\n    expect(new_application.secret).to be_nil\n    new_application.save\n    expect(new_application.secret).to eq(\"custom_application_secret\")\n  end\n\n  context \"when application_owner is enabled\" do\n    context \"when application owner is not required\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          enable_application_owner\n        end\n\n        Doorkeeper.run_orm_hooks\n      end\n\n      it \"is valid given valid attributes\" do\n        expect(new_application).to be_valid\n      end\n    end\n\n    context \"when application owner is required\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          enable_application_owner confirmation: true\n        end\n\n        Doorkeeper.run_orm_hooks\n      end\n\n      it \"is invalid without an owner\" do\n        expect(new_application).not_to be_valid\n      end\n\n      it \"is valid with an owner\" do\n        new_application.owner = owner\n        expect(new_application).to be_valid\n      end\n    end\n  end\n\n  describe \"redirect URI\" do\n    context \"when grant flows allow blank redirect URI\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          grant_flows %w[password client_credentials]\n        end\n      end\n\n      it \"is valid without redirect_uri\" do\n        new_application.save\n        new_application.redirect_uri = nil\n        expect(new_application).to be_valid\n      end\n    end\n\n    context \"when grant flows require redirect URI\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          grant_flows %w[password client_credentials authorization_code]\n        end\n      end\n\n      it \"is invalid without redirect_uri\" do\n        new_application.save\n        new_application.redirect_uri = nil\n        expect(new_application).not_to be_valid\n      end\n    end\n\n    context \"when blank URI option disabled\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          grant_flows %w[password client_credentials]\n          allow_blank_redirect_uri false\n        end\n      end\n\n      it \"is invalid without redirect_uri\" do\n        new_application.save\n        new_application.redirect_uri = nil\n        expect(new_application).not_to be_valid\n      end\n    end\n  end\n\n  context \"with hashing enabled\" do\n    include_context \"with application hashing enabled\"\n    let(:app) { FactoryBot.create :application }\n    let(:default_strategy) { Doorkeeper::SecretStoring::Sha256Hash }\n\n    it \"uses SHA256 to avoid additional dependencies\" do\n      # Ensure token was generated\n      app.validate\n      expect(app.secret).to eq(default_strategy.transform_secret(app.plaintext_secret))\n    end\n\n    context \"when bcrypt strategy is configured\" do\n      # In this text context, we have bcrypt loaded so `bcrypt_present?`\n      # will always be true\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          hash_application_secrets using: \"Doorkeeper::SecretStoring::BCrypt\"\n        end\n      end\n\n      it \"holds a volatile plaintext and BCrypt secret\" do\n        expect(app.secret_strategy).to eq Doorkeeper::SecretStoring::BCrypt\n        expect(app.plaintext_secret).to be_a(String)\n        expect(app.secret).not_to eq(app.plaintext_secret)\n        expect { ::BCrypt::Password.create(app.secret) }.not_to raise_error\n      end\n    end\n\n    it \"does not fallback to plain lookup by default\" do\n      lookup = described_class.by_uid_and_secret(app.uid, app.secret)\n      expect(lookup).to eq(nil)\n\n      lookup = described_class.by_uid_and_secret(app.uid, app.plaintext_secret)\n      expect(lookup).to eq(app)\n    end\n\n    context \"with fallback enabled\" do\n      include_context \"with token hashing and fallback lookup enabled\"\n\n      it \"provides plain and hashed lookup\" do\n        lookup = described_class.by_uid_and_secret(app.uid, app.secret)\n        expect(lookup).to eq(app)\n\n        lookup = described_class.by_uid_and_secret(app.uid, app.plaintext_secret)\n        expect(lookup).to eq(app)\n      end\n    end\n\n    it \"does not provide access to secret after loading\" do\n      lookup = described_class.by_uid_and_secret(app.uid, app.plaintext_secret)\n      expect(lookup.plaintext_secret).to be_nil\n    end\n  end\n\n  describe \"destroy related models on cascade\" do\n    before do\n      new_application.save\n    end\n\n    let(:resource_owner) { FactoryBot.create(:resource_owner) }\n\n    it \"destroys its access grants\" do\n      FactoryBot.create(\n        :access_grant,\n        application: new_application,\n        resource_owner_id: resource_owner.id,\n        resource_owner_type: resource_owner.class.name,\n      )\n\n      expect { new_application.destroy }.to change { Doorkeeper::AccessGrant.count }.by(-1)\n    end\n\n    it \"destroys its access tokens\" do\n      FactoryBot.create(:access_token, application: new_application)\n      FactoryBot.create(:access_token, application: new_application, revoked_at: Time.now.utc)\n      expect do\n        new_application.destroy\n      end.to change { Doorkeeper::AccessToken.count }.by(-2)\n    end\n  end\n\n  describe \"#ordered_by\" do\n    let(:applications) { FactoryBot.create_list(:application, 5) }\n\n    context \"when a direction is not specified\" do\n      it \"calls order with a default order of asc\" do\n        names = applications.map(&:name).sort\n        expect(described_class.ordered_by(:name).map(&:name)).to eq(names)\n      end\n    end\n\n    context \"when a direction is specified\" do\n      it \"calls order with specified direction\" do\n        names = applications.map(&:name).sort.reverse\n        expect(described_class.ordered_by(:name, :desc).map(&:name)).to eq(names)\n      end\n    end\n  end\n\n  describe \"#redirect_uri=\" do\n    context \"when array of valid redirect_uris\" do\n      it \"joins by newline\" do\n        new_application.redirect_uri = [\"http://localhost/callback1\", \"http://localhost/callback2\"]\n        expect(new_application.redirect_uri).to eq(\"http://localhost/callback1\\nhttp://localhost/callback2\")\n      end\n    end\n\n    context \"when string of valid redirect_uris\" do\n      it \"stores as-is\" do\n        new_application.redirect_uri = \"http://localhost/callback1\\nhttp://localhost/callback2\"\n        expect(new_application.redirect_uri).to eq(\"http://localhost/callback1\\nhttp://localhost/callback2\")\n      end\n    end\n  end\n\n  describe \"#renew_secret\" do\n    let(:app) { FactoryBot.create :application }\n\n    it \"generates a new secret\" do\n      old_secret = app.secret\n      app.renew_secret\n      expect(old_secret).not_to eq(app.secret)\n    end\n  end\n\n  describe \"#authorized_for\" do\n    let(:resource_owner) { FactoryBot.create(:resource_owner) }\n    let(:other_resource_owner) { FactoryBot.create(:resource_owner) }\n\n    it \"is empty if the application is not authorized for anyone\" do\n      expect(described_class.authorized_for(resource_owner)).to be_empty\n    end\n\n    it \"returns only application for a specific resource owner\" do\n      FactoryBot.create(\n        :access_token,\n        resource_owner_id: other_resource_owner.id,\n        resource_owner_type: other_resource_owner.class.name,\n      )\n      token = FactoryBot.create(\n        :access_token,\n        resource_owner_id: resource_owner.id,\n        resource_owner_type: resource_owner.class.name,\n      )\n      expect(described_class.authorized_for(resource_owner)).to eq([token.application])\n    end\n\n    it \"excludes revoked tokens\" do\n      FactoryBot.create(\n        :access_token,\n        resource_owner_id: resource_owner.id,\n        resource_owner_type: resource_owner.class.name,\n        revoked_at: 2.days.ago,\n      )\n      expect(described_class.authorized_for(resource_owner)).to be_empty\n    end\n\n    it \"returns all applications that have been authorized\" do\n      token1 = FactoryBot.create(\n        :access_token,\n        resource_owner_id: resource_owner.id,\n        resource_owner_type: resource_owner.class.name,\n      )\n      token2 = FactoryBot.create(\n        :access_token,\n        resource_owner_id: resource_owner.id,\n        resource_owner_type: resource_owner.class.name,\n      )\n      expect(described_class.authorized_for(resource_owner))\n        .to eq([token1.application, token2.application])\n    end\n\n    it \"returns only one application even if it has been authorized twice\" do\n      application = FactoryBot.create(:application)\n      FactoryBot.create(\n        :access_token,\n        resource_owner_id: resource_owner.id,\n        resource_owner_type: resource_owner.class.name,\n        application: application,\n      )\n      FactoryBot.create(\n        :access_token,\n        resource_owner_id: resource_owner.id,\n        resource_owner_type: resource_owner.class.name,\n        application: application,\n      )\n      expect(described_class.authorized_for(resource_owner)).to eq([application])\n    end\n  end\n\n  describe \"#revoke_tokens_and_grants_for\" do\n    it \"revokes all access tokens and access grants\" do\n      application_id = 42\n      resource_owner = double\n      expect(Doorkeeper::AccessToken)\n        .to receive(:revoke_all_for).with(application_id, resource_owner)\n      expect(Doorkeeper::AccessGrant)\n        .to receive(:revoke_all_for).with(application_id, resource_owner)\n\n      described_class.revoke_tokens_and_grants_for(application_id, resource_owner)\n    end\n  end\n\n  describe \"#by_uid_and_secret\" do\n    context \"when application is private/confidential\" do\n      it \"finds the application via uid/secret\" do\n        app = FactoryBot.create :application\n        authenticated = described_class.by_uid_and_secret(app.uid, app.secret)\n        expect(authenticated).to eq(app)\n      end\n\n      context \"when secret is wrong\" do\n        it \"does not find the application\" do\n          app = FactoryBot.create :application\n          authenticated = described_class.by_uid_and_secret(app.uid, \"bad\")\n          expect(authenticated).to eq(nil)\n        end\n      end\n    end\n\n    context \"when application is public/non-confidential\" do\n      context \"when secret is blank\" do\n        it \"finds the application\" do\n          app = FactoryBot.create :application, confidential: false\n          authenticated = described_class.by_uid_and_secret(app.uid, nil)\n          expect(authenticated).to eq(app)\n        end\n      end\n\n      context \"when secret is wrong\" do\n        it \"does not find the application\" do\n          app = FactoryBot.create :application, confidential: false\n          authenticated = described_class.by_uid_and_secret(app.uid, \"bad\")\n          expect(authenticated).to eq(nil)\n        end\n      end\n    end\n  end\n\n  describe \"#confidential?\" do\n    let(:app) do\n      FactoryBot.create(:application, confidential: confidential)\n    end\n\n    context \"when application is private/confidential\" do\n      let(:confidential) { true }\n\n      it { expect(app).to be_confidential }\n    end\n\n    context \"when application is public/non-confidential\" do\n      let(:confidential) { false }\n\n      it { expect(app).not_to be_confidential }\n    end\n  end\n\n  describe \"#as_json\" do\n    let(:app) { FactoryBot.create :application, secret: \"123123123\" }\n\n    before do\n      allow(Doorkeeper.configuration)\n        .to receive(:application_secret_strategy).and_return(Doorkeeper::SecretStoring::Plain)\n    end\n\n    # AR specific feature\n    if DOORKEEPER_ORM == :active_record\n      it \"correctly works with #to_json\" do\n        ActiveRecord::Base.include_root_in_json = true\n        expect(app.to_json(include_root_in_json: true)).to match(/application.+?:\\{/)\n        ActiveRecord::Base.include_root_in_json = false\n      end\n    end\n\n    context \"when called without authorized resource owner\" do\n      it \"includes minimal set of attributes\" do\n        expect(app.as_json).to match(\n          \"id\" => app.id,\n          \"name\" => app.name,\n          \"created_at\" => anything,\n        )\n      end\n\n      it \"includes application UID if it's public\" do\n        app = FactoryBot.create :application, secret: \"123123123\", confidential: false\n\n        expect(app.as_json).to match(\n          \"id\" => app.id,\n          \"name\" => app.name,\n          \"created_at\" => anything,\n          \"uid\" => app.uid,\n        )\n      end\n\n      it \"respects custom options\" do\n        expect(app.as_json(except: :id)).not_to include(\"id\")\n        expect(app.as_json(only: %i[name created_at secret]))\n          .to match(\n            \"name\" => app.name,\n            \"created_at\" => anything,\n          )\n      end\n    end\n\n    context \"when called with authorized resource owner\" do\n      let(:other_owner) { FactoryBot.create(:doorkeeper_testing_user) }\n      let(:app) { FactoryBot.create(:application, secret: \"123123123\", owner: owner) }\n\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          enable_application_owner confirmation: false\n        end\n\n        Doorkeeper.run_orm_hooks\n      end\n\n      it \"includes all the attributes\" do\n        expect(app.as_json(current_resource_owner: owner))\n          .to include(\n            \"secret\" => \"123123123\",\n            \"redirect_uri\" => app.redirect_uri,\n            \"uid\" => app.uid,\n          )\n      end\n\n      it \"doesn't include unsafe attributes if current owner isn't the same as owner\" do\n        expect(app.as_json(current_resource_owner: other_owner))\n          .not_to include(\"redirect_uri\")\n      end\n    end\n  end\n\n  if DOORKEEPER_ORM == :active_record\n    context \"when custom model class configured\", active_record: true do\n      class CustomApp < ::ActiveRecord::Base\n        include Doorkeeper::Orm::ActiveRecord::Mixins::Application\n      end\n\n      let(:new_application) { CustomApp.new(FactoryBot.attributes_for(:application)) }\n\n      context \"without confirmation\" do\n        before do\n          Doorkeeper.configure do\n            orm DOORKEEPER_ORM\n            application_class \"CustomApp\"\n            enable_application_owner confirmation: false\n          end\n\n          Doorkeeper.run_orm_hooks\n        end\n\n        it \"is valid given valid attributes\" do\n          expect(new_application).to be_valid\n        end\n      end\n\n      context \"without confirmation\" do\n        before do\n          Doorkeeper.configure do\n            orm DOORKEEPER_ORM\n            application_class \"CustomApp\"\n            enable_application_owner confirmation: true\n          end\n\n          Doorkeeper.run_orm_hooks\n        end\n\n        it \"is invalid without owner\" do\n          expect(new_application).not_to be_valid\n          new_application.owner = owner\n          expect(new_application).to be_valid\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/requests/applications/applications_request_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nfeature \"Adding applications in application form\" do\n  background do\n    i_am_logged_in\n    visit \"/oauth/applications/new\"\n  end\n\n  scenario \"adding a valid app\" do\n    fill_in \"doorkeeper_application[name]\", with: \"My Application\"\n    fill_in \"doorkeeper_application[redirect_uri]\",\n            with: \"https://example.com\"\n\n    click_button \"Submit\"\n    i_should_see \"Application created\"\n    i_should_see \"My Application\"\n  end\n\n  scenario \"adding invalid app\" do\n    click_button \"Submit\"\n    i_should_see \"Whoops! Check your form for possible errors\"\n  end\n\n  scenario \"adding app ignoring bad scope\" do\n    config_is_set(\"enforce_configured_scopes\", false)\n\n    fill_in \"doorkeeper_application[name]\", with: \"My Application\"\n    fill_in \"doorkeeper_application[redirect_uri]\",\n            with: \"https://example.com\"\n    fill_in \"doorkeeper_application[scopes]\", with: \"blahblah\"\n\n    click_button \"Submit\"\n    i_should_see \"Application created\"\n    i_should_see \"My Application\"\n  end\n\n  scenario \"adding app validating bad scope\" do\n    config_is_set(\"enforce_configured_scopes\", true)\n\n    fill_in \"doorkeeper_application[name]\", with: \"My Application\"\n    fill_in \"doorkeeper_application[redirect_uri]\",\n            with: \"https://example.com\"\n    fill_in \"doorkeeper_application[scopes]\", with: \"blahblah\"\n\n    click_button \"Submit\"\n    i_should_see \"Whoops! Check your form for possible errors\"\n  end\n\n  scenario \"adding app validating scope, blank scope is accepted\" do\n    config_is_set(\"enforce_configured_scopes\", true)\n\n    fill_in \"doorkeeper_application[name]\", with: \"My Application\"\n    fill_in \"doorkeeper_application[redirect_uri]\",\n            with: \"https://example.com\"\n    fill_in \"doorkeeper_application[scopes]\", with: \"\"\n\n    click_button \"Submit\"\n    i_should_see \"Application created\"\n    i_should_see \"My Application\"\n  end\n\n  scenario \"adding app validating scope, multiple scopes configured\" do\n    config_is_set(\"enforce_configured_scopes\", true)\n    scopes = Doorkeeper::OAuth::Scopes.from_array(%w[read write admin])\n    config_is_set(\"optional_scopes\", scopes)\n\n    fill_in \"doorkeeper_application[name]\", with: \"My Application\"\n    fill_in \"doorkeeper_application[redirect_uri]\",\n            with: \"https://example.com\"\n    fill_in \"doorkeeper_application[scopes]\", with: \"read write\"\n\n    click_button \"Submit\"\n    i_should_see \"Application created\"\n    i_should_see \"My Application\"\n  end\n\n  scenario \"adding app validating scope, bad scope with multiple scopes configured\" do\n    config_is_set(\"enforce_configured_scopes\", true)\n    scopes = Doorkeeper::OAuth::Scopes.from_array(%w[read write admin])\n    config_is_set(\"optional_scopes\", scopes)\n\n    fill_in \"doorkeeper_application[name]\", with: \"My Application\"\n    fill_in \"doorkeeper_application[redirect_uri]\",\n            with: \"https://example.com\"\n    fill_in \"doorkeeper_application[scopes]\", with: \"read blah\"\n\n    click_button \"Submit\"\n    i_should_see \"Whoops! Check your form for possible errors\"\n    i_should_see Regexp.new(\n      I18n.t(\"activerecord.errors.models.doorkeeper/application.attributes.scopes.not_match_configured\"),\n      true,\n    )\n  end\n\n  context \"with blank redirect URI\" do\n    scenario \"adding app with blank redirect URI when configured flows requires redirect uri\" do\n      config_is_set(\"grant_flows\", %w[authorization_code implicit client_credentials])\n\n      fill_in \"doorkeeper_application[name]\", with: \"My Application\"\n      fill_in \"doorkeeper_application[redirect_uri]\",\n              with: \"\"\n\n      click_button \"Submit\"\n      i_should_see \"Whoops! Check your form for possible errors\"\n    end\n\n    scenario \"adding app with blank redirect URI when configured flows without redirect uri\" do\n      config_is_set(\"grant_flows\", %w[client_credentials password])\n\n      # Visit it once again to consider grant flows\n      visit \"/oauth/applications/new\"\n\n      i_should_see I18n.t(\"doorkeeper.applications.help.blank_redirect_uri\")\n\n      fill_in \"doorkeeper_application[name]\", with: \"My Application\"\n      fill_in \"doorkeeper_application[redirect_uri]\",\n              with: \"\"\n\n      click_button \"Submit\"\n      i_should_see \"Application created\"\n      i_should_see \"My Application\"\n    end\n  end\nend\n\nfeature \"Listing applications\" do\n  background do\n    i_am_logged_in\n\n    FactoryBot.create :application, name: \"Oauth Dude\"\n    FactoryBot.create :application, name: \"Awesome App\"\n  end\n\n  scenario \"application list\" do\n    visit \"/oauth/applications\"\n\n    i_should_see \"Awesome App\"\n    i_should_see \"Oauth Dude\"\n  end\nend\n\nfeature \"Renders assets\" do\n  scenario \"admin stylesheets\" do\n    visit \"/assets/doorkeeper/admin/application.css\"\n\n    i_should_see \"Bootstrap\"\n    i_should_see \".doorkeeper-admin\"\n  end\n\n  scenario \"application stylesheets\" do\n    visit \"/assets/doorkeeper/application.css\"\n\n    i_should_see \"Bootstrap\"\n    i_should_see \"#oauth-permissions\"\n    i_should_see \"#container\"\n  end\nend\n\nfeature \"Show application\" do\n  given :app do\n    i_am_logged_in\n\n    FactoryBot.create :application, name: \"Just another oauth app\"\n  end\n\n  scenario \"visiting application page\" do\n    visit \"/oauth/applications/#{app.id}\"\n\n    i_should_see \"Just another oauth app\"\n  end\nend\n\nfeature \"Edit application\" do\n  let :app do\n    FactoryBot.create :application, name: \"OMG my app\"\n  end\n\n  background do\n    i_am_logged_in\n\n    visit \"/oauth/applications/#{app.id}/edit\"\n  end\n\n  scenario \"updating a valid app\" do\n    fill_in \"doorkeeper_application[name]\", with: \"Serious app\"\n    click_button \"Submit\"\n\n    i_should_see \"Application updated\"\n    i_should_see \"Serious app\"\n    i_should_not_see \"OMG my app\"\n  end\n\n  scenario \"updating an invalid app\" do\n    fill_in \"doorkeeper_application[name]\", with: \"\"\n    click_button \"Submit\"\n\n    i_should_see \"Whoops! Check your form for possible errors\"\n  end\nend\n\nfeature \"Remove application\" do\n  background do\n    i_am_logged_in\n\n    @app = FactoryBot.create :application\n  end\n\n  scenario \"deleting an application from list\" do\n    visit \"/oauth/applications\"\n\n    i_should_see @app.name\n\n    within(:css, \"tr#application_#{@app.id}\") do\n      click_button \"Destroy\"\n    end\n\n    i_should_see \"Application deleted\"\n    i_should_not_see @app.name\n  end\n\n  scenario \"deleting an application from show\" do\n    visit \"/oauth/applications/#{@app.id}\"\n    click_button \"Destroy\"\n\n    i_should_see \"Application deleted\"\n  end\nend\n\ncontext \"when admin authenticator block is default\" do\n  let(:app) { FactoryBot.create :application, name: \"app\" }\n\n  feature \"application list\" do\n    scenario \"fails with forbidden\" do\n      visit \"/oauth/applications\"\n\n      should_have_status 403\n    end\n  end\n\n  feature \"adding an app\" do\n    scenario \"fails with forbidden\" do\n      visit \"/oauth/applications/new\"\n\n      should_have_status 403\n    end\n  end\n\n  feature \"editing an app\" do\n    scenario \"fails with forbidden\" do\n      visit \"/oauth/applications/#{app.id}/edit\"\n\n      should_have_status 403\n    end\n  end\nend\n"
  },
  {
    "path": "spec/requests/applications/authorized_applications_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nfeature \"Authorized applications\" do\n  background do\n    @user   = User.create!(name: \"Joe\", password: \"sekret\")\n    @client = client_exists(name: \"Amazing Client App\")\n    resource_owner_is_authenticated @user\n    client_is_authorized @client, @user\n  end\n\n  scenario \"display user's authorized applications\" do\n    visit \"/oauth/authorized_applications\"\n    i_should_see \"Amazing Client App\"\n  end\n\n  scenario \"do not display other user's authorized applications\" do\n    client = client_exists(name: \"Another Client App\")\n    client_is_authorized client, User.create!(name: \"Joe\", password: \"sekret\")\n    visit \"/oauth/authorized_applications\"\n    i_should_not_see \"Another Client App\"\n  end\n\n  scenario \"user revoke access to application\" do\n    visit \"/oauth/authorized_applications\"\n    i_should_see \"Amazing Client App\"\n    click_on \"Revoke\"\n    i_should_see \"Application revoked\"\n    i_should_not_see \"Amazing Client App\"\n  end\nend\n"
  },
  {
    "path": "spec/requests/endpoints/authorization_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nfeature \"Authorization endpoint\" do\n  background do\n    default_scopes_exist :default\n    config_is_set(:authenticate_resource_owner) { User.first || redirect_to(\"/sign_in\") }\n    client_exists(name: \"MyApp\")\n  end\n\n  scenario \"requires resource owner to be authenticated\" do\n    visit authorization_endpoint_url(client: @client)\n    i_should_see \"Sign in\"\n    i_should_be_on \"/\"\n  end\n\n  context \"with authenticated resource owner\" do\n    background do\n      create_resource_owner\n      sign_in\n    end\n\n    scenario \"displays the authorization form\" do\n      visit authorization_endpoint_url(client: @client)\n      i_should_see \"Authorize MyApp to use your account?\"\n    end\n\n    scenario \"displays all requested scopes\" do\n      default_scopes_exist :public\n      optional_scopes_exist :write\n      visit authorization_endpoint_url(client: @client, scope: \"public write\")\n      i_should_see \"Access your public data\"\n      i_should_see \"Update your data\"\n    end\n  end\n\n  context \"with a invalid request's param\" do\n    background do\n      create_resource_owner\n      sign_in\n    end\n\n    context \"when missing required param\" do\n      scenario \"displays invalid_request error when missing client\" do\n        visit authorization_endpoint_url(client: nil, response_type: \"code\")\n        i_should_not_see \"Authorize\"\n        i_should_see_translated_invalid_request_error_message :missing_param, :client_id\n      end\n\n      scenario \"displays invalid_request error when missing response_type param\" do\n        visit authorization_endpoint_url(client: @client, response_type: \"\")\n        i_should_not_see \"Authorize\"\n        i_should_see_translated_invalid_request_error_message :missing_param, :response_type\n      end\n\n      scenario \"displays invalid_request error when missing scope param and authorization server has no default scopes\" do\n        config_is_set(:default_scopes, [])\n        visit authorization_endpoint_url(client: @client, response_type: \"code\", scope: \"\")\n        i_should_not_see \"Authorize\"\n        i_should_see_translated_invalid_request_error_message :missing_param, :scope\n      end\n    end\n\n    scenario \"displays unsupported_response_type error when using a disabled response type\" do\n      config_is_set(:grant_flows, [\"implicit\"])\n      visit authorization_endpoint_url(client: @client, response_type: \"code\")\n      i_should_not_see \"Authorize\"\n      i_should_see_translated_error_message :unsupported_response_type\n    end\n\n    scenario \"displays unsupported_response_mode error when using an invalid response mode\" do\n      visit authorization_endpoint_url(client: @client, response_mode: \"invalid_response_mode\")\n      i_should_not_see \"Authorize\"\n      i_should_see_translated_error_message :unsupported_response_mode\n    end\n  end\n\n  context \"when forgery protection enabled\" do\n    background do\n      create_resource_owner\n      sign_in\n    end\n\n    scenario \"raises exception on forged requests\" do\n      allowing_forgery_protection do\n        expect do\n          page.driver.post authorization_endpoint_url(\n            client_id: @client.uid,\n            redirect_uri: @client.redirect_uri,\n            response_type: \"code\",\n          )\n        end.to raise_error(ActionController::InvalidAuthenticityToken)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/requests/endpoints/token_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe \"Token endpoint\" do\n  before do\n    client_exists\n    create_resource_owner\n    authorization_code_exists application: @client,\n                              scopes: \"public\",\n                              resource_owner_id: @resource_owner.id,\n                              resource_owner_type: @resource_owner.class.name\n  end\n\n  it \"respond with correct headers\" do\n    post token_endpoint_url(code: @authorization.token, client: @client)\n\n    expect(headers[\"Cache-Control\"]).to be_in([\"no-store\", \"no-cache, no-store\", \"private, no-store\"])\n    expect(headers[\"Content-Type\"]).to eq(\"application/json; charset=utf-8\")\n    expect(headers[\"Pragma\"]).to eq(\"no-cache\")\n  end\n\n  it \"accepts client credentials with basic auth header\" do\n    post token_endpoint_url,\n         params: {\n           code: @authorization.token,\n           redirect_uri: @client.redirect_uri,\n         },\n         headers: { \"HTTP_AUTHORIZATION\" => basic_auth_header_for_client(@client) }\n\n    expect(json_response).to include(\"access_token\" => Doorkeeper::AccessToken.first.token)\n  end\n\n  it \"returns null for expires_in when a permanent token is set\" do\n    config_is_set(:access_token_expires_in, nil)\n\n    post token_endpoint_url(code: @authorization.token, client: @client)\n\n    expect(json_response).to include(\"access_token\" => Doorkeeper::AccessToken.first.token)\n    expect(json_response).not_to include(\"expires_in\")\n  end\n\n  it \"returns unsupported_grant_type for invalid grant_type param\" do\n    post token_endpoint_url(code: @authorization.token, client: @client, grant_type: \"nothing\")\n\n    expect(json_response).to match(\n      \"error\" => \"unsupported_grant_type\",\n      \"error_description\" => translated_error_message(\"unsupported_grant_type\"),\n    )\n  end\n\n  it \"returns unsupported_grant_type for disabled grant flows\" do\n    config_is_set(:grant_flows, [\"implicit\"])\n    post token_endpoint_url(code: @authorization.token, client: @client, grant_type: \"authorization_code\")\n\n    expect(json_response).to match(\n      \"error\" => \"unsupported_grant_type\",\n      \"error_description\" => translated_error_message(\"unsupported_grant_type\"),\n    )\n  end\n\n  it \"returns unsupported_grant_type when refresh_token is not in use\" do\n    post token_endpoint_url(code: @authorization.token, client: @client, grant_type: \"refresh_token\")\n\n    expect(json_response).to match(\n      \"error\" => \"unsupported_grant_type\",\n      \"error_description\" => translated_error_message(\"unsupported_grant_type\"),\n    )\n  end\n\n  it \"returns invalid_request if grant_type is missing\" do\n    post token_endpoint_url(code: @authorization.token, client: @client, grant_type: \"\")\n\n    expect(json_response).to match(\n      \"error\" => \"invalid_request\",\n      \"error_description\" => translated_invalid_request_error_message(:missing_param, :grant_type),\n    )\n  end\nend\n"
  },
  {
    "path": "spec/requests/flows/authorization_code_errors_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nfeature \"Authorization Code Flow Errors\" do\n  let(:client_params) { {} }\n\n  background do\n    default_scopes_exist :default\n    config_is_set(:authenticate_resource_owner) { User.first || redirect_to(\"/sign_in\") }\n    client_exists client_params\n    create_resource_owner\n    sign_in\n  end\n\n  after do\n    access_grant_should_not_exist\n  end\n\n  context \"with a client trying to xss resource owner\" do\n    let(:client_name) { \"<div id='xss'>XSS</div>\" }\n    let(:client_params) { { name: client_name } }\n\n    scenario \"resource owner visit authorization endpoint\" do\n      visit authorization_endpoint_url(client: @client)\n      expect(page).not_to have_css(\"#xss\")\n    end\n  end\n\n  context \"when access was denied\" do\n    scenario \"redirects with error\" do\n      visit authorization_endpoint_url(client: @client)\n      click_on \"Deny\"\n\n      i_should_be_on_client_callback @client\n      url_should_not_have_param \"code\"\n      url_should_have_param \"error\", \"access_denied\"\n      url_should_have_param \"error_description\", translated_error_message(:access_denied)\n    end\n\n    scenario \"redirects with state parameter\" do\n      visit authorization_endpoint_url(client: @client, state: \"return-this\")\n      click_on \"Deny\"\n\n      i_should_be_on_client_callback @client\n      url_should_not_have_param \"code\"\n      url_should_have_param \"state\", \"return-this\"\n    end\n  end\nend\n\nRSpec.describe \"Authorization Code Flow Errors after authorization\" do\n  before do\n    client_exists\n    create_resource_owner\n    authorization_code_exists application: @client,\n                              resource_owner_id: @resource_owner.id,\n                              resource_owner_type: @resource_owner.class.name\n  end\n\n  it \"returns :invalid_grant error when posting an already revoked grant code\" do\n    # First successful request\n    post token_endpoint_url(code: @authorization.token, client: @client)\n\n    # Second attempt with same token\n    expect do\n      post token_endpoint_url(code: @authorization.token, client: @client)\n    end.not_to(change { Doorkeeper::AccessToken.count })\n\n    expect(json_response).to match(\n      \"error\" => \"invalid_grant\",\n      \"error_description\" => translated_error_message(\"invalid_grant\"),\n    )\n  end\n\n  it \"returns :invalid_grant error for invalid grant code\" do\n    post token_endpoint_url(code: \"invalid\", client: @client)\n\n    access_token_should_not_exist\n\n    expect(json_response).to match(\n      \"error\" => \"invalid_grant\",\n      \"error_description\" => translated_error_message(\"invalid_grant\"),\n    )\n  end\nend\n"
  },
  {
    "path": "spec/requests/flows/authorization_code_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nfeature \"Authorization Code Flow\" do\n  background do\n    default_scopes_exist :default\n    config_is_set(:authenticate_resource_owner) { User.first || redirect_to(\"/sign_in\") }\n    client_exists\n    create_resource_owner\n    sign_in\n  end\n\n  scenario \"resource owner authorizes the client\" do\n    visit authorization_endpoint_url(client: @client)\n    click_on \"Authorize\"\n\n    access_grant_should_exist_for(@client, @resource_owner)\n\n    i_should_be_on_client_callback(@client)\n\n    url_should_have_param(\"code\", Doorkeeper::AccessGrant.first.token)\n    url_should_not_have_param(\"state\")\n    url_should_not_have_param(\"error\")\n  end\n\n  context \"when configured to check application supported grant flow\" do\n    before do\n      config_is_set(:allow_grant_flow_for_client, ->(_grant_flow, client) { client.name == \"admin\" })\n    end\n\n    scenario \"forbids the request when doesn't satisfy condition\" do\n      @client.update(name: \"sample app\")\n\n      visit authorization_endpoint_url(client: @client)\n\n      i_should_see_translated_error_message(\"unauthorized_client\")\n    end\n\n    scenario \"allows the request when satisfies condition\" do\n      @client.update(name: \"admin\")\n\n      visit authorization_endpoint_url(client: @client)\n      i_should_not_see_translated_error_message(\"unauthorized_client\")\n      click_on \"Authorize\"\n\n      authorization_code = Doorkeeper::AccessGrant.first.token\n      create_access_token authorization_code, @client\n\n      access_token_should_exist_for(@client, @resource_owner)\n\n      expect(json_response).to match(\n        \"access_token\" => Doorkeeper::AccessToken.first.token,\n        \"token_type\" => \"Bearer\",\n        \"expires_in\" => 7200,\n        \"scope\" => \"default\",\n        \"created_at\" => an_instance_of(Integer),\n      )\n    end\n  end\n\n  context \"with grant hashing enabled\" do\n    background do\n      config_is_set(:token_secret_strategy, ::Doorkeeper::SecretStoring::Sha256Hash)\n    end\n\n    def authorize(redirect_url)\n      @client.redirect_uri = redirect_url\n      @client.save!\n      visit authorization_endpoint_url(client: @client)\n      click_on \"Authorize\"\n\n      access_grant_should_exist_for(@client, @resource_owner)\n\n      code = current_params[\"code\"]\n      expect(code).not_to be_nil\n\n      hashed_code = Doorkeeper::AccessGrant.secret_strategy.transform_secret code\n      expect(hashed_code).to eq Doorkeeper::AccessGrant.first.token\n\n      [code, hashed_code]\n    end\n\n    scenario \"using redirect_url urn:ietf:wg:oauth:2.0:oob\" do\n      code, hashed_code = authorize(\"urn:ietf:wg:oauth:2.0:oob\")\n      expect(code).not_to eq(hashed_code)\n      i_should_see \"Authorization code:\"\n      i_should_see code\n      i_should_not_see hashed_code\n    end\n\n    scenario \"using redirect_url urn:ietf:wg:oauth:2.0:oob:auto\" do\n      code, hashed_code = authorize(\"urn:ietf:wg:oauth:2.0:oob:auto\")\n      expect(code).not_to eq(hashed_code)\n      i_should_see \"Authorization code:\"\n      i_should_see code\n      i_should_not_see hashed_code\n    end\n  end\n\n  scenario \"resource owner authorizes using oob url\" do\n    @client.redirect_uri = \"urn:ietf:wg:oauth:2.0:oob\"\n    @client.save!\n    visit authorization_endpoint_url(client: @client)\n    click_on \"Authorize\"\n\n    access_grant_should_exist_for(@client, @resource_owner)\n\n    url_should_have_param(\"code\", Doorkeeper::AccessGrant.first.token)\n    i_should_see \"Authorization code:\"\n    i_should_see Doorkeeper::AccessGrant.first.token\n  end\n\n  scenario \"resource owner authorizes the client with state parameter set\" do\n    visit authorization_endpoint_url(client: @client, state: \"return-me\")\n    click_on \"Authorize\"\n    url_should_have_param(\"code\", Doorkeeper::AccessGrant.first.token)\n    url_should_have_param(\"state\", \"return-me\")\n    url_should_not_have_param(\"code_challenge_method\")\n  end\n\n  scenario \"resource owner requests an access token without authorization code\" do\n    create_access_token \"\", @client\n\n    access_token_should_not_exist\n\n    expect(Doorkeeper::AccessToken.count).to be_zero\n\n    expect(json_response).to match(\n      \"error\" => \"invalid_request\",\n      \"error_description\" => translated_invalid_request_error_message(:missing_param, :code),\n    )\n  end\n\n  scenario \"resource owner requests an access token with authorization code\" do\n    visit authorization_endpoint_url(client: @client)\n    click_on \"Authorize\"\n\n    authorization_code = Doorkeeper::AccessGrant.first.token\n    create_access_token authorization_code, @client\n\n    access_token_should_exist_for(@client, @resource_owner)\n\n    expect(json_response).to match(\n      \"access_token\" => Doorkeeper::AccessToken.first.token,\n      \"token_type\" => \"Bearer\",\n      \"expires_in\" => 7200,\n      \"scope\" => \"default\",\n      \"created_at\" => an_instance_of(Integer),\n    )\n  end\n\n  scenario \"resource owner requests an access token with authorization code but without secret\" do\n    visit authorization_endpoint_url(client: @client)\n    click_on \"Authorize\"\n\n    authorization_code = Doorkeeper::AccessGrant.first.token\n    page.driver.post token_endpoint_url(\n      code: authorization_code,\n      client_id: @client.uid,\n      redirect_uri: @client.redirect_uri,\n    )\n\n    expect(Doorkeeper::AccessToken.count).to be_zero\n\n    expect(json_response).to match(\n      \"error\" => \"invalid_client\",\n      \"error_description\" => translated_error_message(:invalid_client),\n    )\n  end\n\n  scenario \"resource owner requests an access token with authorization code but without client id\" do\n    visit authorization_endpoint_url(client: @client)\n    click_on \"Authorize\"\n\n    authorization_code = Doorkeeper::AccessGrant.first.token\n    page.driver.post token_endpoint_url(\n      code: authorization_code,\n      client_secret: @client.secret,\n      redirect_uri: @client.redirect_uri,\n    )\n\n    expect(Doorkeeper::AccessToken.count).to be_zero\n\n    expect(json_response).to match(\n      \"error\" => \"invalid_client\",\n      \"error_description\" => translated_error_message(:invalid_client),\n    )\n  end\n\n  scenario \"silently authorizes if active matching token exists\" do\n    default_scopes_exist :public, :write\n\n    access_token_exists application: @client,\n                        expires_in: 10_000,\n                        resource_owner_id: @resource_owner.id,\n                        resource_owner_type: @resource_owner.class.name,\n                        scopes: \"public write\"\n\n    visit authorization_endpoint_url(client: @client, scope: \"public write\")\n\n    response_status_should_be 200\n    i_should_not_see \"Authorize\"\n  end\n\n  context \"with PKCE\" do\n    context \"when plain\" do\n      let(:code_challenge) { \"a45a9fea-0676-477e-95b1-a40f72ac3cfb\" }\n      let(:code_verifier) { \"a45a9fea-0676-477e-95b1-a40f72ac3cfb\" }\n\n      scenario \"resource owner authorizes the client with code_challenge parameter set\" do\n        visit authorization_endpoint_url(\n          client: @client,\n          code_challenge: code_challenge,\n          code_challenge_method: \"plain\",\n        )\n        click_on \"Authorize\"\n\n        url_should_have_param(\"code\", Doorkeeper::AccessGrant.first.token)\n        url_should_not_have_param(\"code_challenge_method\")\n        url_should_not_have_param(\"code_challenge\")\n      end\n\n      scenario \"mobile app requests an access token with authorization code but not pkce token\" do\n        visit authorization_endpoint_url(client: @client)\n        click_on \"Authorize\"\n\n        url_should_have_param(\"code\", Doorkeeper::AccessGrant.first.token)\n      end\n\n      scenario \"mobile app requests an access token with authorization code and plain code challenge method\" do\n        visit authorization_endpoint_url(\n          client: @client,\n          code_challenge: code_challenge,\n          code_challenge_method: \"plain\",\n        )\n        click_on \"Authorize\"\n\n        authorization_code = current_params[\"code\"]\n        create_access_token authorization_code, @client, code_verifier\n\n        access_token_should_exist_for(@client, @resource_owner)\n\n        expect(json_response).to match(\n          \"access_token\" => Doorkeeper::AccessToken.first.token,\n          \"token_type\" => \"Bearer\",\n          \"expires_in\" => 7200,\n          \"scope\" => \"default\",\n          \"created_at\" => an_instance_of(Integer),\n        )\n      end\n\n      scenario \"mobile app requests an access token with authorization code but without code_verifier\" do\n        visit authorization_endpoint_url(\n          client: @client,\n          code_challenge: code_challenge,\n          code_challenge_method: \"plain\",\n        )\n        click_on \"Authorize\"\n\n        authorization_code = current_params[\"code\"]\n        create_access_token authorization_code, @client, nil\n\n        expect(json_response).to match(\n          \"error\" => \"invalid_request\",\n          \"error_description\" => translated_invalid_request_error_message(:missing_param, :code_verifier),\n        )\n      end\n\n      scenario \"mobile app requests an access token with authorization code with wrong code_verifier\" do\n        visit authorization_endpoint_url(\n          client: @client,\n          code_challenge: code_challenge,\n          code_challenge_method: \"plain\",\n        )\n        click_on \"Authorize\"\n\n        authorization_code = current_params[\"code\"]\n        create_access_token authorization_code, @client, \"wrong_code_verifier\"\n\n        expect(json_response).not_to include(\"access_token\")\n        expect(json_response).to match(\n          \"error\" => \"invalid_grant\",\n          \"error_description\" => translated_error_message(:invalid_grant),\n        )\n      end\n    end\n\n    context \"when S256\" do\n      let(:code_challenge) { \"Oz733NtQ0rJP8b04fgZMJMwprn6Iw8sMCT_9bR1q4tA\" }\n      let(:code_verifier) { \"a45a9fea-0676-477e-95b1-a40f72ac3cfb\" }\n\n      scenario \"resource owner authorizes the client with code_challenge parameter set\" do\n        visit authorization_endpoint_url(\n          client: @client,\n          code_challenge: code_challenge,\n          code_challenge_method: \"S256\",\n        )\n        click_on \"Authorize\"\n\n        url_should_have_param(\"code\", Doorkeeper::AccessGrant.first.token)\n        url_should_not_have_param(\"code_challenge_method\")\n        url_should_not_have_param(\"code_challenge\")\n      end\n\n      scenario \"mobile app requests an access token with authorization code and S256 code challenge method\" do\n        visit authorization_endpoint_url(\n          client: @client,\n          code_challenge: code_challenge,\n          code_challenge_method: \"S256\",\n        )\n        click_on \"Authorize\"\n\n        authorization_code = current_params[\"code\"]\n        create_access_token authorization_code, @client, code_verifier\n\n        access_token_should_exist_for(@client, @resource_owner)\n\n        expect(json_response).to match(\n          \"access_token\" => Doorkeeper::AccessToken.first.token,\n          \"token_type\" => \"Bearer\",\n          \"expires_in\" => 7200,\n          \"scope\" => \"default\",\n          \"created_at\" => an_instance_of(Integer),\n        )\n      end\n\n      scenario \"mobile app requests an access token with authorization code and without secret\" do\n        visit authorization_endpoint_url(\n          client: @client,\n          code_challenge: code_challenge,\n          code_challenge_method: \"S256\",\n        )\n        click_on \"Authorize\"\n\n        authorization_code = current_params[\"code\"]\n        page.driver.post token_endpoint_url(\n          code: authorization_code,\n          client_id: @client.uid,\n          redirect_uri: @client.redirect_uri,\n          code_verifier: code_verifier,\n        )\n\n        expect(json_response).to match(\n          \"error\" => \"invalid_client\",\n          \"error_description\" => translated_error_message(:invalid_client),\n        )\n      end\n\n      scenario \"mobile app requests an access token with authorization code and without secret but is marked as not confidential\" do\n        @client.update_attribute :confidential, false\n        visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: \"S256\")\n        click_on \"Authorize\"\n\n        authorization_code = current_params[\"code\"]\n        page.driver.post token_endpoint_url(\n          code: authorization_code,\n          client_id: @client.uid,\n          redirect_uri: @client.redirect_uri,\n          code_verifier: code_verifier,\n        )\n\n        expect(json_response).to match(\n          \"access_token\" => Doorkeeper::AccessToken.first.token,\n          \"token_type\" => \"Bearer\",\n          \"expires_in\" => 7200,\n          \"scope\" => \"default\",\n          \"created_at\" => an_instance_of(Integer),\n        )\n      end\n\n      scenario \"mobile app requests an access token with authorization code but no code verifier\" do\n        visit authorization_endpoint_url(\n          client: @client,\n          code_challenge: code_challenge,\n          code_challenge_method: \"S256\",\n        )\n        click_on \"Authorize\"\n\n        authorization_code = current_params[\"code\"]\n        create_access_token authorization_code, @client\n\n        expect(json_response).not_to include(\"access_token\")\n        expect(json_response).to match(\n          \"error\" => \"invalid_request\",\n          \"error_description\" => translated_invalid_request_error_message(:missing_param, :code_verifier),\n        )\n      end\n\n      scenario \"mobile app requests an access token with authorization code with wrong verifier\" do\n        visit authorization_endpoint_url(\n          client: @client,\n          code_challenge: code_challenge,\n          code_challenge_method: \"S256\",\n        )\n        click_on \"Authorize\"\n\n        authorization_code = current_params[\"code\"]\n        create_access_token authorization_code, @client, \"incorrect-code-verifier\"\n\n        expect(json_response).to match(\n          \"error\" => \"invalid_grant\",\n          \"error_description\" => translated_error_message(:invalid_grant),\n        )\n      end\n\n      scenario \"code_challenge_methhod in token request is totally ignored\" do\n        visit authorization_endpoint_url(\n          client: @client,\n          code_challenge: code_challenge,\n          code_challenge_method: \"S256\",\n        )\n        click_on \"Authorize\"\n\n        authorization_code = current_params[\"code\"]\n        page.driver.post token_endpoint_url(\n          code: authorization_code,\n          client: @client,\n          code_verifier: code_challenge,\n          code_challenge_method: \"plain\",\n        )\n\n        expect(json_response).to match(\n          \"error\" => \"invalid_grant\",\n          \"error_description\" => translated_error_message(:invalid_grant),\n        )\n      end\n\n      scenario \"expects to set code_challenge_method explicitly without fallback\" do\n        visit authorization_endpoint_url(client: @client, code_challenge: code_challenge)\n        expect(page).to have_content(\"The code_challenge_method must be one of plain, S256.\")\n      end\n    end\n  end\n\n  context \"when application scopes are present and no scope is passed\" do\n    background do\n      @client.update(scopes: \"public write read default\")\n    end\n\n    scenario \"scope is invalid because default scope is different from application scope\" do\n      default_scopes_exist :admin\n      visit authorization_endpoint_url(client: @client)\n      response_status_should_be 400\n      i_should_not_see \"Authorize\"\n      i_should_see_translated_error_message :invalid_scope\n    end\n\n    scenario \"access grant have scopes which are common in application scopees and default scopes\" do\n      default_scopes_exist :public, :write\n      visit authorization_endpoint_url(client: @client)\n      click_on \"Authorize\"\n      access_grant_should_exist_for(@client, @resource_owner)\n      access_grant_should_have_scopes :public, :write\n    end\n  end\n\n  context \"with scopes\" do\n    background do\n      default_scopes_exist :public\n      optional_scopes_exist :write\n    end\n\n    scenario \"resource owner authorizes the client with default scopes\" do\n      visit authorization_endpoint_url(client: @client)\n      click_on \"Authorize\"\n      access_grant_should_exist_for(@client, @resource_owner)\n      access_grant_should_have_scopes :public\n    end\n\n    scenario \"resource owner authorizes the client with required scopes\" do\n      visit authorization_endpoint_url(client: @client, scope: \"public write\")\n      click_on \"Authorize\"\n      access_grant_should_have_scopes :public, :write\n    end\n\n    scenario \"resource owner authorizes the client with required scopes (without defaults)\" do\n      visit authorization_endpoint_url(client: @client, scope: \"write\")\n      click_on \"Authorize\"\n      access_grant_should_have_scopes :write\n    end\n\n    scenario \"new access token matches required scopes\" do\n      visit authorization_endpoint_url(client: @client, scope: \"public write\")\n      click_on \"Authorize\"\n\n      authorization_code = Doorkeeper::AccessGrant.first.token\n      create_access_token authorization_code, @client\n\n      access_token_should_exist_for(@client, @resource_owner)\n      access_token_should_have_scopes :public, :write\n    end\n\n    scenario \"returns new token if scopes have changed\" do\n      client_is_authorized(@client, @resource_owner, scopes: \"public write\")\n      visit authorization_endpoint_url(client: @client, scope: \"public\")\n      click_on \"Authorize\"\n\n      authorization_code = Doorkeeper::AccessGrant.first.token\n      create_access_token authorization_code, @client\n\n      expect(Doorkeeper::AccessToken.count).to be(2)\n\n      expect(json_response).to include(\"access_token\" => Doorkeeper::AccessToken.last.token)\n    end\n\n    scenario \"resource owner authorizes the client with extra scopes\" do\n      client_is_authorized(@client, @resource_owner, scopes: \"public\")\n      visit authorization_endpoint_url(client: @client, scope: \"public write\")\n      click_on \"Authorize\"\n\n      authorization_code = Doorkeeper::AccessGrant.first.token\n      create_access_token authorization_code, @client\n\n      expect(Doorkeeper::AccessToken.count).to be(2)\n\n      expect(json_response).to include(\"access_token\" => Doorkeeper::AccessToken.last.token)\n      access_token_should_have_scopes :public, :write\n    end\n  end\n\n  context \"when two requests sent\" do\n    before do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        use_refresh_token\n      end\n\n      client_exists\n    end\n\n    describe \"issuing a refresh token\" do\n      let(:resource_owner) { FactoryBot.create(:resource_owner) }\n\n      before do\n        authorization_code_exists application: @client,\n                                  resource_owner_id: resource_owner.id,\n                                  resource_owner_type: resource_owner.class.name\n      end\n\n      it \"second of simultaneous client requests get an error for revoked access token\" do\n        authorization_code = Doorkeeper::AccessGrant.first.token\n        allow_any_instance_of(Doorkeeper::AccessGrant)\n          .to receive(:revoked?).and_return(false, true)\n\n        page.driver.post token_endpoint_url(code: authorization_code, client: @client)\n\n        expect(json_response).to match(\n          \"error\" => \"invalid_grant\",\n          \"error_description\" => translated_error_message(:invalid_grant),\n        )\n      end\n    end\n  end\n\n  context \"when custom_access_token_attributes are configured\" do\n    let(:resource_owner) { FactoryBot.create(:resource_owner) }\n    let(:client) { client_exists }\n    let(:grant) do\n      authorization_code_exists(\n         application: client,\n         resource_owner_id: resource_owner.id,\n         resource_owner_type: resource_owner.class.name,\n         tenant_name: \"Tenant 1\",\n       )\n    end\n\n    before do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        custom_access_token_attributes [:tenant_name]\n      end\n    end\n\n    it \"copies custom attributes from the grant into the token\" do\n      page.driver.post token_endpoint_url(code: grant.token, client: client)\n\n      access_token = Doorkeeper::AccessToken.find_by(token: json_response[\"access_token\"])\n      expect(access_token.tenant_name).to eq(\"Tenant 1\")\n    end\n  end\nend\n"
  },
  {
    "path": "spec/requests/flows/client_credentials_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe \"Client Credentials Request\" do\n  let(:client) { FactoryBot.create :application }\n\n  context \"with a valid request\" do\n    it \"authorizes the client and returns the token response\" do\n      headers = authorization client.uid, client.secret\n      params  = { grant_type: \"client_credentials\" }\n\n      post \"/oauth/token\", params: params, headers: headers\n\n      expect(json_response).to match(\n        \"access_token\" => Doorkeeper::AccessToken.first.token,\n        \"token_type\" => \"Bearer\",\n        \"expires_in\" => Doorkeeper.configuration.access_token_expires_in,\n        \"created_at\" => an_instance_of(Integer),\n      )\n    end\n\n    context \"with scopes\" do\n      before do\n        optional_scopes_exist :write\n        default_scopes_exist :public\n      end\n\n      it \"adds the scope to the token an returns in the response\" do\n        headers = authorization client.uid, client.secret\n        params  = { grant_type: \"client_credentials\", scope: \"write\" }\n\n        post \"/oauth/token\", params: params, headers: headers\n\n        expect(json_response).to include(\n          \"access_token\" => Doorkeeper::AccessToken.first.token,\n          \"scope\" => \"write\",\n        )\n      end\n\n      context \"when scopes are default\" do\n        it \"adds the scope to the token an returns in the response\" do\n          headers = authorization client.uid, client.secret\n          params  = { grant_type: \"client_credentials\", scope: \"public\" }\n\n          post \"/oauth/token\", params: params, headers: headers\n\n          expect(json_response).to include(\n            \"access_token\" => Doorkeeper::AccessToken.first.token,\n            \"scope\" => \"public\",\n          )\n        end\n      end\n\n      context \"when scopes are invalid\" do\n        it \"does not authorize the client and returns the error\" do\n          headers = authorization client.uid, client.secret\n          params  = { grant_type: \"client_credentials\", scope: \"random\" }\n\n          post \"/oauth/token\", params: params, headers: headers\n\n          expect(response.status).to eq(400)\n          expect(json_response).to match(\n            \"error\" => \"invalid_scope\",\n            \"error_description\" => translated_error_message(:invalid_scope),\n          )\n        end\n      end\n    end\n  end\n\n  context \"when configured to check application supported grant flow\" do\n    before do\n      Doorkeeper.configuration.instance_variable_set(\n        :@allow_grant_flow_for_client,\n        ->(_grant_flow, client) { client.name == \"admin\" },\n      )\n    end\n\n    scenario \"forbids the request when doesn't satisfy condition\" do\n      client.update(name: \"sample app\")\n\n      headers = authorization client.uid, client.secret\n      params  = { grant_type: \"client_credentials\" }\n\n      post \"/oauth/token\", params: params, headers: headers\n\n      expect(json_response).to match(\n        \"error\" => \"unauthorized_client\",\n        \"error_description\" => translated_error_message(:unauthorized_client),\n      )\n    end\n\n    scenario \"allows the request when satisfies condition\" do\n      client.update(name: \"admin\")\n\n      headers = authorization client.uid, client.secret\n      params  = { grant_type: \"client_credentials\" }\n\n      post \"/oauth/token\", params: params, headers: headers\n\n      expect(json_response).to match(\n        \"access_token\" => Doorkeeper::AccessToken.first.token,\n        \"token_type\" => \"Bearer\",\n        \"expires_in\" => 7200,\n        \"created_at\" => an_instance_of(Integer),\n      )\n    end\n  end\n\n  context \"when application scopes contain some of the default scopes and no scope is passed\" do\n    before do\n      client.update(scopes: \"read write public\")\n    end\n\n    it \"issues new token with one default scope that are present in application scopes\" do\n      default_scopes_exist :public\n\n      headers = authorization client.uid, client.secret\n      params  = { grant_type: \"client_credentials\" }\n\n      expect do\n        post \"/oauth/token\", params: params, headers: headers\n      end.to change { Doorkeeper::AccessToken.count }.by(1)\n\n      token = Doorkeeper::AccessToken.first\n\n      expect(token.application_id).to eq client.id\n      expect(json_response).to include(\n        \"access_token\" => token.token,\n        \"scope\" => \"public\",\n      )\n    end\n\n    it \"issues new token with multiple default scopes that are present in application scopes\" do\n      default_scopes_exist :public, :read, :update\n\n      headers = authorization client.uid, client.secret\n      params  = { grant_type: \"client_credentials\" }\n\n      expect do\n        post \"/oauth/token\", params: params, headers: headers\n      end.to change { Doorkeeper::AccessToken.count }.by(1)\n\n      token = Doorkeeper::AccessToken.first\n\n      expect(token.application_id).to eq client.id\n      expect(json_response).to include(\n        \"access_token\" => token.token,\n        \"scope\" => \"public read\",\n      )\n    end\n\n    it \"forbids the request if the public scope is not present in the application scopes\" do\n      default_scopes_exist :default\n\n      headers = authorization client.uid, client.secret\n      params  = { grant_type: \"client_credentials\" }\n\n      post \"/oauth/token\", params: params, headers: headers\n\n      expect(json_response).to match(\n        \"error\" => \"invalid_scope\",\n        \"error_description\" => translated_error_message(:invalid_scope),\n      )\n    end\n  end\n\n  context \"when request is invalid\" do\n    it \"does not authorize the client and returns the error\" do\n      headers = {}\n      params  = { grant_type: \"client_credentials\" }\n\n      post \"/oauth/token\", params: params, headers: headers\n\n      expect(response.status).to eq(401)\n\n      expect(json_response).to match(\n        \"error\" => \"invalid_client\",\n        \"error_description\" => translated_error_message(:invalid_client),\n      )\n    end\n  end\n\n  context \"when revoke_previous_client_credentials_token is true\" do\n    before do\n      allow(Doorkeeper.config).to receive(:reuse_access_token).and_return(false)\n      allow(Doorkeeper.config).to receive(:revoke_previous_client_credentials_token?).and_return(true)\n    end\n\n    it \"revokes the previous token\" do\n      headers = authorization client.uid, client.secret\n      params  = { grant_type: \"client_credentials\" }\n\n      post \"/oauth/token\", params: params, headers: headers\n      expect(json_response).to include(\"access_token\" => Doorkeeper::AccessToken.first.token)\n\n      token = Doorkeeper::AccessToken.first\n\n      post \"/oauth/token\", params: params, headers: headers\n      expect(json_response).to include(\"access_token\" => Doorkeeper::AccessToken.last.token)\n\n      expect(token.reload).to be_revoked\n      expect(Doorkeeper::AccessToken.last).not_to be_revoked\n    end\n\n    context \"with a simultaneous request\" do\n      let!(:access_token) { FactoryBot.create :access_token, resource_owner_id: nil }\n\n      before do\n        allow(Doorkeeper.config.access_token_model).to receive(:matching_token_for) { access_token }\n        allow(access_token).to receive(:revoked?).and_return(true)\n      end\n\n      it \"returns an error\" do\n        headers = authorization client.uid, client.secret\n        params  = { grant_type: \"client_credentials\" }\n\n        post \"/oauth/token\", params: params, headers: headers\n\n        expect(json_response).to match(\n          \"error\" => \"invalid_token_reuse\",\n          \"error_description\" => translated_error_message(:server_error),\n        )\n      end\n    end\n  end\n\n  def authorization(username, password)\n    credentials = ActionController::HttpAuthentication::Basic.encode_credentials username, password\n    { \"HTTP_AUTHORIZATION\" => credentials }\n  end\nend\n"
  },
  {
    "path": "spec/requests/flows/implicit_grant_errors_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nfeature \"Implicit Grant Flow Errors\" do\n  background do\n    default_scopes_exist :default\n    config_is_set(:authenticate_resource_owner) { User.first || redirect_to(\"/sign_in\") }\n    config_is_set(:grant_flows, [\"implicit\"])\n    client_exists\n    create_resource_owner\n    sign_in\n  end\n\n  after do\n    access_token_should_not_exist\n  end\n\n  context \"when validate client_id param\" do\n    scenario \"displays invalid_client error for invalid client_id\" do\n      visit authorization_endpoint_url(client_id: \"invalid\", response_type: \"token\")\n      i_should_not_see \"Authorize\"\n      i_should_see_translated_error_message :invalid_client\n    end\n\n    scenario \"displays invalid_request error when client_id is missing\" do\n      visit authorization_endpoint_url(client_id: \"\", response_type: \"token\")\n      i_should_not_see \"Authorize\"\n      i_should_see_translated_invalid_request_error_message :missing_param, :client_id\n    end\n  end\n\n  context \"when validate redirect_uri param\" do\n    scenario \"displays invalid_redirect_uri error for invalid redirect_uri\" do\n      visit authorization_endpoint_url(client: @client, redirect_uri: \"invalid\", response_type: \"token\")\n      i_should_not_see \"Authorize\"\n      i_should_see_translated_error_message :invalid_redirect_uri\n    end\n\n    scenario \"displays invalid_redirect_uri error when redirect_uri is missing\" do\n      visit authorization_endpoint_url(client: @client, redirect_uri: \"\", response_type: \"token\")\n      i_should_not_see \"Authorize\"\n      i_should_see_translated_error_message :invalid_redirect_uri\n    end\n  end\n\n  context \"when validate response_mode param\" do\n    scenario \"displays unsupported_response_mode error when using 'query' response mode\" do\n      visit authorization_endpoint_url(client: @client, response_type: \"token\", response_mode: \"query\")\n      i_should_not_see \"Authorize\"\n      i_should_see_translated_error_message :unsupported_response_mode\n    end\n  end\nend\n"
  },
  {
    "path": "spec/requests/flows/implicit_grant_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nfeature \"Implicit Grant Flow (feature spec)\" do\n  background do\n    default_scopes_exist :default\n    config_is_set(:authenticate_resource_owner) { User.first || redirect_to(\"/sign_in\") }\n    config_is_set(:grant_flows, [\"implicit\"])\n    client_exists\n    create_resource_owner\n    sign_in\n  end\n\n  scenario \"resource owner authorizes the client\" do\n    visit authorization_endpoint_url(client: @client, response_type: \"token\")\n    click_on \"Authorize\"\n\n    access_token_should_exist_for @client, @resource_owner\n\n    i_should_be_on_client_callback @client\n  end\n\n  context \"when application scopes are present and no scope is passed\" do\n    background do\n      @client.update(scopes: \"public write read\")\n    end\n\n    scenario \"scope is invalid because default scope is different from application scope\" do\n      default_scopes_exist :admin\n      visit authorization_endpoint_url(client: @client, response_type: \"token\")\n      response_status_should_be 400\n      i_should_not_see \"Authorize\"\n      i_should_see_translated_error_message :invalid_scope\n    end\n\n    scenario \"access token has scopes which are common in application scopes and default scopes\" do\n      default_scopes_exist :public, :write\n      visit authorization_endpoint_url(client: @client, response_type: \"token\")\n      click_on \"Authorize\"\n      access_token_should_exist_for @client, @resource_owner\n      access_token_should_have_scopes :public, :write\n    end\n  end\nend\n\nRSpec.describe \"Implicit Grant Flow (request spec)\" do\n  before do\n    default_scopes_exist :default\n    config_is_set(:authenticate_resource_owner) { User.first || redirect_to(\"/sign_in\") }\n    config_is_set(:grant_flows, [\"implicit\"])\n    client_exists\n    create_resource_owner\n  end\n\n  context \"when reuse_access_token enabled\" do\n    it \"returns a new token each request\" do\n      allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(false)\n\n      token = client_is_authorized(@client, @resource_owner, scopes: \"default\")\n\n      post \"/oauth/authorize\",\n           params: {\n             client_id: @client.uid,\n             state: \"\",\n             redirect_uri: @client.redirect_uri,\n             response_type: \"token\",\n             commit: \"Authorize\",\n           }\n\n      expect(response.location).not_to include(token.token)\n    end\n\n    it \"returns the same token if it is still accessible\" do\n      allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)\n\n      token = client_is_authorized(@client, @resource_owner, scopes: \"default\")\n\n      post \"/oauth/authorize\",\n           params: {\n             client_id: @client.uid,\n             state: \"\",\n             redirect_uri: @client.redirect_uri,\n             response_type: \"token\",\n             commit: \"Authorize\",\n           }\n\n      expect(response.location).to include(token.token)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/requests/flows/password_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe \"Resource Owner Password Credentials Flow\" do\n  context \"when not setup properly\" do\n    before do\n      client_exists\n      create_resource_owner\n    end\n\n    context \"with valid user credentials\" do\n      it \"does not issue new token\" do\n        expect do\n          post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)\n        end.not_to(change { Doorkeeper::AccessToken.count })\n      end\n    end\n  end\n\n  context \"when grant type configured\" do\n    let(:client_attributes) { { redirect_uri: nil } }\n\n    before do\n      config_is_set(:grant_flows, [\"password\"])\n      config_is_set(:resource_owner_from_credentials) { User.authenticate! params[:username], params[:password] }\n      client_exists(client_attributes)\n      create_resource_owner\n    end\n\n    context \"with valid user credentials\" do\n      context \"with confidential client authorized using Basic auth\" do\n        it \"issues a new token\" do\n          expect do\n            post password_token_endpoint_url(\n              resource_owner: @resource_owner,\n            ), headers: { \"HTTP_AUTHORIZATION\" => basic_auth_header_for_client(@client) }\n          end.to(change { Doorkeeper::AccessToken.count })\n\n          token = Doorkeeper::AccessToken.first\n          expect(token.application_id).to eq(@client.id)\n\n          expect(json_response).to match(\n            \"access_token\" => token.token,\n            \"expires_in\" => an_instance_of(Integer),\n            \"token_type\" => \"Bearer\",\n            \"created_at\" => an_instance_of(Integer),\n          )\n        end\n      end\n\n      context \"with non-confidential/public client\" do\n        let(:client_attributes) { { confidential: false } }\n\n        context \"when configured to check application supported grant flow\" do\n          before do\n            Doorkeeper.configuration.instance_variable_set(\n              :@allow_grant_flow_for_client,\n              ->(_grant_flow, client) { client.name == \"admin\" },\n            )\n          end\n\n          scenario \"forbids the request when doesn't satisfy condition\" do\n            @client.update(name: \"sample app\")\n\n            expect do\n              post password_token_endpoint_url(\n                client_id: @client.uid,\n                client_secret: \"foobar\",\n                resource_owner: @resource_owner,\n              )\n            end.not_to(change { Doorkeeper::AccessToken.count })\n\n            expect(response.status).to eq(401)\n            expect(json_response).to match(\n              \"error\" => \"invalid_client\",\n              \"error_description\" => an_instance_of(String),\n            )\n          end\n\n          scenario \"allows the request when satisfies condition\" do\n            @client.update(name: \"admin\")\n\n            expect do\n              post password_token_endpoint_url(client_id: @client.uid, resource_owner: @resource_owner)\n            end.to change { Doorkeeper::AccessToken.count }.by(1)\n\n            token = Doorkeeper::AccessToken.first\n            expect(token.application_id).to eq(@client.id)\n\n            expect(json_response).to include(\"access_token\" => token.token)\n          end\n        end\n\n        context \"when client_secret absent\" do\n          it \"issues a new token\" do\n            expect do\n              post password_token_endpoint_url(client_id: @client.uid, resource_owner: @resource_owner)\n            end.to change { Doorkeeper::AccessToken.count }.by(1)\n\n            token = Doorkeeper::AccessToken.first\n\n            expect(token.application_id).to eq(@client.id)\n            expect(json_response).to include(\"access_token\" => token.token)\n          end\n        end\n\n        context \"when client_secret present\" do\n          it \"issues a new token\" do\n            expect do\n              post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)\n            end.to change { Doorkeeper::AccessToken.count }.by(1)\n\n            token = Doorkeeper::AccessToken.first\n\n            expect(token.application_id).to eq(@client.id)\n            expect(json_response).to include(\"access_token\" => token.token)\n          end\n\n          context \"when client_secret incorrect\" do\n            it \"doesn't issue new token\" do\n              expect do\n                post password_token_endpoint_url(\n                  client_id: @client.uid,\n                  client_secret: \"foobar\",\n                  resource_owner: @resource_owner,\n                )\n              end.not_to(change { Doorkeeper::AccessToken.count })\n\n              expect(response.status).to eq(401)\n              expect(json_response).to include(\n                \"error\" => \"invalid_client\",\n                \"error_description\" => an_instance_of(String),\n              )\n            end\n          end\n        end\n      end\n\n      context \"with confidential/private client\" do\n        it \"issues a new token\" do\n          expect do\n            post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)\n          end.to change { Doorkeeper::AccessToken.count }.by(1)\n\n          token = Doorkeeper::AccessToken.first\n\n          expect(token.application_id).to eq(@client.id)\n          expect(json_response).to include(\"access_token\" => token.token)\n        end\n\n        context \"when client_secret absent\" do\n          it \"doesn't issue new token\" do\n            expect do\n              post password_token_endpoint_url(client_id: @client.uid, resource_owner: @resource_owner)\n            end.not_to(change { Doorkeeper::AccessToken.count })\n\n            expect(response.status).to eq(401)\n            expect(json_response).to match(\n              \"error\" => \"invalid_client\",\n              \"error_description\" => an_instance_of(String),\n            )\n          end\n        end\n      end\n\n      it \"issues a refresh token if enabled\" do\n        config_is_set(:refresh_token_enabled, true)\n\n        post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)\n\n        token = Doorkeeper::AccessToken.first\n        expect(json_response).to include(\"refresh_token\" => token.refresh_token)\n      end\n\n      it \"returns the same token if it is still accessible\" do\n        allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)\n\n        client_is_authorized(@client, @resource_owner)\n\n        post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)\n\n        expect(Doorkeeper::AccessToken.count).to be(1)\n        expect(json_response).to include(\"access_token\" => Doorkeeper::AccessToken.first.token)\n      end\n\n      context \"with valid, default scope\" do\n        before do\n          default_scopes_exist :public\n        end\n\n        it \"issues new token\" do\n          expect do\n            post password_token_endpoint_url(client: @client, resource_owner: @resource_owner, scope: \"public\")\n          end.to change { Doorkeeper::AccessToken.count }.by(1)\n\n          token = Doorkeeper::AccessToken.first\n\n          expect(token.application_id).to eq(@client.id)\n          expect(json_response).to include(\n            \"access_token\" => token.token,\n            \"scope\" => \"public\",\n          )\n        end\n      end\n    end\n\n    context \"with skip_client_authentication_for_password_grant config option\" do\n      context \"when enabled\" do\n        before do\n          allow(Doorkeeper.config)\n            .to receive(:skip_client_authentication_for_password_grant).and_return(true)\n        end\n\n        it \"issues a new token without client credentials\" do\n          expect do\n            post password_token_endpoint_url(resource_owner: @resource_owner)\n          end.to(change { Doorkeeper::AccessToken.count }.by(1))\n\n          token = Doorkeeper::AccessToken.first\n\n          expect(token.application_id).to be_nil\n          expect(json_response).to include(\"access_token\" => token.token)\n        end\n\n        it \"doesn't issue a new token with invalid client credentials in query string\" do\n          expect do\n            post password_token_endpoint_url(\n              resource_owner: @resource_owner,\n              client_id: \"invalid\",\n              client_secret: \"invalid\",\n            )\n          end.not_to(change { Doorkeeper::AccessToken.count })\n\n          expect(response.status).to eq(401)\n          expect(json_response).to match(\n            \"error\" => \"invalid_client\",\n            \"error_description\" => an_instance_of(String),\n          )\n        end\n\n        it \"doesn't issue a new token with invalid client credentials in Basic auth\" do\n          invalid_client = Doorkeeper::OAuth::Client::Credentials.new(\"invalid\", \"invalid\")\n\n          expect do\n            post password_token_endpoint_url(\n              resource_owner: @resource_owner,\n            ), headers: { \"HTTP_AUTHORIZATION\" => basic_auth_header_for_client(invalid_client) }\n          end.not_to(change { Doorkeeper::AccessToken.count })\n\n          expect(response.status).to eq(401)\n          expect(json_response).to match(\n            \"error\" => \"invalid_client\",\n            \"error_description\" => an_instance_of(String),\n          )\n        end\n      end\n\n      context \"when disabled\" do\n        before do\n          allow(Doorkeeper.config)\n            .to receive(:skip_client_authentication_for_password_grant).and_return(false)\n        end\n\n        it \"doesn't issue a new token without client credentials\" do\n          expect do\n            post password_token_endpoint_url(resource_owner: @resource_owner)\n          end.not_to(change { Doorkeeper::AccessToken.count })\n\n          expect(response.status).to eq(401)\n          expect(json_response).to match(\n            \"error\" => \"invalid_client\",\n            \"error_description\" => an_instance_of(String),\n          )\n        end\n      end\n    end\n\n    context \"when application scopes are present and differs from configured default scopes and no scope is passed\" do\n      before do\n        default_scopes_exist :public\n        @client.update(scopes: \"abc\")\n      end\n\n      it \"issues new token without any scope\" do\n        expect do\n          post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)\n        end.to change { Doorkeeper::AccessToken.count }.by(1)\n\n        token = Doorkeeper::AccessToken.first\n\n        expect(token.application_id).to eq(@client.id)\n        expect(token.scopes).to be_empty\n        expect(json_response).to include(\"access_token\" => token.token)\n        expect(json_response).not_to include(\"scope\")\n      end\n    end\n\n    context \"when application scopes contain some of the default scopes and no scope is passed\" do\n      before do\n        @client.update(scopes: \"read write public\")\n      end\n\n      it \"issues new token with one default scope that are present in application scopes\" do\n        default_scopes_exist :public, :admin\n\n        expect do\n          post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)\n        end.to change { Doorkeeper::AccessToken.count }.by(1)\n\n        token = Doorkeeper::AccessToken.first\n\n        expect(token.application_id).to eq(@client.id)\n        expect(json_response).to include(\n          \"access_token\" => token.token,\n          \"scope\" => \"public\",\n        )\n      end\n\n      it \"issues new token with multiple default scopes that are present in application scopes\" do\n        default_scopes_exist :public, :read, :update\n\n        expect do\n          post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)\n        end.to change { Doorkeeper::AccessToken.count }.by(1)\n\n        token = Doorkeeper::AccessToken.first\n\n        expect(token.application_id).to eq(@client.id)\n        expect(json_response).to include(\n          \"access_token\" => token.token,\n          \"scope\" => \"public read\",\n        )\n      end\n    end\n\n    context \"with invalid scopes\" do\n      it \"doesn't issue new token\" do\n        expect do\n          post password_token_endpoint_url(\n            client: @client,\n            resource_owner: @resource_owner,\n            scope: \"random\",\n          )\n        end.not_to(change { Doorkeeper::AccessToken.count })\n      end\n\n      it \"returns invalid_scope error\" do\n        post password_token_endpoint_url(\n          client: @client,\n          resource_owner: @resource_owner,\n          scope: \"random\",\n        )\n\n        expect(response.status).to eq(400)\n        expect(json_response).to match(\n          \"error\" => \"invalid_scope\",\n          \"error_description\" => translated_error_message(:invalid_scope),\n        )\n      end\n    end\n\n    context \"with invalid user credentials\" do\n      it \"doesn't issue new token with bad password\" do\n        expect do\n          post password_token_endpoint_url(\n            client: @client,\n            resource_owner_username: @resource_owner.name,\n            resource_owner_password: \"wrongpassword\",\n          )\n        end.not_to(change { Doorkeeper::AccessToken.count })\n      end\n\n      it \"doesn't issue new token without credentials\" do\n        expect do\n          post password_token_endpoint_url(client: @client)\n        end.not_to(change { Doorkeeper::AccessToken.count })\n      end\n\n      it \"doesn't issue new token if resource_owner_from_credentials returned false or nil\" do\n        config_is_set(:resource_owner_from_credentials) { false }\n\n        expect do\n          post password_token_endpoint_url(client: @client)\n        end.not_to(change { Doorkeeper::AccessToken.count })\n\n        config_is_set(:resource_owner_from_credentials) { nil }\n\n        expect do\n          post password_token_endpoint_url(client: @client)\n        end.not_to(change { Doorkeeper::AccessToken.count })\n      end\n    end\n\n    context \"with invalid confidential client credentials\" do\n      it \"doesn't issue new token with bad client credentials\" do\n        expect do\n          post password_token_endpoint_url(\n            client_id: @client.uid,\n            client_secret: \"bad_secret\",\n            resource_owner: @resource_owner,\n          )\n        end.not_to(change { Doorkeeper::AccessToken.count })\n      end\n    end\n\n    context \"with invalid public client id\" do\n      it \"doesn't issue new token with bad client id\" do\n        expect do\n          post password_token_endpoint_url(client_id: \"bad_id\", resource_owner: @resource_owner)\n        end.not_to(change { Doorkeeper::AccessToken.count })\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/requests/flows/refresh_token_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe \"Refresh Token Flow\" do\n  before do\n    Doorkeeper.configure do\n      orm DOORKEEPER_ORM\n      use_refresh_token\n    end\n\n    client_exists\n  end\n\n  let(:resource_owner) { FactoryBot.create(:resource_owner) }\n\n  describe \"issuing a refresh token\" do\n    before do\n      authorization_code_exists application: @client,\n                                resource_owner_id: resource_owner.id,\n                                resource_owner_type: resource_owner.class.name\n    end\n\n    it \"client gets the refresh token and refreshes it\" do\n      post token_endpoint_url(code: @authorization.token, client: @client)\n\n      token = Doorkeeper::AccessToken.first\n\n      expect(json_response).to include(\n        \"access_token\" => token.token,\n        \"refresh_token\" => token.refresh_token,\n      )\n\n      expect(@authorization.reload).to be_revoked\n\n      post refresh_token_endpoint_url(client: @client, refresh_token: token.refresh_token)\n\n      new_token = Doorkeeper::AccessToken.last\n      expect(json_response).to include(\n        \"access_token\" => new_token.token,\n        \"refresh_token\" => new_token.refresh_token,\n      )\n\n      expect(token.token).not_to eq(new_token.token)\n      expect(token.refresh_token).not_to eq(new_token.refresh_token)\n    end\n  end\n\n  describe \"refreshing the token\" do\n    before do\n      @token = FactoryBot.create(\n        :access_token,\n        application: @client,\n        resource_owner_id: resource_owner.id,\n        resource_owner_type: resource_owner.class.name,\n        use_refresh_token: true,\n      )\n    end\n\n    context \"when refresh_token revoked on use\" do\n      it \"client requests a token with refresh token\" do\n        post refresh_token_endpoint_url(\n          client: @client, refresh_token: @token.refresh_token,\n        )\n        expect(json_response).to include(\n          \"refresh_token\" => Doorkeeper::AccessToken.last.refresh_token,\n        )\n        expect(@token.reload).not_to be_revoked\n      end\n\n      it \"client requests a token with expired access token\" do\n        @token.update_attribute :expires_in, -100\n        post refresh_token_endpoint_url(\n          client: @client, refresh_token: @token.refresh_token,\n        )\n        expect(json_response).to include(\n          \"refresh_token\" => Doorkeeper::AccessToken.last.refresh_token,\n        )\n        expect(@token.reload).not_to be_revoked\n      end\n    end\n\n    context \"when refresh_token revoked on refresh_token request\" do\n      before do\n        allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)\n      end\n\n      it \"client request a token with refresh token\" do\n        post refresh_token_endpoint_url(\n          client: @client, refresh_token: @token.refresh_token,\n        )\n        expect(json_response).to include(\n          \"refresh_token\" => Doorkeeper::AccessToken.last.refresh_token,\n        )\n        expect(@token.reload).to be_revoked\n      end\n\n      it \"client request a token with expired access token\" do\n        @token.update_attribute :expires_in, -100\n        post refresh_token_endpoint_url(\n          client: @client, refresh_token: @token.refresh_token,\n        )\n        expect(json_response).to include(\n          \"refresh_token\" => Doorkeeper::AccessToken.last.refresh_token,\n        )\n        expect(@token.reload).to be_revoked\n      end\n    end\n\n    context \"with public & private clients\" do\n      let(:public_client) do\n        FactoryBot.create(\n          :application,\n          confidential: false,\n        )\n      end\n\n      let(:token_for_private_client) do\n        FactoryBot.create(\n          :access_token,\n          application: @client,\n          resource_owner_id: resource_owner.id,\n          resource_owner_type: resource_owner.class.name,\n          use_refresh_token: true,\n        )\n      end\n\n      let(:token_for_public_client) do\n        FactoryBot.create(\n          :access_token,\n          application: public_client,\n          resource_owner_id: resource_owner.id,\n          resource_owner_type: resource_owner.class.name,\n          use_refresh_token: true,\n        )\n      end\n\n      it \"issues a new token without client_secret when refresh token was issued to a public client\" do\n        post refresh_token_endpoint_url(\n          client_id: public_client.uid,\n          refresh_token: token_for_public_client.refresh_token,\n        )\n\n        new_token = Doorkeeper::AccessToken.last\n        expect(json_response).to include(\n          \"access_token\" => new_token.token,\n          \"refresh_token\" => new_token.refresh_token,\n        )\n      end\n\n      it \"returns an error without credentials\" do\n        post refresh_token_endpoint_url(refresh_token: token_for_private_client.refresh_token)\n\n        expect(json_response).to include(\"error\" => \"invalid_grant\")\n      end\n\n      it \"returns an error with wrong credentials\" do\n        post refresh_token_endpoint_url(\n          client_id: \"1\",\n          client_secret: \"1\",\n          refresh_token: token_for_private_client.refresh_token,\n        )\n        expect(json_response).to match(\n          \"error\" => \"invalid_client\",\n          \"error_description\" => an_instance_of(String),\n        )\n      end\n    end\n\n    it \"client gets an error for invalid refresh token\" do\n      post refresh_token_endpoint_url(client: @client, refresh_token: \"invalid\")\n\n      expect(json_response).to match(\n        \"error\" => \"invalid_grant\",\n        \"error_description\" => an_instance_of(String),\n      )\n    end\n\n    it \"client gets an error for revoked access token\" do\n      @token.revoke\n      post refresh_token_endpoint_url(client: @client, refresh_token: @token.refresh_token)\n\n      expect(json_response).to match(\n        \"error\" => \"invalid_grant\",\n        \"error_description\" => an_instance_of(String),\n      )\n    end\n\n    it \"second of simultaneous client requests get an error for revoked access token\" do\n      allow_any_instance_of(Doorkeeper::AccessToken).to receive(:revoked?).and_return(false, true)\n      post refresh_token_endpoint_url(client: @client, refresh_token: @token.refresh_token)\n\n      expect(json_response).to match(\n        \"error\" => \"invalid_grant\",\n        \"error_description\" => an_instance_of(String),\n      )\n    end\n  end\n\n  context \"when refreshing the token with multiple sessions (devices)\" do\n    before do\n      # enable password auth to simulate other devices\n      config_is_set(:grant_flows, [\"password\"])\n      config_is_set(:resource_owner_from_credentials) do\n        User.authenticate! params[:username], params[:password]\n      end\n      create_resource_owner\n      _another_token = post password_token_endpoint_url(\n        client: @client, resource_owner: resource_owner,\n      )\n      last_token.update(created_at: 5.seconds.ago)\n\n      @token = FactoryBot.create(\n        :access_token,\n        application: @client,\n        resource_owner_id: resource_owner.id,\n        resource_owner_type: resource_owner.class.name,\n        use_refresh_token: true,\n      )\n      @token.update_attribute :expires_in, -100\n    end\n\n    context \"when refresh_token revoked on use\" do\n      it \"client request a token after creating another token with the same user\" do\n        post refresh_token_endpoint_url(\n          client: @client, refresh_token: @token.refresh_token,\n        )\n\n        expect(json_response).to include(\"refresh_token\" => last_token.refresh_token)\n        expect(@token.reload).not_to be_revoked\n      end\n    end\n\n    context \"when refresh_token revoked on refresh_token request\" do\n      before do\n        allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)\n      end\n\n      it \"client request a token after creating another token with the same user\" do\n        post refresh_token_endpoint_url(\n          client: @client, refresh_token: @token.refresh_token,\n        )\n\n        expect(json_response).to include(\"refresh_token\" => last_token.refresh_token)\n        expect(@token.reload).to be_revoked\n      end\n    end\n\n    context \"when custom_access_token_attributes are configured\" do\n      before do\n        Doorkeeper.configure do\n          orm DOORKEEPER_ORM\n          custom_access_token_attributes [:tenant_name]\n        end\n\n        @token = FactoryBot.create(\n          :access_token,\n          application: @client,\n          resource_owner_id: resource_owner.id,\n          resource_owner_type: resource_owner.class.name,\n          use_refresh_token: true,\n          tenant_name: \"Tenant 1\",\n        )\n      end\n\n      it \"copies custom attributes from the previous token into the new token\" do\n        post refresh_token_endpoint_url(\n          client: @client, refresh_token: @token.refresh_token,\n        )\n\n        new_token = Doorkeeper::AccessToken.last\n        expect(new_token.tenant_name).to eq(\"Tenant 1\")\n      end\n    end\n\n    def last_token\n      Doorkeeper::AccessToken.last_authorized_token_for(\n        @client.id, resource_owner,\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "spec/requests/flows/revoke_token_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe \"Revoke Token Flow\" do\n  before do\n    Doorkeeper.configure { orm DOORKEEPER_ORM }\n  end\n\n  let(:private_client_application) { FactoryBot.create :application }\n  let(:public_client_application) { FactoryBot.create :application, confidential: false }\n  let(:resource_owner) { User.create!(name: \"John\", password: \"sekret\") }\n\n  context \"with authenticated, confidential OAuth 2.0 client/application\" do\n    let(:access_token) do\n      FactoryBot.create(\n        :access_token,\n        application: private_client_application,\n        resource_owner_id: resource_owner.id,\n        resource_owner_type: resource_owner.class.name,\n        use_refresh_token: true,\n      )\n    end\n\n    let(:headers) do\n      client_id = private_client_application.uid\n      client_secret = private_client_application.secret\n      credentials = Base64.encode64(\"#{client_id}:#{client_secret}\")\n      { \"HTTP_AUTHORIZATION\" => \"Basic #{credentials}\" }\n    end\n\n    it \"revokes the access token provided\" do\n      post revocation_token_endpoint_url, params: { token: access_token.token }, headers: headers\n\n      expect(response).to be_successful\n      expect(access_token.reload).to be_revoked\n    end\n\n    it \"revokes the refresh token provided\" do\n      post revocation_token_endpoint_url, params: { token: access_token.refresh_token, token_type_hint: \"refresh_token\" }, headers: headers\n\n      expect(response).to be_successful\n      expect(access_token.reload).to be_revoked\n    end\n\n    context \"with invalid token to revoke\" do\n      it \"does not revoke any tokens and must respond with success\" do\n        expect do\n          post revocation_token_endpoint_url,\n               params: { token: \"I_AM_AN_INVALID_TOKEN\" },\n               headers: headers\n        end.not_to(change { Doorkeeper::AccessToken.where(revoked_at: nil).count })\n\n        expect(response).to be_successful\n      end\n    end\n\n    context \"with bad credentials and a valid token\" do\n      let(:headers) do\n        client_id = private_client_application.uid\n        credentials = Base64.encode64(\"#{client_id}:poop\")\n        { \"HTTP_AUTHORIZATION\" => \"Basic #{credentials}\" }\n      end\n\n      it \"does not revoke any tokens and respond with forbidden\" do\n        post revocation_token_endpoint_url, params: { token: access_token.token }, headers: headers\n\n        expect(response).to be_forbidden\n        expect(response.body).to include(\"unauthorized_client\")\n        expect(response.body).to include(I18n.t(\"doorkeeper.errors.messages.revoke.unauthorized\"))\n        expect(access_token.reload).not_to be_revoked\n      end\n    end\n\n    context \"with no credentials and a valid token\" do\n      it \"does not revoke any tokens and respond with forbidden\" do\n        post revocation_token_endpoint_url, params: { token: access_token.token }\n\n        expect(response).to be_forbidden\n        expect(response.body).to include(\"unauthorized_client\")\n        expect(response.body).to include(I18n.t(\"doorkeeper.errors.messages.revoke.unauthorized\"))\n        expect(access_token.reload).not_to be_revoked\n      end\n    end\n\n    context \"with valid token for another client application\" do\n      let(:other_client_application) { FactoryBot.create :application }\n      let(:headers) do\n        client_id = other_client_application.uid\n        client_secret = other_client_application.secret\n        credentials = Base64.encode64(\"#{client_id}:#{client_secret}\")\n        { \"HTTP_AUTHORIZATION\" => \"Basic #{credentials}\" }\n      end\n\n      it \"does not revoke the token as it's unauthorized\" do\n        post revocation_token_endpoint_url, params: { token: access_token.token }, headers: headers\n\n        expect(response).to be_forbidden\n        expect(response.body).to include(\"unauthorized_client\")\n        expect(response.body).to include(I18n.t(\"doorkeeper.errors.messages.revoke.unauthorized\"))\n        expect(access_token.reload).not_to be_revoked\n      end\n    end\n  end\n\n  context \"with authenticated public OAuth 2.0 client/application\" do\n    let(:access_token) do\n      FactoryBot.create(\n        :access_token,\n        application: nil,\n        resource_owner_id: resource_owner.id,\n        resource_owner_type: resource_owner.class.name,\n        use_refresh_token: true,\n      )\n    end\n\n    it \"revokes the access token provided\" do\n      post revocation_token_endpoint_url,\n           params: { client_id: public_client_application.uid, token: access_token.token },\n           headers: headers\n\n      expect(response).to be_successful\n      expect(access_token.reload).to be_revoked\n    end\n\n    it \"revokes the refresh token provided\" do\n      post revocation_token_endpoint_url,\n           params: { client_id: public_client_application.uid, token: access_token.refresh_token },\n           headers: headers\n\n      expect(response).to be_successful\n      expect(access_token.reload).to be_revoked\n    end\n\n    it \"responses with success even for invalid token\" do\n      post revocation_token_endpoint_url,\n           params: { client_id: public_client_application.uid, token: \"dont_exist\" },\n           headers: headers\n\n      expect(response).to be_successful\n    end\n\n    context \"with a valid token issued for a confidential client\" do\n      let(:access_token) do\n        FactoryBot.create(\n          :access_token,\n          application: private_client_application,\n          resource_owner_id: resource_owner.id,\n          resource_owner_type: resource_owner.class.name,\n          use_refresh_token: true,\n        )\n      end\n\n      it \"does not revoke the access token provided\" do\n        post revocation_token_endpoint_url,\n             params: { client_id: public_client_application.uid, token: access_token.token }\n\n        expect(response).to be_forbidden\n        expect(response.body).to include(\"unauthorized_client\")\n        expect(response.body).to include(I18n.t(\"doorkeeper.errors.messages.revoke.unauthorized\"))\n        expect(access_token.reload).not_to be_revoked\n      end\n\n      it \"does not revoke the refresh token provided\" do\n        post revocation_token_endpoint_url,\n             params: { client_id: public_client_application.uid, token: access_token.refresh_token }\n\n        expect(response).to be_forbidden\n        expect(response.body).to include(\"unauthorized_client\")\n        expect(response.body).to include(I18n.t(\"doorkeeper.errors.messages.revoke.unauthorized\"))\n        expect(access_token.reload).not_to be_revoked\n      end\n    end\n  end\n\n  context \"with a token issued to a public client\" do\n    let(:access_token) do\n      FactoryBot.create(\n        :access_token,\n        application: public_client_application,\n        resource_owner_id: resource_owner.id,\n        resource_owner_type: resource_owner.class.name,\n        use_refresh_token: true,\n      )\n    end\n\n    it \"revokes the token when the requesting client matches\" do\n      post revocation_token_endpoint_url,\n           params: { client_id: public_client_application.uid, token: access_token.token }\n\n      expect(response).to be_successful\n      expect(access_token.reload).to be_revoked\n    end\n\n    it \"revokes the refresh token when the requesting client matches\" do\n      post revocation_token_endpoint_url,\n           params: { client_id: public_client_application.uid, token: access_token.refresh_token, token_type_hint: \"refresh_token\" }\n\n      expect(response).to be_successful\n      expect(access_token.reload).to be_revoked\n    end\n\n    it \"does not revoke the token when the requesting client is a different public client\" do\n      other_public_client = FactoryBot.create(:application, confidential: false)\n\n      post revocation_token_endpoint_url,\n           params: { client_id: other_public_client.uid, token: access_token.token }\n\n      expect(response).to be_forbidden\n      expect(response.body).to include(\"unauthorized_client\")\n      expect(access_token.reload).not_to be_revoked\n    end\n\n    it \"does not revoke the refresh token when the requesting client is a different public client\" do\n      other_public_client = FactoryBot.create(:application, confidential: false)\n\n      post revocation_token_endpoint_url,\n           params: { client_id: other_public_client.uid, token: access_token.refresh_token, token_type_hint: \"refresh_token\" }\n\n      expect(response).to be_forbidden\n      expect(response.body).to include(\"unauthorized_client\")\n      expect(access_token.reload).not_to be_revoked\n    end\n  end\n\n  context \"without client authentication, when skip_client_authentication_for_password_grant is false (the default)\" do\n    before do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        skip_client_authentication_for_password_grant false\n      end\n    end\n\n    let(:access_token) do\n      FactoryBot.create(\n        :access_token,\n        application: nil,\n        resource_owner_id: resource_owner.id,\n        resource_owner_type: resource_owner.class.name,\n        use_refresh_token: true,\n      )\n    end\n\n    it \"does not remove the token and responses with an error\" do\n      post revocation_token_endpoint_url,\n           params: { token: access_token.token },\n           headers: headers\n\n      expect(response).not_to be_successful\n      expect(access_token.reload).not_to be_revoked\n    end\n  end\n\n  context \"without client authentication, when skip_client_authentication_for_password_grant is true\" do\n    before do\n      Doorkeeper.configure do\n        orm DOORKEEPER_ORM\n        skip_client_authentication_for_password_grant true\n      end\n    end\n\n    let(:access_token) do\n      FactoryBot.create(\n        :access_token,\n        application: nil,\n        resource_owner_id: resource_owner.id,\n        resource_owner_type: resource_owner.class.name,\n        use_refresh_token: true,\n      )\n    end\n\n    it \"revokes the access token provided\" do\n      post revocation_token_endpoint_url,\n           params: { client_id: public_client_application.uid, token: access_token.token },\n           headers: headers\n\n      expect(response).to be_successful\n      expect(access_token.reload).to be_revoked\n    end\n  end\nend\n"
  },
  {
    "path": "spec/requests/flows/skip_authorization_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nfeature \"Skip authorization form\" do\n  background do\n    config_is_set(:authenticate_resource_owner) { User.first || redirect_to(\"/sign_in\") }\n    client_exists\n    default_scopes_exist  :public\n    optional_scopes_exist :write\n  end\n\n  context \"with previously authorized clients\" do\n    background do\n      create_resource_owner\n      sign_in\n    end\n\n    scenario \"skips the authorization and return a new grant code\" do\n      client_is_authorized(@client, @resource_owner, scopes: \"public\")\n      visit authorization_endpoint_url(client: @client, scope: \"public\")\n\n      i_should_not_see \"Authorize\"\n      client_should_be_authorized @client\n      i_should_be_on_client_callback @client\n      url_should_have_param \"code\", Doorkeeper::AccessGrant.first.token\n    end\n\n    scenario \"skips the authorization if other scopes are not requested\" do\n      client_exists scopes: \"public read write\"\n      client_is_authorized(@client, @resource_owner, scopes: \"public\")\n      visit authorization_endpoint_url(client: @client, scope: \"public\")\n\n      i_should_not_see \"Authorize\"\n      client_should_be_authorized @client\n      i_should_be_on_client_callback @client\n      url_should_have_param \"code\", Doorkeeper::AccessGrant.first.token\n    end\n\n    scenario \"does not skip authorization when scopes differ (new request has fewer scopes)\" do\n      client_is_authorized(@client, @resource_owner, scopes: \"public write\")\n      visit authorization_endpoint_url(client: @client, scope: \"public\")\n      i_should_see \"Authorize\"\n    end\n\n    scenario \"does not skip authorization when scopes differ (new request has more scopes)\" do\n      client_is_authorized(@client, @resource_owner, scopes: \"public write\")\n      visit authorization_endpoint_url(client: @client, scopes: \"public write email\")\n      i_should_see \"Authorize\"\n    end\n\n    scenario \"creates grant with new scope when scopes differ\" do\n      client_is_authorized(@client, @resource_owner, scopes: \"public write\")\n      visit authorization_endpoint_url(client: @client, scope: \"public\")\n      click_on \"Authorize\"\n      access_grant_should_have_scopes :public\n    end\n\n    scenario \"creates grant with new scope when scopes are greater\" do\n      client_is_authorized(@client, @resource_owner, scopes: \"public\")\n      visit authorization_endpoint_url(client: @client, scope: \"public write\")\n      click_on \"Authorize\"\n      access_grant_should_have_scopes :public, :write\n    end\n  end\nend\n"
  },
  {
    "path": "spec/requests/protected_resources/metal_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe \"ActionController::Metal API\" do\n  before do\n    @client   = FactoryBot.create(:application)\n    @resource = User.create!(name: \"Joe\", password: \"sekret\")\n    @token    = client_is_authorized(@client, @resource)\n  end\n\n  it \"client requests protected resource with valid token\" do\n    get \"/metal.json?access_token=#{@token.token}\"\n    expect(json_response).to include(\"ok\" => true)\n  end\nend\n"
  },
  {
    "path": "spec/requests/protected_resources/private_api_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nfeature \"Private API\" do\n  background do\n    @client   = FactoryBot.create(:application)\n    @resource = User.create!(name: \"Joe\", password: \"sekret\")\n    @token    = client_is_authorized(@client, @resource)\n  end\n\n  scenario \"client requests protected resource with valid token\" do\n    with_access_token_header @token.token\n    visit \"/full_protected_resources\"\n    expect(page.body).to have_content(\"index\")\n  end\n\n  scenario \"client requests protected resource with disabled header authentication\" do\n    config_is_set :access_token_methods, [:from_access_token_param]\n    with_access_token_header @token.token\n    visit \"/full_protected_resources\"\n    response_status_should_be 401\n  end\n\n  scenario \"client attempts to request protected resource with invalid token\" do\n    with_access_token_header \"invalid\"\n    visit \"/full_protected_resources\"\n    response_status_should_be 401\n  end\n\n  scenario \"client attempts to request protected resource with expired token\" do\n    @token.update_attribute :expires_in, -100 # expires token\n    with_access_token_header @token.token\n    visit \"/full_protected_resources\"\n    response_status_should_be 401\n  end\n\n  scenario \"client requests protected resource with permanent token\" do\n    @token.update_attribute :expires_in, nil # never expires\n    with_access_token_header @token.token\n    visit \"/full_protected_resources\"\n    expect(page.body).to have_content(\"index\")\n  end\n\n  scenario \"access token with no default scopes\" do\n    Doorkeeper.configuration.instance_eval do\n      @default_scopes = Doorkeeper::OAuth::Scopes.from_array([:public])\n      @scopes = default_scopes + optional_scopes\n    end\n    @token.update_attribute :scopes, \"dummy\"\n    with_access_token_header @token.token\n    visit \"/full_protected_resources\"\n    response_status_should_be 403\n  end\n\n  scenario \"access token with no allowed scopes\" do\n    @token.update_attribute :scopes, nil\n    with_access_token_header @token.token\n    visit \"/full_protected_resources/1.json\"\n    response_status_should_be 403\n  end\n\n  scenario \"access token with one of allowed scopes\" do\n    @token.update_attribute :scopes, \"admin\"\n    with_access_token_header @token.token\n    visit \"/full_protected_resources/1.json\"\n    expect(page.body).to have_content(\"show\")\n  end\n\n  scenario \"access token with another of allowed scopes\" do\n    @token.update_attribute :scopes, \"write\"\n    with_access_token_header @token.token\n    visit \"/full_protected_resources/1.json\"\n    expect(page.body).to have_content(\"show\")\n  end\n\n  scenario \"access token with both allowed scopes\" do\n    @token.update_attribute :scopes, \"write admin\"\n    with_access_token_header @token.token\n    visit \"/full_protected_resources/1.json\"\n    expect(page.body).to have_content(\"show\")\n  end\nend\n"
  },
  {
    "path": "spec/routing/custom_controller_routes_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe \"Custom controller for routes\" do\n  before :all do\n    Doorkeeper.configure do\n      orm DOORKEEPER_ORM\n    end\n\n    Rails.application.routes.disable_clear_and_finalize = true\n\n    Rails.application.routes.draw do\n      scope \"inner_space\" do\n        use_doorkeeper scope: \"scope\" do\n          controllers authorizations: \"custom_authorizations\",\n                      tokens: \"custom_authorizations\",\n                      applications: \"custom_authorizations\",\n                      token_info: \"custom_authorizations\"\n\n          as authorizations: \"custom_auth\",\n             tokens: \"custom_token\",\n             token_info: \"custom_token_info\"\n        end\n      end\n\n      scope \"space\" do\n        use_doorkeeper do\n          controllers authorizations: \"custom_authorizations\",\n                      tokens: \"custom_authorizations\",\n                      applications: \"custom_authorizations\",\n                      token_info: \"custom_authorizations\"\n\n          as authorizations: \"custom_auth\",\n             tokens: \"custom_token\",\n             token_info: \"custom_token_info\"\n        end\n      end\n\n      scope \"outer_space\" do\n        use_doorkeeper do\n          controllers authorizations: \"custom_authorizations\",\n                      tokens: \"custom_authorizations\",\n                      token_info: \"custom_authorizations\"\n\n          as authorizations: \"custom_auth\",\n             tokens: \"custom_token\",\n             token_info: \"custom_token_info\"\n\n          skip_controllers :tokens, :applications, :token_info\n        end\n      end\n    end\n  end\n\n  after :all do\n    Rails.application.routes.clear!\n\n    load File.expand_path(\"../dummy/config/routes.rb\", __dir__)\n  end\n\n  it \"GET /inner_space/scope/authorize routes to custom authorizations controller\" do\n    expect(get(\"/inner_space/scope/authorize\")).to route_to(\"custom_authorizations#new\")\n  end\n\n  it \"POST /inner_space/scope/authorize routes to custom authorizations controller\" do\n    expect(post(\"/inner_space/scope/authorize\")).to route_to(\"custom_authorizations#create\")\n  end\n\n  it \"DELETE /inner_space/scope/authorize routes to custom authorizations controller\" do\n    expect(delete(\"/inner_space/scope/authorize\")).to route_to(\"custom_authorizations#destroy\")\n  end\n\n  it \"POST /inner_space/scope/token routes to tokens controller\" do\n    expect(post(\"/inner_space/scope/token\")).to route_to(\"custom_authorizations#create\")\n  end\n\n  it \"GET /inner_space/scope/applications routes to applications controller\" do\n    expect(get(\"/inner_space/scope/applications\")).to route_to(\"custom_authorizations#index\")\n  end\n\n  it \"GET /inner_space/scope/token/info routes to the token_info controller\" do\n    expect(get(\"/inner_space/scope/token/info\")).to route_to(\"custom_authorizations#show\")\n  end\n\n  it \"GET /space/oauth/authorize routes to custom authorizations controller\" do\n    expect(get(\"/space/oauth/authorize\")).to route_to(\"custom_authorizations#new\")\n  end\n\n  it \"POST /space/oauth/authorize routes to custom authorizations controller\" do\n    expect(post(\"/space/oauth/authorize\")).to route_to(\"custom_authorizations#create\")\n  end\n\n  it \"DELETE /space/oauth/authorize routes to custom authorizations controller\" do\n    expect(delete(\"/space/oauth/authorize\")).to route_to(\"custom_authorizations#destroy\")\n  end\n\n  it \"POST /space/oauth/token routes to tokens controller\" do\n    expect(post(\"/space/oauth/token\")).to route_to(\"custom_authorizations#create\")\n  end\n\n  it \"POST /space/oauth/revoke routes to tokens controller\" do\n    expect(post(\"/space/oauth/revoke\")).to route_to(\"custom_authorizations#revoke\")\n  end\n\n  it \"POST /space/oauth/introspect routes to tokens controller\" do\n    expect(post(\"/space/oauth/introspect\")).to route_to(\"custom_authorizations#introspect\")\n  end\n\n  it \"GET /space/oauth/applications routes to applications controller\" do\n    expect(get(\"/space/oauth/applications\")).to route_to(\"custom_authorizations#index\")\n  end\n\n  it \"GET /space/oauth/token/info routes to the token_info controller\" do\n    expect(get(\"/space/oauth/token/info\")).to route_to(\"custom_authorizations#show\")\n  end\n\n  it \"POST /outer_space/oauth/token is not be routable\" do\n    expect(post(\"/outer_space/oauth/token\")).not_to be_routable\n  end\n\n  it \"GET /outer_space/oauth/authorize routes to custom authorizations controller\" do\n    expect(get(\"/outer_space/oauth/authorize\")).to be_routable\n  end\n\n  it \"GET /outer_space/oauth/applications is not routable\" do\n    expect(get(\"/outer_space/oauth/applications\")).not_to be_routable\n  end\n\n  it \"GET /outer_space/oauth/token_info is not routable\" do\n    expect(get(\"/outer_space/oauth/token/info\")).not_to be_routable\n  end\nend\n"
  },
  {
    "path": "spec/routing/default_routes_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe \"Default routes\" do\n  it \"GET /oauth/authorize routes to authorizations controller\" do\n    expect(get(\"/oauth/authorize\")).to route_to(\"doorkeeper/authorizations#new\")\n  end\n\n  it \"POST /oauth/authorize routes to authorizations controller\" do\n    expect(post(\"/oauth/authorize\")).to route_to(\"doorkeeper/authorizations#create\")\n  end\n\n  it \"DELETE /oauth/authorize routes to authorizations controller\" do\n    expect(delete(\"/oauth/authorize\")).to route_to(\"doorkeeper/authorizations#destroy\")\n  end\n\n  it \"POST /oauth/token routes to tokens controller\" do\n    expect(post(\"/oauth/token\")).to route_to(\"doorkeeper/tokens#create\")\n  end\n\n  it \"POST /oauth/revoke routes to tokens controller\" do\n    expect(post(\"/oauth/revoke\")).to route_to(\"doorkeeper/tokens#revoke\")\n  end\n\n  it \"POST /oauth/introspect routes to tokens controller\" do\n    expect(post(\"/oauth/introspect\")).to route_to(\"doorkeeper/tokens#introspect\")\n  end\n\n  it \"GET /oauth/applications routes to applications controller\" do\n    expect(get(\"/oauth/applications\")).to route_to(\"doorkeeper/applications#index\")\n  end\n\n  it \"GET /oauth/authorized_applications routes to authorized applications controller\" do\n    expect(get(\"/oauth/authorized_applications\")).to route_to(\"doorkeeper/authorized_applications#index\")\n  end\n\n  it \"GET /oauth/token/info route to authorized TokenInfo controller\" do\n    expect(get(\"/oauth/token/info\")).to route_to(\"doorkeeper/token_info#show\")\n  end\nend\n"
  },
  {
    "path": "spec/routing/scoped_routes_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe \"Scoped routes\" do\n  before :all do\n    Doorkeeper.configure do\n      orm DOORKEEPER_ORM\n      allow_token_introspection false\n    end\n\n    Rails.application.routes.disable_clear_and_finalize = true\n\n    Rails.application.routes.draw do\n      use_doorkeeper scope: \"scope\"\n    end\n  end\n\n  after :all do\n    Rails.application.routes.clear!\n\n    load File.expand_path(\"../dummy/config/routes.rb\", __dir__)\n  end\n\n  it \"GET /scope/authorize routes to authorizations controller\" do\n    expect(get(\"/scope/authorize\")).to route_to(\"doorkeeper/authorizations#new\")\n  end\n\n  it \"POST /scope/authorize routes to authorizations controller\" do\n    expect(post(\"/scope/authorize\")).to route_to(\"doorkeeper/authorizations#create\")\n  end\n\n  it \"DELETE /scope/authorize routes to authorizations controller\" do\n    expect(delete(\"/scope/authorize\")).to route_to(\"doorkeeper/authorizations#destroy\")\n  end\n\n  it \"POST /scope/token routes to tokens controller\" do\n    expect(post(\"/scope/token\")).to route_to(\"doorkeeper/tokens#create\")\n  end\n\n  it \"GET /scope/applications routes to applications controller\" do\n    expect(get(\"/scope/applications\")).to route_to(\"doorkeeper/applications#index\")\n  end\n\n  it \"GET /scope/authorized_applications routes to authorized applications controller\" do\n    expect(get(\"/scope/authorized_applications\")).to route_to(\"doorkeeper/authorized_applications#index\")\n  end\n\n  it \"GET /scope/token/info route to authorized TokenInfo controller\" do\n    expect(get(\"/scope/token/info\")).to route_to(\"doorkeeper/token_info#show\")\n  end\n\n  it \"POST /scope/introspect routes not to exist\" do\n    expect(post(\"/scope/introspect\")).not_to be_routable\n  end\nend\n"
  },
  {
    "path": "spec/spec_helper.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"coveralls\"\n\nCoveralls.wear!(\"rails\") do\n  add_filter(\"/spec/\")\n  add_filter(\"/lib/generators/doorkeeper/templates/\")\nend\n\nENV[\"RAILS_ENV\"] ||= \"test\"\n\n$LOAD_PATH.unshift File.dirname(__FILE__)\n\nrequire \"#{File.dirname(__FILE__)}/support/doorkeeper_rspec.rb\"\n\nDOORKEEPER_ORM = Doorkeeper::RSpec.detect_orm\n\nrequire \"dummy/config/environment\"\nrequire \"rspec/rails\"\nrequire \"capybara/rspec\"\nrequire \"database_cleaner\"\nrequire \"generator_spec/test_case\"\n\n# Load JRuby SQLite3 if in that platform\nif defined? JRUBY_VERSION\n  require \"jdbc/sqlite3\"\n  Jdbc::SQLite3.load_driver\nend\n\nDoorkeeper::RSpec.print_configuration_info\n\nrequire \"support/orm/#{DOORKEEPER_ORM}\"\nrequire \"support/render_with_matcher\"\n\nDir[\"#{File.dirname(__FILE__)}/support/{dependencies,helpers,shared}/*.rb\"].sort.each { |file| require file }\n\nRSpec.configure do |config|\n  config.infer_spec_type_from_file_location!\n  config.mock_with :rspec\n\n  config.infer_base_class_for_anonymous_controllers = false\n\n  config.include RSpec::Rails::RequestExampleGroup, type: :request\n\n  config.before do\n    begin\n      DatabaseCleaner.start\n    rescue NameError\n      # ActiveRecord might not be defined in some tests\n    end\n    Doorkeeper.configure { orm DOORKEEPER_ORM }\n  end\n\n  config.after do\n    begin\n      DatabaseCleaner.clean\n    rescue NameError\n      # ActiveRecord might not be defined in some tests\n    end\n  end\n\n  config.order = \"random\"\nend\n"
  },
  {
    "path": "spec/spec_helper_integration.rb",
    "content": "# frozen_string_literal: true\n\n# For compatibility only\nrequire \"spec_helper\"\n"
  },
  {
    "path": "spec/support/dependencies/factory_bot.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"factory_bot\"\nFactoryBot.find_definitions\n"
  },
  {
    "path": "spec/support/doorkeeper_rspec.rb",
    "content": "# frozen_string_literal: true\n\nmodule Doorkeeper\n  class RSpec\n    # Print's useful information about env: Ruby / Rails versions,\n    # Doorkeeper configuration, etc.\n    def self.print_configuration_info\n      puts <<-INFO.strip_heredoc\n        ====> Doorkeeper ORM: '#{Doorkeeper.configuration.orm}'\n        ====> Doorkeeper version: #{Doorkeeper.gem_version}\n        ====> Rails version: #{::Rails.version}\n        ====> Ruby version: #{RUBY_VERSION} on #{RUBY_PLATFORM}\n      INFO\n    end\n\n    # Tries to find ORM from the Gemfile used to run test suite\n    def self.detect_orm\n      orm = (ENV[\"BUNDLE_GEMFILE\"] || \"\").match(/Gemfile\\.(.+)\\.rb/)\n      (orm && orm[1] || ENV[\"ORM\"] || :active_record).to_sym\n    end\n  end\nend\n"
  },
  {
    "path": "spec/support/helpers/access_token_request_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule AccessTokenRequestHelper\n  def client_is_authorized(client, resource_owner, access_token_attributes = {})\n    attributes = {\n      application: client,\n      resource_owner_id: resource_owner.id,\n      resource_owner_type: resource_owner.class.name,\n    }.merge(access_token_attributes)\n    FactoryBot.create(:access_token, attributes)\n  end\nend\n\nRSpec.configuration.send :include, AccessTokenRequestHelper\n"
  },
  {
    "path": "spec/support/helpers/authorization_request_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule AuthorizationRequestHelper\n  def resource_owner_is_authenticated(resource_owner = nil)\n    resource_owner ||= User.create!(name: \"Joe\", password: \"sekret\")\n    Doorkeeper.config.instance_variable_set(:@authenticate_resource_owner, proc { resource_owner })\n  end\n\n  def resource_owner_is_not_authenticated\n    Doorkeeper.config.instance_variable_set(:@authenticate_resource_owner, proc { redirect_to(\"/sign_in\") })\n  end\n\n  def default_scopes_exist(*scopes)\n    Doorkeeper.config.instance_variable_set(:@default_scopes, Doorkeeper::OAuth::Scopes.from_array(scopes))\n  end\n\n  def optional_scopes_exist(*scopes)\n    Doorkeeper.config.instance_variable_set(:@optional_scopes, Doorkeeper::OAuth::Scopes.from_array(scopes))\n  end\n\n  def client_should_be_authorized(client)\n    expect(client.access_grants.size).to eq(1)\n  end\n\n  def client_should_not_be_authorized(client)\n    expect(client.size).to eq(0)\n  end\n\n  def i_should_be_on_client_callback(client)\n    expect(client.redirect_uri).to eq(\"#{current_uri.scheme}://#{current_uri.host}#{current_uri.path}\")\n  end\n\n  def allowing_forgery_protection(&_block)\n    original_value = ActionController::Base.allow_forgery_protection\n    ActionController::Base.allow_forgery_protection = true\n\n    yield\n  ensure\n    ActionController::Base.allow_forgery_protection = original_value\n  end\nend\n\nRSpec.configuration.send :include, AuthorizationRequestHelper\n"
  },
  {
    "path": "spec/support/helpers/config_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule ConfigHelper\n  def config_is_set(setting, value = nil, &block)\n    setting_ivar = \"@#{setting}\"\n    value = block_given? ? block : value\n    Doorkeeper.config.instance_variable_set(setting_ivar, value)\n  end\nend\n\nRSpec.configuration.send :include, ConfigHelper\n"
  },
  {
    "path": "spec/support/helpers/model_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule ModelHelper\n  def client_exists(client_attributes = {})\n    @client = FactoryBot.create(:application, client_attributes)\n  end\n\n  def create_resource_owner\n    @resource_owner = User.create!(name: \"Joe\", password: \"sekret\")\n  end\n\n  def authorization_code_exists(options = {})\n    @authorization = FactoryBot.create(:access_grant, options)\n  end\n\n  def access_token_exists(options = {})\n    @access_token = FactoryBot.create(:access_token, options)\n  end\n\n  def access_grant_should_exist_for(client, resource_owner)\n    grant = Doorkeeper::AccessGrant.first\n\n    expect(grant.application).to have_attributes(id: client.id)\n      .and(be_instance_of(Doorkeeper::Application))\n\n    expect(grant.resource_owner_id).to eq(resource_owner.id)\n  end\n\n  def access_token_should_exist_for(client, resource_owner)\n    token = Doorkeeper::AccessToken.first\n\n    expect(token.application).to have_attributes(id: client.id)\n      .and(be_instance_of(Doorkeeper::Application))\n\n    expect(token.resource_owner_id).to eq(resource_owner.id)\n  end\n\n  def access_grant_should_not_exist\n    expect(Doorkeeper::AccessGrant.all).to be_empty\n  end\n\n  def access_token_should_not_exist\n    expect(Doorkeeper::AccessToken.all).to be_empty\n  end\n\n  def access_grant_should_have_scopes(*args)\n    grant = Doorkeeper::AccessGrant.first\n    expect(grant.scopes).to eq(Doorkeeper::OAuth::Scopes.from_array(args))\n  end\n\n  def access_token_should_have_scopes(*args)\n    grant = Doorkeeper::AccessToken.last\n    expect(grant.scopes).to eq(Doorkeeper::OAuth::Scopes.from_array(args))\n  end\n\n  def uniqueness_error\n    case DOORKEEPER_ORM\n    when :active_record\n      ActiveRecord::RecordNotUnique\n    when :sequel\n      error_classes = [Sequel::UniqueConstraintViolation, Sequel::ValidationFailed]\n      proc { |error| expect(error.class).to be_in(error_classes) }\n    when /mongoid/\n      error_classes = [Mongoid::Errors::Validations]\n      error_classes << Moped::Errors::OperationFailure if defined?(::Moped) # Mongoid 4\n      error_classes << Mongo::Error::OperationFailure if defined?(::Mongo) # Mongoid 5\n\n      proc { |error| expect(error.class).to be_in(error_classes) }\n    else\n      raise \"'#{DOORKEEPER_ORM}' ORM is not supported!\"\n    end\n  end\nend\n\nRSpec.configuration.send :include, ModelHelper\n"
  },
  {
    "path": "spec/support/helpers/request_spec_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule RequestSpecHelper\n  def i_am_logged_in\n    allow(Doorkeeper.configuration).to receive(:authenticate_admin).and_return(->(*) {})\n  end\n\n  def i_should_see(content)\n    expect(page).to have_content(content)\n  end\n\n  def i_should_not_see(content)\n    expect(page).to have_no_content(content)\n  end\n\n  def i_should_be_on(path)\n    expect(page).to have_current_path(path, ignore_query: true)\n  end\n\n  def url_should_have_param(param, value)\n    expect(current_params[param]).to eq(value)\n  end\n\n  def url_should_not_have_param(param)\n    expect(current_params).not_to have_key(param)\n  end\n\n  def current_params\n    Rack::Utils.parse_query(current_uri.query)\n  end\n\n  def current_uri\n    URI.parse(page.current_url)\n  end\n\n  def request_response\n    respond_to?(:response) ? response : page.driver.response\n  end\n\n  def json_response\n    JSON.parse(request_response.body)\n  end\n\n  def should_have_status(status)\n    expect(page.driver.response.status).to eq(status)\n  end\n\n  def with_access_token_header(token)\n    with_header \"Authorization\", \"Bearer #{token}\"\n  end\n\n  def with_header(header, value)\n    page.driver.header(header, value)\n  end\n\n  def basic_auth_header_for_client(client)\n    ActionController::HttpAuthentication::Basic.encode_credentials client.uid, client.secret\n  end\n\n  def sign_in\n    visit \"/\"\n    click_on \"Sign in\"\n  end\n\n  def create_access_token(authorization_code, client, code_verifier = nil)\n    page.driver.post token_endpoint_url(code: authorization_code, client: client, code_verifier: code_verifier)\n  end\n\n  def i_should_see_translated_error_message(key)\n    i_should_see translated_error_message(key)\n  end\n\n  def i_should_not_see_translated_error_message(key)\n    i_should_not_see translated_error_message(key)\n  end\n\n  def translated_error_message(key)\n    I18n.translate(key, scope: %i[doorkeeper errors messages])\n  end\n\n  def i_should_see_translated_invalid_request_error_message(key, value)\n    i_should_see translated_invalid_request_error_message(key, value)\n  end\n\n  def translated_invalid_request_error_message(key, value)\n    I18n.translate key, scope: %i[doorkeeper errors messages invalid_request], value: value\n  end\n\n  def response_status_should_be(status)\n    expect(request_response.status.to_i).to eq(status)\n  end\nend\n\nRSpec.configuration.send :include, RequestSpecHelper\n"
  },
  {
    "path": "spec/support/helpers/url_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule UrlHelper\n  def token_endpoint_url(options = {})\n    parameters = {\n      code: options[:code],\n      client_id: options[:client_id] || options[:client].try(:uid),\n      client_secret: options[:client_secret] || options[:client].try(:secret),\n      redirect_uri: options[:redirect_uri] || options[:client].try(:redirect_uri),\n      grant_type: options[:grant_type] || \"authorization_code\",\n      code_verifier: options[:code_verifier],\n      code_challenge_method: options[:code_challenge_method],\n    }.reject { |_, v| v.blank? }\n    \"/oauth/token?#{build_query(parameters)}\"\n  end\n\n  def password_token_endpoint_url(options = {})\n    parameters = {\n      code: options[:code],\n      client_id: options[:client_id] || options[:client].try(:uid),\n      client_secret: options[:client_secret] || options[:client].try(:secret),\n      username: options[:resource_owner_username] || options[:resource_owner].try(:name),\n      password: options[:resource_owner_password] || options[:resource_owner].try(:password),\n      scope: options[:scope],\n      grant_type: \"password\",\n    }.reject { |_, v| v.blank? }\n    \"/oauth/token?#{build_query(parameters)}\"\n  end\n\n  def authorization_endpoint_url(options = {})\n    parameters = {\n      client_id: options[:client_id] || options[:client].try(:uid),\n      redirect_uri: options[:redirect_uri] || options[:client].try(:redirect_uri),\n      response_type: options[:response_type] || \"code\",\n      response_mode: options[:response_mode] || \"\",\n      scope: options[:scope],\n      state: options[:state],\n      code_challenge: options[:code_challenge],\n      code_challenge_method: options[:code_challenge_method],\n    }.reject { |_, v| v.blank? }\n    \"/oauth/authorize?#{build_query(parameters)}\"\n  end\n\n  def refresh_token_endpoint_url(options = {})\n    parameters = {\n      refresh_token: options[:refresh_token],\n      client_id: options[:client_id] || options[:client].try(:uid),\n      client_secret: options[:client_secret] || options[:client].try(:secret),\n      grant_type: options[:grant_type] || \"refresh_token\",\n    }.reject { |_, v| v.blank? }\n    \"/oauth/token?#{build_query(parameters)}\"\n  end\n\n  def revocation_token_endpoint_url\n    \"/oauth/revoke\"\n  end\n\n  def build_query(hash)\n    Rack::Utils.build_query(hash)\n  end\nend\n\nRSpec.configuration.send :include, UrlHelper\n"
  },
  {
    "path": "spec/support/orm/active_record.rb",
    "content": "# frozen_string_literal: true\n\n# load schema to in memory sqlite\nActiveRecord::Migration.verbose = false\nload Rails.root + \"db/schema.rb\"\n"
  },
  {
    "path": "spec/support/render_with_matcher.rb",
    "content": "# frozen_string_literal: true\n\n# Adds the `render_with` matcher.\n# Ex:\n#   expect(controller).to render_with(template: :show, locals: { alpha: \"beta\" })\n#\nmodule RenderWithMatcher\n  def self.included(base)\n    # Setup spying for our \"render_with\" matcher\n    base.before do\n      allow(controller).to receive(:render).and_wrap_original do |original, *args, **kwargs, &block|\n        original.call(*args, **kwargs, &block)\n      end\n    end\n  end\n\n  RSpec::Matchers.define :render_with do |expected|\n    match do |actual|\n      have_received(:render).with(expected).matches?(actual)\n    end\n  end\nend\n\nRSpec.configure do |config|\n  config.include RenderWithMatcher, type: :controller\nend\n"
  },
  {
    "path": "spec/support/shared/controllers_shared_context.rb",
    "content": "# frozen_string_literal: true\n\nshared_context \"valid token\", token: :valid do\n  let(:token_string) { \"1A2B3C4D\" }\n\n  let :token do\n    double(\n      Doorkeeper::AccessToken,\n      accessible?: true, includes_scope?: true, acceptable?: true,\n      previous_refresh_token: \"\", revoke_previous_refresh_token!: true,\n    )\n  end\n\n  before do\n    allow(\n      Doorkeeper::AccessToken,\n    ).to receive(:by_token).with(token_string).and_return(token)\n  end\nend\n\nshared_context \"invalid token\", token: :invalid do\n  let(:token_string) { \"1A2B3C4D\" }\n\n  let :token do\n    double(\n      Doorkeeper::AccessToken,\n      accessible?: false, revoked?: false, expired?: false,\n      includes_scope?: false, acceptable?: false,\n      previous_refresh_token: \"\", revoke_previous_refresh_token!: true,\n    )\n  end\n\n  before do\n    allow(\n      Doorkeeper::AccessToken,\n    ).to receive(:by_token).with(token_string).and_return(token)\n  end\nend\n\nshared_context \"expired token\", token: :expired do\n  let :token_string do\n    \"1A2B3C4DEXP\"\n  end\n\n  let :token do\n    double(\n      Doorkeeper::AccessToken,\n      accessible?: false, revoked?: false, expired?: true,\n      includes_scope?: false, acceptable?: false,\n      previous_refresh_token: \"\", revoke_previous_refresh_token!: true,\n    )\n  end\n\n  before do\n    allow(\n      Doorkeeper::AccessToken,\n    ).to receive(:by_token).with(token_string).and_return(token)\n  end\nend\n\nshared_context \"revoked token\", token: :revoked do\n  let :token_string do\n    \"1A2B3C4DREV\"\n  end\n\n  let :token do\n    double(\n      Doorkeeper::AccessToken,\n      accessible?: false, revoked?: true, expired?: false,\n      includes_scope?: false, acceptable?: false,\n      previous_refresh_token: \"\", revoke_previous_refresh_token!: true,\n    )\n  end\n\n  before do\n    allow(\n      Doorkeeper::AccessToken,\n    ).to receive(:by_token).with(token_string).and_return(token)\n  end\nend\n\nshared_context \"forbidden token\", token: :forbidden do\n  let :token_string do\n    \"1A2B3C4DFORB\"\n  end\n\n  let :token do\n    double(\n      Doorkeeper::AccessToken,\n      accessible?: true, includes_scope?: true, acceptable?: false,\n      previous_refresh_token: \"\", revoke_previous_refresh_token!: true,\n    )\n  end\n\n  before do\n    allow(\n      Doorkeeper::AccessToken,\n    ).to receive(:by_token).with(token_string).and_return(token)\n  end\nend\n"
  },
  {
    "path": "spec/support/shared/hashing_shared_context.rb",
    "content": "# frozen_string_literal: true\n\nshared_context \"with token hashing enabled\" do\n  let(:hashed_or_plain_token_func) do\n    Doorkeeper::SecretStoring::Sha256Hash.method(:transform_secret)\n  end\n\n  before do\n    Doorkeeper.configure do\n      orm DOORKEEPER_ORM\n      hash_token_secrets\n    end\n  end\nend\n\nshared_context \"with token hashing and fallback lookup enabled\" do\n  let(:hashed_or_plain_token_func) do\n    Doorkeeper::SecretStoring::Sha256Hash.method(:transform_secret)\n  end\n\n  before do\n    Doorkeeper.configure do\n      orm DOORKEEPER_ORM\n      hash_token_secrets fallback: :plain\n    end\n  end\nend\n\nshared_context \"with application hashing enabled\" do\n  let(:hashed_or_plain_token_func) do\n    Doorkeeper::SecretStoring::Sha256Hash.method(:transform_secret)\n  end\n\n  before do\n    Doorkeeper.configure do\n      orm DOORKEEPER_ORM\n      hash_application_secrets\n    end\n  end\nend\n"
  },
  {
    "path": "spec/support/shared/models_shared_examples.rb",
    "content": "# frozen_string_literal: true\n\nshared_examples \"an accessible token\" do\n  describe \"#accessible?\" do\n    it \"is accessible if token is not expired\" do\n      allow(subject).to receive(:expired?).and_return(false)\n      expect(subject).to be_accessible\n    end\n\n    it \"is not accessible if token is expired\" do\n      allow(subject).to receive(:expired?).and_return(true)\n      expect(subject).not_to be_accessible\n    end\n  end\nend\n\nshared_examples \"a revocable token\" do\n  describe \"#accessible?\" do\n    before { subject.save! }\n\n    it \"is accessible if token is not revoked\" do\n      expect(subject).to be_accessible\n    end\n\n    it \"is not accessible if token is revoked\" do\n      subject.revoke\n      expect(subject).not_to be_accessible\n    end\n  end\nend\n\nshared_examples \"a unique token\" do\n  describe \"#token\" do\n    let(:owner) { FactoryBot.create(:resource_owner) }\n\n    it \"is generated before validation\" do\n      expect { subject.valid? }.to change(subject, :token).from(nil)\n    end\n\n    it \"is not valid if token exists\" do\n      token1 = FactoryBot.create factory_name, resource_owner_id: owner.id, resource_owner_type: owner.class.name\n      token2 = FactoryBot.create factory_name, resource_owner_id: owner.id, resource_owner_type: owner.class.name\n      token2.token = token1.token\n      expect(token2).not_to be_valid\n    end\n\n    it \"expects database to throw an error when tokens are the same\" do\n      token1 = FactoryBot.create factory_name, resource_owner_id: owner.id, resource_owner_type: owner.class.name\n      token2 = FactoryBot.create factory_name, resource_owner_id: owner.id, resource_owner_type: owner.class.name\n      token2.token = token1.token\n      expect do\n        token2.save!(validate: false)\n      end.to raise_error(uniqueness_error)\n    end\n  end\nend\n"
  }
]