├── .devcontainer └── devcontainer.json ├── .envrc ├── .github ├── FUNDING.yml ├── dependabot.yml ├── disabled-workflows │ └── danger.yml └── workflows │ ├── ancient.yml │ ├── caboose.yml │ ├── codeql-analysis.yml │ ├── coverage.yml │ ├── current-runtime-heads.yml │ ├── current.yml │ ├── dependency-review.yml │ ├── heads.yml │ ├── jruby.yml │ ├── legacy.yml │ ├── macos.yml │ ├── style.yml │ ├── supported.yml │ ├── truffle.yml │ ├── unsupported.yml │ └── windows.yml ├── .gitignore ├── .gitlab-ci.yml ├── .idea ├── .gitignore ├── GitLink.xml ├── GitlabSettingsPlugin.xml ├── active-tab-highlighter.xml ├── codestream.xml ├── dbnavigator.xml ├── git_toolbox_blame.xml ├── git_toolbox_prj.xml ├── kubernetes-settings.xml ├── misc.xml ├── modules.xml ├── oauth2.iml └── vcs.xml ├── .jrubyrc ├── .overcommit.yml ├── .qlty └── qlty.toml ├── .rspec ├── .rubocop.yml ├── .rubocop_gradual.lock ├── .rubocop_rspec.yml ├── .simplecov ├── .tool-versions ├── .yard_gfm_support.rb ├── .yardopts ├── Appraisal.root.gemfile ├── Appraisals ├── CHANGELOG.md ├── CITATION.cff ├── CNAME ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dangerfile ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── README.md ├── REEK ├── Rakefile ├── SECURITY.md ├── bin ├── appraisal ├── bundle ├── bundle-audit ├── bundler-audit ├── code_climate_reek ├── coderay ├── console ├── gem_checksums ├── github-markup ├── htmldiff ├── irb ├── ldiff ├── racc ├── rake ├── rdbg ├── rdoc ├── redcarpet ├── reek ├── ri ├── rspec ├── rubocop ├── rubocop-gradual ├── ruby-parse ├── ruby-rewrite ├── setup ├── standardrb ├── thor ├── yard ├── yard-junk ├── yardoc └── yri ├── certs └── pboling.pem ├── checksums ├── oauth2-2.0.10.gem.sha256 ├── oauth2-2.0.10.gem.sha512 ├── oauth2-2.0.11.gem.sha256 ├── oauth2-2.0.11.gem.sha512 ├── oauth2-2.0.12.gem.sha256 └── oauth2-2.0.12.gem.sha512 ├── config-ssl.json ├── docker-compose-ssl.yml ├── docs ├── .nojekyll ├── CNAME ├── OAuth2.html ├── OAuth2 │ ├── AccessToken.html │ ├── Authenticator.html │ ├── Client.html │ ├── Error.html │ ├── FilteredAttributes.html │ ├── FilteredAttributes │ │ └── ClassMethods.html │ ├── Response.html │ ├── Strategy.html │ ├── Strategy │ │ ├── Assertion.html │ │ ├── AuthCode.html │ │ ├── Base.html │ │ ├── ClientCredentials.html │ │ ├── Implicit.html │ │ └── Password.html │ └── Version.html ├── _index.html ├── class_list.html ├── css │ ├── common.css │ ├── full_list.css │ └── style.css ├── file.CHANGELOG.html ├── file.CITATION.html ├── file.CODE_OF_CONDUCT.html ├── file.CONTRIBUTING.html ├── file.LICENSE.html ├── file.README.html ├── file.SECURITY.html ├── file_list.html ├── frames.html ├── images │ └── logo │ │ ├── README.txt │ │ ├── galtzo-floss-logos-original.svg │ │ ├── galtzo-floss-logos-wordless.svg │ │ ├── oauth2-logo-124px.png │ │ └── ruby-logo-198px.svg ├── index.html ├── js │ ├── app.js │ ├── full_list.js │ └── jquery.js ├── method_list.html └── top-level-namespace.html ├── gemfiles ├── README.md ├── audit.gemfile ├── coverage.gemfile ├── current.gemfile ├── current_runtime_heads.gemfile ├── head.gemfile ├── modular │ ├── audit.gemfile │ ├── coverage.gemfile │ ├── documentation.gemfile │ ├── faraday_v0.gemfile │ ├── faraday_v1.gemfile │ ├── faraday_v2.gemfile │ ├── hashie_v0.gemfile │ ├── hashie_v1.gemfile │ ├── hashie_v2.gemfile │ ├── hashie_v3.gemfile │ ├── hashie_v4.gemfile │ ├── hashie_v5.gemfile │ ├── jwt_v1.gemfile │ ├── jwt_v2.gemfile │ ├── jwt_v3.gemfile │ ├── logger_v1_2.gemfile │ ├── logger_v1_5.gemfile │ ├── logger_v1_7.gemfile │ ├── multi_xml_v0_5.gemfile │ ├── multi_xml_v0_6.gemfile │ ├── multi_xml_v0_7.gemfile │ ├── rack_v1_2.gemfile │ ├── rack_v1_6.gemfile │ ├── rack_v2.gemfile │ ├── rack_v3.gemfile │ ├── runtime_heads.gemfile │ └── style.gemfile ├── omnibus.gemfile ├── ruby_2_3_hashie_v0.gemfile ├── ruby_2_3_hashie_v1.gemfile ├── ruby_2_3_hashie_v2.gemfile ├── ruby_2_3_hashie_v3.gemfile ├── ruby_2_3_hashie_v4.gemfile ├── ruby_2_3_hashie_v5.gemfile ├── ruby_2_4.gemfile ├── ruby_2_5.gemfile ├── ruby_2_6.gemfile ├── ruby_2_7.gemfile ├── ruby_3_0.gemfile ├── ruby_3_1.gemfile ├── ruby_3_2.gemfile ├── ruby_3_3.gemfile ├── style.gemfile └── vanilla.gemfile ├── lib ├── oauth2.rb └── oauth2 │ ├── access_token.rb │ ├── authenticator.rb │ ├── client.rb │ ├── error.rb │ ├── filtered_attributes.rb │ ├── response.rb │ ├── strategy │ ├── assertion.rb │ ├── auth_code.rb │ ├── base.rb │ ├── client_credentials.rb │ ├── implicit.rb │ └── password.rb │ └── version.rb ├── oauth2.gemspec └── spec ├── config ├── constants.rb ├── debug.rb ├── faraday.rb ├── multi_xml.rb └── rspec │ ├── rspec_core.rb │ └── silent_stream.rb ├── examples └── google_spec.rb ├── ext └── backports.rb ├── fixtures ├── README.md ├── RS256 │ ├── jwtRS256.key │ └── jwtRS256.key.pub └── google_service_account_key.p12 ├── oauth2 ├── access_token_spec.rb ├── authenticator_spec.rb ├── client_spec.rb ├── error_spec.rb ├── response_spec.rb ├── strategy │ ├── assertion_spec.rb │ ├── auth_code_spec.rb │ ├── base_spec.rb │ ├── client_credentials_spec.rb │ ├── implicit_spec.rb │ └── password_spec.rb └── version_spec.rb ├── oauth2_spec.rb └── spec_helper.rb /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/ruby 3 | { 4 | "name": "Ruby", 5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 6 | "image": "mcr.microsoft.com/devcontainers/ruby:1-3-bookworm", 7 | 8 | // Features to add to the dev container. More info: https://containers.dev/features. 9 | // "features": {}, 10 | 11 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 12 | // "forwardPorts": [], 13 | 14 | // Use 'postCreateCommand' to run commands after the container is created. 15 | // "postCreateCommand": "ruby --version", 16 | 17 | // Configure tool-specific properties. 18 | "customizations" : { 19 | "jetbrains" : { 20 | "backend" : "RubyMine" 21 | } 22 | }, 23 | 24 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 25 | // "remoteUser": "root" 26 | } 27 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | # Run any command in this library's bin/ without the bin/ prefix! 2 | PATH_add bin 3 | 4 | # Only add things to this file that should be shared with the team. 5 | 6 | # **dotenv** (See end of file for .env.local integration) 7 | # .env would override anything in this file, if enabled. 8 | # .env is a DOCKER standard, and if we use it, it would be in deployed, or DOCKER, environments. 9 | # Override and customize anything below in your own .env.local 10 | # If you are using dotenv and not direnv, 11 | # copy the following `export` statements to your own .env file. 12 | 13 | ### General Ruby ### 14 | # Turn off Ruby Warnings about deprecated code 15 | # export RUBYOPT="-W0" 16 | 17 | ### External Testing Controls 18 | export K_SOUP_COV_DO=true # Means you want code coverage 19 | # Available formats are html, xml, rcov, lcov, json, tty 20 | export K_SOUP_COV_COMMAND_NAME="RSpec Coverage" 21 | export K_SOUP_COV_FORMATTERS="html,tty" 22 | export K_SOUP_COV_MIN_BRANCH=100 # Means you want to enforce X% branch coverage 23 | export K_SOUP_COV_MIN_LINE=100 # Means you want to enforce X% line coverage 24 | export K_SOUP_COV_MIN_HARD=true # Means you want the build to fail if the coverage thresholds are not met 25 | export K_SOUP_COV_MULTI_FORMATTERS=true 26 | export K_SOUP_COV_OPEN_BIN= # Means don't try to open coverage results in browser 27 | export MAX_ROWS=1 # Setting for simplecov-console gem for tty output, limits to the worst N rows of bad coverage 28 | 29 | # Internal Debugging Controls 30 | export DEBUG=false # do not allow byebug statements (override in .env.local) 31 | 32 | # Concurrently developing the rubocop-lts suite? 33 | export RUBOCOP_LTS_LOCAL=false 34 | 35 | # .env would override anything in this file, if `dotenv` is uncommented below. 36 | # .env is a DOCKER standard, and if we use it, it would be in deployed, or DOCKER, environments, 37 | # and that is why we generally want to leave it commented out. 38 | # dotenv 39 | 40 | # .env.local will override anything in this file. 41 | dotenv_if_exists .env.local 42 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | buy_me_a_coffee: pboling 4 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 5 | github: [pboling] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 6 | issuehunt: pboling # Replace with a single IssueHunt username 7 | ko_fi: pboling # Replace with a single Ko-fi username 8 | liberapay: pboling # Replace with a single Liberapay username 9 | open_collective: # Replace with a single Open Collective username 10 | patreon: galtzo # Replace with a single Patreon username 11 | polar: pboling 12 | thanks_dev: u/gh/pboling 13 | tidelift: rubygems/oauth2 # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: bundler 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:28" 8 | open-pull-requests-limit: 10 9 | ignore: 10 | - dependency-name: "rubocop-lts" 11 | - package-ecosystem: "github-actions" 12 | directory: "/" 13 | schedule: 14 | interval: "daily" 15 | -------------------------------------------------------------------------------- /.github/disabled-workflows/danger.yml: -------------------------------------------------------------------------------- 1 | name: What's up Danger? 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - 'main' 7 | - '*-stable' 8 | 9 | jobs: 10 | danger: 11 | runs-on: ubuntu-latest 12 | env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps 13 | BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile 14 | if: false 15 | # if: github.event_name == 'pull_request' # if only run pull request when multiple trigger workflow 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | gemfile: 20 | - vanilla 21 | rubygems: 22 | - latest 23 | bundler: 24 | - latest 25 | ruby: 26 | - "ruby" 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v4 30 | - name: Setup Ruby & Bundle 31 | uses: ruby/setup-ruby@v1 32 | with: 33 | ruby-version: ${{ matrix.ruby }} 34 | rubygems: ${{ matrix.rubygems }} 35 | bundler: ${{ matrix.bundler }} 36 | bundler-cache: true 37 | - uses: MeilCli/danger-action@v6 38 | with: 39 | plugins_file: 'Gemfile' 40 | install_path: 'vendor/bundle' 41 | danger_file: 'Dangerfile' 42 | danger_id: 'danger-pr' 43 | env: 44 | DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }} 45 | -------------------------------------------------------------------------------- /.github/workflows/ancient.yml: -------------------------------------------------------------------------------- 1 | name: MRI 2.4, 2.5 (EOL) 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | push: 8 | branches: 9 | - 'main' 10 | - '*-stable' 11 | tags: 12 | - '!*' # Do not execute on tags 13 | pull_request: 14 | branches: 15 | - '*' 16 | # Allow manually triggering the workflow. 17 | workflow_dispatch: 18 | 19 | # Cancels all previous workflow runs for the same branch that have not yet completed. 20 | concurrency: 21 | # The concurrency group contains the workflow name and the branch name. 22 | group: "${{ github.workflow }}-${{ github.ref }}" 23 | cancel-in-progress: true 24 | 25 | jobs: 26 | test: 27 | name: Specs ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} 28 | if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" 29 | runs-on: ubuntu-22.04 30 | continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} 31 | env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps 32 | BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile 33 | strategy: 34 | fail-fast: false 35 | matrix: 36 | include: 37 | # Ruby 2.4 38 | - ruby: "ruby-2.4" 39 | appraisal: "ruby-2-4" 40 | exec_cmd: "rake test" 41 | gemfile: "Appraisal.root" 42 | rubygems: "3.3.27" 43 | bundler: "2.3.27" 44 | 45 | # Ruby 2.5 46 | - ruby: "ruby-2.5" 47 | appraisal: "ruby-2-5" 48 | exec_cmd: "rake test" 49 | gemfile: "Appraisal.root" 50 | rubygems: "3.3.27" 51 | bundler: "2.3.27" 52 | 53 | steps: 54 | - name: Checkout 55 | uses: actions/checkout@v4 56 | 57 | - name: Setup Ruby & RubyGems 58 | uses: ruby/setup-ruby@v1 59 | with: 60 | ruby-version: ${{ matrix.ruby }} 61 | rubygems: ${{ matrix.rubygems }} 62 | bundler: ${{ matrix.bundler }} 63 | bundler-cache: false 64 | 65 | # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) 66 | # We need to do this first to get appraisal installed. 67 | # NOTE: This does not use the primary Gemfile at all. 68 | - name: Install Root Appraisal 69 | run: bundle 70 | - name: Appraisal for ${{ matrix.appraisal }} 71 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle 72 | - name: Tests for ${{ matrix.ruby }} via ${{ matrix.exec_cmd }} 73 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} 74 | -------------------------------------------------------------------------------- /.github/workflows/caboose.yml: -------------------------------------------------------------------------------- 1 | # THE CABOOSE IS AN ABSOLUTE WAGON 2 | name: MRI 2.3 X Hashie WAGON (EOL) 3 | 4 | permissions: 5 | contents: read 6 | 7 | on: 8 | push: 9 | branches: 10 | - 'main' 11 | - '*-stable' 12 | tags: 13 | - '!*' # Do not execute on tags 14 | pull_request: 15 | branches: 16 | - '*' 17 | # Allow manually triggering the workflow. 18 | workflow_dispatch: 19 | 20 | # Cancels all previous workflow runs for the same branch that have not yet completed. 21 | concurrency: 22 | # The concurrency group contains the workflow name and the branch name. 23 | group: "${{ github.workflow }}-${{ github.ref }}" 24 | cancel-in-progress: true 25 | 26 | jobs: 27 | test: 28 | name: Specs ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} 29 | if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" 30 | runs-on: ubuntu-22.04 31 | continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} 32 | env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps 33 | BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile 34 | strategy: 35 | fail-fast: false 36 | matrix: 37 | include: 38 | # Ruby 2.3 39 | - ruby: "ruby-2.3" 40 | appraisal: "ruby-2-3-hashie_v0" 41 | exec_cmd: "rake test" 42 | gemfile: "Appraisal.root" 43 | rubygems: "3.3.27" 44 | bundler: "2.3.27" 45 | 46 | # Ruby 2.3 47 | - ruby: "ruby-2.3" 48 | appraisal: "ruby-2-3-hashie_v1" 49 | exec_cmd: "rake test" 50 | gemfile: "Appraisal.root" 51 | rubygems: "3.3.27" 52 | bundler: "2.3.27" 53 | 54 | # Ruby 2.3 55 | - ruby: "ruby-2.3" 56 | appraisal: "ruby-2-3-hashie_v2" 57 | exec_cmd: "rake test" 58 | gemfile: "Appraisal.root" 59 | rubygems: "3.3.27" 60 | bundler: "2.3.27" 61 | 62 | # Ruby 2.3 63 | - ruby: "ruby-2.3" 64 | appraisal: "ruby-2-3-hashie_v3" 65 | exec_cmd: "rake test" 66 | gemfile: "Appraisal.root" 67 | rubygems: "3.3.27" 68 | bundler: "2.3.27" 69 | 70 | # Ruby 2.3 71 | - ruby: "ruby-2.3" 72 | appraisal: "ruby-2-3-hashie_v4" 73 | exec_cmd: "rake test" 74 | gemfile: "Appraisal.root" 75 | rubygems: "3.3.27" 76 | bundler: "2.3.27" 77 | 78 | # Ruby 2.3 79 | - ruby: "ruby-2.3" 80 | appraisal: "ruby-2-3-hashie_v5" 81 | exec_cmd: "rake test" 82 | gemfile: "Appraisal.root" 83 | rubygems: "3.3.27" 84 | bundler: "2.3.27" 85 | 86 | steps: 87 | - name: Checkout 88 | uses: actions/checkout@v4 89 | 90 | - name: Setup Ruby & RubyGems 91 | uses: ruby/setup-ruby@v1 92 | with: 93 | ruby-version: ${{ matrix.ruby }} 94 | rubygems: ${{ matrix.rubygems }} 95 | bundler: ${{ matrix.bundler }} 96 | bundler-cache: false 97 | 98 | # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) 99 | # We need to do this first to get appraisal installed. 100 | # NOTE: This does not use the primary Gemfile at all. 101 | - name: Install Root Appraisal 102 | run: bundle > /dev/null 2>&1 103 | - name: Appraisal for ${{ matrix.appraisal }} 104 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle > /dev/null 2>&1 105 | - name: Tests for ${{ matrix.ruby }} via ${{ matrix.exec_cmd }} 106 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} 107 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main, "*-stable" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main, "*-stable" ] 20 | schedule: 21 | - cron: '35 1 * * 5' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'ruby' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v4 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v3 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 54 | # If this step fails, then you should remove it and run the build manually (see below) 55 | - name: Autobuild 56 | uses: github/codeql-action/autobuild@v3 57 | 58 | # ℹ️ Command-line programs to run using the OS shell. 59 | # 📚 https://git.io/JvXDl 60 | 61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 62 | # and modify them (or add more) to build your code if your project 63 | # uses a compiled language 64 | 65 | #- run: | 66 | # make bootstrap 67 | # make release 68 | 69 | - name: Perform CodeQL Analysis 70 | uses: github/codeql-action/analyze@v3 71 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: Test Coverage 2 | 3 | permissions: 4 | contents: read 5 | pull-requests: write 6 | 7 | env: 8 | K_SOUP_COV_MIN_BRANCH: 100 9 | K_SOUP_COV_MIN_LINE: 100 10 | K_SOUP_COV_MIN_HARD: true 11 | K_SOUP_COV_FORMATTERS: "html,xml,rcov,lcov,json,tty" 12 | K_SOUP_COV_DO: true 13 | K_SOUP_COV_MULTI_FORMATTERS: true 14 | K_SOUP_COV_COMMAND_NAME: "RSpec Coverage" 15 | 16 | on: 17 | push: 18 | branches: 19 | - 'main' 20 | - '*-stable' 21 | tags: 22 | - '!*' # Do not execute on tags 23 | pull_request: 24 | branches: 25 | - '*' 26 | # Allow manually triggering the workflow. 27 | workflow_dispatch: 28 | 29 | # Cancels all previous workflow runs for the same branch that have not yet completed. 30 | concurrency: 31 | # The concurrency group contains the workflow name and the branch name. 32 | group: "${{ github.workflow }}-${{ github.ref }}" 33 | cancel-in-progress: true 34 | 35 | jobs: 36 | coverage: 37 | name: Code Coverage on ${{ matrix.ruby }}@current 38 | if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" 39 | runs-on: ubuntu-latest 40 | continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} 41 | env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps 42 | BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile 43 | strategy: 44 | fail-fast: false 45 | matrix: 46 | include: 47 | # Coverage 48 | - ruby: "ruby" 49 | appraisal: "coverage" 50 | exec_cmd: "rake test" 51 | gemfile: "Appraisal.root" 52 | rubygems: latest 53 | bundler: latest 54 | 55 | steps: 56 | - name: Checkout 57 | uses: actions/checkout@v4 58 | 59 | - name: Setup Ruby & RubyGems 60 | uses: ruby/setup-ruby@v1 61 | with: 62 | ruby-version: "${{ matrix.ruby }}" 63 | rubygems: "${{ matrix.rubygems }}" 64 | bundler: "${{ matrix.bundler }}" 65 | bundler-cache: false 66 | 67 | # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) 68 | # We need to do this first to get appraisal installed. 69 | # NOTE: This does not use the primary Gemfile at all. 70 | - name: Install Root Appraisal 71 | run: bundle 72 | - name: Appraisal for ${{ matrix.appraisal }} 73 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle 74 | - name: Tests for ${{ matrix.ruby }}@current via ${{ matrix.exec_cmd }} 75 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} 76 | 77 | # Do SaaS coverage uploads first 78 | - name: Upload coverage to Coveralls 79 | uses: coverallsapp/github-action@master 80 | with: 81 | github-token: ${{ secrets.GITHUB_TOKEN }} 82 | continue-on-error: ${{ matrix.experimental != 'false' }} 83 | 84 | - name: Upload coverage to QLTY 85 | uses: qltysh/qlty-action/coverage@main 86 | with: 87 | token: ${{secrets.QLTY_COVERAGE_TOKEN}} 88 | files: coverage/.resultset.json 89 | continue-on-error: ${{ matrix.experimental != 'false' }} 90 | 91 | # Then PR comments 92 | - name: Code Coverage Summary Report 93 | uses: irongut/CodeCoverageSummary@v1.3.0 94 | if: ${{ github.event_name == 'pull_request' }} 95 | with: 96 | filename: ./coverage/coverage.xml 97 | badge: true 98 | fail_below_min: true 99 | format: markdown 100 | hide_branch_rate: false 101 | hide_complexity: true 102 | indicators: true 103 | output: both 104 | thresholds: '100 100' 105 | continue-on-error: ${{ matrix.experimental != 'false' }} 106 | 107 | - name: Add Coverage PR Comment 108 | uses: marocchino/sticky-pull-request-comment@v2 109 | if: ${{ github.event_name == 'pull_request' }} 110 | with: 111 | recreate: true 112 | path: code-coverage-results.md 113 | continue-on-error: ${{ matrix.experimental != 'false' }} 114 | -------------------------------------------------------------------------------- /.github/workflows/current-runtime-heads.yml: -------------------------------------------------------------------------------- 1 | # Targets the evergreen latest release of ruby, truffleruby, and jruby 2 | # and tests against the HEAD of runtime dependencies 3 | name: Runtime Deps @ HEAD 4 | 5 | permissions: 6 | contents: read 7 | 8 | env: 9 | K_SOUP_COV_DO: false 10 | 11 | on: 12 | push: 13 | branches: 14 | - 'main' 15 | - '*-stable' 16 | tags: 17 | - '!*' # Do not execute on tags 18 | pull_request: 19 | branches: 20 | - '*' 21 | # Allow manually triggering the workflow. 22 | workflow_dispatch: 23 | 24 | # Cancels all previous workflow runs for the same branch that have not yet completed. 25 | concurrency: 26 | # The concurrency group contains the workflow name and the branch name. 27 | group: "${{ github.workflow }}-${{ github.ref }}" 28 | cancel-in-progress: true 29 | 30 | jobs: 31 | test: 32 | name: Specs ${{ matrix.ruby }}@${{ matrix.appraisal }} 33 | if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" 34 | runs-on: ubuntu-latest 35 | continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} 36 | env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps 37 | BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile 38 | strategy: 39 | matrix: 40 | include: 41 | # Ruby 3.4 42 | - ruby: "ruby" 43 | appraisal: "current-runtime-heads" 44 | exec_cmd: "rake test" 45 | gemfile: "Appraisal.root" 46 | rubygems: latest 47 | bundler: latest 48 | 49 | # truffleruby-24.1 50 | # (according to documentation: targets Ruby 3.3 compatibility) 51 | # (according to runtime: targets Ruby 3.2 compatibility) 52 | - ruby: "truffleruby" 53 | appraisal: "current-runtime-heads" 54 | exec_cmd: "rake test" 55 | gemfile: "Appraisal.root" 56 | rubygems: default 57 | bundler: default 58 | 59 | # jruby-10.0 (targets Ruby 3.4 compatibility) 60 | - ruby: "jruby" 61 | appraisal: "current-runtime-heads" 62 | exec_cmd: "rake test" 63 | gemfile: "Appraisal.root" 64 | rubygems: default 65 | bundler: default 66 | 67 | steps: 68 | - name: Checkout 69 | uses: actions/checkout@v4 70 | 71 | - name: Setup Ruby & RubyGems 72 | uses: ruby/setup-ruby@v1 73 | with: 74 | ruby-version: ${{ matrix.ruby }} 75 | rubygems: ${{ matrix.rubygems }} 76 | bundler: ${{ matrix.bundler }} 77 | bundler-cache: false 78 | 79 | # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) 80 | # We need to do this first to get appraisal installed. 81 | # NOTE: This does not use the primary Gemfile at all. 82 | - name: Install Root Appraisal 83 | run: bundle 84 | - name: Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }} 85 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle 86 | - name: Tests for ${{ matrix.ruby }}@${{ matrix.appraisal }} via ${{ matrix.exec_cmd }} 87 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} 88 | -------------------------------------------------------------------------------- /.github/workflows/current.yml: -------------------------------------------------------------------------------- 1 | # Targets the evergreen latest release of ruby, truffleruby, and jruby 2 | name: Current 3 | 4 | permissions: 5 | contents: read 6 | 7 | env: 8 | K_SOUP_COV_DO: false 9 | 10 | on: 11 | push: 12 | branches: 13 | - 'main' 14 | - '*-stable' 15 | tags: 16 | - '!*' # Do not execute on tags 17 | pull_request: 18 | branches: 19 | - '*' 20 | # Allow manually triggering the workflow. 21 | workflow_dispatch: 22 | 23 | # Cancels all previous workflow runs for the same branch that have not yet completed. 24 | concurrency: 25 | # The concurrency group contains the workflow name and the branch name. 26 | group: "${{ github.workflow }}-${{ github.ref }}" 27 | cancel-in-progress: true 28 | 29 | jobs: 30 | test: 31 | name: Specs ${{ matrix.ruby }}@${{ matrix.appraisal }} 32 | if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" 33 | runs-on: ubuntu-latest 34 | continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} 35 | env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps 36 | BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile 37 | strategy: 38 | matrix: 39 | include: 40 | # Ruby 3.4 41 | - ruby: "ruby" 42 | appraisal: "current" 43 | exec_cmd: "rake test" 44 | gemfile: "Appraisal.root" 45 | rubygems: latest 46 | bundler: latest 47 | 48 | # truffleruby-24.1 49 | # (according to documentation: targets Ruby 3.3 compatibility) 50 | # (according to runtime: targets Ruby 3.2 compatibility) 51 | - ruby: "truffleruby" 52 | appraisal: "current" 53 | exec_cmd: "rake test" 54 | gemfile: "Appraisal.root" 55 | rubygems: default 56 | bundler: default 57 | 58 | # jruby-10.0 (targets Ruby 3.4 compatibility) 59 | - ruby: "jruby" 60 | appraisal: "current" 61 | exec_cmd: "rake test" 62 | gemfile: "Appraisal.root" 63 | rubygems: default 64 | bundler: default 65 | 66 | steps: 67 | - name: Checkout 68 | uses: actions/checkout@v4 69 | 70 | - name: Setup Ruby & RubyGems 71 | uses: ruby/setup-ruby@v1 72 | with: 73 | ruby-version: ${{ matrix.ruby }} 74 | rubygems: ${{ matrix.rubygems }} 75 | bundler: ${{ matrix.bundler }} 76 | bundler-cache: false 77 | 78 | # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) 79 | # We need to do this first to get appraisal installed. 80 | # NOTE: This does not use the primary Gemfile at all. 81 | - name: Install Root Appraisal 82 | run: bundle 83 | - name: Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }} 84 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle 85 | - name: Tests for ${{ matrix.ruby }}@${{ matrix.appraisal }} via ${{ matrix.exec_cmd }} 86 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} 87 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | # Dependency Review Action 2 | # 3 | # This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. 4 | # 5 | # Source repository: https://github.com/actions/dependency-review-action 6 | # Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement 7 | name: 'Dependency Review' 8 | on: [pull_request] 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | dependency-review: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: 'Checkout Repository' 18 | uses: actions/checkout@v4 19 | - name: 'Dependency Review' 20 | uses: actions/dependency-review-action@v4 21 | -------------------------------------------------------------------------------- /.github/workflows/heads.yml: -------------------------------------------------------------------------------- 1 | name: Heads 2 | 3 | permissions: 4 | contents: read 5 | 6 | env: 7 | K_SOUP_COV_DO: false 8 | 9 | on: 10 | push: 11 | branches: 12 | - 'main' 13 | - '*-stable' 14 | tags: 15 | - '!*' # Do not execute on tags 16 | pull_request: 17 | branches: 18 | - '*' 19 | # Allow manually triggering the workflow. 20 | workflow_dispatch: 21 | 22 | # Cancels all previous workflow runs for the same branch that have not yet completed. 23 | concurrency: 24 | # The concurrency group contains the workflow name and the branch name. 25 | group: "${{ github.workflow }}-${{ github.ref }}" 26 | cancel-in-progress: true 27 | 28 | jobs: 29 | test: 30 | name: Specs ${{ matrix.ruby }}@${{ matrix.appraisal }}${{ matrix.name_extra || '' }} 31 | if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" 32 | runs-on: ubuntu-latest 33 | continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} 34 | env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps 35 | BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile 36 | strategy: 37 | fail-fast: true 38 | matrix: 39 | include: 40 | # NOTE: Heads use default rubygems / bundler; their defaults are custom, unreleased, and from the future! 41 | # ruby-head 42 | - ruby: "ruby-head" 43 | appraisal: "head" 44 | exec_cmd: "rake test" 45 | gemfile: "Appraisal.root" 46 | rubygems: default 47 | bundler: default 48 | 49 | # truffleruby-head 50 | - ruby: "truffleruby-head" 51 | appraisal: "head" 52 | exec_cmd: "rake test" 53 | gemfile: "Appraisal.root" 54 | rubygems: default 55 | bundler: default 56 | 57 | # jruby-head 58 | - ruby: "jruby-head" 59 | appraisal: "head" 60 | exec_cmd: "rake test" 61 | gemfile: "Appraisal.root" 62 | rubygems: default 63 | bundler: default 64 | 65 | steps: 66 | - name: Checkout 67 | uses: actions/checkout@v4 68 | 69 | - name: Setup Ruby & RubyGems 70 | uses: ruby/setup-ruby@v1 71 | with: 72 | ruby-version: ${{ matrix.ruby }} 73 | rubygems: ${{ matrix.rubygems }} 74 | bundler: ${{ matrix.bundler }} 75 | bundler-cache: false 76 | 77 | # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) 78 | # We need to do this first to get appraisal installed. 79 | # NOTE: This does not use the primary Gemfile at all. 80 | - name: "Install Root Appraisal" 81 | run: bundle 82 | 83 | - name: "[Attempt 1] Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }}" 84 | id: bundleAttempt1 85 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle 86 | # Continue to the next step on failure 87 | continue-on-error: true 88 | 89 | # Effectively an automatic retry of the previous step. 90 | - name: "[Attempt 2] Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }}" 91 | id: bundleAttempt2 92 | # If bundleAttempt1 failed, try again here; Otherwise skip. 93 | if: steps.bundleAttempt1.outcome == 'failure' 94 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle 95 | 96 | - name: Tests for ${{ matrix.ruby }}@${{ matrix.appraisal }} via ${{ matrix.exec_cmd }} 97 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} 98 | -------------------------------------------------------------------------------- /.github/workflows/jruby.yml: -------------------------------------------------------------------------------- 1 | name: JRuby 2 | 3 | permissions: 4 | contents: read 5 | 6 | env: 7 | K_SOUP_COV_DO: false 8 | 9 | on: 10 | push: 11 | branches: 12 | - 'main' 13 | - '*-stable' 14 | tags: 15 | - '!*' # Do not execute on tags 16 | pull_request: 17 | branches: 18 | - '*' 19 | # Allow manually triggering the workflow. 20 | workflow_dispatch: 21 | 22 | # Cancels all previous workflow runs for the same branch that have not yet completed. 23 | concurrency: 24 | # The concurrency group contains the workflow name and the branch name. 25 | group: "${{ github.workflow }}-${{ github.ref }}" 26 | cancel-in-progress: true 27 | 28 | jobs: 29 | test: 30 | name: Specs ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} 31 | if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" 32 | runs-on: ubuntu-22.04 33 | continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} 34 | env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps 35 | BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile 36 | strategy: 37 | matrix: 38 | include: 39 | # jruby-9.2 (targets Ruby 2.5 compatibility) 40 | - ruby: "jruby-9.2" 41 | appraisal: "ruby-2-5" 42 | exec_cmd: "rake test" 43 | gemfile: "Appraisal.root" 44 | rubygems: default 45 | bundler: default 46 | 47 | # jruby-9.3 (targets Ruby 2.6 compatibility) 48 | - ruby: "jruby-9.3" 49 | appraisal: "ruby-2-6" 50 | exec_cmd: "rake test" 51 | gemfile: "Appraisal.root" 52 | rubygems: default 53 | bundler: default 54 | 55 | # jruby-9.4 (targets Ruby 3.1 compatibility) 56 | - ruby: "jruby-9.4" 57 | appraisal: "ruby-3-1" 58 | exec_cmd: "rake test" 59 | gemfile: "Appraisal.root" 60 | rubygems: default 61 | bundler: default 62 | 63 | steps: 64 | - name: Checkout 65 | uses: actions/checkout@v4 66 | 67 | - name: Setup Ruby & RubyGems 68 | uses: ruby/setup-ruby@v1 69 | with: 70 | ruby-version: ${{ matrix.ruby }} 71 | rubygems: ${{ matrix.rubygems }} 72 | bundler: ${{ matrix.bundler }} 73 | bundler-cache: false 74 | 75 | # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) 76 | # We need to do this first to get appraisal installed. 77 | # NOTE: This does not use the primary Gemfile at all. 78 | - name: Install Root Appraisal 79 | run: bundle 80 | - name: Appraisal for ${{ matrix.appraisal }} 81 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle 82 | - name: Tests for ${{ matrix.ruby }} via ${{ matrix.exec_cmd }} 83 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} 84 | -------------------------------------------------------------------------------- /.github/workflows/legacy.yml: -------------------------------------------------------------------------------- 1 | name: MRI 3.0, 3.1 (EOL) 2 | 3 | permissions: 4 | contents: read 5 | 6 | env: 7 | K_SOUP_COV_DO: false 8 | 9 | on: 10 | push: 11 | branches: 12 | - 'main' 13 | - '*-stable' 14 | tags: 15 | - '!*' # Do not execute on tags 16 | pull_request: 17 | branches: 18 | - '*' 19 | # Allow manually triggering the workflow. 20 | workflow_dispatch: 21 | 22 | # Cancels all previous workflow runs for the same branch that have not yet completed. 23 | concurrency: 24 | # The concurrency group contains the workflow name and the branch name. 25 | group: "${{ github.workflow }}-${{ github.ref }}" 26 | cancel-in-progress: true 27 | 28 | jobs: 29 | test: 30 | name: Specs ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} 31 | if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" 32 | runs-on: ubuntu-22.04 33 | continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} 34 | env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps 35 | BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile 36 | strategy: 37 | fail-fast: false 38 | matrix: 39 | include: 40 | # Ruby 3.1 41 | - ruby: "ruby-3.1" 42 | appraisal: "ruby-3-1" 43 | exec_cmd: "rake test" 44 | gemfile: "Appraisal.root" 45 | rubygems: latest 46 | bundler: latest 47 | 48 | # Ruby 3.0 49 | - ruby: "ruby-3.0" 50 | appraisal: "ruby-3-0" 51 | exec_cmd: "rake test" 52 | gemfile: "Appraisal.root" 53 | rubygems: '3.5.23' 54 | bundler: '2.5.23' 55 | 56 | steps: 57 | - name: Checkout 58 | uses: actions/checkout@v4 59 | 60 | - name: Setup Ruby & RubyGems 61 | uses: ruby/setup-ruby@v1 62 | with: 63 | ruby-version: ${{ matrix.ruby }} 64 | rubygems: ${{ matrix.rubygems }} 65 | bundler: ${{ matrix.bundler }} 66 | bundler-cache: false 67 | 68 | # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) 69 | # We need to do this first to get appraisal installed. 70 | # NOTE: This does not use the primary Gemfile at all. 71 | - name: Install Root Appraisal 72 | run: bundle 73 | - name: Appraisal for ${{ matrix.appraisal }} 74 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle 75 | - name: Tests for ${{ matrix.ruby }} via ${{ matrix.exec_cmd }} 76 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} 77 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | # Targets the evergreen latest release of ruby, truffleruby, and jruby 2 | name: MacOS 3 | 4 | permissions: 5 | contents: read 6 | 7 | env: 8 | K_SOUP_COV_DO: false 9 | 10 | on: 11 | push: 12 | branches: 13 | - 'main' 14 | - '*-stable' 15 | tags: 16 | - '!*' # Do not execute on tags 17 | pull_request: 18 | branches: 19 | - '*' 20 | # Allow manually triggering the workflow. 21 | workflow_dispatch: 22 | 23 | # Cancels all previous workflow runs for the same branch that have not yet completed. 24 | concurrency: 25 | # The concurrency group contains the workflow name and the branch name. 26 | group: "${{ github.workflow }}-${{ github.ref }}" 27 | cancel-in-progress: true 28 | 29 | jobs: 30 | test: 31 | name: Specs ${{ matrix.ruby }}@${{ matrix.appraisal }} 32 | if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" 33 | runs-on: macos-latest 34 | continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} 35 | env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps 36 | BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile 37 | strategy: 38 | matrix: 39 | include: 40 | # Ruby 3.4 41 | - ruby: "ruby" 42 | appraisal: "current" 43 | exec_cmd: "rake test" 44 | gemfile: "Appraisal.root" 45 | rubygems: latest 46 | bundler: latest 47 | 48 | # truffleruby-24.1 49 | # (according to documentation: targets Ruby 3.3 compatibility) 50 | # (according to runtime: targets Ruby 3.2 compatibility) 51 | - ruby: "truffleruby" 52 | appraisal: "current" 53 | exec_cmd: "rake test" 54 | gemfile: "Appraisal.root" 55 | rubygems: default 56 | bundler: default 57 | 58 | # jruby-10.0 (targets Ruby 3.4 compatibility) 59 | - ruby: "jruby" 60 | appraisal: "current" 61 | exec_cmd: "rake test" 62 | gemfile: "Appraisal.root" 63 | rubygems: default 64 | bundler: default 65 | 66 | steps: 67 | - name: Checkout 68 | uses: actions/checkout@v4 69 | 70 | - name: Setup Ruby & RubyGems 71 | uses: ruby/setup-ruby@v1 72 | with: 73 | ruby-version: ${{ matrix.ruby }} 74 | rubygems: ${{ matrix.rubygems }} 75 | bundler: ${{ matrix.bundler }} 76 | bundler-cache: false 77 | 78 | # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) 79 | # We need to do this first to get appraisal installed. 80 | # NOTE: This does not use the primary Gemfile at all. 81 | - name: Install Root Appraisal 82 | run: bundle 83 | - name: Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }} 84 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle 85 | - name: Tests for ${{ matrix.ruby }}@${{ matrix.appraisal }} via ${{ matrix.exec_cmd }} 86 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} 87 | -------------------------------------------------------------------------------- /.github/workflows/style.yml: -------------------------------------------------------------------------------- 1 | name: Style 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | push: 8 | branches: 9 | - 'main' 10 | - '*-stable' 11 | tags: 12 | - '!*' # Do not execute on tags 13 | pull_request: 14 | branches: 15 | - '*' 16 | # Allow manually triggering the workflow. 17 | workflow_dispatch: 18 | 19 | # Cancels all previous workflow runs for the same branch that have not yet completed. 20 | concurrency: 21 | # The concurrency group contains the workflow name and the branch name. 22 | group: "${{ github.workflow }}-${{ github.ref }}" 23 | cancel-in-progress: true 24 | 25 | jobs: 26 | rubocop: 27 | name: Style on ${{ matrix.ruby }}@current 28 | if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" 29 | runs-on: ubuntu-latest 30 | continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} 31 | env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps 32 | BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile 33 | strategy: 34 | fail-fast: false 35 | matrix: 36 | include: 37 | # Style 38 | - ruby: "ruby" 39 | appraisal: "style" 40 | exec_cmd: "rake rubocop_gradual:check" 41 | gemfile: "Appraisal.root" 42 | rubygems: latest 43 | bundler: latest 44 | 45 | steps: 46 | - name: Checkout 47 | uses: actions/checkout@v4 48 | 49 | - name: Setup Ruby & RubyGems 50 | uses: ruby/setup-ruby@v1 51 | with: 52 | ruby-version: ${{ matrix.ruby }} 53 | rubygems: ${{ matrix.rubygems }} 54 | bundler: ${{ matrix.bundler }} 55 | bundler-cache: false 56 | 57 | # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) 58 | # We need to do this first to get appraisal installed. 59 | # NOTE: This does not use the primary Gemfile at all. 60 | - name: Install Root Appraisal 61 | run: bundle 62 | - name: Appraisal for ${{ matrix.appraisal }} 63 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle 64 | - name: Run ${{ matrix.appraisal }} checks via ${{ matrix.exec_cmd }} 65 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} 66 | -------------------------------------------------------------------------------- /.github/workflows/supported.yml: -------------------------------------------------------------------------------- 1 | name: MRI Non-EOL 2 | 3 | permissions: 4 | contents: read 5 | 6 | env: 7 | K_SOUP_COV_DO: false 8 | 9 | on: 10 | push: 11 | branches: 12 | - 'main' 13 | - '*-stable' 14 | tags: 15 | - '!*' # Do not execute on tags 16 | pull_request: 17 | branches: 18 | - '*' 19 | # Allow manually triggering the workflow. 20 | workflow_dispatch: 21 | 22 | # Cancels all previous workflow runs for the same branch that have not yet completed. 23 | concurrency: 24 | # The concurrency group contains the workflow name and the branch name. 25 | group: "${{ github.workflow }}-${{ github.ref }}" 26 | cancel-in-progress: true 27 | 28 | jobs: 29 | test: 30 | name: Specs ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} 31 | if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" 32 | runs-on: ubuntu-latest 33 | continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} 34 | env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps 35 | BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile 36 | strategy: 37 | matrix: 38 | include: 39 | # Ruby 3.2 40 | - ruby: "ruby-3.2" 41 | appraisal: "ruby-3-2" 42 | exec_cmd: "rake test" 43 | gemfile: "Appraisal.root" 44 | rubygems: latest 45 | bundler: latest 46 | 47 | # Ruby 3.3 48 | - ruby: "ruby-3.3" 49 | appraisal: "ruby-3-3" 50 | exec_cmd: "rake test" 51 | gemfile: "Appraisal.root" 52 | rubygems: latest 53 | bundler: latest 54 | 55 | steps: 56 | - name: Checkout 57 | uses: actions/checkout@v4 58 | 59 | - name: Setup Ruby & RubyGems 60 | uses: ruby/setup-ruby@v1 61 | with: 62 | ruby-version: ${{ matrix.ruby }} 63 | rubygems: ${{ matrix.rubygems }} 64 | bundler: ${{ matrix.bundler }} 65 | bundler-cache: false 66 | 67 | # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) 68 | # We need to do this first to get appraisal installed. 69 | # NOTE: This does not use the primary Gemfile at all. 70 | - name: Install Root Appraisal 71 | run: bundle 72 | - name: Appraisal for ${{ matrix.ruby }} ${{ matrix.appraisal }} 73 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle 74 | - name: Tests for ${{ matrix.ruby }} ${{ matrix.appraisal }} via ${{ matrix.exec_cmd }} 75 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} 76 | -------------------------------------------------------------------------------- /.github/workflows/truffle.yml: -------------------------------------------------------------------------------- 1 | name: Truffle 2 | 3 | permissions: 4 | contents: read 5 | 6 | env: 7 | K_SOUP_COV_DO: false 8 | 9 | on: 10 | push: 11 | branches: 12 | - 'main' 13 | - '*-stable' 14 | tags: 15 | - '!*' # Do not execute on tags 16 | pull_request: 17 | branches: 18 | - '*' 19 | # Allow manually triggering the workflow. 20 | workflow_dispatch: 21 | 22 | # Cancels all previous workflow runs for the same branch that have not yet completed. 23 | concurrency: 24 | # The concurrency group contains the workflow name and the branch name. 25 | group: "${{ github.workflow }}-${{ github.ref }}" 26 | cancel-in-progress: true 27 | 28 | jobs: 29 | test: 30 | name: Specs ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} 31 | if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" 32 | runs-on: ubuntu-22.04 33 | continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} 34 | env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps 35 | BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile 36 | strategy: 37 | matrix: 38 | include: 39 | # NOTE: truffleruby does not support upgrading rubygems. 40 | # truffleruby-23.1 (targets Ruby 3.2 compatibility) 41 | - ruby: "truffleruby-23.1" 42 | appraisal: "ruby-3-2" 43 | exec_cmd: "rake test" 44 | gemfile: "Appraisal.root" 45 | rubygems: default 46 | bundler: default 47 | 48 | steps: 49 | - name: Checkout 50 | uses: actions/checkout@v4 51 | 52 | - name: Setup Ruby & RubyGems 53 | uses: ruby/setup-ruby@v1 54 | with: 55 | ruby-version: ${{ matrix.ruby }} 56 | rubygems: ${{ matrix.rubygems }} 57 | bundler: ${{ matrix.bundler }} 58 | bundler-cache: false 59 | 60 | # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) 61 | # We need to do this first to get appraisal installed. 62 | # NOTE: This does not use the primary Gemfile at all. 63 | - name: Install Root Appraisal 64 | run: bundle 65 | 66 | - name: "[Attempt 1] Install Root Appraisal" 67 | id: bundleAttempt1 68 | run: bundle 69 | # Continue to the next step on failure 70 | continue-on-error: true 71 | 72 | # Effectively an automatic retry of the previous step. 73 | - name: "[Attempt 2] Install Root Appraisal" 74 | id: bundleAttempt2 75 | # If bundleAttempt1 failed, try again here; Otherwise skip. 76 | if: steps.bundleAttempt1.outcome == 'failure' 77 | run: bundle 78 | 79 | - name: "[Attempt 1] Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }}" 80 | id: bundleAppraisalAttempt1 81 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle 82 | # Continue to the next step on failure 83 | continue-on-error: true 84 | 85 | # Effectively an automatic retry of the previous step. 86 | - name: "[Attempt 2] Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }}" 87 | id: bundleAppraisalAttempt2 88 | # If bundleAttempt1 failed, try again here; Otherwise skip. 89 | if: steps.bundleAppraisalAttempt1.outcome == 'failure' 90 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle 91 | 92 | - name: Tests for ${{ matrix.ruby }} via ${{ matrix.exec_cmd }} 93 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} 94 | -------------------------------------------------------------------------------- /.github/workflows/unsupported.yml: -------------------------------------------------------------------------------- 1 | name: MRI 2.6 & 2.7 (EOL) 2 | 3 | permissions: 4 | contents: read 5 | 6 | env: 7 | K_SOUP_COV_DO: false 8 | 9 | on: 10 | push: 11 | branches: 12 | - 'main' 13 | - '*-stable' 14 | tags: 15 | - '!*' # Do not execute on tags 16 | pull_request: 17 | branches: 18 | - '*' 19 | # Allow manually triggering the workflow. 20 | workflow_dispatch: 21 | 22 | # Cancels all previous workflow runs for the same branch that have not yet completed. 23 | concurrency: 24 | # The concurrency group contains the workflow name and the branch name. 25 | group: "${{ github.workflow }}-${{ github.ref }}" 26 | cancel-in-progress: true 27 | 28 | jobs: 29 | test: 30 | name: Specs ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} 31 | if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" 32 | runs-on: ubuntu-22.04 33 | continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} 34 | env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps 35 | BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile 36 | strategy: 37 | fail-fast: false 38 | matrix: 39 | include: 40 | # Ruby 2.6 41 | - ruby: "ruby-2.6" 42 | appraisal: "ruby-2-6" 43 | exec_cmd: "rake test" 44 | gemfile: "Appraisal.root" 45 | rubygems: '3.4.22' 46 | bundler: '2.4.22' 47 | 48 | # Ruby 2.7 49 | - ruby: "ruby-2.7" 50 | appraisal: "ruby-2-7" 51 | exec_cmd: "rake test" 52 | gemfile: "Appraisal.root" 53 | rubygems: '3.4.22' 54 | bundler: '2.4.22' 55 | 56 | steps: 57 | - name: Checkout 58 | uses: actions/checkout@v4 59 | 60 | - name: Setup Ruby & RubyGems 61 | uses: ruby/setup-ruby@v1 62 | with: 63 | ruby-version: ${{ matrix.ruby }} 64 | rubygems: ${{ matrix.rubygems }} 65 | bundler: ${{ matrix.bundler }} 66 | bundler-cache: false 67 | 68 | # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) 69 | # We need to do this first to get appraisal installed. 70 | # NOTE: This does not use the primary Gemfile at all. 71 | - name: Install Root Appraisal 72 | run: bundle 73 | - name: Appraisal for ${{ matrix.appraisal }} 74 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle 75 | - name: Tests for ${{ matrix.ruby }} via ${{ matrix.exec_cmd }} 76 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} 77 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | # Targets the evergreen latest release of ruby, truffleruby, and jruby 2 | name: Windows 3 | 4 | permissions: 5 | contents: read 6 | 7 | env: 8 | K_SOUP_COV_DO: false 9 | 10 | on: 11 | push: 12 | branches: 13 | - 'main' 14 | - '*-stable' 15 | tags: 16 | - '!*' # Do not execute on tags 17 | pull_request: 18 | branches: 19 | - '*' 20 | # Allow manually triggering the workflow. 21 | workflow_dispatch: 22 | 23 | # Cancels all previous workflow runs for the same branch that have not yet completed. 24 | concurrency: 25 | # The concurrency group contains the workflow name and the branch name. 26 | group: "${{ github.workflow }}-${{ github.ref }}" 27 | cancel-in-progress: true 28 | 29 | jobs: 30 | test: 31 | name: Specs ${{ matrix.ruby }}@${{ matrix.appraisal }} 32 | if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" 33 | runs-on: windows-latest 34 | continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} 35 | env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps 36 | BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile 37 | strategy: 38 | matrix: 39 | include: 40 | # Ruby 3.4 41 | - ruby: "ruby" 42 | appraisal: "current" 43 | exec_cmd: "rake test" 44 | gemfile: "Appraisal.root" 45 | rubygems: latest 46 | bundler: latest 47 | 48 | steps: 49 | - name: Checkout 50 | uses: actions/checkout@v4 51 | 52 | - name: Setup Ruby & RubyGems 53 | uses: ruby/setup-ruby@v1 54 | with: 55 | ruby-version: ${{ matrix.ruby }} 56 | rubygems: ${{ matrix.rubygems }} 57 | bundler: ${{ matrix.bundler }} 58 | bundler-cache: false 59 | 60 | # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) 61 | # We need to do this first to get appraisal installed. 62 | # NOTE: This does not use the primary Gemfile at all. 63 | - name: Install Root Appraisal 64 | run: bundle 65 | - name: Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }} 66 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle 67 | - name: Tests for ${{ matrix.ruby }}@${{ matrix.appraisal }} via ${{ matrix.exec_cmd }} 68 | run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} 69 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Artifacts 2 | /pkg/ 3 | /tmp/ 4 | *.gem 5 | 6 | # Bundler 7 | /.bundle/ 8 | /gemfiles/*.lock 9 | /gemfiles/.bundle/ 10 | /gemfiles/.bundle/config 11 | /gemfiles/vendor/ 12 | Appraisal.*.gemfile.lock 13 | 14 | # Specs 15 | .rspec_status 16 | /coverage/ 17 | /spec/reports/ 18 | 19 | # Documentation 20 | /.yardoc/ 21 | /_yardoc/ 22 | /rdoc/ 23 | /doc/ 24 | 25 | # Ruby Version Managers (RVM, rbenv, etc) 26 | # Ignored because we currently use .tool-versions 27 | .rvmrc 28 | .ruby-version 29 | .ruby-gemset 30 | 31 | # Benchmarking 32 | /measurement/ 33 | 34 | # Debugger detritus 35 | .byebug_history 36 | 37 | # direnv - brew install direnv 38 | .env.local 39 | 40 | # OS Detritus 41 | .DS_Store 42 | 43 | # Editors 44 | *~ 45 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | default: 2 | image: ruby:3.2 3 | 4 | variables: 5 | BUNDLE_INSTALL_FLAGS: "--quiet --jobs=$(nproc) --retry=3" 6 | BUNDLE_FROZEN: "false" # No lockfile! 7 | K_SOUP_COV_DEBUG: true 8 | K_SOUP_COV_DO: true 9 | K_SOUP_COV_HARD: true 10 | K_SOUP_COV_MIN_BRANCH: 100 11 | K_SOUP_COV_MIN_LINE: 100 12 | K_SOUP_COV_VERBOSE: true 13 | K_SOUP_COV_FORMATTERS: "html,xml,rcov,lcov,json,tty" 14 | K_SOUP_COV_MULTI_FORMATTERS: true 15 | K_SOUP_COV_COMMAND_NAME: "RSpec Coverage" 16 | 17 | workflow: 18 | rules: 19 | # For merge requests, create a pipeline. 20 | - if: '$CI_MERGE_REQUEST_IID' 21 | # For default branch, create a pipeline (this includes on schedules, pushes, merges, etc.). 22 | - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' 23 | # For tags, create a pipeline. 24 | - if: '$CI_COMMIT_TAG' 25 | 26 | .test_template-current: &test_definition-current 27 | image: ruby:${RUBY_VERSION} 28 | stage: test 29 | script: 30 | - gem update --silent --system 31 | - bundle config --local path vendor 32 | - bundle install 33 | - bundle exec rake 34 | cache: 35 | key: ${CI_JOB_IMAGE} 36 | paths: 37 | - vendor/ruby 38 | 39 | .test_template-legacy: &test_definition-legacy 40 | image: ruby:${RUBY_VERSION} 41 | stage: test 42 | script: 43 | # Because we support EOL Ruby still... 44 | - gem install rubygems-update -v ${RUBYGEMS_VERSION} 45 | # Actually updates both RubyGems and Bundler! 46 | - update_rubygems 47 | - bundle config --local path vendor 48 | - bundle install 49 | - bundle exec rake test 50 | cache: 51 | key: ${CI_JOB_IMAGE} 52 | paths: 53 | - vendor/ruby 54 | 55 | ruby-current: 56 | variables: 57 | BUNDLE_GEMFILE: gemfiles/omnibus.gemfile 58 | K_SOUP_COV_DO: true 59 | <<: *test_definition-current 60 | parallel: 61 | matrix: 62 | - RUBY_VERSION: ["3.2", "3.3", "3.4"] 63 | 64 | ruby-ruby3_1: 65 | variables: 66 | RUBYGEMS_VERSION: "3.6.9" 67 | BUNDLE_GEMFILE: gemfiles/vanilla.gemfile 68 | K_SOUP_COV_DO: false 69 | <<: *test_definition-legacy 70 | parallel: 71 | matrix: 72 | - RUBY_VERSION: ["3.1"] 73 | 74 | ruby-ruby3_0: 75 | variables: 76 | RUBYGEMS_VERSION: "3.5.23" 77 | BUNDLE_GEMFILE: gemfiles/vanilla.gemfile 78 | K_SOUP_COV_DO: false 79 | <<: *test_definition-legacy 80 | parallel: 81 | matrix: 82 | - RUBY_VERSION: ["3.0"] 83 | 84 | ruby-ruby2_7: 85 | variables: 86 | RUBYGEMS_VERSION: "3.4.22" 87 | BUNDLE_GEMFILE: gemfiles/vanilla.gemfile 88 | K_SOUP_COV_DO: false 89 | <<: *test_definition-legacy 90 | parallel: 91 | matrix: 92 | - RUBY_VERSION: ["2.7"] 93 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/GitLink.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/GitlabSettingsPlugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/active-tab-highlighter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/codestream.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/git_toolbox_blame.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/git_toolbox_prj.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/kubernetes-settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.jrubyrc: -------------------------------------------------------------------------------- 1 | debug.fullTrace=true 2 | -------------------------------------------------------------------------------- /.overcommit.yml: -------------------------------------------------------------------------------- 1 | # Use this file to configure the Overcommit hooks you wish to use. This will 2 | # extend the default configuration defined in: 3 | # https://github.com/sds/overcommit/blob/master/config/default.yml 4 | # 5 | # At the topmost level of this YAML file is a key representing type of hook 6 | # being run (e.g. pre-commit, commit-msg, etc.). Within each type you can 7 | # customize each hook, such as whether to only run it on certain files (via 8 | # `include`), whether to only display output if it fails (via `quiet`), etc. 9 | # 10 | # For a complete list of hooks, see: 11 | # https://github.com/sds/overcommit/tree/master/lib/overcommit/hook 12 | # 13 | # For a complete list of options that you can use to customize hooks, see: 14 | # https://github.com/sds/overcommit#configuration 15 | # 16 | # Uncomment the following lines to make the configuration take effect. 17 | 18 | PreCommit: 19 | RuboCop: 20 | enabled: true 21 | on_warn: fail # Treat all warnings as failures 22 | 23 | TrailingWhitespace: 24 | enabled: true 25 | 26 | PostCheckout: 27 | ALL: # Special hook name that customizes all hooks of this type 28 | quiet: true # Change all post-checkout hooks to only display output on failure 29 | # 30 | # IndexTags: 31 | # enabled: true # Generate a tags file with `ctags` each time HEAD changes 32 | -------------------------------------------------------------------------------- /.qlty/qlty.toml: -------------------------------------------------------------------------------- 1 | # For a guide to configuration, visit https://qlty.sh/d/config 2 | # Or for a full reference, visit https://qlty.sh/d/qlty-toml 3 | config_version = "0" 4 | 5 | exclude_patterns = [ 6 | "*_min.*", 7 | "*-min.*", 8 | "*.min.*", 9 | "**/.yarn/**", 10 | "**/*.d.ts", 11 | "**/assets/**", 12 | "**/bin/**", 13 | "**/bower_components/**", 14 | "**/build/**", 15 | "**/cache/**", 16 | "**/config/**", 17 | "**/.devcontainer", 18 | "**/db/**", 19 | "**/deps/**", 20 | "**/dist/**", 21 | "**/doc/**", 22 | "**/extern/**", 23 | "**/external/**", 24 | "**/generated/**", 25 | "**/Godeps/**", 26 | "**/gradlew/**", 27 | "**/mvnw/**", 28 | "**/node_modules/**", 29 | "**/protos/**", 30 | "**/seed/**", 31 | "**/target/**", 32 | "**/templates/**", 33 | "**/testdata/**", 34 | "**/vendor/**", 35 | ".github/workflows/codeql-analysis.yml" 36 | ] 37 | 38 | test_patterns = [ 39 | "**/test/**", 40 | "**/spec/**", 41 | "**/*.test.*", 42 | "**/*.spec.*", 43 | "**/*_test.*", 44 | "**/*_spec.*", 45 | "**/test_*.*", 46 | "**/spec_*.*", 47 | ] 48 | 49 | [smells] 50 | mode = "comment" 51 | 52 | [smells.boolean_logic] 53 | threshold = 4 54 | enabled = true 55 | 56 | [smells.file_complexity] 57 | threshold = 55 58 | enabled = false 59 | 60 | [smells.return_statements] 61 | threshold = 4 62 | enabled = true 63 | 64 | [smells.nested_control_flow] 65 | threshold = 4 66 | enabled = true 67 | 68 | [smells.function_parameters] 69 | threshold = 4 70 | enabled = true 71 | 72 | [smells.function_complexity] 73 | threshold = 5 74 | enabled = true 75 | 76 | [smells.duplication] 77 | enabled = true 78 | threshold = 20 -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | --require spec_helper 4 | --order random 5 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_gem: 2 | rubocop-lts: rubocop-lts.yml 3 | 4 | inherit_from: 5 | - .rubocop_rspec.yml 6 | 7 | AllCops: 8 | DisplayCopNames: true # Display the name of the failing cops 9 | Exclude: 10 | - 'gemfiles/vendor/**/*' 11 | - 'vendor/**/*' 12 | - '**/.irbrc' 13 | 14 | Metrics/BlockLength: 15 | Enabled: false 16 | 17 | Gemspec/RequiredRubyVersion: 18 | Enabled: false 19 | 20 | Metrics/BlockNesting: 21 | Max: 2 22 | 23 | Layout/LineLength: 24 | Enabled: false 25 | 26 | Metrics/ParameterLists: 27 | Max: 4 28 | 29 | Layout/AccessModifierIndentation: 30 | EnforcedStyle: outdent 31 | 32 | Layout/DotPosition: 33 | EnforcedStyle: trailing 34 | 35 | Layout/SpaceInsideHashLiteralBraces: 36 | EnforcedStyle: no_space 37 | 38 | Lint/UnusedBlockArgument: 39 | Exclude: 40 | - 'spec/**/*.rb' 41 | - 'gemfiles/vendor/**/*' 42 | - 'vendor/**/*' 43 | - '**/.irbrc' 44 | 45 | # Test if we can turn this back on after upgrading rubocop-md. 46 | # It is still an open issue, so not expecting it to be fixed. 47 | # See: https://github.com/rubocop/rubocop-md/issues/28 48 | Layout/InitialIndentation: 49 | Enabled: false 50 | 51 | Style/ClassVars: 52 | Enabled: false 53 | 54 | Style/CollectionMethods: 55 | PreferredMethods: 56 | map: 'collect' 57 | reduce: 'inject' 58 | find: 'detect' 59 | find_all: 'select' 60 | 61 | Style/Documentation: 62 | Enabled: false 63 | 64 | Style/DoubleNegation: 65 | Enabled: false 66 | 67 | Style/EmptyMethod: 68 | EnforcedStyle: expanded 69 | 70 | Style/Encoding: 71 | Enabled: false 72 | 73 | # Does not work with older rubies 74 | #Style/MapToHash: 75 | # Enabled: false 76 | 77 | # Does not work with older rubies 78 | #Style/RedundantBegin: 79 | # Enabled: false 80 | 81 | Style/TrailingCommaInArrayLiteral: 82 | EnforcedStyleForMultiline: comma 83 | 84 | Style/TrailingCommaInHashLiteral: 85 | EnforcedStyleForMultiline: comma 86 | 87 | Gemspec/DependencyVersion: 88 | Enabled: false 89 | 90 | Lint/LiteralInInterpolation: 91 | Exclude: 92 | - 'spec/**/*.rb' -------------------------------------------------------------------------------- /.rubocop_rspec.yml: -------------------------------------------------------------------------------- 1 | RSpec/MultipleExpectations: 2 | Enabled: false 3 | 4 | RSpec/NamedSubject: 5 | Enabled: false 6 | 7 | RSpec/ExampleLength: 8 | Enabled: false 9 | 10 | RSpec/VerifiedDoubles: 11 | Enabled: false 12 | 13 | RSpec/MessageSpies: 14 | Enabled: false 15 | 16 | RSpec/InstanceVariable: 17 | Enabled: false 18 | 19 | RSpec/NestedGroups: 20 | Enabled: false 21 | 22 | RSpec/ExpectInHook: 23 | Enabled: false 24 | 25 | RSpec/DescribeClass: 26 | Exclude: 27 | - 'spec/examples/*' 28 | 29 | RSpec/MultipleMemoizedHelpers: 30 | Enabled: false 31 | -------------------------------------------------------------------------------- /.simplecov: -------------------------------------------------------------------------------- 1 | require "kettle/soup/cover/config" 2 | 3 | SimpleCov.start 4 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | direnv 2.32.2 2 | ruby 3.4.3 3 | -------------------------------------------------------------------------------- /.yard_gfm_support.rb: -------------------------------------------------------------------------------- 1 | # Gratefully and liberally taken from the MIT-licensed https://github.com/bensheldon/good_job/pull/113/files 2 | require "kramdown" 3 | require "kramdown-parser-gfm" 4 | 5 | # Custom markup provider class that always renders Kramdown using GFM (Github Flavored Markdown). 6 | # GFM is needed to render markdown tables and fenced code blocks in the README. 7 | class KramdownGfmDocument < Kramdown::Document 8 | def initialize(source, options = {}) 9 | options[:input] = "GFM" unless options.key?(:input) 10 | super(source, options) 11 | end 12 | end 13 | 14 | # Insert the new provider as the highest priority option for Markdown. 15 | # See: 16 | # - https://github.com/lsegal/yard/issues/1157 17 | # - https://github.com/lsegal/yard/issues/1017 18 | # - https://github.com/lsegal/yard/blob/main/lib/yard/templates/helpers/markup_helper.rb 19 | YARD::Templates::Helpers::MarkupHelper::MARKUP_PROVIDERS[:markdown].insert( 20 | 0, 21 | {const: "KramdownGfmDocument"}, 22 | ) 23 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --plugin junk 2 | --plugin relative_markdown_links 3 | --readme README.md 4 | --charset utf-8 5 | --markup markdown 6 | --output docs 7 | --load .yard_gfm_support.rb 8 | 'lib/**/*.rb' 9 | - 10 | '*.md' 11 | '*.txt' 12 | '*.cff' -------------------------------------------------------------------------------- /Appraisal.root.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } 4 | 5 | source "https://rubygems.org" 6 | 7 | # Appraisal Root Gemfile is for running appraisal to generate the Appraisal Gemfiles 8 | # in gemfiles/*gemfile. 9 | # On CI, we use it for the Appraisal-based builds. 10 | # We do not load the standard Gemfile, as it is tailored for local development. 11 | 12 | gemspec 13 | 14 | gem "appraisal", github: "pboling/appraisal", branch: "galtzo" 15 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | title: oauth2 3 | message: >- 4 | If you use this work and you want to cite it, 5 | then you can use the metadata from this file. 6 | type: software 7 | authors: 8 | - given-names: Peter Hurn 9 | family-names: Boling 10 | email: peter@railsbling.com 11 | affiliation: railsbling.com 12 | orcid: 'https://orcid.org/0009-0008-8519-441X' 13 | - given-names: Aboling0 14 | email: aboling@railsbling.com 15 | affiliation: railsbling.com 16 | identifiers: 17 | - type: url 18 | value: 'https://github.com/oauth-xx/oauth2' 19 | description: oauth2 20 | repository-code: 'https://github.com/oauth-xx/oauth2' 21 | abstract: >- 22 | oauth2 23 | license: See license file -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | oauth2.galtzo.com -------------------------------------------------------------------------------- /Dangerfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ideas... 4 | # 1. Check for hashtags in PR title, and disallow changes to changelog? 5 | # e.g. github.pr_title.include? "#trivial" 6 | 7 | # Make it more obvious that a PR is a work in progress and shouldn't be merged yet 8 | warn("PR is classed as Work in Progress") if github.pr_title.include?("[WIP]") 9 | 10 | # Warn when there is a big PR 11 | warn("Big PR") if git.lines_of_code > 500 12 | 13 | # Don't let testing shortcuts get into main by accident 14 | raise("fdescribe left in tests") if %x(grep -r fdescribe specs/).length > 1 15 | raise("fit left in tests") if %x(grep -r fit specs/).length > 1 16 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } 6 | git_source(:gitlab) { |repo_name| "https://gitlab.com/#{repo_name}" } 7 | 8 | #### IMPORTANT ####################################################### 9 | # Gemfile is for local development ONLY; Gemfile is NOT loaded in CI # 10 | ####################################################### IMPORTANT #### 11 | 12 | # Include dependencies from .gemspec 13 | gemspec 14 | 15 | platform :mri do 16 | # Use binding.break, binding.b, or debugger in code 17 | gem "debug", ">= 1.0.0" # ruby >= 2.7 18 | gem "gem_bench", "~> 2.0", ">= 2.0.5" 19 | end 20 | 21 | # Security Audit 22 | eval_gemfile "gemfiles/modular/audit.gemfile" 23 | 24 | # Code Coverage 25 | eval_gemfile "gemfiles/modular/coverage.gemfile" 26 | 27 | # Linting 28 | eval_gemfile "gemfiles/modular/style.gemfile" 29 | 30 | # Documentation 31 | eval_gemfile "gemfiles/modular/documentation.gemfile" 32 | 33 | gem "appraisal", github: "pboling/appraisal", branch: "galtzo" 34 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2025 Peter H. Boling, of Galtzo.com, and oauth2 contributors 4 | Copyright (c) 2011-2013 Michael Bleigh and Intridea, Inc. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | defaults = [] 4 | 5 | # See: https://docs.gitlab.com/ci/variables/predefined_variables/ 6 | is_gitlab = ENV.fetch("GITLAB_CI", "false").casecmp("true") == 0 7 | 8 | ### DEVELOPMENT TASKS 9 | # Setup Kettle Soup Cover 10 | begin 11 | require "kettle-soup-cover" 12 | 13 | Kettle::Soup::Cover.install_tasks 14 | # NOTE: Coverage on CI is configured independent of this task. 15 | # This task is for local development, as it opens results in browser 16 | defaults << "coverage" unless Kettle::Soup::Cover::IS_CI 17 | rescue LoadError 18 | desc("(stub) coverage is unavailable") 19 | task("coverage") do 20 | warn("NOTE: kettle-soup-cover isn't installed, or is disabled for #{RUBY_VERSION} in the current environment") 21 | end 22 | end 23 | 24 | # Setup Bundle Audit 25 | begin 26 | require "bundler/audit/task" 27 | 28 | Bundler::Audit::Task.new 29 | defaults.push("bundle:audit:update", "bundle:audit") 30 | rescue LoadError 31 | desc("(stub) bundle:audit is unavailable") 32 | task("bundle:audit") do 33 | warn("NOTE: bundler-audit isn't installed, or is disabled for #{RUBY_VERSION} in the current environment") 34 | end 35 | desc("(stub) bundle:audit:update is unavailable") 36 | task("bundle:audit:update") do 37 | warn("NOTE: bundler-audit isn't installed, or is disabled for #{RUBY_VERSION} in the current environment") 38 | end 39 | end 40 | 41 | begin 42 | require "rspec/core/rake_task" 43 | 44 | RSpec::Core::RakeTask.new(:spec) 45 | defaults << "spec" 46 | rescue LoadError 47 | desc("spec task stub") 48 | task(:spec) do 49 | warn("NOTE: rspec isn't installed, or is disabled for #{RUBY_VERSION} in the current environment") 50 | end 51 | end 52 | 53 | desc "run spec task with test task" 54 | task test: :spec 55 | 56 | # Setup RuboCop-LTS 57 | begin 58 | require "rubocop/lts" 59 | 60 | Rubocop::Lts.install_tasks 61 | # Make autocorrect the default rubocop task 62 | defaults << "rubocop_gradual:autocorrect" 63 | rescue LoadError 64 | desc("(stub) rubocop_gradual is unavailable") 65 | task(:rubocop_gradual) do 66 | warn("NOTE: rubocop-lts isn't installed, or is disabled for #{RUBY_VERSION} in the current environment") 67 | end 68 | end 69 | 70 | # Setup Yard 71 | begin 72 | require "yard" 73 | 74 | YARD::Rake::YardocTask.new(:yard) do |t| 75 | t.files = [ 76 | # Source Splats (alphabetical) 77 | "lib/**/*.rb", 78 | "-", # source and extra docs are separated by "-" 79 | # Extra Files (alphabetical) 80 | "*.cff", 81 | "*.md", 82 | "*.txt", 83 | ] 84 | end 85 | defaults << "yard" 86 | rescue LoadError 87 | desc("(stub) yard is unavailable") 88 | task(:yard) do 89 | warn("NOTE: yard isn't installed, or is disabled for #{RUBY_VERSION} in the current environment") 90 | end 91 | end 92 | 93 | # Setup Reek 94 | begin 95 | require "reek/rake/task" 96 | 97 | Reek::Rake::Task.new do |t| 98 | t.fail_on_error = true 99 | t.verbose = false 100 | t.source_files = "{lib,spec}/**/*.rb" 101 | end 102 | defaults << "reek" unless is_gitlab 103 | rescue LoadError 104 | desc("(stub) reek is unavailable") 105 | task(:reek) do 106 | warn("NOTE: reek isn't installed, or is disabled for #{RUBY_VERSION} in the current environment") 107 | end 108 | end 109 | 110 | ### RELEASE TASKS 111 | # Setup stone_checksums 112 | begin 113 | require "stone_checksums" 114 | 115 | GemChecksums.install_tasks 116 | rescue LoadError 117 | desc("(stub) build:generate_checksums is unavailable") 118 | task("build:generate_checksums") do 119 | warn("NOTE: stone_checksums isn't installed, or is disabled for #{RUBY_VERSION} in the current environment") 120 | end 121 | end 122 | 123 | task default: defaults 124 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | | Version | Supported | Post-EOL / Enterprise | 6 | |----------|-----------|---------------------------------------| 7 | | 2.latest | ✅ | [Tidelift Subscription][tidelift-ref] | 8 | | 1.latest | ✅ | [Tidelift Subscription][tidelift-ref] | 9 | | <= 1 | ⛔ | ⛔ | 10 | 11 | ### EOL Policy 12 | 13 | Non-commercial support for the oldest version of Ruby (which itself is going EOL) will be dropped each year in April. 14 | 15 | ## Security contact information 16 | 17 | To report a security vulnerability, please use the 18 | [Tidelift security contact](https://tidelift.com/security). 19 | Tidelift will coordinate the fix and disclosure. 20 | 21 | ## Additional Support 22 | 23 | If you are interested in support for versions older than the latest release, 24 | please consider sponsoring the project / maintainer @ https://liberapay.com/pboling/donate, 25 | or find other sponsorship links in the [README]. 26 | 27 | [README]: README.md 28 | 29 | ## Enterprise Support 30 | 31 | Available as part of the Tidelift Subscription. 32 | 33 | The maintainers of this library and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. [Learn more.][tidelift-ref] 34 | 35 | [tidelift-ref]: https://tidelift.com/subscription/pkg/rubygems-oauth2?utm_source=rubygems-oauth2&utm_medium=referral&utm_campaign=enterprise&utm_term=repo 36 | -------------------------------------------------------------------------------- /bin/appraisal: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'appraisal' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("appraisal", "appraisal") 28 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'bundle' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | require "rubygems" 12 | 13 | m = Module.new do 14 | module_function 15 | 16 | def invoked_as_script? 17 | File.expand_path($PROGRAM_NAME) == File.expand_path(__FILE__) 18 | end 19 | 20 | def env_var_version 21 | ENV["BUNDLER_VERSION"] 22 | end 23 | 24 | def cli_arg_version 25 | return unless invoked_as_script? # don't want to hijack other binstubs 26 | return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` 27 | 28 | bundler_version = nil 29 | update_index = nil 30 | ARGV.each_with_index do |a, i| 31 | bundler_version = a if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN 32 | next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/o 33 | 34 | bundler_version = Regexp.last_match(1) 35 | update_index = i 36 | end 37 | bundler_version 38 | end 39 | 40 | def gemfile 41 | gemfile = ENV["BUNDLE_GEMFILE"] 42 | return gemfile if gemfile && !gemfile.empty? 43 | 44 | File.expand_path("../Gemfile", __dir__) 45 | end 46 | 47 | def lockfile 48 | lockfile = 49 | case File.basename(gemfile) 50 | when "gems.rb" then gemfile.sub(/\.rb$/, gemfile) 51 | else "#{gemfile}.lock" 52 | end 53 | File.expand_path(lockfile) 54 | end 55 | 56 | def lockfile_version 57 | return unless File.file?(lockfile) 58 | 59 | lockfile_contents = File.read(lockfile) 60 | return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/o 61 | 62 | Regexp.last_match(1) 63 | end 64 | 65 | def bundler_requirement 66 | @bundler_requirement ||= 67 | env_var_version || cli_arg_version || 68 | bundler_requirement_for(lockfile_version) 69 | end 70 | 71 | def bundler_requirement_for(version) 72 | return "#{Gem::Requirement.default}.a" unless version 73 | 74 | bundler_gem_version = Gem::Version.new(version) 75 | 76 | requirement = bundler_gem_version.approximate_recommendation 77 | 78 | return requirement unless Gem.rubygems_version < Gem::Version.new("2.7.0") 79 | 80 | requirement += ".a" if bundler_gem_version.prerelease? 81 | 82 | requirement 83 | end 84 | 85 | def load_bundler! 86 | ENV["BUNDLE_GEMFILE"] ||= gemfile 87 | 88 | activate_bundler 89 | end 90 | 91 | def activate_bundler 92 | gem_error = activation_error_handling do 93 | gem("bundler", bundler_requirement) 94 | end 95 | return if gem_error.nil? 96 | 97 | require_error = activation_error_handling do 98 | require "bundler/version" 99 | end 100 | return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) 101 | 102 | warn("Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`") 103 | exit(42) 104 | end 105 | 106 | def activation_error_handling 107 | yield 108 | nil 109 | rescue StandardError, LoadError => e 110 | e 111 | end 112 | end 113 | 114 | m.load_bundler! 115 | 116 | load Gem.bin_path("bundler", "bundle") if m.invoked_as_script? 117 | -------------------------------------------------------------------------------- /bin/bundle-audit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'bundle-audit' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("bundler-audit", "bundle-audit") 28 | -------------------------------------------------------------------------------- /bin/bundler-audit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'bundler-audit' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("bundler-audit", "bundler-audit") 28 | -------------------------------------------------------------------------------- /bin/code_climate_reek: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'code_climate_reek' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("reek", "code_climate_reek") 28 | -------------------------------------------------------------------------------- /bin/coderay: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'coderay' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("coderay", "coderay") 28 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require "bundler/setup" 5 | require "oauth2" 6 | 7 | # You can add fixtures and/or initialization code here to make experimenting 8 | # with your gem easier. You can also use a different console, if you like. 9 | 10 | # (If you use this, don't forget to add pry to your Gemfile!) 11 | # require "pry" 12 | # Pry.start 13 | 14 | require "irb" 15 | IRB.start(__FILE__) 16 | -------------------------------------------------------------------------------- /bin/gem_checksums: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'gem_checksums' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("stone_checksums", "gem_checksums") 28 | -------------------------------------------------------------------------------- /bin/github-markup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'github-markup' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("github-markup", "github-markup") 28 | -------------------------------------------------------------------------------- /bin/htmldiff: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'htmldiff' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("diff-lcs", "htmldiff") 28 | -------------------------------------------------------------------------------- /bin/irb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'irb' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("irb", "irb") 28 | -------------------------------------------------------------------------------- /bin/ldiff: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'ldiff' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("diff-lcs", "ldiff") 28 | -------------------------------------------------------------------------------- /bin/racc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'racc' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("racc", "racc") 28 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'rake' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/ 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("rake", "rake") 28 | -------------------------------------------------------------------------------- /bin/rdbg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'rdbg' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("debug", "rdbg") 28 | -------------------------------------------------------------------------------- /bin/rdoc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'rdoc' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("rdoc", "rdoc") 28 | -------------------------------------------------------------------------------- /bin/redcarpet: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'redcarpet' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("redcarpet", "redcarpet") 28 | -------------------------------------------------------------------------------- /bin/reek: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'reek' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("reek", "reek") 28 | -------------------------------------------------------------------------------- /bin/ri: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'ri' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("rdoc", "ri") 28 | -------------------------------------------------------------------------------- /bin/rspec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'rspec' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/ 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("rspec-core", "rspec") 28 | -------------------------------------------------------------------------------- /bin/rubocop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'rubocop' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/ 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("rubocop", "rubocop") 28 | -------------------------------------------------------------------------------- /bin/rubocop-gradual: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'rubocop-gradual' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("rubocop-gradual", "rubocop-gradual") 28 | -------------------------------------------------------------------------------- /bin/ruby-parse: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'ruby-parse' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("parser", "ruby-parse") 28 | -------------------------------------------------------------------------------- /bin/ruby-rewrite: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'ruby-rewrite' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("parser", "ruby-rewrite") 28 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /bin/standardrb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'standardrb' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("standard", "standardrb") 28 | -------------------------------------------------------------------------------- /bin/thor: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'thor' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("thor", "thor") 28 | -------------------------------------------------------------------------------- /bin/yard: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'yard' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("yard", "yard") 28 | -------------------------------------------------------------------------------- /bin/yard-junk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'yard-junk' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("yard-junk", "yard-junk") 28 | -------------------------------------------------------------------------------- /bin/yardoc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'yardoc' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("yard", "yardoc") 28 | -------------------------------------------------------------------------------- /bin/yri: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'yri' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("yard", "yri") 28 | -------------------------------------------------------------------------------- /certs/pboling.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEgDCCAuigAwIBAgIBATANBgkqhkiG9w0BAQsFADBDMRUwEwYDVQQDDAxwZXRl 3 | ci5ib2xpbmcxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkW 4 | A2NvbTAeFw0yNTA1MDQxNTMzMDlaFw00NTA0MjkxNTMzMDlaMEMxFTATBgNVBAMM 5 | DHBldGVyLmJvbGluZzEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPy 6 | LGQBGRYDY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAruUoo0WA 7 | uoNuq6puKWYeRYiZekz/nsDeK5x/0IEirzcCEvaHr3Bmz7rjo1I6On3gGKmiZs61 8 | LRmQ3oxy77ydmkGTXBjruJB+pQEn7UfLSgQ0xa1/X3kdBZt6RmabFlBxnHkoaGY5 9 | mZuZ5+Z7walmv6sFD9ajhzj+oIgwWfnEHkXYTR8I6VLN7MRRKGMPoZ/yvOmxb2DN 10 | coEEHWKO9CvgYpW7asIihl/9GMpKiRkcYPm9dGQzZc6uTwom1COfW0+ZOFrDVBuV 11 | FMQRPswZcY4Wlq0uEBLPU7hxnCL9nKK6Y9IhdDcz1mY6HZ91WImNslOSI0S8hRpj 12 | yGOWxQIhBT3fqCBlRIqFQBudrnD9jSNpSGsFvbEijd5ns7Z9ZMehXkXDycpGAUj1 13 | to/5cuTWWw1JqUWrKJYoifnVhtE1o1DZ+LkPtWxHtz5kjDG/zR3MG0Ula0UOavlD 14 | qbnbcXPBnwXtTFeZ3C+yrWpE4pGnl3yGkZj9SMTlo9qnTMiPmuWKQDatAgMBAAGj 15 | fzB9MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBQE8uWvNbPVNRXZ 16 | HlgPbc2PCzC4bjAhBgNVHREEGjAYgRZwZXRlci5ib2xpbmdAZ21haWwuY29tMCEG 17 | A1UdEgQaMBiBFnBldGVyLmJvbGluZ0BnbWFpbC5jb20wDQYJKoZIhvcNAQELBQAD 18 | ggGBAJbnUwfJQFPkBgH9cL7hoBfRtmWiCvdqdjeTmi04u8zVNCUox0A4gT982DE9 19 | wmuN12LpdajxZONqbXuzZvc+nb0StFwmFYZG6iDwaf4BPywm2e/Vmq0YG45vZXGR 20 | L8yMDSK1cQXjmA+ZBKOHKWavxP6Vp7lWvjAhz8RFwqF9GuNIdhv9NpnCAWcMZtpm 21 | GUPyIWw/Cw/2wZp74QzZj6Npx+LdXoLTF1HMSJXZ7/pkxLCsB8m4EFVdb/IrW/0k 22 | kNSfjtAfBHO8nLGuqQZVH9IBD1i9K6aSs7pT6TW8itXUIlkIUI2tg5YzW6OFfPzq 23 | QekSkX3lZfY+HTSp/o+YvKkqWLUV7PQ7xh1ZYDtocpaHwgxe/j3bBqHE+CUPH2vA 24 | 0V/FwdTRWcwsjVoOJTrYcff8pBZ8r2MvtAc54xfnnhGFzeRHfcltobgFxkAXdE6p 25 | DVjBtqT23eugOqQ73umLcYDZkc36vnqGxUBSsXrzY9pzV5gGr2I8YUxMqf6ATrZt 26 | L9nRqA== 27 | -----END CERTIFICATE----- 28 | -------------------------------------------------------------------------------- /checksums/oauth2-2.0.10.gem.sha256: -------------------------------------------------------------------------------- 1 | 8f132679598d21885d4bcc68d7e7e6ef0a29f9a782abca00d67d884280dc3a42 -------------------------------------------------------------------------------- /checksums/oauth2-2.0.10.gem.sha512: -------------------------------------------------------------------------------- 1 | e692f68ab79677ee7fa9300bbd5e0c41de08642d51659a49ca7fd742230445601ad3c2d271ee110718d58a27383aba0c25ddbdbef5b13f7c18585cdfda74850b -------------------------------------------------------------------------------- /checksums/oauth2-2.0.11.gem.sha256: -------------------------------------------------------------------------------- 1 | 29e0505c2a39bc78dfb655dbf85a826b4408e55e7c3d02ce07b3dfd9b40da16c -------------------------------------------------------------------------------- /checksums/oauth2-2.0.11.gem.sha512: -------------------------------------------------------------------------------- 1 | 048743f9efd89460231738885c9c0de7b36433055eefc66331b91eee343885cd9145bbac239c6121d13b716633fb8385fa886ce854bf14142f9894e6c8f19ba2 -------------------------------------------------------------------------------- /checksums/oauth2-2.0.12.gem.sha256: -------------------------------------------------------------------------------- 1 | f7edb8549c7912724d07087d808c3fa6756298fd64d55d4968324df69c64ab3f -------------------------------------------------------------------------------- /checksums/oauth2-2.0.12.gem.sha512: -------------------------------------------------------------------------------- 1 | a209c7a0c4b9d46ccb00e750af8899c01d52648ca77a0d40b934593de53edc4f2774440fc50733c0e5098672c6c5a4a20f8709046be427fcf032f45922dff2d2 -------------------------------------------------------------------------------- /config-ssl.json: -------------------------------------------------------------------------------- 1 | { 2 | "interactiveLogin": true, 3 | "httpServer": { 4 | "type": "NettyWrapper", 5 | "ssl": {} 6 | } 7 | } -------------------------------------------------------------------------------- /docker-compose-ssl.yml: -------------------------------------------------------------------------------- 1 | services: 2 | mock-oauth2-server: 3 | image: ghcr.io/navikt/mock-oauth2-server:2.1.11 4 | ports: 5 | - "8080:8080" 6 | hostname: host.docker.internal 7 | volumes: 8 | - ./config-ssl.json:/app/config.json:Z 9 | environment: 10 | LOG_LEVEL: "debug" 11 | SERVER_PORT: 8080 12 | JSON_CONFIG_PATH: /app/config.json 13 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oauth-xx/oauth2/75bae598fc513b2ef8f0d47abab9a8eb877036ec/docs/.nojekyll -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | oauth2.galtzo.com -------------------------------------------------------------------------------- /docs/OAuth2/Strategy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Module: OAuth2::Strategy 8 | 9 | — Documentation by YARD 0.9.37 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Module: OAuth2::Strategy 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
80 |
Defined in:
81 |
lib/oauth2/strategy/base.rb,
82 | lib/oauth2/strategy/implicit.rb,
lib/oauth2/strategy/password.rb,
lib/oauth2/strategy/assertion.rb,
lib/oauth2/strategy/auth_code.rb,
lib/oauth2/strategy/client_credentials.rb
83 |
84 |
85 | 86 |
87 | 88 |

Defined Under Namespace

89 |

90 | 91 | 92 | 93 | 94 | Classes: Assertion, AuthCode, Base, ClientCredentials, Implicit, Password 95 | 96 | 97 |

98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 |
108 | 109 | 114 | 115 |
116 | 117 | -------------------------------------------------------------------------------- /docs/OAuth2/Strategy/Base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Class: OAuth2::Strategy::Base 8 | 9 | — Documentation by YARD 0.9.37 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Class: OAuth2::Strategy::Base 63 | 64 | 65 | 66 |

67 |
68 | 69 |
70 |
Inherits:
71 |
72 | Object 73 | 74 |
    75 |
  • Object
  • 76 | 77 | 78 | 79 |
80 | show all 81 | 82 |
83 |
84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
96 |
Defined in:
97 |
lib/oauth2/strategy/base.rb
98 |
99 | 100 |
101 | 102 |
103 |

Direct Known Subclasses

104 |

Assertion, AuthCode, ClientCredentials, Implicit, Password

105 |
106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 |

115 | Instance Method Summary 116 | collapse 117 |

118 | 119 |
    120 | 121 |
  • 122 | 123 | 124 | #initialize(client) ⇒ Base 125 | 126 | 127 | 128 | 129 | 130 | 131 | constructor 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 |

    A new instance of Base.

    141 |
    142 | 143 |
  • 144 | 145 | 146 |
147 | 148 | 149 |
150 |

Constructor Details

151 | 152 |
153 |

154 | 155 | #initialize(client) ⇒ Base 156 | 157 | 158 | 159 | 160 | 161 |

162 |
163 |

Returns a new instance of Base.

164 | 165 | 166 |
167 |
168 |
169 | 170 | 171 |
172 | 173 | 181 | 188 | 189 |
174 |
175 | 
176 | 
177 | 6
178 | 7
179 | 8
180 |
182 |
# File 'lib/oauth2/strategy/base.rb', line 6
183 | 
184 | def initialize(client)
185 |   @client = client
186 | end
187 |
190 |
191 | 192 |
193 | 194 | 195 |
196 | 197 | 202 | 203 |
204 | 205 | -------------------------------------------------------------------------------- /docs/OAuth2/Version.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Module: OAuth2::Version 8 | 9 | — Documentation by YARD 0.9.37 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Module: OAuth2::Version 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
80 |
Defined in:
81 |
lib/oauth2/version.rb
82 |
83 | 84 |
85 | 86 | 87 | 88 |

89 | Constant Summary 90 | collapse 91 |

92 | 93 |
94 | 95 |
VERSION = 96 | 97 |
98 |
"2.0.12"
99 | 100 |
101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 |
112 | 113 | 118 | 119 |
120 | 121 | -------------------------------------------------------------------------------- /docs/css/common.css: -------------------------------------------------------------------------------- 1 | /* Override this file with custom rules */ -------------------------------------------------------------------------------- /docs/css/full_list.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: "Lucida Sans", "Lucida Grande", Verdana, Arial, sans-serif; 4 | font-size: 13px; 5 | height: 101%; 6 | overflow-x: hidden; 7 | background: #fafafa; 8 | } 9 | 10 | h1 { padding: 12px 10px; padding-bottom: 0; margin: 0; font-size: 1.4em; } 11 | .clear { clear: both; } 12 | .fixed_header { position: fixed; background: #fff; width: 100%; padding-bottom: 10px; margin-top: 0; top: 0; z-index: 9999; height: 70px; } 13 | #search { position: absolute; right: 5px; top: 9px; padding-left: 24px; } 14 | #content.insearch #search, #content.insearch #noresults { background: url(data:image/gif;base64,R0lGODlhEAAQAPYAAP///wAAAPr6+pKSkoiIiO7u7sjIyNjY2J6engAAAI6OjsbGxjIyMlJSUuzs7KamppSUlPLy8oKCghwcHLKysqSkpJqamvT09Pj4+KioqM7OzkRERAwMDGBgYN7e3ujo6Ly8vCoqKjY2NkZGRtTU1MTExDw8PE5OTj4+PkhISNDQ0MrKylpaWrS0tOrq6nBwcKysrLi4uLq6ul5eXlxcXGJiYoaGhuDg4H5+fvz8/KKiohgYGCwsLFZWVgQEBFBQUMzMzDg4OFhYWBoaGvDw8NbW1pycnOLi4ubm5kBAQKqqqiQkJCAgIK6urnJyckpKSjQ0NGpqatLS0sDAwCYmJnx8fEJCQlRUVAoKCggICLCwsOTk5ExMTPb29ra2tmZmZmhoaNzc3KCgoBISEiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCAAAACwAAAAAEAAQAAAHaIAAgoMgIiYlg4kACxIaACEJCSiKggYMCRselwkpghGJBJEcFgsjJyoAGBmfggcNEx0flBiKDhQFlIoCCA+5lAORFb4AJIihCRbDxQAFChAXw9HSqb60iREZ1omqrIPdJCTe0SWI09GBACH5BAkIAAAALAAAAAAQABAAAAdrgACCgwc0NTeDiYozCQkvOTo9GTmDKy8aFy+NOBA7CTswgywJDTIuEjYFIY0JNYMtKTEFiRU8Pjwygy4ws4owPyCKwsMAJSTEgiQlgsbIAMrO0dKDGMTViREZ14kYGRGK38nHguHEJcvTyIEAIfkECQgAAAAsAAAAABAAEAAAB2iAAIKDAggPg4iJAAMJCRUAJRIqiRGCBI0WQEEJJkWDERkYAAUKEBc4Po1GiKKJHkJDNEeKig4URLS0ICImJZAkuQAhjSi/wQyNKcGDCyMnk8u5rYrTgqDVghgZlYjcACTA1sslvtHRgQAh+QQJCAAAACwAAAAAEAAQAAAHZ4AAgoOEhYaCJSWHgxGDJCQARAtOUoQRGRiFD0kJUYWZhUhKT1OLhR8wBaaFBzQ1NwAlkIszCQkvsbOHL7Y4q4IuEjaqq0ZQD5+GEEsJTDCMmIUhtgk1lo6QFUwJVDKLiYJNUd6/hoEAIfkECQgAAAAsAAAAABAAEAAAB2iAAIKDhIWGgiUlh4MRgyQkjIURGRiGGBmNhJWHm4uen4ICCA+IkIsDCQkVACWmhwSpFqAABQoQF6ALTkWFnYMrVlhWvIKTlSAiJiVVPqlGhJkhqShHV1lCW4cMqSkAR1ofiwsjJyqGgQAh+QQJCAAAACwAAAAAEAAQAAAHZ4AAgoOEhYaCJSWHgxGDJCSMhREZGIYYGY2ElYebi56fhyWQniSKAKKfpaCLFlAPhl0gXYNGEwkhGYREUywag1wJwSkHNDU3D0kJYIMZQwk8MjPBLx9eXwuETVEyAC/BOKsuEjYFhoEAIfkECQgAAAAsAAAAABAAEAAAB2eAAIKDhIWGgiUlh4MRgyQkjIURGRiGGBmNhJWHm4ueICImip6CIQkJKJ4kigynKaqKCyMnKqSEK05StgAGQRxPYZaENqccFgIID4KXmQBhXFkzDgOnFYLNgltaSAAEpxa7BQoQF4aBACH5BAkIAAAALAAAAAAQABAAAAdogACCg4SFggJiPUqCJSWGgkZjCUwZACQkgxGEXAmdT4UYGZqCGWQ+IjKGGIUwPzGPhAc0NTewhDOdL7Ykji+dOLuOLhI2BbaFETICx4MlQitdqoUsCQ2vhKGjglNfU0SWmILaj43M5oEAOwAAAAAAAAAAAA==) no-repeat center left; } 15 | #full_list { padding: 0; list-style: none; margin-left: 0; margin-top: 80px; font-size: 1.1em; } 16 | #full_list ul { padding: 0; } 17 | #full_list li { padding: 0; margin: 0; list-style: none; } 18 | #full_list li .item { padding: 5px 5px 5px 12px; } 19 | #noresults { padding: 7px 12px; background: #fff; } 20 | #content.insearch #noresults { margin-left: 7px; } 21 | li.collapsed ul { display: none; } 22 | li a.toggle { cursor: default; position: relative; left: -5px; top: 4px; text-indent: -999px; width: 10px; height: 9px; margin-left: -10px; display: block; float: left; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK8AAACvABQqw0mAAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTM5jWRgMAAAAVdEVYdENyZWF0aW9uIFRpbWUAMy8xNC8wOeNZPpQAAAE2SURBVDiNrZTBccIwEEXfelIAHUA6CZ24BGaWO+FuzZAK4k6gg5QAdGAq+Bxs2Yqx7BzyL7Llp/VfzZeQhCTc/ezuGzKKnKSzpCxXJM8fwNXda3df5RZETlIt6YUzSQDs93sl8w3wBZxCCE10GM1OcWbWjB2mWgEH4Mfdyxm3PSepBHibgQE2wLe7r4HjEidpnXMYdQPKEMJcsZ4zs2POYQOcaPfwMVOo58zsAdMt18BuoVDPxUJRacELbXv3hUIX2vYmOUvi8C8ydz/ThjXrqKqqLbDIAdsCKBd+Wo7GWa7o9qzOQHVVVXeAbs+yHHCH4aTsaCOQqunmUy1yBUAXkdMIfMlgF5EXLo2OpV/c/Up7jG4hhHcYLgWzAZXUc2b2ixsfvc/RmNNfOXD3Q/oeL9axJE1yT9IOoUu6MGUkAAAAAElFTkSuQmCC) no-repeat bottom left; } 23 | li.collapsed a.toggle { cursor: default; background-position: top left; } 24 | li { color: #666; cursor: pointer; } 25 | li.deprecated { text-decoration: line-through; font-style: italic; } 26 | li.odd { background: #f0f0f0; } 27 | li.even { background: #fafafa; } 28 | .item:hover { background: #ddd; } 29 | li small:before { content: "("; } 30 | li small:after { content: ")"; } 31 | li small.search_info { display: none; } 32 | a, a:visited { text-decoration: none; color: #05a; } 33 | li.clicked > .item { background: #05a; color: #ccc; } 34 | li.clicked > .item a, li.clicked > .item a:visited { color: #eee; } 35 | li.clicked > .item a.toggle { opacity: 0.5; background-position: bottom right; } 36 | li.collapsed.clicked a.toggle { background-position: top right; } 37 | #search input { border: 1px solid #bbb; border-radius: 3px; } 38 | #full_list_nav { margin-left: 10px; font-size: 0.9em; display: block; color: #aaa; } 39 | #full_list_nav a, #nav a:visited { color: #358; } 40 | #full_list_nav a:hover { background: transparent; color: #5af; } 41 | #full_list_nav span:after { content: ' | '; } 42 | #full_list_nav span:last-child:after { content: ''; } 43 | 44 | #content h1 { margin-top: 0; } 45 | li { white-space: nowrap; cursor: normal; } 46 | li small { display: block; font-size: 0.8em; } 47 | li small:before { content: ""; } 48 | li small:after { content: ""; } 49 | li small.search_info { display: none; } 50 | #search { width: 170px; position: static; margin: 3px; margin-left: 10px; font-size: 0.9em; color: #666; padding-left: 0; padding-right: 24px; } 51 | #content.insearch #search { background-position: center right; } 52 | #search input { width: 110px; } 53 | 54 | #full_list.insearch ul { display: block; } 55 | #full_list.insearch .item { display: none; } 56 | #full_list.insearch .found { display: block; padding-left: 11px !important; } 57 | #full_list.insearch li a.toggle { display: none; } 58 | #full_list.insearch li small.search_info { display: block; } 59 | -------------------------------------------------------------------------------- /docs/file.CITATION.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | File: CITATION 8 | 9 | — Documentation by YARD 0.9.37 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 59 | 60 |

cff-version: 1.2.0
61 | title: oauth2
62 | message: >-
63 | If you use this work and you want to cite it,
64 | then you can use the metadata from this file.
65 | type: software
66 | authors:

67 |
    68 |
  • given-names: Peter Hurn
    69 | family-names: Boling
    70 | email: peter@railsbling.com
    71 | affiliation: railsbling.com
    72 | orcid: ‘https://orcid.org/0009-0008-8519-441X’
  • 73 |
  • given-names: Aboling0
    74 | email: aboling@railsbling.com
    75 | affiliation: railsbling.com
    76 | identifiers:
  • 77 |
  • type: url
    78 | value: ‘https://github.com/oauth-xx/oauth2’
    79 | description: oauth2
    80 | repository-code: ‘https://github.com/oauth-xx/oauth2’
    81 | abstract: >-
    82 | oauth2
    83 | license: See license file
  • 84 |
85 |
86 | 87 | 92 | 93 |
94 | 95 | -------------------------------------------------------------------------------- /docs/file.LICENSE.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | File: LICENSE 8 | 9 | — Documentation by YARD 0.9.37 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 59 | 60 |
MIT License

Copyright (c) 2017-2025 Peter H. Boling, of Galtzo.com, and oauth2 contributors
Copyright (c) 2011-2013 Michael Bleigh and Intridea, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
61 | 62 | 67 | 68 |
69 | 70 | -------------------------------------------------------------------------------- /docs/file.SECURITY.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | File: SECURITY 8 | 9 | — Documentation by YARD 0.9.37 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 59 | 60 |

Security Policy

61 | 62 |

Supported Versions

63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 |
VersionSupportedPost-EOL / Enterprise
2.latestTidelift Subscription
1.latestTidelift Subscription
<= 1
90 | 91 |

EOL Policy

92 | 93 |

Non-commercial support for the oldest version of Ruby (which itself is going EOL) will be dropped each year in April.

94 | 95 |

Security contact information

96 | 97 |

To report a security vulnerability, please use the
98 | Tidelift security contact.
99 | Tidelift will coordinate the fix and disclosure.

100 | 101 |

Additional Support

102 | 103 |

If you are interested in support for versions older than the latest release,
104 | please consider sponsoring the project / maintainer @ https://liberapay.com/pboling/donate,
105 | or find other sponsorship links in the README.

106 | 107 |

Enterprise Support

108 | 109 |

Available as part of the Tidelift Subscription.

110 | 111 |

The maintainers of this library and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. Learn more.

112 | 113 |
114 | 115 | 120 | 121 |
122 | 123 | -------------------------------------------------------------------------------- /docs/file_list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | File List 19 | 20 | 21 | 22 |
23 |
24 |

File List

25 |
26 | 27 | 28 | Classes 29 | 30 | 31 | 32 | Methods 33 | 34 | 35 | 36 | Files 37 | 38 | 39 |
40 | 41 | 45 |
46 | 47 | 87 |
88 | 89 | 90 | -------------------------------------------------------------------------------- /docs/frames.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Documentation by YARD 0.9.37 6 | 7 | 18 | 22 | 23 | -------------------------------------------------------------------------------- /docs/images/logo/README.txt: -------------------------------------------------------------------------------- 1 | Galtzo.com Logos 2 | - galtzo-floss-logos-original.svg 3 | - galtzo-floss-logos-wordless.svg 4 | 5 | © 2025 by Aboling0 (https://github.com/Aboling0) 6 | 7 | Licensed under CC BY-SA 4.0 8 | 9 | https://creativecommons.org/licenses/by-sa/4.0/ 10 | 11 | --- 12 | 13 | The OAuth 2.0 Logo - oauth2-logo-124px.png (resized) 14 | 15 | https://oauth.net/about/credits/ 16 | 17 | The OAuth logo was designed by Chris Messina. 18 | 19 | --- 20 | 21 | The Ruby Logo - ruby-logo-198px.svg 22 | 23 | https://www.ruby-lang.org/en/about/logo/ 24 | 25 | Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5 26 | 27 | https://creativecommons.org/licenses/by-sa/2.5 28 | -------------------------------------------------------------------------------- /docs/images/logo/galtzo-floss-logos-wordless.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/images/logo/oauth2-logo-124px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oauth-xx/oauth2/75bae598fc513b2ef8f0d47abab9a8eb877036ec/docs/images/logo/oauth2-logo-124px.png -------------------------------------------------------------------------------- /docs/top-level-namespace.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Top Level Namespace 8 | 9 | — Documentation by YARD 0.9.37 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Top Level Namespace 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
80 | 81 |

Defined Under Namespace

82 |

83 | 84 | 85 | Modules: OAuth2 86 | 87 | 88 | 89 | 90 |

91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 |
101 | 102 | 107 | 108 |
109 | 110 | -------------------------------------------------------------------------------- /gemfiles/audit.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "mutex_m", "~> 0.2" 6 | gem "stringio", "~> 3.0" 7 | 8 | gemspec path: "../" 9 | 10 | eval_gemfile("modular/audit.gemfile") 11 | 12 | eval_gemfile("modular/faraday_v2.gemfile") 13 | 14 | eval_gemfile("modular/hashie_v5.gemfile") 15 | 16 | eval_gemfile("modular/jwt_v2.gemfile") 17 | 18 | eval_gemfile("modular/logger_v1_7.gemfile") 19 | 20 | eval_gemfile("modular/multi_xml_v0_7.gemfile") 21 | 22 | eval_gemfile("modular/rack_v3.gemfile") 23 | -------------------------------------------------------------------------------- /gemfiles/coverage.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "mutex_m", "~> 0.2" 6 | gem "stringio", "~> 3.0" 7 | 8 | gemspec path: "../" 9 | 10 | eval_gemfile("modular/coverage.gemfile") 11 | 12 | eval_gemfile("modular/faraday_v2.gemfile") 13 | 14 | eval_gemfile("modular/hashie_v5.gemfile") 15 | 16 | eval_gemfile("modular/jwt_v2.gemfile") 17 | 18 | eval_gemfile("modular/logger_v1_7.gemfile") 19 | 20 | eval_gemfile("modular/multi_xml_v0_7.gemfile") 21 | 22 | eval_gemfile("modular/rack_v3.gemfile") 23 | -------------------------------------------------------------------------------- /gemfiles/current.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "mutex_m", ">= 0.2" 6 | gem "stringio", ">= 3.0" 7 | 8 | gemspec path: "../" 9 | 10 | eval_gemfile("modular/faraday_v2.gemfile") 11 | 12 | eval_gemfile("modular/hashie_v5.gemfile") 13 | 14 | eval_gemfile("modular/jwt_v3.gemfile") 15 | 16 | eval_gemfile("modular/logger_v1_7.gemfile") 17 | 18 | eval_gemfile("modular/multi_xml_v0_7.gemfile") 19 | 20 | eval_gemfile("modular/rack_v3.gemfile") 21 | -------------------------------------------------------------------------------- /gemfiles/current_runtime_heads.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "mutex_m", ">= 0.2" 6 | gem "stringio", ">= 3.0" 7 | 8 | gemspec path: "../" 9 | 10 | eval_gemfile("modular/runtime_heads.gemfile") 11 | -------------------------------------------------------------------------------- /gemfiles/head.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "mutex_m", ">= 0.2" 6 | gem "stringio", ">= 3.0" 7 | 8 | gemspec path: "../" 9 | 10 | eval_gemfile("modular/runtime_heads.gemfile") 11 | -------------------------------------------------------------------------------- /gemfiles/modular/audit.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Many gems are dropping support for Ruby < 3, 4 | # so we only want to run our security audit in CI on Ruby 3+ 5 | gem "bundler-audit", "~> 0.9.2" 6 | -------------------------------------------------------------------------------- /gemfiles/modular/coverage.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # We run code coverage on the latest version of Ruby only. 4 | 5 | # Coverage 6 | gem "kettle-soup-cover", "~> 1.0", ">= 1.0.6", require: false 7 | -------------------------------------------------------------------------------- /gemfiles/modular/documentation.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Documentation 4 | gem "kramdown", "~> 2.5", ">= 2.5.1" # Ruby >= 2.5 5 | gem "kramdown-parser-gfm", "~> 1.1" # Ruby >= 2.3 6 | gem "yard", "~> 0.9", ">= 0.9.37", require: false 7 | gem "yard-junk", "~> 0.0", ">= 0.0.10", github: "pboling/yard-junk", branch: "next", require: false 8 | gem "yard-relative_markdown_links", "~> 0.5.0" 9 | 10 | # Std Lib extractions 11 | gem "rdoc", "~> 6.11" 12 | -------------------------------------------------------------------------------- /gemfiles/modular/faraday_v0.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 1.9 4 | # 0.17.3 is the first version to not use &Proc.new when forwarding blocks. 5 | # Thus, it is the oldest version oauth2 is compatible with. 6 | # This version of faraday is the last compatible with Ruby 1.9, 2.0, 2.1, 2.2, and 2.3: 7 | gem "faraday", "~> 0.17.6" 8 | -------------------------------------------------------------------------------- /gemfiles/modular/faraday_v1.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Last version compatible with Ruby 2.4, and 2.5 4 | gem "faraday", "~> 1.10", ">= 1.10.4" 5 | -------------------------------------------------------------------------------- /gemfiles/modular/faraday_v2.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # last version that will install on Ruby 2.6 & 2.7 4 | gem "faraday", "~> 2.8", ">=2.8.1" 5 | -------------------------------------------------------------------------------- /gemfiles/modular/hashie_v0.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 0 4 | # We will test it against Ruby 2.3. 5 | gem "hashie", "~> 0.4", ">= 0.4.0" 6 | -------------------------------------------------------------------------------- /gemfiles/modular/hashie_v1.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 0 4 | # We will test it against Ruby 2.4. 5 | gem "hashie", "~> 1.2", ">= 1.2.0" 6 | -------------------------------------------------------------------------------- /gemfiles/modular/hashie_v2.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 0 4 | # We will test it against Ruby 2.5. 5 | gem "hashie", "~> 2.1", ">= 2.1.2" 6 | -------------------------------------------------------------------------------- /gemfiles/modular/hashie_v3.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 0 4 | # We will test it against Ruby 2.6. 5 | gem "hashie", "~> 3.6", ">= 3.6.0" 6 | -------------------------------------------------------------------------------- /gemfiles/modular/hashie_v4.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 0 4 | # We will test it against Ruby 2.7. 5 | gem "hashie", "~> 4.1", ">= 4.1.0" 6 | -------------------------------------------------------------------------------- /gemfiles/modular/hashie_v5.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 0 4 | # We will test it against Ruby 3.0+. 5 | gem "hashie", "~> 5.0", ">= 5.0.0" 6 | -------------------------------------------------------------------------------- /gemfiles/modular/jwt_v1.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 0 4 | # This version of jwt is the last compatible with Ruby 1.9, 2.0, 2.1, 2.2, 2.3, 2.4: 5 | gem "jwt", "~> 1.5", ">= 1.5.6" 6 | -------------------------------------------------------------------------------- /gemfiles/modular/jwt_v2.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 2.5 4 | gem "jwt", "~> 2.10", ">= 2.10.1" 5 | -------------------------------------------------------------------------------- /gemfiles/modular/jwt_v3.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 2.5 4 | gem "jwt", ">= 3.0.0.beta1", "< 4" 5 | -------------------------------------------------------------------------------- /gemfiles/modular/logger_v1_2.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 2.3 4 | # This version of logger is the last compatible with Ruby 1.8. 1.9, 2.0, 2.1, 2.2: 5 | # We will test it against 2.3, and 2.4. 6 | gem "logger", "~> 1.2.8", ">= 1.2.8.1" 7 | -------------------------------------------------------------------------------- /gemfiles/modular/logger_v1_5.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 2.3 4 | # This version of logger is the last compatible with Ruby 2.3, 2.4: 5 | # We will test it against 2.5, and 2.6. 6 | gem "logger", "~> 1.5.3" 7 | -------------------------------------------------------------------------------- /gemfiles/modular/logger_v1_7.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 2.5 4 | # We will test it against Ruby 2.7, 3.0, 3.1, 3.2, 3.3, 3.4. 5 | gem "logger", "~> 1.7", ">= 1.7.0" 6 | -------------------------------------------------------------------------------- /gemfiles/modular/multi_xml_v0_5.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 0 4 | # We will test it against Ruby 2.3, 2.4 5 | # This minor version of multi_xml is the oldest compatible with this gem: 6 | gem "multi_xml", "~> 0.5", ">= 0.5.5" 7 | -------------------------------------------------------------------------------- /gemfiles/modular/multi_xml_v0_6.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 0 4 | # We will test it against Ruby 2.5, 2.6, 2.7, 3.0, 3.1. 5 | # This version of multi_xml is the last compatible with Ruby 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 3.0: 6 | gem "multi_xml", "~> 0.6", ">= 0.6.0" 7 | -------------------------------------------------------------------------------- /gemfiles/modular/multi_xml_v0_7.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 2.5 4 | # We will test it against Ruby 2.5, 2.6, 2.7, 3.0, 3.1, 3.2, 3.3, 3.4. 5 | gem "multi_xml", "~> 0.7", ">= 0.7.2" 6 | -------------------------------------------------------------------------------- /gemfiles/modular/rack_v1_2.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 0 4 | # We will test it against Ruby 2.3. 5 | # This minor version of rack is the oldest compatible with this gem: 6 | gem "rack", "~> 1.2", ">= 1.2.8" 7 | -------------------------------------------------------------------------------- /gemfiles/modular/rack_v1_6.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 0 4 | # This is the newest version of rack that installs on Ruby < 2.2.2. 5 | # We will test it against Ruby 2.4. 6 | gem "rack", "~> 1.6", ">= 1.6.13" 7 | -------------------------------------------------------------------------------- /gemfiles/modular/rack_v2.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 2.3 4 | # This version of rack is the last compatible with Ruby 2.3. 5 | # We will test it against Ruby 2.5. 6 | gem "rack", "~> 2.2", ">= 2.2.14" 7 | -------------------------------------------------------------------------------- /gemfiles/modular/rack_v3.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Ruby >= 2.4 4 | # We will test it against Ruby 2.6+. 5 | gem "rack", "~> 3.1", ">= 3.1.14" 6 | -------------------------------------------------------------------------------- /gemfiles/modular/runtime_heads.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Test against HEAD of runtime dependencies so we can proactively file bugs 4 | 5 | # Ruby >= 3.0 6 | gem "faraday", github: "lostisland/faraday", branch: "main" 7 | 8 | # Ruby >= 0 9 | gem "hashie", github: "hashie/hashie", branch: "master" 10 | 11 | # Ruby >= 2.5 12 | gem "jwt", github: "jwt/ruby-jwt", branch: "main" 13 | 14 | # Ruby >= 2.5 15 | gem "logger", github: "ruby/logger", branch: "master" 16 | 17 | # Ruby >= 3.2 18 | gem "multi_xml", github: "sferik/multi_xml", branch: "master" 19 | 20 | # Ruby >= 2.4 21 | gem "rack", github: "rack/rack", branch: "main" 22 | 23 | # Ruby >= 2.2 24 | gem "version_gem", github: "oauth-xx/version_gem", branch: "main" 25 | 26 | # Ruby >= 2.2 27 | gem "snaky_hash", github: "oauth-xx/snaky_hash", branch: "main" 28 | -------------------------------------------------------------------------------- /gemfiles/modular/style.gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # We run rubocop on the latest version of Ruby, 4 | # but in support of the oldest supported version of Ruby 5 | 6 | gem "reek", "~> 6.4" 7 | gem "rubocop", "~> 1.73", ">= 1.73.2" 8 | gem "rubocop-packaging", "~> 0.5", ">= 0.5.2" 9 | gem "standard", "~> 1.47" 10 | 11 | # Std Lib extractions 12 | gem "benchmark", "~> 0.4" # Removed from Std Lib in Ruby 3.5 13 | 14 | if ENV.fetch("RUBOCOP_LTS_LOCAL", "false").casecmp("true").zero? 15 | home = ENV["HOME"] 16 | gem "rubocop-lts", path: "#{home}/src/rubocop-lts/rubocop-lts" 17 | gem "rubocop-lts-rspec", path: "#{home}/src/rubocop-lts/rubocop-lts-rspec" 18 | gem "rubocop-ruby2_2", path: "#{home}/src/rubocop-lts/rubocop-ruby2_2" 19 | gem "standard-rubocop-lts", path: "#{home}/src/rubocop-lts/standard-rubocop-lts" 20 | else 21 | gem "rubocop-lts", "~> 8.1", ">= 8.1.1" # Linting for Ruby >= 2.2 22 | gem "rubocop-rspec", "~> 3.2" 23 | end 24 | -------------------------------------------------------------------------------- /gemfiles/omnibus.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gemspec path: "../" 6 | 7 | eval_gemfile("modular/audit.gemfile") 8 | 9 | eval_gemfile("modular/coverage.gemfile") 10 | 11 | eval_gemfile("modular/documentation.gemfile") 12 | 13 | eval_gemfile("modular/faraday_v2.gemfile") 14 | 15 | eval_gemfile("modular/hashie_v5.gemfile") 16 | 17 | eval_gemfile("modular/jwt_v2.gemfile") 18 | 19 | eval_gemfile("modular/logger_v1_7.gemfile") 20 | 21 | eval_gemfile("modular/multi_xml_v0_7.gemfile") 22 | 23 | eval_gemfile("modular/rack_v3.gemfile") 24 | 25 | eval_gemfile("modular/style.gemfile") 26 | -------------------------------------------------------------------------------- /gemfiles/ruby_2_3_hashie_v0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gemspec path: "../" 6 | 7 | eval_gemfile("modular/faraday_v0.gemfile") 8 | 9 | eval_gemfile("modular/hashie_v0.gemfile") 10 | 11 | eval_gemfile("modular/jwt_v1.gemfile") 12 | 13 | eval_gemfile("modular/logger_v1_2.gemfile") 14 | 15 | eval_gemfile("modular/multi_xml_v0_5.gemfile") 16 | 17 | eval_gemfile("modular/rack_v1_2.gemfile") 18 | -------------------------------------------------------------------------------- /gemfiles/ruby_2_3_hashie_v1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gemspec path: "../" 6 | 7 | eval_gemfile("modular/faraday_v0.gemfile") 8 | 9 | eval_gemfile("modular/hashie_v1.gemfile") 10 | 11 | eval_gemfile("modular/jwt_v1.gemfile") 12 | 13 | eval_gemfile("modular/logger_v1_2.gemfile") 14 | 15 | eval_gemfile("modular/multi_xml_v0_5.gemfile") 16 | 17 | eval_gemfile("modular/rack_v1_2.gemfile") 18 | -------------------------------------------------------------------------------- /gemfiles/ruby_2_3_hashie_v2.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gemspec path: "../" 6 | 7 | eval_gemfile("modular/faraday_v0.gemfile") 8 | 9 | eval_gemfile("modular/hashie_v2.gemfile") 10 | 11 | eval_gemfile("modular/jwt_v1.gemfile") 12 | 13 | eval_gemfile("modular/logger_v1_2.gemfile") 14 | 15 | eval_gemfile("modular/multi_xml_v0_5.gemfile") 16 | 17 | eval_gemfile("modular/rack_v1_2.gemfile") 18 | -------------------------------------------------------------------------------- /gemfiles/ruby_2_3_hashie_v3.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gemspec path: "../" 6 | 7 | eval_gemfile("modular/faraday_v0.gemfile") 8 | 9 | eval_gemfile("modular/hashie_v3.gemfile") 10 | 11 | eval_gemfile("modular/jwt_v1.gemfile") 12 | 13 | eval_gemfile("modular/logger_v1_2.gemfile") 14 | 15 | eval_gemfile("modular/multi_xml_v0_5.gemfile") 16 | 17 | eval_gemfile("modular/rack_v1_2.gemfile") 18 | -------------------------------------------------------------------------------- /gemfiles/ruby_2_3_hashie_v4.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gemspec path: "../" 6 | 7 | eval_gemfile("modular/faraday_v0.gemfile") 8 | 9 | eval_gemfile("modular/hashie_v4.gemfile") 10 | 11 | eval_gemfile("modular/jwt_v1.gemfile") 12 | 13 | eval_gemfile("modular/logger_v1_2.gemfile") 14 | 15 | eval_gemfile("modular/multi_xml_v0_5.gemfile") 16 | 17 | eval_gemfile("modular/rack_v1_2.gemfile") 18 | -------------------------------------------------------------------------------- /gemfiles/ruby_2_3_hashie_v5.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gemspec path: "../" 6 | 7 | eval_gemfile("modular/faraday_v0.gemfile") 8 | 9 | eval_gemfile("modular/hashie_v5.gemfile") 10 | 11 | eval_gemfile("modular/jwt_v1.gemfile") 12 | 13 | eval_gemfile("modular/logger_v1_2.gemfile") 14 | 15 | eval_gemfile("modular/multi_xml_v0_5.gemfile") 16 | 17 | eval_gemfile("modular/rack_v1_2.gemfile") 18 | -------------------------------------------------------------------------------- /gemfiles/ruby_2_4.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gemspec path: "../" 6 | 7 | eval_gemfile("modular/faraday_v1.gemfile") 8 | 9 | eval_gemfile("modular/hashie_v1.gemfile") 10 | 11 | eval_gemfile("modular/jwt_v1.gemfile") 12 | 13 | eval_gemfile("modular/logger_v1_2.gemfile") 14 | 15 | eval_gemfile("modular/multi_xml_v0_5.gemfile") 16 | 17 | eval_gemfile("modular/rack_v1_6.gemfile") 18 | -------------------------------------------------------------------------------- /gemfiles/ruby_2_5.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gemspec path: "../" 6 | 7 | eval_gemfile("modular/faraday_v1.gemfile") 8 | 9 | eval_gemfile("modular/hashie_v2.gemfile") 10 | 11 | eval_gemfile("modular/jwt_v2.gemfile") 12 | 13 | eval_gemfile("modular/logger_v1_5.gemfile") 14 | 15 | eval_gemfile("modular/multi_xml_v0_6.gemfile") 16 | 17 | eval_gemfile("modular/rack_v2.gemfile") 18 | -------------------------------------------------------------------------------- /gemfiles/ruby_2_6.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "mutex_m", "~> 0.2" 6 | gem "stringio", "~> 3.0" 7 | 8 | gemspec path: "../" 9 | 10 | eval_gemfile("modular/faraday_v2.gemfile") 11 | 12 | eval_gemfile("modular/hashie_v3.gemfile") 13 | 14 | eval_gemfile("modular/jwt_v2.gemfile") 15 | 16 | eval_gemfile("modular/logger_v1_5.gemfile") 17 | 18 | eval_gemfile("modular/multi_xml_v0_6.gemfile") 19 | 20 | eval_gemfile("modular/rack_v3.gemfile") 21 | -------------------------------------------------------------------------------- /gemfiles/ruby_2_7.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "mutex_m", "~> 0.2" 6 | gem "stringio", "~> 3.0" 7 | 8 | gemspec path: "../" 9 | 10 | eval_gemfile("modular/faraday_v2.gemfile") 11 | 12 | eval_gemfile("modular/hashie_v4.gemfile") 13 | 14 | eval_gemfile("modular/jwt_v2.gemfile") 15 | 16 | eval_gemfile("modular/logger_v1_7.gemfile") 17 | 18 | eval_gemfile("modular/multi_xml_v0_6.gemfile") 19 | 20 | eval_gemfile("modular/rack_v3.gemfile") 21 | -------------------------------------------------------------------------------- /gemfiles/ruby_3_0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "mutex_m", "~> 0.2" 6 | gem "stringio", "~> 3.0" 7 | 8 | gemspec path: "../" 9 | 10 | eval_gemfile("modular/faraday_v2.gemfile") 11 | 12 | eval_gemfile("modular/hashie_v5.gemfile") 13 | 14 | eval_gemfile("modular/jwt_v2.gemfile") 15 | 16 | eval_gemfile("modular/logger_v1_7.gemfile") 17 | 18 | eval_gemfile("modular/multi_xml_v0_6.gemfile") 19 | 20 | eval_gemfile("modular/rack_v3.gemfile") 21 | -------------------------------------------------------------------------------- /gemfiles/ruby_3_1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "mutex_m", "~> 0.2" 6 | gem "stringio", "~> 3.0" 7 | 8 | gemspec path: "../" 9 | 10 | eval_gemfile("modular/faraday_v2.gemfile") 11 | 12 | eval_gemfile("modular/hashie_v5.gemfile") 13 | 14 | eval_gemfile("modular/jwt_v2.gemfile") 15 | 16 | eval_gemfile("modular/logger_v1_7.gemfile") 17 | 18 | eval_gemfile("modular/multi_xml_v0_6.gemfile") 19 | 20 | eval_gemfile("modular/rack_v3.gemfile") 21 | -------------------------------------------------------------------------------- /gemfiles/ruby_3_2.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "mutex_m", "~> 0.2" 6 | gem "stringio", "~> 3.0" 7 | 8 | gemspec path: "../" 9 | 10 | eval_gemfile("modular/faraday_v2.gemfile") 11 | 12 | eval_gemfile("modular/hashie_v5.gemfile") 13 | 14 | eval_gemfile("modular/jwt_v2.gemfile") 15 | 16 | eval_gemfile("modular/logger_v1_7.gemfile") 17 | 18 | eval_gemfile("modular/multi_xml_v0_7.gemfile") 19 | 20 | eval_gemfile("modular/rack_v3.gemfile") 21 | -------------------------------------------------------------------------------- /gemfiles/ruby_3_3.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "mutex_m", "~> 0.2" 6 | gem "stringio", "~> 3.0" 7 | 8 | gemspec path: "../" 9 | 10 | eval_gemfile("modular/faraday_v2.gemfile") 11 | 12 | eval_gemfile("modular/hashie_v5.gemfile") 13 | 14 | eval_gemfile("modular/jwt_v2.gemfile") 15 | 16 | eval_gemfile("modular/logger_v1_7.gemfile") 17 | 18 | eval_gemfile("modular/multi_xml_v0_7.gemfile") 19 | 20 | eval_gemfile("modular/rack_v3.gemfile") 21 | -------------------------------------------------------------------------------- /gemfiles/style.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "mutex_m", "~> 0.2" 6 | gem "stringio", "~> 3.0" 7 | 8 | gemspec path: "../" 9 | 10 | eval_gemfile("modular/style.gemfile") 11 | -------------------------------------------------------------------------------- /gemfiles/vanilla.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gemspec path: "../" 6 | -------------------------------------------------------------------------------- /lib/oauth2.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # includes modules from stdlib 4 | require "cgi" 5 | require "time" 6 | 7 | # third party gems 8 | require "snaky_hash" 9 | require "version_gem" 10 | 11 | # includes gem files 12 | require_relative "oauth2/version" 13 | require_relative "oauth2/filtered_attributes" 14 | require_relative "oauth2/error" 15 | require_relative "oauth2/authenticator" 16 | require_relative "oauth2/client" 17 | require_relative "oauth2/strategy/base" 18 | require_relative "oauth2/strategy/auth_code" 19 | require_relative "oauth2/strategy/implicit" 20 | require_relative "oauth2/strategy/password" 21 | require_relative "oauth2/strategy/client_credentials" 22 | require_relative "oauth2/strategy/assertion" 23 | require_relative "oauth2/access_token" 24 | require_relative "oauth2/response" 25 | 26 | # The namespace of this library 27 | module OAuth2 28 | OAUTH_DEBUG = ENV.fetch("OAUTH_DEBUG", "false").casecmp("true").zero? 29 | DEFAULT_CONFIG = SnakyHash::SymbolKeyed.new( 30 | silence_extra_tokens_warning: true, 31 | silence_no_tokens_warning: true, 32 | ) 33 | @config = DEFAULT_CONFIG.dup 34 | class << self 35 | attr_reader :config 36 | end 37 | def configure 38 | yield @config 39 | end 40 | module_function :configure 41 | end 42 | 43 | OAuth2::Version.class_eval do 44 | extend VersionGem::Basic 45 | end 46 | -------------------------------------------------------------------------------- /lib/oauth2/authenticator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "base64" 4 | 5 | module OAuth2 6 | class Authenticator 7 | include FilteredAttributes 8 | 9 | attr_reader :mode, :id, :secret 10 | filtered_attributes :secret 11 | 12 | def initialize(id, secret, mode) 13 | @id = id 14 | @secret = secret 15 | @mode = mode 16 | end 17 | 18 | # Apply the request credentials used to authenticate to the Authorization Server 19 | # 20 | # Depending on the configuration, this might be as request params or as an 21 | # Authorization header. 22 | # 23 | # User-provided params and header take precedence. 24 | # 25 | # @param [Hash] params a Hash of params for the token endpoint 26 | # @return [Hash] params amended with appropriate authentication details 27 | def apply(params) 28 | case mode.to_sym 29 | when :basic_auth 30 | apply_basic_auth(params) 31 | when :request_body 32 | apply_params_auth(params) 33 | when :tls_client_auth 34 | apply_client_id(params) 35 | when :private_key_jwt 36 | params 37 | else 38 | raise NotImplementedError 39 | end 40 | end 41 | 42 | def self.encode_basic_auth(user, password) 43 | "Basic #{Base64.strict_encode64("#{user}:#{password}")}" 44 | end 45 | 46 | private 47 | 48 | # Adds client_id and client_secret request parameters if they are not 49 | # already set. 50 | def apply_params_auth(params) 51 | result = {} 52 | result["client_id"] = id unless id.nil? 53 | result["client_secret"] = secret unless secret.nil? 54 | result.merge(params) 55 | end 56 | 57 | # When using schemes that don't require the client_secret to be passed i.e TLS Client Auth, 58 | # we don't want to send the secret 59 | def apply_client_id(params) 60 | result = {} 61 | result["client_id"] = id unless id.nil? 62 | result.merge(params) 63 | end 64 | 65 | # Adds an `Authorization` header with Basic Auth credentials if and only if 66 | # it is not already set in the params. 67 | def apply_basic_auth(params) 68 | headers = params.fetch(:headers, {}) 69 | headers = basic_auth_header.merge(headers) 70 | params.merge(headers: headers) 71 | end 72 | 73 | # @see https://datatracker.ietf.org/doc/html/rfc2617#section-2 74 | def basic_auth_header 75 | {"Authorization" => self.class.encode_basic_auth(id, secret)} 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /lib/oauth2/error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module OAuth2 4 | class Error < StandardError 5 | attr_reader :response, :body, :code, :description 6 | 7 | # standard error codes include: 8 | # 'invalid_request', 'invalid_client', 'invalid_token', 'invalid_grant', 'unsupported_grant_type', 'invalid_scope' 9 | # response might be a Response object, or the response.parsed hash 10 | def initialize(response) 11 | @response = response 12 | if response.respond_to?(:parsed) 13 | if response.parsed.is_a?(Hash) 14 | @code = response.parsed["error"] 15 | @description = response.parsed["error_description"] 16 | end 17 | elsif response.is_a?(Hash) 18 | @code = response["error"] 19 | @description = response["error_description"] 20 | end 21 | @body = if response.respond_to?(:body) 22 | response.body 23 | else 24 | @response 25 | end 26 | message_opts = parse_error_description(@code, @description) 27 | super(error_message(@body, message_opts)) 28 | end 29 | 30 | private 31 | 32 | def error_message(response_body, opts = {}) 33 | lines = [] 34 | 35 | lines << opts[:error_description] if opts[:error_description] 36 | 37 | error_string = if response_body.respond_to?(:encode) && opts[:error_description].respond_to?(:encoding) 38 | script_encoding = opts[:error_description].encoding 39 | response_body.encode(script_encoding, invalid: :replace, undef: :replace) 40 | else 41 | response_body 42 | end 43 | 44 | lines << error_string 45 | 46 | lines.join("\n") 47 | end 48 | 49 | def parse_error_description(code, description) 50 | return {} unless code || description 51 | 52 | error_description = "" 53 | error_description += "#{code}: " if code 54 | error_description += description if description 55 | 56 | {error_description: error_description} 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/oauth2/filtered_attributes.rb: -------------------------------------------------------------------------------- 1 | module OAuth2 2 | module FilteredAttributes 3 | def self.included(base) 4 | base.extend(ClassMethods) 5 | end 6 | 7 | module ClassMethods 8 | def filtered_attributes(*attributes) 9 | @filtered_attribute_names = attributes.map(&:to_sym) 10 | end 11 | 12 | def filtered_attribute_names 13 | @filtered_attribute_names || [] 14 | end 15 | end 16 | 17 | def inspect 18 | filtered_attribute_names = self.class.filtered_attribute_names 19 | return super if filtered_attribute_names.empty? 20 | 21 | inspected_vars = instance_variables.map do |var| 22 | if filtered_attribute_names.any? { |filtered_var| var.to_s.include?(filtered_var.to_s) } 23 | "#{var}=[FILTERED]" 24 | else 25 | "#{var}=#{instance_variable_get(var).inspect}" 26 | end 27 | end 28 | "#<#{self.class}:#{object_id} #{inspected_vars.join(", ")}>" 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/oauth2/strategy/assertion.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "jwt" 4 | 5 | module OAuth2 6 | module Strategy 7 | # The Client Assertion Strategy 8 | # 9 | # @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-10#section-4.1.3 10 | # 11 | # Sample usage: 12 | # client = OAuth2::Client.new(client_id, client_secret, 13 | # :site => 'http://localhost:8080', 14 | # :auth_scheme => :request_body) 15 | # 16 | # claim_set = { 17 | # :iss => "http://localhost:3001", 18 | # :aud => "http://localhost:8080/oauth2/token", 19 | # :sub => "me@example.com", 20 | # :exp => Time.now.utc.to_i + 3600, 21 | # } 22 | # 23 | # encoding = { 24 | # :algorithm => 'HS256', 25 | # :key => 'secret_key', 26 | # } 27 | # 28 | # access = client.assertion.get_token(claim_set, encoding) 29 | # access.token # actual access_token string 30 | # access.get("/api/stuff") # making api calls with access token in header 31 | # 32 | class Assertion < Base 33 | # Not used for this strategy 34 | # 35 | # @raise [NotImplementedError] 36 | def authorize_url 37 | raise(NotImplementedError, "The authorization endpoint is not used in this strategy") 38 | end 39 | 40 | # Retrieve an access token given the specified client. 41 | # 42 | # @param [Hash] claims the hash representation of the claims that should be encoded as a JWT (JSON Web Token) 43 | # 44 | # For reading on JWT and claim keys: 45 | # @see https://github.com/jwt/ruby-jwt 46 | # @see https://datatracker.ietf.org/doc/html/rfc7519#section-4.1 47 | # @see https://datatracker.ietf.org/doc/html/rfc7523#section-3 48 | # @see https://www.iana.org/assignments/jwt/jwt.xhtml 49 | # 50 | # There are many possible claim keys, and applications may ask for their own custom keys. 51 | # Some typically required ones: 52 | # :iss (issuer) 53 | # :aud (audience) 54 | # :sub (subject) -- formerly :prn https://datatracker.ietf.org/doc/html/draft-ietf-oauth-json-web-token-06#appendix-F 55 | # :exp, (expiration time) -- in seconds, e.g. Time.now.utc.to_i + 3600 56 | # 57 | # Note that this method does *not* validate presence of those four claim keys indicated as required by RFC 7523. 58 | # There are endpoints that may not conform with this RFC, and this gem should still work for those use cases. 59 | # 60 | # @param [Hash] encoding_opts a hash containing instructions on how the JWT should be encoded 61 | # @option algorithm [String] the algorithm with which you would like the JWT to be encoded 62 | # @option key [Object] the key with which you would like to encode the JWT 63 | # 64 | # These two options are passed directly to `JWT.encode`. For supported encoding arguments: 65 | # @see https://github.com/jwt/ruby-jwt#algorithms-and-usage 66 | # @see https://datatracker.ietf.org/doc/html/rfc7518#section-3.1 67 | # 68 | # The object type of `:key` may depend on the value of `:algorithm`. Sample arguments: 69 | # get_token(claim_set, {:algorithm => 'HS256', :key => 'secret_key'}) 70 | # get_token(claim_set, {:algorithm => 'RS256', :key => OpenSSL::PKCS12.new(File.read('my_key.p12'), 'not_secret')}) 71 | # 72 | # @param [Hash] request_opts options that will be used to assemble the request 73 | # @option request_opts [String] :scope the url parameter `scope` that may be required by some endpoints 74 | # @see https://datatracker.ietf.org/doc/html/rfc7521#section-4.1 75 | # 76 | # @param [Hash] response_opts this will be merged with the token response to create the AccessToken object 77 | # @see the access_token_opts argument to Client#get_token 78 | 79 | def get_token(claims, encoding_opts, request_opts = {}, response_opts = {}) 80 | assertion = build_assertion(claims, encoding_opts) 81 | params = build_request(assertion, request_opts) 82 | 83 | @client.get_token(params, response_opts) 84 | end 85 | 86 | private 87 | 88 | def build_request(assertion, request_opts = {}) 89 | { 90 | grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer", 91 | assertion: assertion, 92 | }.merge(request_opts) 93 | end 94 | 95 | def build_assertion(claims, encoding_opts) 96 | raise ArgumentError.new(message: "Please provide an encoding_opts hash with :algorithm and :key") if !encoding_opts.is_a?(Hash) || (%i[algorithm key] - encoding_opts.keys).any? 97 | 98 | headers = {} 99 | headers[:kid] = encoding_opts[:kid] if encoding_opts.key?(:kid) 100 | 101 | JWT.encode(claims, encoding_opts[:key], encoding_opts[:algorithm], headers) 102 | end 103 | end 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /lib/oauth2/strategy/auth_code.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module OAuth2 4 | module Strategy 5 | # The Authorization Code Strategy 6 | # 7 | # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.1 8 | class AuthCode < Base 9 | # The required query parameters for the authorize URL 10 | # 11 | # @param [Hash] params additional query parameters 12 | def authorize_params(params = {}) 13 | params.merge("response_type" => "code", "client_id" => @client.id) 14 | end 15 | 16 | # The authorization URL endpoint of the provider 17 | # 18 | # @param [Hash] params additional query parameters for the URL 19 | def authorize_url(params = {}) 20 | assert_valid_params(params) 21 | @client.authorize_url(authorize_params.merge(params)) 22 | end 23 | 24 | # Retrieve an access token given the specified validation code. 25 | # 26 | # @param [String] code The Authorization Code value 27 | # @param [Hash] params additional params 28 | # @param [Hash] opts access_token_opts, @see Client#get_token 29 | # @note that you must also provide a :redirect_uri with most OAuth 2.0 providers 30 | def get_token(code, params = {}, opts = {}) 31 | params = {"grant_type" => "authorization_code", "code" => code}.merge(@client.redirection_params).merge(params) 32 | params_dup = params.dup 33 | params.each_key do |key| 34 | params_dup[key.to_s] = params_dup.delete(key) if key.is_a?(Symbol) 35 | end 36 | 37 | @client.get_token(params_dup, opts) 38 | end 39 | 40 | private 41 | 42 | def assert_valid_params(params) 43 | raise(ArgumentError, "client_secret is not allowed in authorize URL query params") if params.key?(:client_secret) || params.key?("client_secret") 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/oauth2/strategy/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module OAuth2 4 | module Strategy 5 | class Base 6 | def initialize(client) 7 | @client = client 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/oauth2/strategy/client_credentials.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module OAuth2 4 | module Strategy 5 | # The Client Credentials Strategy 6 | # 7 | # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.4 8 | class ClientCredentials < Base 9 | # Not used for this strategy 10 | # 11 | # @raise [NotImplementedError] 12 | def authorize_url 13 | raise(NotImplementedError, "The authorization endpoint is not used in this strategy") 14 | end 15 | 16 | # Retrieve an access token given the specified client. 17 | # 18 | # @param [Hash] params additional params 19 | # @param [Hash] opts options 20 | def get_token(params = {}, opts = {}) 21 | params = params.merge("grant_type" => "client_credentials") 22 | @client.get_token(params, opts) 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/oauth2/strategy/implicit.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module OAuth2 4 | module Strategy 5 | # The Implicit Strategy 6 | # 7 | # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-26#section-4.2 8 | class Implicit < Base 9 | # The required query parameters for the authorize URL 10 | # 11 | # @param [Hash] params additional query parameters 12 | def authorize_params(params = {}) 13 | params.merge("response_type" => "token", "client_id" => @client.id) 14 | end 15 | 16 | # The authorization URL endpoint of the provider 17 | # 18 | # @param [Hash] params additional query parameters for the URL 19 | def authorize_url(params = {}) 20 | assert_valid_params(params) 21 | @client.authorize_url(authorize_params.merge(params)) 22 | end 23 | 24 | # Not used for this strategy 25 | # 26 | # @raise [NotImplementedError] 27 | def get_token(*) 28 | raise(NotImplementedError, "The token is accessed differently in this strategy") 29 | end 30 | 31 | private 32 | 33 | def assert_valid_params(params) 34 | raise(ArgumentError, "client_secret is not allowed in authorize URL query params") if params.key?(:client_secret) || params.key?("client_secret") 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/oauth2/strategy/password.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module OAuth2 4 | module Strategy 5 | # The Resource Owner Password Credentials Authorization Strategy 6 | # 7 | # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.3 8 | class Password < Base 9 | # Not used for this strategy 10 | # 11 | # @raise [NotImplementedError] 12 | def authorize_url 13 | raise(NotImplementedError, "The authorization endpoint is not used in this strategy") 14 | end 15 | 16 | # Retrieve an access token given the specified End User username and password. 17 | # 18 | # @param [String] username the End User username 19 | # @param [String] password the End User password 20 | # @param [Hash] params additional params 21 | def get_token(username, password, params = {}, opts = {}) 22 | params = { 23 | "grant_type" => "password", 24 | "username" => username, 25 | "password" => password, 26 | }.merge(params) 27 | @client.get_token(params, opts) 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/oauth2/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module OAuth2 4 | module Version 5 | VERSION = "2.0.12" 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/config/constants.rb: -------------------------------------------------------------------------------- 1 | VERBS = %i[get post put delete patch].freeze 2 | -------------------------------------------------------------------------------- /spec/config/debug.rb: -------------------------------------------------------------------------------- 1 | load_debugger = ENV.fetch("DEBUG", "false").casecmp("true") == 0 2 | 3 | puts "LOADING DEBUGGER: #{load_debugger}" if load_debugger 4 | 5 | require "debug" if load_debugger 6 | -------------------------------------------------------------------------------- /spec/config/faraday.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "faraday" 4 | 5 | Faraday.default_adapter = :test 6 | -------------------------------------------------------------------------------- /spec/config/multi_xml.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "multi_xml" 4 | 5 | MultiXml.parser = :rexml 6 | -------------------------------------------------------------------------------- /spec/config/rspec/rspec_core.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.configure do |config| 4 | # Enable flags like --only-failures and --next-failure 5 | config.example_status_persistence_file_path = ".rspec_status" 6 | 7 | # Disable RSpec exposing methods globally on `Module` and `main` 8 | config.disable_monkey_patching! 9 | 10 | config.expect_with :rspec do |c| 11 | c.syntax = :expect 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/config/rspec/silent_stream.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.configure do |config| 4 | config.include SilentStream 5 | end 6 | -------------------------------------------------------------------------------- /spec/ext/backports.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "backports/2.5.0/hash/transform_keys" 4 | -------------------------------------------------------------------------------- /spec/fixtures/README.md: -------------------------------------------------------------------------------- 1 | # RS256 2 | 3 | ## How keys were made 4 | 5 | ```shell 6 | # No passphrase 7 | # Generates the public and private keys: 8 | ssh-keygen -t rsa -b 4096 -m PEM -f jwtRS256.key 9 | # Converts the key to PEM format 10 | openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256.key.pub 11 | ``` 12 | -------------------------------------------------------------------------------- /spec/fixtures/RS256/jwtRS256.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIJKwIBAAKCAgEA5hdXV/4YSymY1T9VNvK2bWRfulwIty1RnAPNINQmfh3aRRkV 3 | +PNrbC2Crji9G0AHmQwgW1bZ3kgkkpIm6RVn44fHvBvuXkZ9ABgXw0d2cLIHmwOF 4 | xSKmWAm/EW//GszUTLLLsMZUe2udtFJW0jxXB2GRY0WVYuo6Oo58RCeP719lw3Ag 5 | s0YF9/IobxKkGd4BautUPw6ZszAa3o+j0zR74x7ouPxybZAOuPsMxqanyeYJeH4o 6 | sJjLMYV9qem9uG2sj7GENJ8UszcpmGbqxBhexPEB7mgDeONIF0XJF23zdOf8ANE5 7 | mAU2h2v7M6moAfkdUzJ+j48+VT2omHAzAL5yNcmrl2xiWdyoxOw1Y1UmfEmJYV5V 8 | gGYyZ12JZRKY+szPT+vR+MDuYxbquF40O7kvkFNBfL1yCpzfSQCLnEs4rX8qRzZX 9 | ciLeyq4Ht5FLuRFgxjA//XI8LAmp0u7gk+Q7FUH1UgW3kmJDTG0XaxQxYTBSIO7m 10 | cmyjDyBgKVuQmt5E1ycFeteOVdPD/CG/fPYhthvc4UytEFwsMdNy3iD6/wuUH68t 11 | AKam28UZaOb0qK+00cQQD8fulY9rKtSL10LvJFWUOa/SJyLvk9vUmfvFn182il1n 12 | X6GpyxyMmE/FCnH4CT/DjrSZf08mOO8eL5ofYHMK/oiXr1eODqx+pOwClNsCAwEA 13 | AQKCAgEAy34vMFI4WBk04rx9d/hWoQ7Znu8QgjihaZLvEy6t0HJEfUH/bcqS4fyq 14 | C72Aeh452gCgiUeZrf4t4jdCFHhrBg8q9dHaEiTTHocwVPPZ6zd4hH8sCrpnVYth 15 | IWHkw2YOCLtEbFYrl3AI7Na5lHvrGEsREzQSN4Yh83Has0guAy1iyeNb+FFgq/XO 16 | DtX0ri/rHw1717zo8FIGIXn2EK/lNWw7tIcICKAUdUMK/JGd6XD6RUeGYxDu/CAs 17 | kF55/Sd6Kyd7XjKnUwzhS7kRvlYzUog4BgqVr4+LTZHZlFAYtfcJqAtinXFW1ZQJ 18 | eZp9TSlt5wvMZNjx7t92QUNRyEGmrQAU+8COHnT0/drFf0MCiyHSUN0E7/5fswhc 19 | uMSU9XiJA9G0wYvJl4zIuOuIYWZWhIqvjYSkvdlP70t9XO2gk/ZcCWsMW8i+xbwC 20 | w1+MMjsKsNedXxI99TIPPHcCNMxqlt1E1kHH3SAwCuEH/ez7PRMyEQQ0EyAk22x/ 21 | piYIWXkX5835cLbLRIYafXgOiugWZjCwIqfRIcIpscmcijZwCF2DyevveYdx3krR 22 | FGA2PFydFyxCNG7XwvKb9kHb7WBERUPV/H3eCqu2SZ/RvF+I94LUYP4bu6CmFdO9 23 | wCJcGJoL1P7tVhS9lA5Oj0QWczrjnejCoI9XMMduWk032rR1VYECggEBAPZDnTBY 24 | H2uiVmGdMfWTAmX86kiHVpkL03OG6rgvDMsMOYKnik9Lb3gNeUIuPeAWFNrXCoD1 25 | qp0loxPhKSojNOOM8Yiz/GwQ/QI9dzgtxs7E7rFFyTuJcY48Do8uOFyUHbAbeOBF 26 | b9UL/uBfWZGVV1YY753xyqYlCpxTVQGms1jsbVFdZE1iVpOwAkFVuoLYaHLut4zB 27 | 01ORyBSoWan173P+IQH6F1uNXE2Kk/FIMDN6bgP1pXkdkrTx4WjAmRnP/Sc4r38/ 28 | F1xN+gxnWGPUKDVRPYBpVzDR036w65ODgg2FROK2vIxlStiAC/rc0JLsvaWfb1Rn 29 | dsWdJJ1V6mZ6a5sCggEBAO8wC1jcIoiBz3xoA8E5BSt8qLJ7ZuSFaaidvWX2/xj6 30 | lSWJxCGQfhR7P6ozvH6UDo1WbJT6nNyXPkiDkAzcmAdsYVjULW3K2LI9oPajaJxY 31 | L7KJpylgh9JhMvbMz3VVjTgYRt+kjX+3uFMZNx1YfiBP+S6xx5sjK9CKDz3H99kC 32 | q9bX95YFqZ7yFE3aBCR6CENo2tXpMN96CLQGpwa0bwt3xNzC4MhZMXbGR3DdBYbD 33 | tS9lJfQvAVUYxbSE/2FBgjpO6ArMyU2ZUEDFx9J6IhfhVbQV4VeITMyRNo0XwBiQ 34 | /+XpLXgHkw7LiNMIoc7d+M7yLA1Vz7+r8XxWHHZCL8ECggEBAPK8VrYORno7e1Wg 35 | MlxS2WxZzTxMWmlkpLoc5END7SI/HHjSV5wtSORWs40uM0MrwMasa+gNPmzDamjv 36 | 6Tllln4ssO8EKe0DGcAZgefYBzxMFNKbbOzIXyvJurga4Ocv/8tUaOL2znJ67nGO 37 | yqSbRYjR724JpKv7mufXo9SK0gD2mhI3MeSs55WPScnIjJzoXpva/QU7D+gxq7vg 38 | 7PCAP9RfS329W0Sco7yyuXx8oTY8mTBB8ybcpXzBZmNwY/hzcJ42W5XbRFVxbuTH 39 | APL1beSP/UUTkCPIzuTz0mCGoaxeDjZB1Lu2I/4eyLAu80+/FneoHX5etU23xR1o 40 | UDFOvb0CggEBALTTc6CoPAtLaBs7X6tSelAYHEli9bTKD8kEB83wX4b42ozYjEh7 41 | vnWpf8Yi+twO/rlnnws6NCCoztNvcxXmJ6FlFGtdbULV2eFWqjwL6ehY2yZ03sVv 42 | Tv+DsE3ZJPYlyW+hGuO0uazWrilUpNAwuJmhHFdq2+azPkqYNVGVvhB37oWsHGd0 43 | vHmHtkXtDris8VZVDSwu8V3iGnZPmTJ+cn0O/OuRAPM2SyjqWdQ/pA/wIShFpd3n 44 | M3CsG7uP2KokJloCkXaov39E6uEtJRZAc0nudyaAbC4Kw1Tca4tba0SnSm78S/20 45 | bD8BLN2uZvXH5nQ9rYQfXcIgMZ64UygsfYECggEBAIw0fQaIVmafa0Hz3ipD4PJI 46 | 5QNkh2t9hvOCSKm1xYTNATl0q/VIkZoy1WoxY6SSchcObLxQKbJ9ORi4XNr+IJK5 47 | 3C1Qz/3iv/S3/ktgmqGhQiqybkkHZcbqTXB2wxrx+aaLS7PEfYiuYCrPbX93160k 48 | MVns8PjvYU8KCNMbL2e+AiKEt1KkKAZIpNQdeeJOEhV9wuLYFosd400aYssuSOVW 49 | IkJhGI0lT/7FDJaw0LV98DhQtauANPSUQKN5iw6vciwtsaF1kXMfGlMXj58ntiMq 50 | NizQPR6/Ar1ewLPMh1exDoAfLnCIMk8nbSraW+cebLAZctPugUpfpu3j2LM98aE= 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /spec/fixtures/RS256/jwtRS256.key.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5hdXV/4YSymY1T9VNvK2 3 | bWRfulwIty1RnAPNINQmfh3aRRkV+PNrbC2Crji9G0AHmQwgW1bZ3kgkkpIm6RVn 4 | 44fHvBvuXkZ9ABgXw0d2cLIHmwOFxSKmWAm/EW//GszUTLLLsMZUe2udtFJW0jxX 5 | B2GRY0WVYuo6Oo58RCeP719lw3Ags0YF9/IobxKkGd4BautUPw6ZszAa3o+j0zR7 6 | 4x7ouPxybZAOuPsMxqanyeYJeH4osJjLMYV9qem9uG2sj7GENJ8UszcpmGbqxBhe 7 | xPEB7mgDeONIF0XJF23zdOf8ANE5mAU2h2v7M6moAfkdUzJ+j48+VT2omHAzAL5y 8 | Ncmrl2xiWdyoxOw1Y1UmfEmJYV5VgGYyZ12JZRKY+szPT+vR+MDuYxbquF40O7kv 9 | kFNBfL1yCpzfSQCLnEs4rX8qRzZXciLeyq4Ht5FLuRFgxjA//XI8LAmp0u7gk+Q7 10 | FUH1UgW3kmJDTG0XaxQxYTBSIO7mcmyjDyBgKVuQmt5E1ycFeteOVdPD/CG/fPYh 11 | thvc4UytEFwsMdNy3iD6/wuUH68tAKam28UZaOb0qK+00cQQD8fulY9rKtSL10Lv 12 | JFWUOa/SJyLvk9vUmfvFn182il1nX6GpyxyMmE/FCnH4CT/DjrSZf08mOO8eL5of 13 | YHMK/oiXr1eODqx+pOwClNsCAwEAAQ== 14 | -----END PUBLIC KEY----- 15 | -------------------------------------------------------------------------------- /spec/fixtures/google_service_account_key.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oauth-xx/oauth2/75bae598fc513b2ef8f0d47abab9a8eb877036ec/spec/fixtures/google_service_account_key.p12 -------------------------------------------------------------------------------- /spec/oauth2/authenticator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe OAuth2::Authenticator do 4 | subject do 5 | described_class.new(client_id, client_secret, mode) 6 | end 7 | 8 | let(:client_id) { "foo" } 9 | let(:client_secret) { "bar" } 10 | let(:mode) { :undefined } 11 | 12 | it "raises NotImplementedError for unknown authentication mode" do 13 | expect { subject.apply({}) }.to raise_error(NotImplementedError) 14 | end 15 | 16 | describe "#apply" do 17 | context "with parameter-based authentication" do 18 | let(:mode) { :request_body } 19 | 20 | it "adds client_id and client_secret to params" do 21 | output = subject.apply({}) 22 | expect(output).to eq("client_id" => "foo", "client_secret" => "bar") 23 | end 24 | 25 | context "when client_id nil" do 26 | let(:client_id) { nil } 27 | 28 | it "ignores client_id, but adds client_secret to params" do 29 | output = subject.apply({}) 30 | expect(output).to eq("client_secret" => "bar") 31 | end 32 | end 33 | 34 | it "does not overwrite existing credentials" do 35 | input = {"client_secret" => "s3cr3t"} 36 | output = subject.apply(input) 37 | expect(output).to eq("client_id" => "foo", "client_secret" => "s3cr3t") 38 | end 39 | 40 | it "preserves other parameters" do 41 | input = {"state" => "42", :headers => {"A" => "b"}} 42 | output = subject.apply(input) 43 | expect(output).to eq( 44 | "client_id" => "foo", 45 | "client_secret" => "bar", 46 | "state" => "42", 47 | :headers => {"A" => "b"}, 48 | ) 49 | end 50 | 51 | context "passing nil secret" do 52 | let(:client_secret) { nil } 53 | 54 | it "does not set nil client_secret" do 55 | output = subject.apply({}) 56 | expect(output).to eq("client_id" => "foo") 57 | end 58 | end 59 | 60 | context "using tls client authentication" do 61 | let(:mode) { :tls_client_auth } 62 | 63 | it "does not add client_secret" do 64 | output = subject.apply({}) 65 | expect(output).to eq("client_id" => "foo") 66 | end 67 | end 68 | 69 | context "using private key jwt authentication" do 70 | let(:mode) { :private_key_jwt } 71 | 72 | it "does not include client_id or client_secret" do 73 | output = subject.apply({}) 74 | expect(output).to eq({}) 75 | end 76 | end 77 | end 78 | 79 | context "using tls_client_auth" do 80 | let(:mode) { :tls_client_auth } 81 | 82 | context "when client_id present" do 83 | let(:client_id) { "foobar" } 84 | 85 | it "adds client_id to params" do 86 | output = subject.apply({}) 87 | expect(output).to eq("client_id" => "foobar") 88 | end 89 | end 90 | 91 | context "when client_id nil" do 92 | let(:client_id) { nil } 93 | 94 | it "ignores client_id for params" do 95 | output = subject.apply({}) 96 | expect(output).to eq({}) 97 | end 98 | end 99 | end 100 | 101 | context "with Basic authentication" do 102 | let(:mode) { :basic_auth } 103 | let(:header) { "Basic #{Base64.strict_encode64("#{client_id}:#{client_secret}")}" } 104 | 105 | it "encodes credentials in headers" do 106 | output = subject.apply({}) 107 | expect(output).to eq(headers: {"Authorization" => header}) 108 | end 109 | 110 | it "does not overwrite existing credentials" do 111 | input = {headers: {"Authorization" => "Bearer abc123"}} 112 | output = subject.apply(input) 113 | expect(output).to eq(headers: {"Authorization" => "Bearer abc123"}) 114 | end 115 | 116 | it "does not overwrite existing params or headers" do 117 | input = {"state" => "42", :headers => {"A" => "b"}} 118 | output = subject.apply(input) 119 | expect(output).to eq( 120 | "state" => "42", 121 | :headers => {"A" => "b", "Authorization" => header}, 122 | ) 123 | end 124 | end 125 | end 126 | 127 | describe "#inspect" do 128 | it "filters secret by default" do 129 | expect(described_class.filtered_attribute_names).to include(:secret) 130 | end 131 | 132 | it "filters out the @secret value" do 133 | expect(subject.inspect).to include("@secret=[FILTERED]") 134 | end 135 | 136 | context "when filter is changed" do 137 | before do 138 | @original_filter = described_class.filtered_attribute_names 139 | described_class.filtered_attributes :vanilla 140 | end 141 | 142 | after do 143 | described_class.filtered_attributes(*@original_filter) 144 | end 145 | 146 | it "changes the filter" do 147 | expect(described_class.filtered_attribute_names).to eq([:vanilla]) 148 | end 149 | 150 | it "does not filter out the @secret value" do 151 | expect(subject.inspect).to include("@secret=\"bar\"") 152 | end 153 | end 154 | 155 | context "when filter is empty" do 156 | before do 157 | @original_filter = described_class.filtered_attribute_names 158 | described_class.filtered_attributes 159 | end 160 | 161 | after do 162 | described_class.filtered_attributes(*@original_filter) 163 | end 164 | 165 | it "changes the filter" do 166 | expect(described_class.filtered_attribute_names).to eq([]) 167 | end 168 | 169 | it "does not filter out the @secret value" do 170 | expect(subject.inspect).to include("@secret=\"bar\"") 171 | end 172 | end 173 | end 174 | end 175 | -------------------------------------------------------------------------------- /spec/oauth2/strategy/base_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe OAuth2::Strategy::Base do 4 | it "initializes with a Client" do 5 | expect { described_class.new(OAuth2::Client.new("abc", "def")) }.not_to raise_error 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/oauth2/strategy/client_credentials_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe OAuth2::Strategy::ClientCredentials do 4 | subject { client.client_credentials } 5 | 6 | let(:kvform_token) { "expires_in=600&access_token=salmon&refresh_token=trout" } 7 | let(:json_token) { '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}' } 8 | 9 | let(:client) do 10 | OAuth2::Client.new("abc", "def", site: "http://api.example.com") do |builder| 11 | builder.adapter :test do |stub| 12 | stub.post("/oauth/token", "grant_type" => "client_credentials") do |env| 13 | client_id, client_secret = Base64.decode64(env[:request_headers]["Authorization"].split(" ", 2)[1]).split(":", 2) 14 | (client_id == "abc" && client_secret == "def") || raise(Faraday::Adapter::Test::Stubs::NotFound) 15 | @last_headers = env[:request_headers] 16 | case @mode 17 | when "formencoded" 18 | [200, {"Content-Type" => "application/x-www-form-urlencoded"}, kvform_token] 19 | when "json" 20 | [200, {"Content-Type" => "application/json"}, json_token] 21 | else raise ArgumentError, "Bad @mode: #{@mode}" 22 | end 23 | end 24 | stub.post("/oauth/token", "client_id" => "abc", "client_secret" => "def", "grant_type" => "client_credentials") do |_env| 25 | case @mode 26 | when "formencoded" 27 | [200, {"Content-Type" => "application/x-www-form-urlencoded"}, kvform_token] 28 | when "json" 29 | [200, {"Content-Type" => "application/json"}, json_token] 30 | else raise ArgumentError, "Bad @mode: #{@mode}" 31 | end 32 | end 33 | end 34 | end 35 | end 36 | 37 | describe "#authorize_url" do 38 | it "raises NotImplementedError" do 39 | expect { subject.authorize_url }.to raise_error(NotImplementedError) 40 | end 41 | end 42 | 43 | %w[json formencoded].each do |mode| 44 | %i[basic_auth request_body].each do |auth_scheme| 45 | describe "#get_token (#{mode}) (#{auth_scheme})" do 46 | before do 47 | @mode = mode 48 | client.options[:auth_scheme] = auth_scheme 49 | @access = subject.get_token 50 | end 51 | 52 | it "returns AccessToken with same Client" do 53 | expect(@access.client).to eq(client) 54 | end 55 | 56 | it "returns AccessToken with #token" do 57 | expect(@access.token).to eq("salmon") 58 | end 59 | 60 | it "returns AccessToken without #refresh_token" do 61 | expect(@access.refresh_token).to eq("trout") 62 | end 63 | 64 | it "returns AccessToken with #expires_in" do 65 | expect(@access.expires_in).to eq(600) 66 | end 67 | 68 | it "returns AccessToken with #expires_at" do 69 | expect(@access.expires_at).not_to be_nil 70 | end 71 | end 72 | end 73 | end 74 | 75 | describe "#get_token (with extra header parameters)" do 76 | before do 77 | @mode = "json" 78 | @access = subject.get_token(headers: {"X-Extra-Header" => "wow"}) 79 | end 80 | 81 | it "sends the header correctly." do 82 | expect(@last_headers["X-Extra-Header"]).to eq("wow") 83 | end 84 | end 85 | 86 | describe "#get_token (with option overriding response)" do 87 | before do 88 | @mode = "json" 89 | @access = subject.get_token({}, {"refresh_token" => "guppy"}) 90 | end 91 | 92 | it "override is applied" do 93 | expect(@access.token).to eq("salmon") 94 | expect(@access.refresh_token).to eq("guppy") 95 | end 96 | end 97 | end 98 | -------------------------------------------------------------------------------- /spec/oauth2/strategy/implicit_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe OAuth2::Strategy::Implicit do 4 | subject { client.implicit } 5 | 6 | let(:client) { OAuth2::Client.new("abc", "def", site: "http://api.example.com") } 7 | 8 | describe "#authorize_url" do 9 | it "includes the client_id" do 10 | expect(subject.authorize_url).to include("client_id=abc") 11 | end 12 | 13 | it "includes the type" do 14 | expect(subject.authorize_url).to include("response_type=token") 15 | end 16 | 17 | it "does not include the client_secret" do 18 | expect(subject.authorize_url).not_to include("client_secret=def") 19 | end 20 | 21 | it "raises an error if the client_secret is passed in" do 22 | expect { subject.authorize_url(client_secret: "def") }.to raise_error(ArgumentError) 23 | end 24 | 25 | it "raises an error if the client_secret is passed in with string keys" do 26 | expect { subject.authorize_url("client_secret" => "def") }.to raise_error(ArgumentError) 27 | end 28 | 29 | it "includes passed in options" do 30 | cb = "http://myserver.local/oauth/callback" 31 | expect(subject.authorize_url(redirect_uri: cb)).to include("redirect_uri=#{CGI.escape(cb)}") 32 | end 33 | end 34 | 35 | describe "#get_token" do 36 | it "raises NotImplementedError" do 37 | expect { subject.get_token }.to raise_error(NotImplementedError) 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/oauth2/strategy/password_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe OAuth2::Strategy::Password do 4 | subject { client.password } 5 | 6 | let(:client) do 7 | cli = OAuth2::Client.new("abc", "def", site: "http://api.example.com") 8 | cli.connection = Faraday.new(cli.site, cli.options[:connection_opts]) do |b| 9 | b.request :url_encoded 10 | b.adapter :test do |stub| 11 | stub.post("/oauth/token") do |_env| 12 | case @mode 13 | when "formencoded" 14 | [200, {"Content-Type" => "application/x-www-form-urlencoded"}, "expires_in=600&access_token=salmon&refresh_token=trout"] 15 | when "json" 16 | [200, {"Content-Type" => "application/json"}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}'] 17 | else raise ArgumentError, "Bad @mode: #{@mode}" 18 | end 19 | end 20 | end 21 | end 22 | cli 23 | end 24 | 25 | describe "#authorize_url" do 26 | it "raises NotImplementedError" do 27 | expect { subject.authorize_url }.to raise_error(NotImplementedError) 28 | end 29 | end 30 | 31 | %w[json formencoded].each do |mode| 32 | describe "#get_token (#{mode})" do 33 | before do 34 | @mode = mode 35 | @access = subject.get_token("username", "password") 36 | end 37 | 38 | it "returns AccessToken with same Client" do 39 | expect(@access.client).to eq(client) 40 | end 41 | 42 | it "returns AccessToken with #token" do 43 | expect(@access.token).to eq("salmon") 44 | end 45 | 46 | it "returns AccessToken with #refresh_token" do 47 | expect(@access.refresh_token).to eq("trout") 48 | end 49 | 50 | it "returns AccessToken with #expires_in" do 51 | expect(@access.expires_in).to eq(600) 52 | end 53 | 54 | it "returns AccessToken with #expires_at" do 55 | expect(@access.expires_at).not_to be_nil 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /spec/oauth2/version_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe OAuth2::Version do 4 | it_behaves_like "a Version module", described_class 5 | 6 | it "is greater than 1.0.0" do 7 | expect(Gem::Version.new(described_class) >= Gem::Version.new("1.0.0")).to(be(true)) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/oauth2_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe OAuth2 do 4 | it "silence_extra_tokens_warning is a boolean" do 5 | expect(described_class.config.silence_extra_tokens_warning).to be(true).or be(false) 6 | end 7 | 8 | describe ".configure" do 9 | subject(:configure) do 10 | described_class.configure do |config| 11 | config.silence_extra_tokens_warning = true 12 | config.silence_no_tokens_warning = true 13 | end 14 | end 15 | 16 | before do 17 | described_class.configure do |config| 18 | config.silence_extra_tokens_warning = false 19 | config.silence_no_tokens_warning = false 20 | end 21 | end 22 | 23 | after do 24 | described_class.configure do |config| 25 | config.silence_extra_tokens_warning = false 26 | config.silence_no_tokens_warning = false 27 | end 28 | end 29 | 30 | it "can change setting of silence_extra_tokens_warning" do 31 | block_is_expected.to change(described_class.config, :silence_extra_tokens_warning).from(false).to(true) 32 | end 33 | 34 | it "can change setting of silence_no_tokens_warning" do 35 | block_is_expected.to change(described_class.config, :silence_no_tokens_warning).from(false).to(true) 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # ensure test env 4 | ENV["RACK_ENV"] = "test" 5 | 6 | # Third Party Libraries 7 | require "addressable/uri" 8 | require "rspec/block_is_expected" 9 | require "rspec/pending_for" 10 | require "rspec/stubbed_env" 11 | require "silent_stream" 12 | require "version_gem/rspec" 13 | require "version_gem/ruby" 14 | 15 | # Extensions 16 | require_relative "ext/backports" 17 | 18 | # Library Configs 19 | require_relative "config/debug" 20 | require_relative "config/multi_xml" 21 | require_relative "config/faraday" 22 | require_relative "config/constants" 23 | 24 | # RSpec Configs 25 | require_relative "config/rspec/rspec_core" 26 | require_relative "config/rspec/silent_stream" 27 | 28 | # NOTE: Gemfiles for older rubies won't have kettle-soup-cover. 29 | # The rescue LoadError handles that scenario. 30 | begin 31 | require "kettle-soup-cover" 32 | require "simplecov" if Kettle::Soup::Cover::DO_COV # `.simplecov` is run here! 33 | rescue LoadError => error 34 | # check the error message, and re-raise if not what is expected 35 | raise error unless error.message.include?("kettle") 36 | end 37 | 38 | # This gem 39 | require "oauth2" 40 | --------------------------------------------------------------------------------