-## What
+# Ruby OAuth 2 has moved to GitLab
-OAuth 2.0 is the industry-standard protocol for authorization.
-OAuth 2.0 focuses on client developer simplicity while providing specific authorization flows for web applications,
- desktop applications, mobile phones, and living room devices.
-This is a RubyGem for implementing OAuth 2.0 clients and servers in Ruby applications.
-See the sibling `oauth` gem for OAuth 1.0 implementations in Ruby.
+Please update references: https://gitlab.com/oauth-xx/oauth2/
----
-
-* [OAuth 2.0 Spec][oauth2-spec]
-* [oauth sibling gem][sibling-gem] for OAuth 1.0 implementations in Ruby.
-
-[oauth2-spec]: https://oauth.net/2/
-[sibling-gem]: https://github.com/oauth-xx/oauth-ruby
-
-## Release Documentation
-
-### Version 2.0.x
-
-
- 2.0.x Readmes
-
-| Version | Release Date | Readme |
-|---------|--------------|----------------------------------------------------------|
-| 2.0.7 | 2022-08-22 | https://github.com/oauth-xx/oauth2/blob/v2.0.7/README.md |
-| 2.0.6 | 2022-07-13 | https://github.com/oauth-xx/oauth2/blob/v2.0.6/README.md |
-| 2.0.5 | 2022-07-07 | https://github.com/oauth-xx/oauth2/blob/v2.0.5/README.md |
-| 2.0.4 | 2022-07-01 | https://github.com/oauth-xx/oauth2/blob/v2.0.4/README.md |
-| 2.0.3 | 2022-06-28 | https://github.com/oauth-xx/oauth2/blob/v2.0.3/README.md |
-| 2.0.2 | 2022-06-24 | https://github.com/oauth-xx/oauth2/blob/v2.0.2/README.md |
-| 2.0.1 | 2022-06-22 | https://github.com/oauth-xx/oauth2/blob/v2.0.1/README.md |
-| 2.0.0 | 2022-06-21 | https://github.com/oauth-xx/oauth2/blob/v2.0.0/README.md |
-
-
-### Older Releases
-
-
- 1.4.x Readmes
-
-| Version | Release Date | Readme |
-|---------|--------------|-----------------------------------------------------------|
-| 1.4.10 | Jul 1, 2022 | https://github.com/oauth-xx/oauth2/blob/v1.4.10/README.md |
-| 1.4.9 | Feb 20, 2022 | https://github.com/oauth-xx/oauth2/blob/v1.4.9/README.md |
-| 1.4.8 | Feb 18, 2022 | https://github.com/oauth-xx/oauth2/blob/v1.4.8/README.md |
-| 1.4.7 | Mar 19, 2021 | https://github.com/oauth-xx/oauth2/blob/v1.4.7/README.md |
-| 1.4.6 | Mar 19, 2021 | https://github.com/oauth-xx/oauth2/blob/v1.4.6/README.md |
-| 1.4.5 | Mar 18, 2021 | https://github.com/oauth-xx/oauth2/blob/v1.4.5/README.md |
-| 1.4.4 | Feb 12, 2020 | https://github.com/oauth-xx/oauth2/blob/v1.4.4/README.md |
-| 1.4.3 | Jan 29, 2020 | https://github.com/oauth-xx/oauth2/blob/v1.4.3/README.md |
-| 1.4.2 | Oct 1, 2019 | https://github.com/oauth-xx/oauth2/blob/v1.4.2/README.md |
-| 1.4.1 | Oct 13, 2018 | https://github.com/oauth-xx/oauth2/blob/v1.4.1/README.md |
-| 1.4.0 | Jun 9, 2017 | https://github.com/oauth-xx/oauth2/blob/v1.4.0/README.md |
-
-
-
- 1.3.x Readmes
-
-| Version | Release Date | Readme |
-|----------|--------------|----------------------------------------------------------|
-| 1.3.1 | Mar 3, 2017 | https://github.com/oauth-xx/oauth2/blob/v1.3.1/README.md |
-| 1.3.0 | Dec 27, 2016 | https://github.com/oauth-xx/oauth2/blob/v1.3.0/README.md |
-
-
-
- ≤= 1.2.x Readmes (2016 and before)
-
-| Version | Release Date | Readme |
-|----------|--------------|----------------------------------------------------------|
-| 1.2.0 | Jun 30, 2016 | https://github.com/oauth-xx/oauth2/blob/v1.2.0/README.md |
-| 1.1.0 | Jan 30, 2016 | https://github.com/oauth-xx/oauth2/blob/v1.1.0/README.md |
-| 1.0.0 | May 23, 2014 | https://github.com/oauth-xx/oauth2/blob/v1.0.0/README.md |
-| < 1.0.0 | Find here | https://github.com/oauth-xx/oauth2/tags |
-
-
-## Status
-
-
-
-| | Project | bundle add oauth2 |
-|:----|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| 1️⃣ | name, license, docs | [![RubyGems.org][⛳️name-img]][⛳️gem] [![License: MIT][🖇src-license-img]][🖇src-license] [![FOSSA][🏘fossa-img]][🏘fossa] [![RubyDoc.info][🚎yard-img]][🚎yard] [![InchCI][🖐inch-ci-img]][🚎yard] |
-| 2️⃣ | version & activity | [![Gem Version][⛳️version-img]][⛳️gem] [![Total Downloads][🖇DL-total-img]][⛳️gem] [![Download Rank][🏘DL-rank-img]][⛳️gem] [![Source Code][🚎src-home-img]][🚎src-home] [![Open PRs][🖐prs-o-img]][🖐prs-o] [![Closed PRs][🧮prs-c-img]][🧮prs-c] [![Next Version][📗next-img♻️]][📗next♻️] |
-| 3️⃣ | maintanence & linting | [![Maintainability][⛳cclim-maint-img♻️]][⛳cclim-maint] [![Helpers][🖇triage-help-img]][🖇triage-help] [![Depfu][🏘depfu-img♻️]][🏘depfu♻️] [![Contributors][🚎contributors-img]][🚎contributors] [![Style][🖐style-wf-img]][🖐style-wf] [![Kloc Roll][🧮kloc-img]][🧮kloc] |
-| 4️⃣ | testing | [![Open Issues][⛳iss-o-img]][⛳iss-o] [![Closed Issues][🖇iss-c-img]][🖇iss-c] [![Supported][🏘sup-wf-img]][🏘sup-wf] [![Heads][🚎heads-wf-img]][🚎heads-wf] [![Unofficial Support][🖐uns-wf-img]][🖐uns-wf] [![MacOS][🧮mac-wf-img]][🧮mac-wf] [![Windows][📗win-wf-img]][📗win-wf] |
-| 5️⃣ | coverage & security | [![CodeClimate][⛳cclim-cov-img♻️]][⛳cclim-cov] [![CodeCov][🖇codecov-img♻️]][🖇codecov] [![Coveralls][🏘coveralls-img]][🏘coveralls] [![Security Policy][🚎sec-pol-img]][🚎sec-pol] [![CodeQL][🖐codeQL-img]][🖐codeQL] [![Code Coverage][🧮cov-wf-img]][🧮cov-wf] |
-| 6️⃣ | resources | [![Discussion][⛳gh-discussions-img]][⛳gh-discussions] [![Get help on Codementor][🖇codementor-img]][🖇codementor] [![Chat][🏘chat-img]][🏘chat] [![Blog][🚎blog-img]][🚎blog] [![Blog][🖐wiki-img]][🖐wiki] |
-| 7️⃣ | spread 💖 | [![Liberapay Patrons][⛳liberapay-img]][⛳liberapay] [![Sponsor Me][🖇sponsor-img]][🖇sponsor] [![Tweet @ Peter][🏘tweet-img]][🏘tweet] [🌏][aboutme] [👼][angelme] [💻][coderme] |
-
-
-
-
-[⛳️gem]: https://rubygems.org/gems/oauth2
-[⛳️name-img]: https://img.shields.io/badge/name-oauth2-brightgreen.svg?style=flat
-[🖇src-license]: https://opensource.org/licenses/MIT
-[🖇src-license-img]: https://img.shields.io/badge/License-MIT-green.svg
-[🏘fossa]: https://app.fossa.io/projects/git%2Bgithub.amrom.workers.dev%2Foauth-xx%2Foauth2?ref=badge_shield
-[🏘fossa-img]: https://app.fossa.io/api/projects/git%2Bgithub.amrom.workers.dev%2Foauth-xx%2Foauth2.svg?type=shield
-[🚎yard]: https://www.rubydoc.info/github/oauth-xx/oauth2
-[🚎yard-img]: https://img.shields.io/badge/documentation-rubydoc-brightgreen.svg?style=flat
-[🖐inch-ci-img]: http://inch-ci.org/github/oauth-xx/oauth2.png
-
-
-[⛳️version-img]: http://img.shields.io/gem/v/oauth2.svg
-[🖇DL-total-img]: https://img.shields.io/gem/dt/oauth2.svg
-[🏘DL-rank-img]: https://img.shields.io/gem/rt/oauth2.svg
-[🚎src-home]: https://github.com/oauth-xx/oauth2
-[🚎src-home-img]: https://img.shields.io/badge/source-github-brightgreen.svg?style=flat
-[🖐prs-o]: https://github.com/oauth-xx/oauth2/pulls
-[🖐prs-o-img]: https://img.shields.io/github/issues-pr/oauth-xx/oauth2
-[🧮prs-c]: https://github.com/oauth-xx/oauth2/pulls?q=is%3Apr+is%3Aclosed
-[🧮prs-c-img]: https://img.shields.io/github/issues-pr-closed/oauth-xx/oauth2
-[📗next♻️]: https://github.com/oauth-xx/oauth2/milestone/2
-[📗next-img♻️]: https://img.shields.io/github/milestones/progress/oauth-xx/oauth2/2?label=Next%20Version
-
-
-[⛳cclim-maint]: https://codeclimate.com/github/oauth-xx/oauth2/maintainability
-[⛳cclim-maint-img♻️]: https://api.codeclimate.com/v1/badges/688c612528ff90a46955/maintainability
-[🖇triage-help]: https://www.codetriage.com/oauth-xx/oauth2
-[🖇triage-help-img]: https://www.codetriage.com/oauth-xx/oauth2/badges/users.svg
-[🏘depfu♻️]: https://depfu.com/github/oauth-xx/oauth2?project_id=4445
-[🏘depfu-img♻️]: https://badges.depfu.com/badges/6d34dc1ba682bbdf9ae2a97848241743/count.svg
-[🚎contributors]: https://github.com/oauth-xx/oauth2/graphs/contributors
-[🚎contributors-img]: https://img.shields.io/github/contributors-anon/oauth-xx/oauth2
-[🖐style-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/style.yml
-[🖐style-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/style.yml/badge.svg
-[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
-[🧮kloc-img]: https://img.shields.io/tokei/lines/github.com/oauth-xx/oauth2
-
-
-[⛳iss-o]: https://github.com/oauth-xx/oauth2/issues
-[⛳iss-o-img]: https://img.shields.io/github/issues-raw/oauth-xx/oauth2
-[🖇iss-c]: https://github.com/oauth-xx/oauth2/issues?q=is%3Aissue+is%3Aclosed
-[🖇iss-c-img]: https://img.shields.io/github/issues-closed-raw/oauth-xx/oauth2
-[🏘sup-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/supported.yml
-[🏘sup-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/supported.yml/badge.svg
-[🚎heads-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/heads.yml
-[🚎heads-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/heads.yml/badge.svg
-[🖐uns-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/unsupported.yml
-[🖐uns-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/unsupported.yml/badge.svg
-[🧮mac-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/macos.yml
-[🧮mac-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/macos.yml/badge.svg
-[📗win-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/windows.yml
-[📗win-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/windows.yml/badge.svg
-
-
-[⛳cclim-cov]: https://codeclimate.com/github/oauth-xx/oauth2/test_coverage
-[⛳cclim-cov-img♻️]: https://api.codeclimate.com/v1/badges/688c612528ff90a46955/test_coverage
-[🖇codecov-img♻️]: https://codecov.io/gh/oauth-xx/oauth2/branch/master/graph/badge.svg?token=bNqSzNiuo2
-[🖇codecov]: https://codecov.io/gh/oauth-xx/oauth2
-[🏘coveralls]: https://coveralls.io/github/oauth-xx/oauth2?branch=master
-[🏘coveralls-img]: https://coveralls.io/repos/github/oauth-xx/oauth2/badge.svg?branch=master
-[🚎sec-pol]: https://github.com/oauth-xx/oauth2/blob/master/SECURITY.md
-[🚎sec-pol-img]: https://img.shields.io/badge/security-policy-brightgreen.svg?style=flat
-[🖐codeQL]: https://github.com/oauth-xx/oauth2/security/code-scanning
-[🖐codeQL-img]: https://github.com/oauth-xx/oauth2/actions/workflows/codeql-analysis.yml/badge.svg
-[🧮cov-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/coverage.yml
-[🧮cov-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/coverage.yml/badge.svg
-
-
-[⛳gh-discussions]: https://github.com/oauth-xx/oauth2/discussions
-[⛳gh-discussions-img]: https://img.shields.io/github/discussions/oauth-xx/oauth2
-[🖇codementor]: https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github
-[🖇codementor-img]: https://cdn.codementor.io/badges/get_help_github.svg
-[🏘chat]: https://gitter.im/oauth-xx/oauth2
-[🏘chat-img]: https://img.shields.io/gitter/room/oauth-xx/oauth2.svg
-[🚎blog]: http://www.railsbling.com/tags/oauth2/
-[🚎blog-img]: https://img.shields.io/badge/blog-railsbling-brightgreen.svg?style=flat
-[🖐wiki]: https://github.com/oauth-xx/oauth2/wiki
-[🖐wiki-img]: https://img.shields.io/badge/wiki-examples-brightgreen.svg?style=flat
-
-
-[⛳liberapay-img]: https://img.shields.io/liberapay/patrons/pboling.svg?logo=liberapay
-[⛳liberapay]: https://liberapay.com/pboling/donate
-[🖇sponsor-img]: https://img.shields.io/badge/sponsor-pboling.svg?style=social&logo=github
-[🖇sponsor]: https://github.com/sponsors/pboling
-[🏘tweet-img]: https://img.shields.io/twitter/follow/galtzo.svg?style=social&label=Follow
-[🏘tweet]: http://twitter.com/galtzo
-
-
-[railsbling]: http://www.railsbling.com
-[peterboling]: http://www.peterboling.com
-[aboutme]: https://about.me/peter.boling
-[angelme]: https://angel.co/peter-boling
-[coderme]:http://coderwall.com/pboling
-
-## Installation
-
-Install the gem and add to the application's Gemfile by executing:
-
- $ bundle add oauth2
-
-If bundler is not being used to manage dependencies, install the gem by executing:
-
- $ gem install oauth2
-
-## OAuth2 for Enterprise
-
-Available as part of the Tidelift Subscription.
-
-The maintainers of OAuth2 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]
-
-[tidelift-ref]: https://tidelift.com/subscription/pkg/rubygems-oauth2?utm_source=rubygems-oauth2&utm_medium=referral&utm_campaign=enterprise
-
-## Security contact information
-
-To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security).
-Tidelift will coordinate the fix and disclosure.
-
-For more see [SECURITY.md][🚎sec-pol].
-
-## What is new for v2.0?
-
-- Officially support Ruby versions >= 2.7
-- Unofficially support Ruby versions >= 2.5
-- Incidentally support Ruby versions >= 2.2
-- Drop support for the expired MAC Draft (all versions)
-- Support IETF rfc7523 JWT Bearer Tokens
-- Support IETF rfc7231 Relative Location in Redirect
-- Support IETF rfc6749 Don't set oauth params when nil
-- Support [OIDC 1.0 Private Key JWT](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication); based on the OAuth JWT assertion specification [(RFC 7523)](https://tools.ietf.org/html/rfc7523)
-- Support new formats, including from [jsonapi.org](http://jsonapi.org/format/): `application/vdn.api+json`, `application/vnd.collection+json`, `application/hal+json`, `application/problem+json`
-- Adds new option to `OAuth2::Client#get_token`:
- - `:access_token_class` (`AccessToken`); user specified class to use for all calls to `get_token`
-- Adds new option to `OAuth2::AccessToken#initialize`:
- - `:expires_latency` (`nil`); number of seconds by which AccessToken validity will be reduced to offset latency
-- By default, keys are transformed to camel case.
- - Original keys will still work as previously, in most scenarios, thanks to `rash_alt` gem.
- - However, this is a _breaking_ change if you rely on `response.parsed.to_h`, as the keys in the result will be camel case.
- - As of version 2.0.4 you can turn key transformation off with the `snaky: false` option.
-- By default, the `:auth_scheme` is now `:basic_auth` (instead of `:request_body`)
- - Third-party strategies and gems may need to be updated if a provider was requiring client id/secret in the request body
-- [... A lot more](https://github.com/oauth-xx/oauth2/blob/master/CHANGELOG.md#2.0.0)
-
-## Compatibility
-
-Targeted ruby compatibility is non-EOL versions of Ruby, currently 2.7, 3.0 and
-3.1. Compatibility is further distinguished by supported and unsupported versions of Ruby.
-Ruby is limited to 2.2+ for 2.x releases. See `1-4-stable` branch for older rubies.
-
-
- Ruby Engine Compatibility Policy
-
-This gem is tested against MRI, JRuby, and Truffleruby.
-Each of those has varying versions that target a specific version of MRI Ruby.
-This gem should work in the just-listed Ruby engines according to the targeted MRI compatibility in the table below.
-If you would like to add support for additional engines,
- first make sure Github Actions supports the engine,
- then submit a PR to the correct maintenance branch as according to the table below.
-
-
-
- Ruby Version Compatibility Policy
-
-If something doesn't work on one of these interpreters, it's a bug.
-
-This library may inadvertently work (or seem to work) on other Ruby
-implementations, however support will only be provided for the versions listed
-above.
-
-If you would like this library to support another Ruby version, you may
-volunteer to be a maintainer. Being a maintainer entails making sure all tests
-run and pass on that implementation. When something breaks on your
-implementation, you will be responsible for providing patches in a timely
-fashion. If critical issues for a particular implementation exist at the time
-of a major release, support for that Ruby version may be dropped.
-
-
-| | Ruby OAuth2 Version | Maintenance Branch | Supported Officially | Supported Unofficially | Supported Incidentally |
-|:----|---------------------|--------------------|-------------------------|------------------------|------------------------|
-| 1️⃣ | 2.0.x | `master` | 2.7, 3.0, 3.1 | 2.5, 2.6 | 2.2, 2.3, 2.4 |
-| 2️⃣ | 1.4.x | `1-4-stable` | 2.5, 2.6, 2.7, 3.0, 3.1 | 2.1, 2.2, 2.3, 2.4 | 1.9, 2.0 |
-| 3️⃣ | older | N/A | Best of luck to you! | Please upgrade! | |
-
-NOTE: The 1.4 series will only receive critical security updates.
-See [SECURITY.md][🚎sec-pol]
-
-## Usage Examples
-
-### `authorize_url` and `token_url` are on site root (Just Works!)
-
-```ruby
-require 'oauth2'
-client = OAuth2::Client.new('client_id', 'client_secret', site: 'https://example.org')
-# => # "https://example.org/oauth/authorize?client_id=client_id&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Foauth2%2Fcallback&response_type=code"
-
-access = client.auth_code.get_token('authorization_code_value', redirect_uri: 'http://localhost:8080/oauth2/callback', headers: {'Authorization' => 'Basic some_password'})
-response = access.get('/api/resource', params: {'query_foo' => 'bar'})
-response.class.name
-# => OAuth2::Response
-```
-
-### Relative `authorize_url` and `token_url` (Not on site root, Just Works!)
-
-In above example, the default Authorization URL is `oauth/authorize` and default Access Token URL is `oauth/token`, and, as they are missing a leading `/`, both are relative.
-
-```ruby
-client = OAuth2::Client.new('client_id', 'client_secret', site: 'https://example.org/nested/directory/on/your/server')
-# => # "https://example.org/nested/directory/on/your/server/oauth/authorize?client_id=client_id&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Foauth2%2Fcallback&response_type=code"
-```
-
-### Customize `authorize_url` and `token_url`
-
-You can specify custom URLs for authorization and access token, and when using a leading `/` they will _not be relative_, as shown below:
-
-```ruby
-client = OAuth2::Client.new('client_id', 'client_secret',
- site: 'https://example.org/nested/directory/on/your/server',
- authorize_url: '/jaunty/authorize/',
- token_url: '/stirrups/access_token')
-# => # "https://example.org/jaunty/authorize/?client_id=client_id&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Foauth2%2Fcallback&response_type=code"
-client.class.name
-# => OAuth2::Client
-```
-
-### snake_case and indifferent access in Response#parsed
-
-```ruby
-response = access.get('/api/resource', params: {'query_foo' => 'bar'})
-# Even if the actual response is CamelCase. it will be made available as snaky:
-JSON.parse(response.body) # => {"accessToken"=>"aaaaaaaa", "additionalData"=>"additional"}
-response.parsed # => {"access_token"=>"aaaaaaaa", "additional_data"=>"additional"}
-response.parsed.access_token # => "aaaaaaaa"
-response.parsed[:access_token] # => "aaaaaaaa"
-response.parsed.additional_data # => "additional"
-response.parsed[:additional_data] # => "additional"
-response.parsed.class.name # => OAuth2::SnakyHash (subclass of Hashie::Mash::Rash, from `rash_alt` gem)
-```
-
-#### What if I hate snakes and/or indifference?
-
-```ruby
-response = access.get('/api/resource', params: {'query_foo' => 'bar'}, snaky: false)
-JSON.parse(response.body) # => {"accessToken"=>"aaaaaaaa", "additionalData"=>"additional"}
-response.parsed # => {"accessToken"=>"aaaaaaaa", "additionalData"=>"additional"}
-response.parsed['accessToken'] # => "aaaaaaaa"
-response.parsed['additionalData'] # => "additional"
-response.parsed.class.name # => Hash (just, regular old Hash)
-```
-
-
- Debugging
-
-Set an environment variable, however you would [normally do that](https://github.com/bkeepers/dotenv).
-
-```ruby
-# will log both request and response, including bodies
-ENV['OAUTH_DEBUG'] = 'true'
+If you have the project checked out, simply:
+```bash
+git remote set-url origin git@gitlab.com:oauth-xx/oauth2.git
```
-By default, debug output will go to `$stdout`. This can be overridden when
-initializing your OAuth2::Client.
-
-```ruby
-require 'oauth2'
-client = OAuth2::Client.new(
- 'client_id',
- 'client_secret',
- site: 'https://example.org',
- logger: Logger.new('example.log', 'weekly')
-)
-```
-
-
-## OAuth2::Response
-
-The `AccessToken` methods `#get`, `#post`, `#put` and `#delete` and the generic `#request`
-will return an instance of the #OAuth2::Response class.
-
-This instance contains a `#parsed` method that will parse the response body and
-return a Hash-like [`OAuth2::SnakyHash`](https://github.com/oauth-xx/oauth2/blob/master/lib/oauth2/snaky_hash.rb) if the `Content-Type` is `application/x-www-form-urlencoded` or if
-the body is a JSON object. It will return an Array if the body is a JSON
-array. Otherwise, it will return the original body string.
-
-The original response body, headers, and status can be accessed via their
-respective methods.
-
-## OAuth2::AccessToken
-
-If you have an existing Access Token for a user, you can initialize an instance
-using various class methods including the standard new, `from_hash` (if you have
-a hash of the values), or `from_kvform` (if you have an
-`application/x-www-form-urlencoded` encoded string of the values).
-
-## OAuth2::Error
-
-On 400+ status code responses, an `OAuth2::Error` will be raised. If it is a
-standard OAuth2 error response, the body will be parsed and `#code` and `#description` will contain the values provided from the error and
-`error_description` parameters. The `#response` property of `OAuth2::Error` will
-always contain the `OAuth2::Response` instance.
-
-If you do not want an error to be raised, you may use `:raise_errors => false`
-option on initialization of the client. In this case the `OAuth2::Response`
-instance will be returned as usual and on 400+ status code responses, the
-Response instance will contain the `OAuth2::Error` instance.
-
-## Authorization Grants
-
-Currently the Authorization Code, Implicit, Resource Owner Password Credentials, Client Credentials, and Assertion
-authentication grant types have helper strategy classes that simplify client
-use. They are available via the [`#auth_code`](https://github.com/oauth-xx/oauth2/blob/master/lib/oauth2/strategy/auth_code.rb), [`#implicit`](https://github.com/oauth-xx/oauth2/blob/master/lib/oauth2/strategy/implicit.rb), [`#password`](https://github.com/oauth-xx/oauth2/blob/master/lib/oauth2/strategy/password.rb), [`#client_credentials`](https://github.com/oauth-xx/oauth2/blob/master/lib/oauth2/strategy/client_credentials.rb), and [`#assertion`](https://github.com/oauth-xx/oauth2/blob/master/lib/oauth2/strategy/assertion.rb) methods respectively.
-
-These aren't full examples, but demonstrative of the differences between usage for each strategy.
-```ruby
-auth_url = client.auth_code.authorize_url(redirect_uri: 'http://localhost:8080/oauth/callback')
-access = client.auth_code.get_token('code_value', redirect_uri: 'http://localhost:8080/oauth/callback')
-
-auth_url = client.implicit.authorize_url(redirect_uri: 'http://localhost:8080/oauth/callback')
-# get the token params in the callback and
-access = OAuth2::AccessToken.from_kvform(client, query_string)
-
-access = client.password.get_token('username', 'password')
-
-access = client.client_credentials.get_token
-
-# Client Assertion Strategy
-# see: https://tools.ietf.org/html/rfc7523
-claimset = {
- iss: 'http://localhost:3001',
- aud: 'http://localhost:8080/oauth2/token',
- sub: 'me@example.com',
- exp: Time.now.utc.to_i + 3600,
-}
-assertion_params = [claimset, 'HS256', 'secret_key']
-access = client.assertion.get_token(assertion_params)
-
-# The `access` (i.e. access token) is then used like so:
-access.token # actual access_token string, if you need it somewhere
-access.get('/api/stuff') # making api calls with access token
-```
-
-If you want to specify additional headers to be sent out with the
-request, add a 'headers' hash under 'params':
-
-```ruby
-access = client.auth_code.get_token('code_value', redirect_uri: 'http://localhost:8080/oauth/callback', headers: {'Some' => 'Header'})
-```
-
-You can always use the `#request` method on the `OAuth2::Client` instance to make
-requests for tokens for any Authentication grant type.
-
-## Versioning
-
-This library aims to adhere to [Semantic Versioning 2.0.0][semver].
-Violations of this scheme should be reported as bugs. Specifically,
-if a minor or patch version is released that breaks backward
-compatibility, a new version should be immediately released that
-restores compatibility. Breaking changes to the public API will
-only be introduced with new major versions.
-
-As a result of this policy, you can (and should) specify a
-dependency on this gem using the [Pessimistic Version Constraint][pvc] with two digits of precision.
-
-For example:
-
-```ruby
-spec.add_dependency 'oauth2', '~> 2.0'
-```
-
-[semver]: http://semver.org/
-[pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint
-
-## License
-
-[![License: MIT][🖇src-license-img]][🖇src-license]
-
-- Copyright (c) 2011-2013 Michael Bleigh and Intridea, Inc.
-- Copyright (c) 2017-2022 [oauth-xx organization][oauth-xx]
-- See [LICENSE][license] for details.
-
-[][fossa2]
-
-[license]: https://github.com/oauth-xx/oauth2/blob/master/LICENSE
-[oauth-xx]: https://github.com/oauth-xx
-[fossa2]: https://app.fossa.io/projects/git%2Bgithub.amrom.workers.dev%2Foauth-xx%2Foauth2?ref=badge_large
-
-## Development
-
-After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
-
-To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
-
-## Contributing
-
-See [CONTRIBUTING.md][contributing]
-
-[contributing]: https://github.com/oauth-xx/oauth2/blob/master/CONTRIBUTING.md
-
-## Contributors
-
-[]("https://github.com/oauth-xx/oauth2/graphs/contributors")
+We also took the opportunity of the migration to switch the default branch from `master` to `main`.
-Made with [contributors-img](https://contrib.rocks).
+Changes are still being pushed back to the Github branches `1-4-stable` and `main`, but please update dependencies to reference released versions or the new Gitlab source host.
-## Code of Conduct
+* Source Code: [][source]
+* RubyDoc Documentation: [][documentation]
+* Mailing List/Google Group: [][mailinglist]
+* Live Chat on Gitter: [][chat]
+* Maintainer's Blog: [][blogpage]
-Everyone interacting in the OAuth2 project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/oauth-xx/oauth2/blob/master/CODE_OF_CONDUCT.md).
+[documentation]: https://rubydoc.info/github/oauth-xx/oauth2
+[mailinglist]: http://groups.google.com/group/oauth-ruby
+[chat]: https://gitter.im/oauth-xx/oauth2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
+[blogpage]: http://www.railsbling.com/tags/oauth2/
+[source]: https://gitlab.com/oauth-xx/oauth2/
diff --git a/Rakefile b/Rakefile
deleted file mode 100644
index eb413795..00000000
--- a/Rakefile
+++ /dev/null
@@ -1,43 +0,0 @@
-# encoding: utf-8
-# frozen_string_literal: true
-
-# !/usr/bin/env rake
-
-require 'bundler/gem_tasks'
-
-begin
- require 'rspec/core/rake_task'
- RSpec::Core::RakeTask.new(:spec)
-rescue LoadError
- desc 'spec task stub'
- task :spec do
- warn 'rspec is disabled'
- end
-end
-desc 'alias test task to spec'
-task test: :spec
-
-begin
- require 'rubocop/rake_task'
- RuboCop::RakeTask.new do |task|
- task.options = ['-D'] # Display the name of the failing cops
- end
-rescue LoadError
- desc 'rubocop task stub'
- task :rubocop do
- warn 'RuboCop is disabled'
- end
-end
-
-# namespace :doc do
-# require 'rdoc/task'
-# require 'oauth2/version'
-# RDoc::Task.new do |rdoc|
-# rdoc.rdoc_dir = 'rdoc'
-# rdoc.title = "oauth2 #{OAuth2::Version}"
-# rdoc.main = 'README.md'
-# rdoc.rdoc_files.include('README.md', 'LICENSE.md', 'lib/**/*.rb')
-# end
-# end
-
-task default: %i[test rubocop]
diff --git a/bin/bundle b/bin/bundle
deleted file mode 100755
index fece50fe..00000000
--- a/bin/bundle
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-#
-# This file was generated by Bundler.
-#
-# The application 'bundle' is installed as part of a gem, and
-# this file is here to facilitate running it.
-#
-
-require 'rubygems'
-
-m = Module.new do
-module_function
-
- def invoked_as_script?
- File.expand_path($PROGRAM_NAME) == File.expand_path(__FILE__)
- end
-
- def env_var_version
- ENV['BUNDLER_VERSION']
- end
-
- def cli_arg_version
- return unless invoked_as_script? # don't want to hijack other binstubs
- return unless 'update'.start_with?(ARGV.first || ' ') # must be running `bundle update`
-
- bundler_version = nil
- update_index = nil
- ARGV.each_with_index do |a, i|
- bundler_version = a if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
- next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
-
- bundler_version = Regexp.last_match(1)
- update_index = i
- end
- bundler_version
- end
-
- def gemfile
- gemfile = ENV['BUNDLE_GEMFILE']
- return gemfile if gemfile && !gemfile.empty?
-
- File.expand_path('../Gemfile', __dir__)
- end
-
- def lockfile
- lockfile =
- case File.basename(gemfile)
- when 'gems.rb' then gemfile.sub(/\.rb$/, gemfile)
- else "#{gemfile}.lock"
- end
- File.expand_path(lockfile)
- end
-
- def lockfile_version
- return unless File.file?(lockfile)
-
- lockfile_contents = File.read(lockfile)
- return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
-
- Regexp.last_match(1)
- end
-
- def bundler_requirement
- @bundler_requirement ||=
- env_var_version || cli_arg_version ||
- bundler_requirement_for(lockfile_version)
- end
-
- def bundler_requirement_for(version)
- return "#{Gem::Requirement.default}.a" unless version
-
- bundler_gem_version = Gem::Version.new(version)
-
- requirement = bundler_gem_version.approximate_recommendation
-
- return requirement unless Gem.rubygems_version < Gem::Version.new('2.7.0')
-
- requirement += '.a' if bundler_gem_version.prerelease?
-
- requirement
- end
-
- def load_bundler!
- ENV['BUNDLE_GEMFILE'] ||= gemfile
-
- activate_bundler
- end
-
- def activate_bundler
- gem_error = activation_error_handling do
- gem 'bundler', bundler_requirement
- end
- return if gem_error.nil?
-
- require_error = activation_error_handling do
- require 'bundler/version'
- end
- return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
-
- 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}'`"
- exit 42
- end
-
- def activation_error_handling
- yield
- nil
- rescue StandardError, LoadError => e
- e
- end
-end
-
-m.load_bundler!
-
-load Gem.bin_path('bundler', 'bundle') if m.invoked_as_script?
diff --git a/bin/console b/bin/console
deleted file mode 100755
index b3c40a59..00000000
--- a/bin/console
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-require 'bundler/setup'
-require 'oauth2'
-
-# You can add fixtures and/or initialization code here to make experimenting
-# with your gem easier. You can also use a different console, if you like.
-
-# (If you use this, don't forget to add pry to your Gemfile!)
-# require "pry"
-# Pry.start
-
-require 'irb'
-IRB.start(__FILE__)
diff --git a/bin/rake b/bin/rake
deleted file mode 100755
index 5f615c2a..00000000
--- a/bin/rake
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-#
-# This file was generated by Bundler.
-#
-# The application 'rake' is installed as part of a gem, and
-# this file is here to facilitate running it.
-#
-
-ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
-
-bundle_binstub = File.expand_path('bundle', __dir__)
-
-if File.file?(bundle_binstub)
- if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
- load(bundle_binstub)
- else
- abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
-Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
- end
-end
-
-require 'rubygems'
-require 'bundler/setup'
-
-load Gem.bin_path('rake', 'rake')
diff --git a/bin/rspec b/bin/rspec
deleted file mode 100755
index d3f4959a..00000000
--- a/bin/rspec
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-#
-# This file was generated by Bundler.
-#
-# The application 'rspec' is installed as part of a gem, and
-# this file is here to facilitate running it.
-#
-
-ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
-
-bundle_binstub = File.expand_path('bundle', __dir__)
-
-if File.file?(bundle_binstub)
- if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
- load(bundle_binstub)
- else
- abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
-Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
- end
-end
-
-require 'rubygems'
-require 'bundler/setup'
-
-load Gem.bin_path('rspec-core', 'rspec')
diff --git a/bin/rubocop b/bin/rubocop
deleted file mode 100755
index cc105e8d..00000000
--- a/bin/rubocop
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-#
-# This file was generated by Bundler.
-#
-# The application 'rubocop' is installed as part of a gem, and
-# this file is here to facilitate running it.
-#
-
-ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
-
-bundle_binstub = File.expand_path('bundle', __dir__)
-
-if File.file?(bundle_binstub)
- if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
- load(bundle_binstub)
- else
- abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
-Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
- end
-end
-
-require 'rubygems'
-require 'bundler/setup'
-
-load Gem.bin_path('rubocop', 'rubocop')
diff --git a/bin/setup b/bin/setup
deleted file mode 100755
index dce67d86..00000000
--- a/bin/setup
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-IFS=$'\n\t'
-set -vx
-
-bundle install
-
-# Do any other automated setup that you need to do here
diff --git a/docs/images/logo/README.txt b/docs/images/logo/README.txt
deleted file mode 100644
index bb405554..00000000
--- a/docs/images/logo/README.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-The OAuth 2.0 Logo - oauth2-logo-124px.png (resized)
-
-https://oauth.net/about/credits/
-
-The OAuth logo was designed by Chris Messina.
-
----
-
-The Ruby Logo - ruby-logo-124px.jpeg (resized)
-
-https://www.ruby-lang.org/en/about/logo/
-
-Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5
-
-https://creativecommons.org/licenses/by-sa/2.5
diff --git a/docs/images/logo/oauth2-logo-124px.png b/docs/images/logo/oauth2-logo-124px.png
deleted file mode 100644
index 41a8d35a..00000000
Binary files a/docs/images/logo/oauth2-logo-124px.png and /dev/null differ
diff --git a/docs/images/logo/ruby-logo-124px.jpeg b/docs/images/logo/ruby-logo-124px.jpeg
deleted file mode 100644
index f1fc0df8..00000000
Binary files a/docs/images/logo/ruby-logo-124px.jpeg and /dev/null differ
diff --git a/docs/images/logo/ruby-logo-198px.svg b/docs/images/logo/ruby-logo-198px.svg
deleted file mode 100644
index 59cf324f..00000000
--- a/docs/images/logo/ruby-logo-198px.svg
+++ /dev/null
@@ -1,948 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/gemfiles/README.md b/gemfiles/README.md
deleted file mode 100644
index 1ac3a713..00000000
--- a/gemfiles/README.md
+++ /dev/null
@@ -1,106 +0,0 @@
-# History
-
-`faraday` v0.17.3 is the first version that stops using `&Proc.new` for block forwarding,
- and thus is the oldest version oauth2 is compatible with.
-
-```ruby
-gem 'faraday', ['>= 0.17.3', '< 3.0']
-```
-
-# Ruby
-
-We use the Github Action `ruby/setup-ruby@master` to install Ruby, and it has a matrix of
-[supported versions](https://github.com/ruby/setup-ruby/blob/master/README.md#supported-versions) (copied below).
-
-| Interpreter | Versions |
-|-----------------------|------------------------------------------------------------------------------------------|
-| `ruby` | 1.9.3, 2.0.0, 2.1.9, 2.2, all versions from 2.3.0 until 3.1.1, head, debug, mingw, mswin |
-| `jruby` | 9.1.17.0 - 9.3.3.0, head |
-| `truffleruby` | 19.3.0 - 22.0.0, head |
-| `truffleruby+graalvm` | 21.2.0 - 22.0.0, head |
-
-In the naming of gemfiles, we will use the below shorthand for interpreter,
-and version. Platforms will be represented without modification.
-
-| Interpreter | Shorthand |
-|-----------------------|-----------|
-| `ruby` | r |
-| `jruby` | jr |
-| `truffleruby` | tr |
-| `truffleruby+graalvm` | trg |
-
-Building onto that, we can add the MRI target spec,
-since that's what all Rubygems use for minimum version compatibility.
-
-| Interpreter + Version | MRI spec | Shorthand |
-|----------------------------|----------|------------|
-| ruby-1.9.3 | 1.9 | r1_9 |
-| ruby-2.0.0 | 2.0 | r2_0 |
-| ruby-2.1.9 | 2.1 | r2_1 |
-| ruby-2.2.x | 2.2 | r2_2 |
-| ruby-2.3.x | 2.3 | r2_3 |
-| ruby-2.4.x | 2.4 | r2_4 |
-| ruby-2.5.x | 2.5 | r2_5 |
-| ruby-2.6.x | 2.6 | r2_6 |
-| ruby-2.7.x | 2.7 | r2_7 |
-| ruby-3.0.x | 3.0 | r3_0 |
-| ruby-3.1.x | 3.1 | r3_1 |
-| ruby-head | 3.2 | rH3_2 |
-| ruby-mingw | (?) | rmin |
-| ruby-mswin | (?) | rMS |
-| jruby-9.1.x.x | 2.3 | jr9_1-r2_3 |
-| jruby-9.2.x.x | 2.5 | jr9_2-r2_5 |
-| jruby-9.3.x.x | 2.6 | jr9_3-r2_6 |
-| jruby-head | 2.7 | jrH-r2_7 |
-| truffleruby-19.3.x | 2.5(?) | tr19-r2_5 |
-| truffleruby-20.x.x | 2.6.5 | tr20-r2_6 |
-| truffleruby-21.x.x | 2.7.4 | tr21-r2_7 |
-| truffleruby-22.x.x | 3.0.2 | tr22-r3_0 |
-| truffleruby-head | 3.1(?) | trH-r3_1 |
-| truffleruby+graalvm-21.2.x | 2.7.4 | trg21-r2_7 |
-| truffleruby+graalvm-22.x.x | 3.0.2 | trg22-r3_0 |
-| truffleruby+graalvm-head | 3.1(?) | trgH-r3_1 |
-
-We will run tests on as many of these as possible, in a matrix with each supported major version of `faraday`,
-which means 0.17.3+ (as `f0`), 1.10.x (as `f1`), 2.2.x (as `f2`).
-
-Discrete versions of `faraday` to test against, as of 2022.02.19, with minimum version of Ruby for each:
-
-* 2.2.0, Ruby >= 2.6
-* 1.10.0, Ruby >= 2.4
-* 0.17.4, Ruby >= 1.9
-
-❌ - Incompatible
-✅ - Official Support
-🚧 - Unofficial Support
-🤡 - Incidental Compatibility
-🙈 - Unknown Compatibility
-
-| Shorthand | f0 - 0.17.3+ | f1 - 1.10.x | f2 - 2.2.x |
-|------------|------------------|------------------|-----------------|
-| r1_9 | 🤡 f0-r1_9 | ❌ | ❌ |
-| r2_0 | 🤡 f0-r2_0 | ❌ | ❌ |
-| r2_1 | 🤡 f0-r2_1 | ❌ | ❌ |
-| r2_2 | 🤡 f0-r2_2 | ❌ | ❌ |
-| r2_3 | 🚧 f0-r2_3 | ❌ | ❌ |
-| r2_4 | 🚧 f0-r2_4 | 🚧 f1-r2_4 | ❌ |
-| r2_5 | 🚧 f0-r2_5 | 🚧 f1-r2_5 | ❌ |
-| r2_6 | 🚧 f0-r2_6 | 🚧 f1-r2_6 | 🚧 f2-r2_6 |
-| r2_7 | ✅ f0-r2_7 | ✅ f1-r2_7 | ✅ f2-r2_7 |
-| r3_0 | ✅ f0-r3_0 | ✅ f1-r3_0 | ✅ f2-r3_0 |
-| r3_1 | ✅ f0-r3_1 | ✅ f1-r3_1 | ✅ f2-r3_1 |
-| rH3_2 | 🚧 f0-rH3_2 | 🚧 f1-rH3_2 | 🚧 f2-rH3_2 |
-| rmin | 🙈 f0-rmin | 🙈 f1-rmin | 🙈 f2-rmin |
-| rMS | 🙈 f0-rMS | 🙈 f1-rMS | 🙈 f2-rMS |
-| jr9_1-r2_3 | 🚧 f0-jr9_1-r2_3 | ❌ | ❌ |
-| jr9_2-r2_5 | 🚧 f0-jr9_2-r2_5 | 🚧 f1-jr9_2-r2_5 | ❌ |
-| jr9_3-r2_6 | ✅ f0-jr9_3-r2_6 | ✅ f1-jr9_3-r2_6 | ✅ f2-jr9_3-r2_6 |
-| jrH-r2_7 | 🚧 f0-jrH-r2_7 | 🚧 f1-jrH-r2_7 | 🚧 f2-jrH-r2_7 |
-| tr19-r2_5 | 🚧 f0-tr19-r2_5 | 🚧 f1-tr19-r2_5 | ❌ |
-| tr20-r2_6 | 🚧 f0-tr20-r2_6 | 🚧 f1-tr20-r2_6 | 🚧 f2-tr20-r2_6 |
-| tr21-r2_7 | ✅ f0-tr21-r2_7 | ✅ f1-tr21-r2_7 | ✅ f2-tr21-r2_7 |
-| tr22-r3_0 | ✅ f0-tr22-r3_0 | ✅ f1-tr22-r3_0 | ✅ f2-tr22-r3_0 |
-| trH-r3_1 | 🚧 f0-trH-r3_1 | 🚧 f1-trH-r3_1 | 🚧 f2-trH-r3_1 |
-| trg21-r2_7 | ✅ f0-trg21-r2_7 | ✅ f1-trg21-r2_7 | ✅ f2-trg21-r2_7 |
-| trg22-r3_0 | ✅ f0-trg22-r3_0 | ✅ f1-trg22-r3_0 | ✅ f2-trg22-r3_0 |
-| trgH-r3_1 | 🚧 f0-trgH-r3_1 | 🚧 f1-trgH-r3_1 | 🚧 f2-trgH-r3_1 |
diff --git a/gemfiles/f0.gemfile b/gemfiles/f0.gemfile
deleted file mode 100644
index 4cb7f887..00000000
--- a/gemfiles/f0.gemfile
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-source 'https://rubygems.org'
-
-# See README.md in this directory
-
-# 0.17.3 is the first version that stops using &Proc.new for block forwarding,
-# and thus is the oldest version oauth2 is compatible with.
-gem 'faraday', '~> 0.17.4'
-
-gemspec path: '../'
diff --git a/gemfiles/f1.gemfile b/gemfiles/f1.gemfile
deleted file mode 100644
index 94cba5c6..00000000
--- a/gemfiles/f1.gemfile
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-source 'https://rubygems.org'
-
-# See README.md in this directory
-
-gem 'faraday', '~> 1.10'
-
-gemspec path: '../'
diff --git a/gemfiles/f2.gemfile b/gemfiles/f2.gemfile
deleted file mode 100644
index 7c3868df..00000000
--- a/gemfiles/f2.gemfile
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-source 'https://rubygems.org'
-
-# See README.md in this directory
-
-gem 'faraday', '~> 2.2'
-
-gemspec path: '../'
diff --git a/gemfiles/jruby_9.1.gemfile b/gemfiles/jruby_9.1.gemfile
deleted file mode 100644
index fb2b9158..00000000
--- a/gemfiles/jruby_9.1.gemfile
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-source 'https://rubygems.org'
-
-gemspec path: '../'
diff --git a/gemfiles/jruby_9.2.gemfile b/gemfiles/jruby_9.2.gemfile
deleted file mode 100644
index fb2b9158..00000000
--- a/gemfiles/jruby_9.2.gemfile
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-source 'https://rubygems.org'
-
-gemspec path: '../'
diff --git a/gemfiles/jruby_head.gemfile b/gemfiles/jruby_head.gemfile
deleted file mode 100644
index fb2b9158..00000000
--- a/gemfiles/jruby_head.gemfile
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-source 'https://rubygems.org'
-
-gemspec path: '../'
diff --git a/gemfiles/ruby_head.gemfile b/gemfiles/ruby_head.gemfile
deleted file mode 100644
index fb2b9158..00000000
--- a/gemfiles/ruby_head.gemfile
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-source 'https://rubygems.org'
-
-gemspec path: '../'
diff --git a/gemfiles/truffleruby.gemfile b/gemfiles/truffleruby.gemfile
deleted file mode 100644
index fb2b9158..00000000
--- a/gemfiles/truffleruby.gemfile
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-source 'https://rubygems.org'
-
-gemspec path: '../'
diff --git a/lib/oauth2.rb b/lib/oauth2.rb
deleted file mode 100644
index 1efe7923..00000000
--- a/lib/oauth2.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-# includes modules from stdlib
-require 'cgi'
-require 'time'
-
-# third party gems
-require 'rash'
-require 'version_gem'
-
-# includes gem files
-require 'oauth2/version'
-require 'oauth2/error'
-require 'oauth2/snaky_hash'
-require 'oauth2/authenticator'
-require 'oauth2/client'
-require 'oauth2/strategy/base'
-require 'oauth2/strategy/auth_code'
-require 'oauth2/strategy/implicit'
-require 'oauth2/strategy/password'
-require 'oauth2/strategy/client_credentials'
-require 'oauth2/strategy/assertion'
-require 'oauth2/access_token'
-require 'oauth2/response'
-
-# The namespace of this library
-module OAuth2
-end
-
-OAuth2::Version.class_eval do
- extend VersionGem::Basic
-end
diff --git a/lib/oauth2/access_token.rb b/lib/oauth2/access_token.rb
deleted file mode 100644
index 60f6b090..00000000
--- a/lib/oauth2/access_token.rb
+++ /dev/null
@@ -1,212 +0,0 @@
-# frozen_string_literal: true
-
-module OAuth2
- class AccessToken # rubocop:disable Metrics/ClassLength
- TOKEN_KEYS_STR = %w[access_token id_token token accessToken idToken].freeze
- TOKEN_KEYS_SYM = %i[access_token id_token token accessToken idToken].freeze
- TOKEN_KEY_LOOKUP = TOKEN_KEYS_STR + TOKEN_KEYS_SYM
-
- attr_reader :client, :token, :expires_in, :expires_at, :expires_latency, :params
- attr_accessor :options, :refresh_token, :response
-
- class << self
- # Initializes an AccessToken from a Hash
- #
- # @param [Client] client the OAuth2::Client instance
- # @param [Hash] hash a hash of AccessToken property values
- # @option hash [String] 'access_token', 'id_token', 'token', :access_token, :id_token, or :token the access token
- # @return [AccessToken] the initialized AccessToken
- def from_hash(client, hash)
- fresh = hash.dup
- supported_keys = TOKEN_KEY_LOOKUP & fresh.keys
- key = supported_keys[0]
- # Having too many is sus, and may lead to bugs. Having none is fine (e.g. refresh flow doesn't need a token).
- warn("OAuth2::AccessToken.from_hash: `hash` contained more than one 'token' key (#{supported_keys}); using #{key.inspect}.") if supported_keys.length > 1
- token = fresh.delete(key)
- new(client, token, fresh)
- end
-
- # Initializes an AccessToken from a key/value application/x-www-form-urlencoded string
- #
- # @param [Client] client the OAuth2::Client instance
- # @param [String] kvform the application/x-www-form-urlencoded string
- # @return [AccessToken] the initialized AccessToken
- def from_kvform(client, kvform)
- from_hash(client, Rack::Utils.parse_query(kvform))
- end
- end
-
- # Initialize an AccessToken
- #
- # @param [Client] client the OAuth2::Client instance
- # @param [String] token the Access Token value (optional, may not be used in refresh flows)
- # @param [Hash] opts the options to create the Access Token with
- # @option opts [String] :refresh_token (nil) the refresh_token value
- # @option opts [FixNum, String] :expires_in (nil) the number of seconds in which the AccessToken will expire
- # @option opts [FixNum, String] :expires_at (nil) the epoch time in seconds in which AccessToken will expire
- # @option opts [FixNum, String] :expires_latency (nil) the number of seconds by which AccessToken validity will be reduced to offset latency, @version 2.0+
- # @option opts [Symbol] :mode (:header) the transmission mode of the Access Token parameter value
- # one of :header, :body or :query
- # @option opts [String] :header_format ('Bearer %s') the string format to use for the Authorization header
- # @option opts [String] :param_name ('access_token') the parameter name to use for transmission of the
- # Access Token value in :body or :query transmission mode
- def initialize(client, token, opts = {})
- @client = client
- @token = token.to_s
-
- opts = opts.dup
- %i[refresh_token expires_in expires_at expires_latency].each do |arg|
- instance_variable_set("@#{arg}", opts.delete(arg) || opts.delete(arg.to_s))
- end
- no_tokens = (@token.nil? || @token.empty?) && (@refresh_token.nil? || @refresh_token.empty?)
- if no_tokens
- if @client.options[:raise_errors]
- error = Error.new(opts)
- raise(error)
- else
- warn('OAuth2::AccessToken has no token')
- end
- end
- @expires_in ||= opts.delete('expires')
- @expires_in &&= @expires_in.to_i
- @expires_at &&= convert_expires_at(@expires_at)
- @expires_latency &&= @expires_latency.to_i
- @expires_at ||= Time.now.to_i + @expires_in if @expires_in
- @expires_at -= @expires_latency if @expires_latency
- @options = {mode: opts.delete(:mode) || :header,
- header_format: opts.delete(:header_format) || 'Bearer %s',
- param_name: opts.delete(:param_name) || 'access_token'}
- @params = opts
- end
-
- # Indexer to additional params present in token response
- #
- # @param [String] key entry key to Hash
- def [](key)
- @params[key]
- end
-
- # Whether or not the token expires
- #
- # @return [Boolean]
- def expires?
- !!@expires_at
- end
-
- # Whether or not the token is expired
- #
- # @return [Boolean]
- def expired?
- expires? && (expires_at <= Time.now.to_i)
- end
-
- # Refreshes the current Access Token
- #
- # @return [AccessToken] a new AccessToken
- # @note options should be carried over to the new AccessToken
- def refresh(params = {}, access_token_opts = {})
- raise('A refresh_token is not available') unless refresh_token
-
- params[:grant_type] = 'refresh_token'
- params[:refresh_token] = refresh_token
- new_token = @client.get_token(params, access_token_opts)
- new_token.options = options
- if new_token.refresh_token
- # Keep it, if there is one
- else
- new_token.refresh_token = refresh_token
- end
- new_token
- end
- # A compatibility alias
- # @note does not modify the receiver, so bang is not the default method
- alias refresh! refresh
-
- # Convert AccessToken to a hash which can be used to rebuild itself with AccessToken.from_hash
- #
- # @return [Hash] a hash of AccessToken property values
- def to_hash
- params.merge(access_token: token, refresh_token: refresh_token, expires_at: expires_at)
- end
-
- # Make a request with the Access Token
- #
- # @param [Symbol] verb the HTTP request method
- # @param [String] path the HTTP URL path of the request
- # @param [Hash] opts the options to make the request with
- # @see Client#request
- def request(verb, path, opts = {}, &block)
- configure_authentication!(opts)
- @client.request(verb, path, opts, &block)
- end
-
- # Make a GET request with the Access Token
- #
- # @see AccessToken#request
- def get(path, opts = {}, &block)
- request(:get, path, opts, &block)
- end
-
- # Make a POST request with the Access Token
- #
- # @see AccessToken#request
- def post(path, opts = {}, &block)
- request(:post, path, opts, &block)
- end
-
- # Make a PUT request with the Access Token
- #
- # @see AccessToken#request
- def put(path, opts = {}, &block)
- request(:put, path, opts, &block)
- end
-
- # Make a PATCH request with the Access Token
- #
- # @see AccessToken#request
- def patch(path, opts = {}, &block)
- request(:patch, path, opts, &block)
- end
-
- # Make a DELETE request with the Access Token
- #
- # @see AccessToken#request
- def delete(path, opts = {}, &block)
- request(:delete, path, opts, &block)
- end
-
- # Get the headers hash (includes Authorization token)
- def headers
- {'Authorization' => options[:header_format] % token}
- end
-
- private
-
- def configure_authentication!(opts)
- case options[:mode]
- when :header
- opts[:headers] ||= {}
- opts[:headers].merge!(headers)
- when :query
- opts[:params] ||= {}
- opts[:params][options[:param_name]] = token
- when :body
- opts[:body] ||= {}
- if opts[:body].is_a?(Hash)
- opts[:body][options[:param_name]] = token
- else
- opts[:body] += "{options[:param_name]}=#{token}"
- end
- # @todo support for multi-part (file uploads)
- else
- raise("invalid :mode option of #{options[:mode]}")
- end
- end
-
- def convert_expires_at(expires_at)
- Time.iso8601(expires_at.to_s).to_i
- rescue ArgumentError
- expires_at.to_i
- end
- end
-end
diff --git a/lib/oauth2/authenticator.rb b/lib/oauth2/authenticator.rb
deleted file mode 100644
index eafc3ef0..00000000
--- a/lib/oauth2/authenticator.rb
+++ /dev/null
@@ -1,75 +0,0 @@
-# frozen_string_literal: true
-
-require 'base64'
-
-module OAuth2
- class Authenticator
- attr_reader :mode, :id, :secret
-
- def initialize(id, secret, mode)
- @id = id
- @secret = secret
- @mode = mode
- end
-
- # Apply the request credentials used to authenticate to the Authorization Server
- #
- # Depending on configuration, this might be as request params or as an
- # Authorization header.
- #
- # User-provided params and header take precedence.
- #
- # @param [Hash] params a Hash of params for the token endpoint
- # @return [Hash] params amended with appropriate authentication details
- def apply(params)
- case mode.to_sym
- when :basic_auth
- apply_basic_auth(params)
- when :request_body
- apply_params_auth(params)
- when :tls_client_auth
- apply_client_id(params)
- when :private_key_jwt
- params
- else
- raise NotImplementedError
- end
- end
-
- def self.encode_basic_auth(user, password)
- "Basic #{Base64.strict_encode64("#{user}:#{password}")}"
- end
-
- private
-
- # Adds client_id and client_secret request parameters if they are not
- # already set.
- def apply_params_auth(params)
- result = {}
- result['client_id'] = id unless id.nil?
- result['client_secret'] = secret unless secret.nil?
- result.merge(params)
- end
-
- # When using schemes that don't require the client_secret to be passed i.e TLS Client Auth,
- # we don't want to send the secret
- def apply_client_id(params)
- result = {}
- result['client_id'] = id unless id.nil?
- result.merge(params)
- end
-
- # Adds an `Authorization` header with Basic Auth credentials if and only if
- # it is not already set in the params.
- def apply_basic_auth(params)
- headers = params.fetch(:headers, {})
- headers = basic_auth_header.merge(headers)
- params.merge(headers: headers)
- end
-
- # @see https://datatracker.ietf.org/doc/html/rfc2617#section-2
- def basic_auth_header
- {'Authorization' => self.class.encode_basic_auth(id, secret)}
- end
- end
-end
diff --git a/lib/oauth2/client.rb b/lib/oauth2/client.rb
deleted file mode 100644
index bffd9cca..00000000
--- a/lib/oauth2/client.rb
+++ /dev/null
@@ -1,354 +0,0 @@
-# frozen_string_literal: true
-
-require 'faraday'
-require 'logger'
-
-module OAuth2
- ConnectionError = Class.new(Faraday::ConnectionFailed)
- TimeoutError = Class.new(Faraday::TimeoutError)
-
- # The OAuth2::Client class
- class Client # rubocop:disable Metrics/ClassLength
- RESERVED_PARAM_KEYS = %w[body headers params parse snaky].freeze
-
- attr_reader :id, :secret, :site
- attr_accessor :options
- attr_writer :connection
-
- # Instantiate a new OAuth 2.0 client using the
- # Client ID and Client Secret registered to your
- # application.
- #
- # @param [String] client_id the client_id value
- # @param [String] client_secret the client_secret value
- # @param [Hash] options the options to create the client with
- # @option options [String] :site the OAuth2 provider site host
- # @option options [String] :redirect_uri the absolute URI to the Redirection Endpoint for use in authorization grants and token exchange
- # @option options [String] :authorize_url ('/oauth/authorize') absolute or relative URL path to the Authorization endpoint
- # @option options [String] :token_url ('/oauth/token') absolute or relative URL path to the Token endpoint
- # @option options [Symbol] :token_method (:post) HTTP method to use to request token (:get, :post, :post_with_query_string)
- # @option options [Symbol] :auth_scheme (:basic_auth) HTTP method to use to authorize request (:basic_auth or :request_body)
- # @option options [Hash] :connection_opts ({}) Hash of connection options to pass to initialize Faraday with
- # @option options [FixNum] :max_redirects (5) maximum number of redirects to follow
- # @option options [Boolean] :raise_errors (true) whether or not to raise an OAuth2::Error on responses with 400+ status codes
- # @option options [Logger] :logger (::Logger.new($stdout)) which logger to use when OAUTH_DEBUG is enabled
- # @option options [Proc] :extract_access_token proc that takes the client and the response Hash and extracts the access token from the response (DEPRECATED)
- # @option options [Class] :access_token_class [Class] class of access token for easier subclassing OAuth2::AccessToken, @version 2.0+
- # @yield [builder] The Faraday connection builder
- def initialize(client_id, client_secret, options = {}, &block)
- opts = options.dup
- @id = client_id
- @secret = client_secret
- @site = opts.delete(:site)
- ssl = opts.delete(:ssl)
- warn('OAuth2::Client#initialize argument `extract_access_token` will be removed in oauth2 v3. Refactor to use `access_token_class`.') if opts[:extract_access_token]
- @options = {
- authorize_url: 'oauth/authorize',
- token_url: 'oauth/token',
- token_method: :post,
- auth_scheme: :basic_auth,
- connection_opts: {},
- connection_build: block,
- max_redirects: 5,
- raise_errors: true,
- logger: ::Logger.new($stdout),
- access_token_class: AccessToken,
- }.merge(opts)
- @options[:connection_opts][:ssl] = ssl if ssl
- end
-
- # Set the site host
- #
- # @param value [String] the OAuth2 provider site host
- def site=(value)
- @connection = nil
- @site = value
- end
-
- # The Faraday connection object
- def connection
- @connection ||=
- Faraday.new(site, options[:connection_opts]) do |builder|
- oauth_debug_logging(builder)
- if options[:connection_build]
- options[:connection_build].call(builder)
- else
- builder.request :url_encoded # form-encode POST params
- builder.adapter Faraday.default_adapter # make requests with Net::HTTP
- end
- end
- end
-
- # The authorize endpoint URL of the OAuth2 provider
- #
- # @param [Hash] params additional query parameters
- def authorize_url(params = {})
- params = (params || {}).merge(redirection_params)
- connection.build_url(options[:authorize_url], params).to_s
- end
-
- # The token endpoint URL of the OAuth2 provider
- #
- # @param [Hash] params additional query parameters
- def token_url(params = nil)
- connection.build_url(options[:token_url], params).to_s
- end
-
- # Makes a request relative to the specified site root.
- # Updated HTTP 1.1 specification (IETF RFC 7231) relaxed the original constraint (IETF RFC 2616),
- # allowing the use of relative URLs in Location headers.
- # @see https://datatracker.ietf.org/doc/html/rfc7231#section-7.1.2
- #
- # @param [Symbol] verb one of :get, :post, :put, :delete
- # @param [String] url URL path of request
- # @param [Hash] opts the options to make the request with
- # @option opts [Hash] :params additional query parameters for the URL of the request
- # @option opts [Hash, String] :body the body of the request
- # @option opts [Hash] :headers http request headers
- # @option opts [Boolean] :raise_errors whether or not to raise an OAuth2::Error on 400+ status
- # code response for this request. Will default to client option
- # @option opts [Symbol] :parse @see Response::initialize
- # @option opts [true, false] :snaky (true) @see Response::initialize
- # @yield [req] @see Faraday::Connection#run_request
- def request(verb, url, opts = {}, &block)
- response = execute_request(verb, url, opts, &block)
-
- case response.status
- when 301, 302, 303, 307
- opts[:redirect_count] ||= 0
- opts[:redirect_count] += 1
- return response if opts[:redirect_count] > options[:max_redirects]
-
- if response.status == 303
- verb = :get
- opts.delete(:body)
- end
- location = response.headers['location']
- if location
- full_location = response.response.env.url.merge(location)
- request(verb, full_location, opts)
- else
- error = Error.new(response)
- raise(error, "Got #{response.status} status code, but no Location header was present")
- end
- when 200..299, 300..399
- # on non-redirecting 3xx statuses, just return the response
- response
- when 400..599
- error = Error.new(response)
- raise(error) if opts.fetch(:raise_errors, options[:raise_errors])
-
- response
- else
- error = Error.new(response)
- raise(error, "Unhandled status code value of #{response.status}")
- end
- end
-
- # Initializes an AccessToken by making a request to the token endpoint
- #
- # @param params [Hash] a Hash of params for the token endpoint, except:
- # @option params [Symbol] :parse @see Response#initialize
- # @option params [true, false] :snaky (true) @see Response#initialize
- # @param access_token_opts [Hash] access token options, to pass to the AccessToken object
- # @param extract_access_token [Proc] proc that extracts the access token from the response (DEPRECATED)
- # @yield [req] @see Faraday::Connection#run_request
- # @return [AccessToken] the initialized AccessToken
- def get_token(params, access_token_opts = {}, extract_access_token = nil, &block)
- warn('OAuth2::Client#get_token argument `extract_access_token` will be removed in oauth2 v3. Refactor to use `access_token_class` on #initialize.') if extract_access_token
- extract_access_token ||= options[:extract_access_token]
- parse, snaky, params, headers = parse_snaky_params_headers(params)
-
- request_opts = {
- raise_errors: options[:raise_errors],
- parse: parse,
- snaky: snaky,
- }
- if options[:token_method] == :post
-
- # NOTE: If proliferation of request types continues we should implement a parser solution for Request,
- # just like we have with Response.
- request_opts[:body] = if headers['Content-Type'] == 'application/json'
- params.to_json
- else
- params
- end
-
- request_opts[:headers] = {'Content-Type' => 'application/x-www-form-urlencoded'}
- else
- request_opts[:params] = params
- request_opts[:headers] = {}
- end
- request_opts[:headers].merge!(headers)
- response = request(http_method, token_url, request_opts, &block)
-
- # In v1.4.x, the deprecated extract_access_token option retrieves the token from the response.
- # We preserve this behavior here, but a custom access_token_class that implements #from_hash
- # should be used instead.
- if extract_access_token
- parse_response_legacy(response, access_token_opts, extract_access_token)
- else
- parse_response(response, access_token_opts)
- end
- end
-
- # The HTTP Method of the request
- # @return [Symbol] HTTP verb, one of :get, :post, :put, :delete
- def http_method
- http_meth = options[:token_method].to_sym
- return :post if http_meth == :post_with_query_string
-
- http_meth
- end
-
- # The Authorization Code strategy
- #
- # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.1
- def auth_code
- @auth_code ||= OAuth2::Strategy::AuthCode.new(self)
- end
-
- # The Implicit strategy
- #
- # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-26#section-4.2
- def implicit
- @implicit ||= OAuth2::Strategy::Implicit.new(self)
- end
-
- # The Resource Owner Password Credentials strategy
- #
- # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.3
- def password
- @password ||= OAuth2::Strategy::Password.new(self)
- end
-
- # The Client Credentials strategy
- #
- # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.4
- def client_credentials
- @client_credentials ||= OAuth2::Strategy::ClientCredentials.new(self)
- end
-
- def assertion
- @assertion ||= OAuth2::Strategy::Assertion.new(self)
- end
-
- # The redirect_uri parameters, if configured
- #
- # The redirect_uri query parameter is OPTIONAL (though encouraged) when
- # requesting authorization. If it is provided at authorization time it MUST
- # also be provided with the token exchange request.
- #
- # Providing the :redirect_uri to the OAuth2::Client instantiation will take
- # care of managing this.
- #
- # @api semipublic
- #
- # @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1
- # @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3
- # @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.2.1
- # @see https://datatracker.ietf.org/doc/html/rfc6749#section-10.6
- # @return [Hash] the params to add to a request or URL
- def redirection_params
- if options[:redirect_uri]
- {'redirect_uri' => options[:redirect_uri]}
- else
- {}
- end
- end
-
- private
-
- def parse_snaky_params_headers(params)
- params = params.map do |key, value|
- if RESERVED_PARAM_KEYS.include?(key)
- [key.to_sym, value]
- else
- [key, value]
- end
- end.to_h
- parse = params.key?(:parse) ? params.delete(:parse) : Response::DEFAULT_OPTIONS[:parse]
- snaky = params.key?(:snaky) ? params.delete(:snaky) : Response::DEFAULT_OPTIONS[:snaky]
- params = authenticator.apply(params)
- # authenticator may add :headers, and we remove them here
- headers = params.delete(:headers) || {}
- [parse, snaky, params, headers]
- end
-
- def execute_request(verb, url, opts = {})
- url = connection.build_url(url).to_s
-
- begin
- response = connection.run_request(verb, url, opts[:body], opts[:headers]) do |req|
- req.params.update(opts[:params]) if opts[:params]
- yield(req) if block_given?
- end
- rescue Faraday::ConnectionFailed => e
- raise ConnectionError, e
- rescue Faraday::TimeoutError => e
- raise TimeoutError, e
- end
-
- parse = opts.key?(:parse) ? opts.delete(:parse) : Response::DEFAULT_OPTIONS[:parse]
- snaky = opts.key?(:snaky) ? opts.delete(:snaky) : Response::DEFAULT_OPTIONS[:snaky]
-
- Response.new(response, parse: parse, snaky: snaky)
- end
-
- # Returns the authenticator object
- #
- # @return [Authenticator] the initialized Authenticator
- def authenticator
- Authenticator.new(id, secret, options[:auth_scheme])
- end
-
- def parse_response_legacy(response, access_token_opts, extract_access_token)
- access_token = build_access_token_legacy(response, access_token_opts, extract_access_token)
-
- return access_token if access_token
-
- if options[:raise_errors]
- error = Error.new(response)
- raise(error)
- end
-
- nil
- end
-
- def parse_response(response, access_token_opts)
- access_token_class = options[:access_token_class]
- data = response.parsed
-
- unless data.is_a?(Hash) && !data.empty?
- return unless options[:raise_errors]
-
- error = Error.new(response)
- raise(error)
- end
-
- build_access_token(response, access_token_opts, access_token_class)
- end
-
- # Builds the access token from the response of the HTTP call
- #
- # @return [AccessToken] the initialized AccessToken
- def build_access_token(response, access_token_opts, access_token_class)
- access_token_class.from_hash(self, response.parsed.merge(access_token_opts)).tap do |access_token|
- access_token.response = response if access_token.respond_to?(:response=)
- end
- end
-
- # Builds the access token from the response of the HTTP call with legacy extract_access_token
- #
- # @return [AccessToken] the initialized AccessToken
- def build_access_token_legacy(response, access_token_opts, extract_access_token)
- extract_access_token.call(self, response.parsed.merge(access_token_opts))
- rescue StandardError
- nil
- end
-
- def oauth_debug_logging(builder)
- builder.response :logger, options[:logger], bodies: true if ENV['OAUTH_DEBUG'] == 'true'
- end
- end
-end
diff --git a/lib/oauth2/error.rb b/lib/oauth2/error.rb
deleted file mode 100644
index cd99ff86..00000000
--- a/lib/oauth2/error.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-# frozen_string_literal: true
-
-module OAuth2
- class Error < StandardError
- attr_reader :response, :body, :code, :description
-
- # standard error codes include:
- # 'invalid_request', 'invalid_client', 'invalid_token', 'invalid_grant', 'unsupported_grant_type', 'invalid_scope'
- # response might be a Response object, or the response.parsed hash
- def initialize(response)
- @response = response
- if response.respond_to?(:parsed)
- if response.parsed.is_a?(Hash)
- @code = response.parsed['error']
- @description = response.parsed['error_description']
- end
- elsif response.is_a?(Hash)
- @code = response['error']
- @description = response['error_description']
- end
- @body = if response.respond_to?(:body)
- response.body
- else
- @response
- end
- message_opts = parse_error_description(@code, @description)
- super(error_message(@body, message_opts))
- end
-
- private
-
- def error_message(response_body, opts = {})
- lines = []
-
- lines << opts[:error_description] if opts[:error_description]
-
- error_string = if response_body.respond_to?(:encode) && opts[:error_description].respond_to?(:encoding)
- script_encoding = opts[:error_description].encoding
- response_body.encode(script_encoding, invalid: :replace, undef: :replace)
- else
- response_body
- end
-
- lines << error_string
-
- lines.join("\n")
- end
-
- def parse_error_description(code, description)
- return {} unless code || description
-
- error_description = ''
- error_description += "#{code}: " if code
- error_description += description if description
-
- {error_description: error_description}
- end
- end
-end
diff --git a/lib/oauth2/response.rb b/lib/oauth2/response.rb
deleted file mode 100644
index b1742e7c..00000000
--- a/lib/oauth2/response.rb
+++ /dev/null
@@ -1,148 +0,0 @@
-# frozen_string_literal: true
-
-require 'json'
-require 'multi_xml'
-require 'rack'
-
-module OAuth2
- # OAuth2::Response class
- class Response
- DEFAULT_OPTIONS = {
- parse: :automatic,
- snaky: true,
- }.freeze
- attr_reader :response
- attr_accessor :options
-
- # Procs that, when called, will parse a response body according
- # to the specified format.
- @@parsers = {
- query: ->(body) { Rack::Utils.parse_query(body) },
- text: ->(body) { body },
- }
-
- # Content type assignments for various potential HTTP content types.
- @@content_types = {
- 'application/x-www-form-urlencoded' => :query,
- 'text/plain' => :text,
- }
-
- # Adds a new content type parser.
- #
- # @param [Symbol] key A descriptive symbol key such as :json or :query.
- # @param [Array] mime_types One or more mime types to which this parser applies.
- # @yield [String] A block returning parsed content.
- def self.register_parser(key, mime_types, &block)
- key = key.to_sym
- @@parsers[key] = block
- Array(mime_types).each do |mime_type|
- @@content_types[mime_type] = key
- end
- end
-
- # Initializes a Response instance
- #
- # @param [Faraday::Response] response The Faraday response instance
- # @param [Symbol] parse (:automatic) how to parse the response body. one of :query (for x-www-form-urlencoded),
- # :json, or :automatic (determined by Content-Type response header)
- # @param [true, false] snaky (true) Convert @parsed to a snake-case,
- # indifferent-access OAuth2::SnakyHash, which is a subclass of Hashie::Mash::Rash (from rash_alt gem)?
- # @param [Hash] options all other options for initializing the instance
- def initialize(response, parse: :automatic, snaky: true, **options)
- @response = response
- @options = {
- parse: parse,
- snaky: snaky,
- }.merge(options)
- end
-
- # The HTTP response headers
- def headers
- response.headers
- end
-
- # The HTTP response status code
- def status
- response.status
- end
-
- # The HTTP response body
- def body
- response.body || ''
- end
-
- # The {#response} {#body} as parsed by {#parser}.
- #
- # @return [Object] As returned by {#parser} if it is #call-able.
- # @return [nil] If the {#parser} is not #call-able.
- def parsed
- return @parsed if defined?(@parsed)
-
- @parsed =
- if parser.respond_to?(:call)
- case parser.arity
- when 0
- parser.call
- when 1
- parser.call(body)
- else
- parser.call(body, response)
- end
- end
-
- @parsed = OAuth2::SnakyHash.new(@parsed) if options[:snaky] && @parsed.is_a?(Hash)
-
- @parsed
- end
-
- # Attempts to determine the content type of the response.
- def content_type
- return nil unless response.headers
-
- ((response.headers.values_at('content-type', 'Content-Type').compact.first || '').split(';').first || '').strip.downcase
- end
-
- # Determines the parser (a Proc or other Object which responds to #call)
- # that will be passed the {#body} (and optional {#response}) to supply
- # {#parsed}.
- #
- # The parser can be supplied as the +:parse+ option in the form of a Proc
- # (or other Object responding to #call) or a Symbol. In the latter case,
- # the actual parser will be looked up in {@@parsers} by the supplied Symbol.
- #
- # If no +:parse+ option is supplied, the lookup Symbol will be determined
- # by looking up {#content_type} in {@@content_types}.
- #
- # If {#parser} is a Proc, it will be called with no arguments, just
- # {#body}, or {#body} and {#response}, depending on the Proc's arity.
- #
- # @return [Proc, #call] If a parser was found.
- # @return [nil] If no parser was found.
- def parser
- return @parser if defined?(@parser)
-
- @parser =
- if options[:parse].respond_to?(:call)
- options[:parse]
- elsif options[:parse]
- @@parsers[options[:parse].to_sym]
- end
-
- @parser ||= @@parsers[@@content_types[content_type]]
- end
- end
-end
-
-OAuth2::Response.register_parser(:xml, ['text/xml', 'application/rss+xml', 'application/rdf+xml', 'application/atom+xml', 'application/xml']) do |body|
- next body unless body.respond_to?(:to_str)
-
- MultiXml.parse(body)
-end
-
-OAuth2::Response.register_parser(:json, ['application/json', 'text/javascript', 'application/hal+json', 'application/vnd.collection+json', 'application/vnd.api+json', 'application/problem+json']) do |body|
- next body unless body.respond_to?(:to_str)
-
- body = body.dup.force_encoding(::Encoding::ASCII_8BIT) if body.respond_to?(:force_encoding)
-
- ::JSON.parse(body)
-end
diff --git a/lib/oauth2/snaky_hash.rb b/lib/oauth2/snaky_hash.rb
deleted file mode 100644
index 836d9adb..00000000
--- a/lib/oauth2/snaky_hash.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-module OAuth2
- # Hash which allow assign string key in camel case
- # and query on both camel and snake case
- class SnakyHash < ::Hashie::Mash::Rash
- end
-end
diff --git a/lib/oauth2/strategy/assertion.rb b/lib/oauth2/strategy/assertion.rb
deleted file mode 100644
index 5d921fbc..00000000
--- a/lib/oauth2/strategy/assertion.rb
+++ /dev/null
@@ -1,102 +0,0 @@
-# frozen_string_literal: true
-
-require 'jwt'
-
-module OAuth2
- module Strategy
- # The Client Assertion Strategy
- #
- # @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-10#section-4.1.3
- #
- # Sample usage:
- # client = OAuth2::Client.new(client_id, client_secret,
- # :site => 'http://localhost:8080',
- # :auth_scheme => :request_body)
- #
- # claim_set = {
- # :iss => "http://localhost:3001",
- # :aud => "http://localhost:8080/oauth2/token",
- # :sub => "me@example.com",
- # :exp => Time.now.utc.to_i + 3600,
- # }
- #
- # encoding = {
- # :algorithm => 'HS256',
- # :key => 'secret_key',
- # }
- #
- # access = client.assertion.get_token(claim_set, encoding)
- # access.token # actual access_token string
- # access.get("/api/stuff") # making api calls with access token in header
- #
- class Assertion < Base
- # Not used for this strategy
- #
- # @raise [NotImplementedError]
- def authorize_url
- raise(NotImplementedError, 'The authorization endpoint is not used in this strategy')
- end
-
- # Retrieve an access token given the specified client.
- #
- # @param [Hash] claims the hash representation of the claims that should be encoded as a JWT (JSON Web Token)
- #
- # For reading on JWT and claim keys:
- # @see https://github.com/jwt/ruby-jwt
- # @see https://datatracker.ietf.org/doc/html/rfc7519#section-4.1
- # @see https://datatracker.ietf.org/doc/html/rfc7523#section-3
- # @see https://www.iana.org/assignments/jwt/jwt.xhtml
- #
- # There are many possible claim keys, and applications may ask for their own custom keys.
- # Some typically required ones:
- # :iss (issuer)
- # :aud (audience)
- # :sub (subject) -- formerly :prn https://datatracker.ietf.org/doc/html/draft-ietf-oauth-json-web-token-06#appendix-F
- # :exp, (expiration time) -- in seconds, e.g. Time.now.utc.to_i + 3600
- #
- # Note that this method does *not* validate presence of those four claim keys indicated as required by RFC 7523.
- # There are endpoints that may not conform with this RFC, and this gem should still work for those use cases.
- #
- # @param [Hash] encoding_opts a hash containing instructions on how the JWT should be encoded
- # @option algorithm [String] the algorithm with which you would like the JWT to be encoded
- # @option key [Object] the key with which you would like to encode the JWT
- #
- # These two options are passed directly to `JWT.encode`. For supported encoding arguments:
- # @see https://github.com/jwt/ruby-jwt#algorithms-and-usage
- # @see https://datatracker.ietf.org/doc/html/rfc7518#section-3.1
- #
- # The object type of `:key` may depend on the value of `:algorithm`. Sample arguments:
- # get_token(claim_set, {:algorithm => 'HS256', :key => 'secret_key'})
- # get_token(claim_set, {:algorithm => 'RS256', :key => OpenSSL::PKCS12.new(File.read('my_key.p12'), 'not_secret')})
- #
- # @param [Hash] request_opts options that will be used to assemble the request
- # @option request_opts [String] :scope the url parameter `scope` that may be required by some endpoints
- # @see https://datatracker.ietf.org/doc/html/rfc7521#section-4.1
- #
- # @param [Hash] response_opts this will be merged with the token response to create the AccessToken object
- # @see the access_token_opts argument to Client#get_token
-
- def get_token(claims, encoding_opts, request_opts = {}, response_opts = {})
- assertion = build_assertion(claims, encoding_opts)
- params = build_request(assertion, request_opts)
-
- @client.get_token(params, response_opts)
- end
-
- private
-
- def build_request(assertion, request_opts = {})
- {
- grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
- assertion: assertion,
- }.merge(request_opts)
- end
-
- def build_assertion(claims, encoding_opts)
- 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?
-
- JWT.encode(claims, encoding_opts[:key], encoding_opts[:algorithm])
- end
- end
- end
-end
diff --git a/lib/oauth2/strategy/auth_code.rb b/lib/oauth2/strategy/auth_code.rb
deleted file mode 100644
index f3aaad0a..00000000
--- a/lib/oauth2/strategy/auth_code.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# frozen_string_literal: true
-
-module OAuth2
- module Strategy
- # The Authorization Code Strategy
- #
- # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.1
- class AuthCode < Base
- # The required query parameters for the authorize URL
- #
- # @param [Hash] params additional query parameters
- def authorize_params(params = {})
- params.merge('response_type' => 'code', 'client_id' => @client.id)
- end
-
- # The authorization URL endpoint of the provider
- #
- # @param [Hash] params additional query parameters for the URL
- def authorize_url(params = {})
- assert_valid_params(params)
- @client.authorize_url(authorize_params.merge(params))
- end
-
- # Retrieve an access token given the specified validation code.
- #
- # @param [String] code The Authorization Code value
- # @param [Hash] params additional params
- # @param [Hash] opts access_token_opts, @see Client#get_token
- # @note that you must also provide a :redirect_uri with most OAuth 2.0 providers
- def get_token(code, params = {}, opts = {})
- params = {'grant_type' => 'authorization_code', 'code' => code}.merge(@client.redirection_params).merge(params)
- params_dup = params.dup
- params.each_key do |key|
- params_dup[key.to_s] = params_dup.delete(key) if key.is_a?(Symbol)
- end
-
- @client.get_token(params_dup, opts)
- end
-
- private
-
- def assert_valid_params(params)
- raise(ArgumentError, 'client_secret is not allowed in authorize URL query params') if params.key?(:client_secret) || params.key?('client_secret')
- end
- end
- end
-end
diff --git a/lib/oauth2/strategy/base.rb b/lib/oauth2/strategy/base.rb
deleted file mode 100644
index 801a723e..00000000
--- a/lib/oauth2/strategy/base.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-module OAuth2
- module Strategy
- class Base
- def initialize(client)
- @client = client
- end
- end
- end
-end
diff --git a/lib/oauth2/strategy/client_credentials.rb b/lib/oauth2/strategy/client_credentials.rb
deleted file mode 100644
index 2fba0e86..00000000
--- a/lib/oauth2/strategy/client_credentials.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# frozen_string_literal: true
-
-module OAuth2
- module Strategy
- # The Client Credentials Strategy
- #
- # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.4
- class ClientCredentials < Base
- # Not used for this strategy
- #
- # @raise [NotImplementedError]
- def authorize_url
- raise(NotImplementedError, 'The authorization endpoint is not used in this strategy')
- end
-
- # Retrieve an access token given the specified client.
- #
- # @param [Hash] params additional params
- # @param [Hash] opts options
- def get_token(params = {}, opts = {})
- params = params.merge('grant_type' => 'client_credentials')
- @client.get_token(params, opts)
- end
- end
- end
-end
diff --git a/lib/oauth2/strategy/implicit.rb b/lib/oauth2/strategy/implicit.rb
deleted file mode 100644
index 5e61d1d6..00000000
--- a/lib/oauth2/strategy/implicit.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-# frozen_string_literal: true
-
-module OAuth2
- module Strategy
- # The Implicit Strategy
- #
- # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-26#section-4.2
- class Implicit < Base
- # The required query parameters for the authorize URL
- #
- # @param [Hash] params additional query parameters
- def authorize_params(params = {})
- params.merge('response_type' => 'token', 'client_id' => @client.id)
- end
-
- # The authorization URL endpoint of the provider
- #
- # @param [Hash] params additional query parameters for the URL
- def authorize_url(params = {})
- assert_valid_params(params)
- @client.authorize_url(authorize_params.merge(params))
- end
-
- # Not used for this strategy
- #
- # @raise [NotImplementedError]
- def get_token(*)
- raise(NotImplementedError, 'The token is accessed differently in this strategy')
- end
-
- private
-
- def assert_valid_params(params)
- raise(ArgumentError, 'client_secret is not allowed in authorize URL query params') if params.key?(:client_secret) || params.key?('client_secret')
- end
- end
- end
-end
diff --git a/lib/oauth2/strategy/password.rb b/lib/oauth2/strategy/password.rb
deleted file mode 100644
index d41ca07a..00000000
--- a/lib/oauth2/strategy/password.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-# frozen_string_literal: true
-
-module OAuth2
- module Strategy
- # The Resource Owner Password Credentials Authorization Strategy
- #
- # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.3
- class Password < Base
- # Not used for this strategy
- #
- # @raise [NotImplementedError]
- def authorize_url
- raise(NotImplementedError, 'The authorization endpoint is not used in this strategy')
- end
-
- # Retrieve an access token given the specified End User username and password.
- #
- # @param [String] username the End User username
- # @param [String] password the End User password
- # @param [Hash] params additional params
- def get_token(username, password, params = {}, opts = {})
- params = {'grant_type' => 'password',
- 'username' => username,
- 'password' => password}.merge(params)
- @client.get_token(params, opts)
- end
- end
- end
-end
diff --git a/lib/oauth2/version.rb b/lib/oauth2/version.rb
deleted file mode 100644
index 46ec4307..00000000
--- a/lib/oauth2/version.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-module OAuth2
- module Version
- VERSION = '2.0.7'.freeze
- end
-end
diff --git a/oauth2.gemspec b/oauth2.gemspec
deleted file mode 100644
index 1af5d585..00000000
--- a/oauth2.gemspec
+++ /dev/null
@@ -1,68 +0,0 @@
-# encoding: utf-8
-# frozen_string_literal: true
-
-require_relative 'lib/oauth2/version'
-
-Gem::Specification.new do |spec|
- spec.add_dependency 'faraday', ['>= 0.17.3', '< 3.0']
- spec.add_dependency 'jwt', ['>= 1.0', '< 3.0']
- spec.add_dependency 'multi_xml', '~> 0.5'
- spec.add_dependency 'rack', ['>= 1.2', '< 3']
- spec.add_dependency 'rash_alt', ['>= 0.4', '< 1']
- spec.add_dependency 'version_gem', '~> 1.1'
-
- spec.authors = ['Peter Boling', 'Erik Michaels-Ober', 'Michael Bleigh']
- spec.description = 'A Ruby wrapper for the OAuth 2.0 protocol built with a similar style to the original OAuth spec.'
- spec.email = ['peter.boling@gmail.com']
- spec.homepage = 'https://github.com/oauth-xx/oauth2'
- spec.licenses = %w[MIT]
- spec.name = 'oauth2'
- spec.required_ruby_version = '>= 2.2.0'
- spec.summary = 'A Ruby wrapper for the OAuth 2.0 protocol.'
- spec.version = OAuth2::Version::VERSION
- spec.post_install_message = "
-You have installed oauth2 version #{OAuth2::Version::VERSION}, congratulations!
-
-There are BREAKING changes, but most will not encounter them, and updating your code should be easy!
-
-Please see:
-• https://github.com/oauth-xx/oauth2#what-is-new-for-v20
-• https://github.com/oauth-xx/oauth2/blob/master/CHANGELOG.md
-
-Please report issues, and support the project! Thanks, |7eter l-|. l3oling
-
-"
-
- spec.metadata['homepage_uri'] = spec.homepage
- spec.metadata['source_code_uri'] = "#{spec.homepage}/tree/v#{spec.version}"
- spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/v#{spec.version}/CHANGELOG.md"
- spec.metadata['bug_tracker_uri'] = "#{spec.homepage}/issues"
- spec.metadata['documentation_uri'] = "https://www.rubydoc.info/gems/#{spec.name}/#{spec.version}"
- spec.metadata['wiki_uri'] = "#{spec.homepage}/wiki"
- spec.metadata['rubygems_mfa_required'] = 'true'
-
- spec.require_paths = %w[lib]
- spec.bindir = 'exe'
- spec.files = Dir[
- 'lib/**/*',
- 'CHANGELOG.md',
- 'CODE_OF_CONDUCT.md',
- 'CONTRIBUTING.md',
- 'LICENSE',
- 'README.md',
- 'SECURITY.md',
- ]
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
-
- spec.add_development_dependency 'addressable', '>= 2'
- spec.add_development_dependency 'backports', '>= 3'
- spec.add_development_dependency 'bundler', '>= 2'
- spec.add_development_dependency 'rake', '>= 12'
- spec.add_development_dependency 'rexml', '>= 3'
- spec.add_development_dependency 'rspec', '>= 3'
- spec.add_development_dependency 'rspec-block_is_expected'
- spec.add_development_dependency 'rspec-pending_for'
- spec.add_development_dependency 'rspec-stubbed_env'
- spec.add_development_dependency 'rubocop-lts', '~> 8.0'
- spec.add_development_dependency 'silent_stream'
-end
diff --git a/spec/config/faraday.rb b/spec/config/faraday.rb
deleted file mode 100644
index 2051ebb1..00000000
--- a/spec/config/faraday.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-# frozen_string_literal: true
-
-Faraday.default_adapter = :test
diff --git a/spec/config/multi_xml.rb b/spec/config/multi_xml.rb
deleted file mode 100644
index 2d788eb5..00000000
--- a/spec/config/multi_xml.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-# frozen_string_literal: true
-
-MultiXml.parser = :rexml
diff --git a/spec/config/rspec/rspec_core.rb b/spec/config/rspec/rspec_core.rb
deleted file mode 100644
index 7ee40059..00000000
--- a/spec/config/rspec/rspec_core.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.configure do |config|
- # Enable flags like --only-failures and --next-failure
- config.example_status_persistence_file_path = '.rspec_status'
-
- # Disable RSpec exposing methods globally on `Module` and `main`
- config.disable_monkey_patching!
-
- config.expect_with :rspec do |c|
- c.syntax = :expect
- end
-end
diff --git a/spec/config/rspec/silent_stream.rb b/spec/config/rspec/silent_stream.rb
deleted file mode 100644
index e6755424..00000000
--- a/spec/config/rspec/silent_stream.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.configure do |config|
- config.include SilentStream
-end
diff --git a/spec/examples/google_spec.rb b/spec/examples/google_spec.rb
deleted file mode 100644
index cf1aa7ee..00000000
--- a/spec/examples/google_spec.rb
+++ /dev/null
@@ -1,141 +0,0 @@
-# frozen_string_literal: true
-
-require 'jwt'
-
-RSpec.describe 'using OAuth2 with Google' do
- # This describes authenticating to a Google API via a service account.
- # See their docs: https://developers.google.com/identity/protocols/OAuth2ServiceAccount
-
- describe 'via 2-legged JWT assertion' do
- let(:client) do
- OAuth2::Client.new(
- '',
- '',
- site: 'https://accounts.google.com',
- authorize_url: '/o/oauth2/auth',
- token_url: '/o/oauth2/token',
- auth_scheme: :request_body
- )
- end
-
- # These are taken directly from Google's documentation example:
-
- let(:required_claims) do
- {
- 'iss' => '761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com',
- # The email address of the service account.
-
- 'scope' => 'https://www.googleapis.com/auth/devstorage.readonly https://www.googleapis.com/auth/prediction',
- # A space-delimited list of the permissions that the application requests.
-
- 'aud' => 'https://www.googleapis.com/oauth2/v4/token',
- # A descriptor of the intended target of the assertion. When making an access token request this value
- # is always https://www.googleapis.com/oauth2/v4/token.
-
- 'exp' => Time.now.to_i + 3600,
- # The expiration time of the assertion, specified as seconds since 00:00:00 UTC, January 1, 1970. This value
- # has a maximum of 1 hour after the issued time.
-
- 'iat' => Time.now.to_i,
- # The time the assertion was issued, specified as seconds since 00:00:00 UTC, January 1, 1970.
- }
- end
-
- let(:optional_claims) do
- {
- 'sub' => 'some.user@example.com',
- # The email address of the user for which the application is requesting delegated access.
- }
- end
-
- let(:algorithm) { 'RS256' }
- # Per Google: "Service accounts rely on the RSA SHA-256 algorithm"
-
- let(:key) do
- begin
- OpenSSL::PKCS12.new(File.read('spec/fixtures/google_service_account_key.p12'), 'notasecret').key
- # This simulates the .p12 file that Google gives you to download and keep somewhere. This is meant to
- # illustrate extracting the key and using it to generate the JWT.
- rescue OpenSSL::PKCS12::PKCS12Error
- # JRuby CI builds are blowing up trying to extract a sample key for some reason. This simulates the end result
- # of actually figuring out the problem.
- OpenSSL::PKey::RSA.new(1024)
- end
- end
- # Per Google:
-
- # "Take note of the service account's email address and store the service account's P12 private key file in a
- # location accessible to your application. Your application needs them to make authorized API calls."
-
- let(:encoding_options) { {key: key, algorithm: algorithm} }
-
- before do
- client.connection = Faraday.new(client.site, client.options[:connection_opts]) do |builder|
- builder.request :url_encoded
- builder.adapter :test do |stub|
- stub.post('https://accounts.google.com/o/oauth2/token') do |token_request|
- @request_body = Rack::Utils.parse_nested_query(token_request.body).transform_keys(&:to_sym)
-
- [
- 200,
-
- {
- 'Content-Type' => 'application/json',
- },
-
- {
- 'access_token' => '1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M',
- 'token_type' => 'Bearer',
- 'expires_in' => 3600,
- }.to_json,
- ]
- end
- end
- end
- end
-
- context 'when passing the required claims' do
- let(:claims) { required_claims }
-
- it 'sends a JWT with the 5 keys' do
- client.assertion.get_token(claims, encoding_options)
-
- expect(@request_body).not_to be_nil, 'No access token request was made!'
- expect(@request_body[:grant_type]).to eq('urn:ietf:params:oauth:grant-type:jwt-bearer')
- expect(@request_body[:assertion]).to be_a(String)
-
- payload, header = JWT.decode(@request_body[:assertion], key, true, algorithm: algorithm)
- expect(header['alg']).to eq('RS256')
- expect(payload.keys).to match_array(%w[iss scope aud exp iat])
-
- # Note that these specifically do _not_ include the 'sub' claim, which is indicated as being 'required'
- # by the OAuth2 JWT RFC: https://tools.ietf.org/html/rfc7523#section-3
- # This may indicate that this is a nonstandard use case by Google.
-
- payload.each do |key, value|
- expect(value).to eq(claims[key])
- end
- end
- end
-
- context 'when including the optional `sub` claim' do
- let(:claims) { required_claims.merge(optional_claims) }
-
- it 'sends a JWT with the 6 keys' do
- client.assertion.get_token(claims, encoding_options)
-
- expect(@request_body).not_to be_nil, 'No access token request was made!'
- expect(@request_body[:grant_type]).to eq('urn:ietf:params:oauth:grant-type:jwt-bearer')
- expect(@request_body[:assertion]).to be_a(String)
-
- payload, header = JWT.decode(@request_body[:assertion], key, true, algorithm: algorithm)
- expect(header['alg']).to eq('RS256')
- expect(payload.keys).to match_array(%w[iss scope aud exp iat sub])
-
- payload.each do |key, value|
- expect(value).to eq(claims[key])
- end
- end
- end
- end
-end
diff --git a/spec/ext/backports.rb b/spec/ext/backports.rb
deleted file mode 100644
index 5811858b..00000000
--- a/spec/ext/backports.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-# frozen_string_literal: true
-
-require 'backports/2.5.0/hash/transform_keys'
diff --git a/spec/fixtures/README.md b/spec/fixtures/README.md
deleted file mode 100644
index e8ed536a..00000000
--- a/spec/fixtures/README.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# RS256
-
-## How keys were made
-
-```shell
-# No passphrase
-# Generates the public and private keys:
-ssh-keygen -t rsa -b 4096 -m PEM -f jwtRS256.key
-# Converts the key to PEM format
-openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256.key.pub
-```
diff --git a/spec/fixtures/RS256/jwtRS256.key b/spec/fixtures/RS256/jwtRS256.key
deleted file mode 100644
index 72005e50..00000000
--- a/spec/fixtures/RS256/jwtRS256.key
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKwIBAAKCAgEA5hdXV/4YSymY1T9VNvK2bWRfulwIty1RnAPNINQmfh3aRRkV
-+PNrbC2Crji9G0AHmQwgW1bZ3kgkkpIm6RVn44fHvBvuXkZ9ABgXw0d2cLIHmwOF
-xSKmWAm/EW//GszUTLLLsMZUe2udtFJW0jxXB2GRY0WVYuo6Oo58RCeP719lw3Ag
-s0YF9/IobxKkGd4BautUPw6ZszAa3o+j0zR74x7ouPxybZAOuPsMxqanyeYJeH4o
-sJjLMYV9qem9uG2sj7GENJ8UszcpmGbqxBhexPEB7mgDeONIF0XJF23zdOf8ANE5
-mAU2h2v7M6moAfkdUzJ+j48+VT2omHAzAL5yNcmrl2xiWdyoxOw1Y1UmfEmJYV5V
-gGYyZ12JZRKY+szPT+vR+MDuYxbquF40O7kvkFNBfL1yCpzfSQCLnEs4rX8qRzZX
-ciLeyq4Ht5FLuRFgxjA//XI8LAmp0u7gk+Q7FUH1UgW3kmJDTG0XaxQxYTBSIO7m
-cmyjDyBgKVuQmt5E1ycFeteOVdPD/CG/fPYhthvc4UytEFwsMdNy3iD6/wuUH68t
-AKam28UZaOb0qK+00cQQD8fulY9rKtSL10LvJFWUOa/SJyLvk9vUmfvFn182il1n
-X6GpyxyMmE/FCnH4CT/DjrSZf08mOO8eL5ofYHMK/oiXr1eODqx+pOwClNsCAwEA
-AQKCAgEAy34vMFI4WBk04rx9d/hWoQ7Znu8QgjihaZLvEy6t0HJEfUH/bcqS4fyq
-C72Aeh452gCgiUeZrf4t4jdCFHhrBg8q9dHaEiTTHocwVPPZ6zd4hH8sCrpnVYth
-IWHkw2YOCLtEbFYrl3AI7Na5lHvrGEsREzQSN4Yh83Has0guAy1iyeNb+FFgq/XO
-DtX0ri/rHw1717zo8FIGIXn2EK/lNWw7tIcICKAUdUMK/JGd6XD6RUeGYxDu/CAs
-kF55/Sd6Kyd7XjKnUwzhS7kRvlYzUog4BgqVr4+LTZHZlFAYtfcJqAtinXFW1ZQJ
-eZp9TSlt5wvMZNjx7t92QUNRyEGmrQAU+8COHnT0/drFf0MCiyHSUN0E7/5fswhc
-uMSU9XiJA9G0wYvJl4zIuOuIYWZWhIqvjYSkvdlP70t9XO2gk/ZcCWsMW8i+xbwC
-w1+MMjsKsNedXxI99TIPPHcCNMxqlt1E1kHH3SAwCuEH/ez7PRMyEQQ0EyAk22x/
-piYIWXkX5835cLbLRIYafXgOiugWZjCwIqfRIcIpscmcijZwCF2DyevveYdx3krR
-FGA2PFydFyxCNG7XwvKb9kHb7WBERUPV/H3eCqu2SZ/RvF+I94LUYP4bu6CmFdO9
-wCJcGJoL1P7tVhS9lA5Oj0QWczrjnejCoI9XMMduWk032rR1VYECggEBAPZDnTBY
-H2uiVmGdMfWTAmX86kiHVpkL03OG6rgvDMsMOYKnik9Lb3gNeUIuPeAWFNrXCoD1
-qp0loxPhKSojNOOM8Yiz/GwQ/QI9dzgtxs7E7rFFyTuJcY48Do8uOFyUHbAbeOBF
-b9UL/uBfWZGVV1YY753xyqYlCpxTVQGms1jsbVFdZE1iVpOwAkFVuoLYaHLut4zB
-01ORyBSoWan173P+IQH6F1uNXE2Kk/FIMDN6bgP1pXkdkrTx4WjAmRnP/Sc4r38/
-F1xN+gxnWGPUKDVRPYBpVzDR036w65ODgg2FROK2vIxlStiAC/rc0JLsvaWfb1Rn
-dsWdJJ1V6mZ6a5sCggEBAO8wC1jcIoiBz3xoA8E5BSt8qLJ7ZuSFaaidvWX2/xj6
-lSWJxCGQfhR7P6ozvH6UDo1WbJT6nNyXPkiDkAzcmAdsYVjULW3K2LI9oPajaJxY
-L7KJpylgh9JhMvbMz3VVjTgYRt+kjX+3uFMZNx1YfiBP+S6xx5sjK9CKDz3H99kC
-q9bX95YFqZ7yFE3aBCR6CENo2tXpMN96CLQGpwa0bwt3xNzC4MhZMXbGR3DdBYbD
-tS9lJfQvAVUYxbSE/2FBgjpO6ArMyU2ZUEDFx9J6IhfhVbQV4VeITMyRNo0XwBiQ
-/+XpLXgHkw7LiNMIoc7d+M7yLA1Vz7+r8XxWHHZCL8ECggEBAPK8VrYORno7e1Wg
-MlxS2WxZzTxMWmlkpLoc5END7SI/HHjSV5wtSORWs40uM0MrwMasa+gNPmzDamjv
-6Tllln4ssO8EKe0DGcAZgefYBzxMFNKbbOzIXyvJurga4Ocv/8tUaOL2znJ67nGO
-yqSbRYjR724JpKv7mufXo9SK0gD2mhI3MeSs55WPScnIjJzoXpva/QU7D+gxq7vg
-7PCAP9RfS329W0Sco7yyuXx8oTY8mTBB8ybcpXzBZmNwY/hzcJ42W5XbRFVxbuTH
-APL1beSP/UUTkCPIzuTz0mCGoaxeDjZB1Lu2I/4eyLAu80+/FneoHX5etU23xR1o
-UDFOvb0CggEBALTTc6CoPAtLaBs7X6tSelAYHEli9bTKD8kEB83wX4b42ozYjEh7
-vnWpf8Yi+twO/rlnnws6NCCoztNvcxXmJ6FlFGtdbULV2eFWqjwL6ehY2yZ03sVv
-Tv+DsE3ZJPYlyW+hGuO0uazWrilUpNAwuJmhHFdq2+azPkqYNVGVvhB37oWsHGd0
-vHmHtkXtDris8VZVDSwu8V3iGnZPmTJ+cn0O/OuRAPM2SyjqWdQ/pA/wIShFpd3n
-M3CsG7uP2KokJloCkXaov39E6uEtJRZAc0nudyaAbC4Kw1Tca4tba0SnSm78S/20
-bD8BLN2uZvXH5nQ9rYQfXcIgMZ64UygsfYECggEBAIw0fQaIVmafa0Hz3ipD4PJI
-5QNkh2t9hvOCSKm1xYTNATl0q/VIkZoy1WoxY6SSchcObLxQKbJ9ORi4XNr+IJK5
-3C1Qz/3iv/S3/ktgmqGhQiqybkkHZcbqTXB2wxrx+aaLS7PEfYiuYCrPbX93160k
-MVns8PjvYU8KCNMbL2e+AiKEt1KkKAZIpNQdeeJOEhV9wuLYFosd400aYssuSOVW
-IkJhGI0lT/7FDJaw0LV98DhQtauANPSUQKN5iw6vciwtsaF1kXMfGlMXj58ntiMq
-NizQPR6/Ar1ewLPMh1exDoAfLnCIMk8nbSraW+cebLAZctPugUpfpu3j2LM98aE=
------END RSA PRIVATE KEY-----
diff --git a/spec/fixtures/RS256/jwtRS256.key.pub b/spec/fixtures/RS256/jwtRS256.key.pub
deleted file mode 100644
index 1a2f63d1..00000000
--- a/spec/fixtures/RS256/jwtRS256.key.pub
+++ /dev/null
@@ -1,14 +0,0 @@
------BEGIN PUBLIC KEY-----
-MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5hdXV/4YSymY1T9VNvK2
-bWRfulwIty1RnAPNINQmfh3aRRkV+PNrbC2Crji9G0AHmQwgW1bZ3kgkkpIm6RVn
-44fHvBvuXkZ9ABgXw0d2cLIHmwOFxSKmWAm/EW//GszUTLLLsMZUe2udtFJW0jxX
-B2GRY0WVYuo6Oo58RCeP719lw3Ags0YF9/IobxKkGd4BautUPw6ZszAa3o+j0zR7
-4x7ouPxybZAOuPsMxqanyeYJeH4osJjLMYV9qem9uG2sj7GENJ8UszcpmGbqxBhe
-xPEB7mgDeONIF0XJF23zdOf8ANE5mAU2h2v7M6moAfkdUzJ+j48+VT2omHAzAL5y
-Ncmrl2xiWdyoxOw1Y1UmfEmJYV5VgGYyZ12JZRKY+szPT+vR+MDuYxbquF40O7kv
-kFNBfL1yCpzfSQCLnEs4rX8qRzZXciLeyq4Ht5FLuRFgxjA//XI8LAmp0u7gk+Q7
-FUH1UgW3kmJDTG0XaxQxYTBSIO7mcmyjDyBgKVuQmt5E1ycFeteOVdPD/CG/fPYh
-thvc4UytEFwsMdNy3iD6/wuUH68tAKam28UZaOb0qK+00cQQD8fulY9rKtSL10Lv
-JFWUOa/SJyLvk9vUmfvFn182il1nX6GpyxyMmE/FCnH4CT/DjrSZf08mOO8eL5of
-YHMK/oiXr1eODqx+pOwClNsCAwEAAQ==
------END PUBLIC KEY-----
diff --git a/spec/fixtures/google_service_account_key.p12 b/spec/fixtures/google_service_account_key.p12
deleted file mode 100644
index 8d945088..00000000
Binary files a/spec/fixtures/google_service_account_key.p12 and /dev/null differ
diff --git a/spec/oauth2/access_token_spec.rb b/spec/oauth2/access_token_spec.rb
deleted file mode 100644
index 795e3810..00000000
--- a/spec/oauth2/access_token_spec.rb
+++ /dev/null
@@ -1,716 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe OAuth2::AccessToken do
- subject { described_class.new(client, token) }
-
- let(:base_options) { {site: 'https://api.example.com'} }
- let(:options) { {} }
- let(:token) { 'monkey' }
- let(:refresh_body) { JSON.dump(access_token: 'refreshed_foo', expires_in: 600, refresh_token: 'refresh_bar') }
- let(:client) do
- OAuth2::Client.new('abc', 'def', options.merge(base_options)) do |builder|
- builder.request :url_encoded
- builder.adapter :test do |stub|
- VERBS.each do |verb|
- stub.send(verb, '/token/header') { |env| [200, {}, env[:request_headers]['Authorization']] }
- stub.send(verb, "/token/query?access_token=#{token}") { |env| [200, {}, Addressable::URI.parse(env[:url]).query_values['access_token']] }
- stub.send(verb, '/token/query_string') { |env| [200, {}, CGI.unescape(Addressable::URI.parse(env[:url]).query)] }
- stub.send(verb, '/token/body') { |env| [200, {}, env[:body]] }
- end
- stub.post('/oauth/token') { |_env| [200, {'Content-Type' => 'application/json'}, refresh_body] }
- end
- end
- end
-
- describe '.from_hash' do
- subject(:target) { described_class.from_hash(client, hash) }
-
- let(:hash) do
- {
- :access_token => token,
- :id_token => 'confusing bug here',
- :refresh_token => 'foobar',
- :expires_at => Time.now.to_i + 200,
- 'foo' => 'bar',
- }
- end
-
- it 'return a hash equals to the hash used to initialize access token' do
- expect(target.to_hash).to eq(hash)
- end
-
- context 'with warning for too many tokens' do
- subject(:printed) do
- capture(:stderr) do
- target
- end
- end
-
- it 'warns on STDERR' do
- msg = <<-MSG.lstrip
- OAuth2::AccessToken.from_hash: `hash` contained more than one 'token' key ([:access_token, :id_token]); using :access_token.
- MSG
- expect(printed).to eq(msg)
- end
- end
-
- context 'with keys in a different order to the lookup' do
- subject(:printed) do
- capture(:stderr) do
- target
- end
- end
-
- let(:hash) do
- {
- id_token: 'confusing bug here',
- access_token: token,
- }
- end
-
- it 'warns on STDERR and selects the correct key' do
- msg = <<-MSG.lstrip
- OAuth2::AccessToken.from_hash: `hash` contained more than one 'token' key ([:access_token, :id_token]); using :access_token.
- MSG
- expect(printed).to eq(msg)
- end
- end
- end
-
- describe '#initialize' do
- it 'assigns client and token' do
- expect(subject.client).to eq(client)
- expect(subject.token).to eq(token)
- end
-
- it 'assigns extra params' do
- target = described_class.new(client, token, 'foo' => 'bar')
- expect(target.params).to include('foo')
- expect(target.params['foo']).to eq('bar')
- end
-
- def assert_initialized_token(target)
- expect(target.token).to eq(token)
- expect(target).to be_expires
- expect(target.params.keys).to include('foo')
- expect(target.params['foo']).to eq('bar')
- end
-
- it 'initializes with a Hash' do
- hash = {:access_token => token, :expires_at => Time.now.to_i + 200, 'foo' => 'bar'}
- target = described_class.from_hash(client, hash)
- assert_initialized_token(target)
- end
-
- it 'from_hash does not modify opts hash' do
- hash = {access_token: token, expires_at: Time.now.to_i}
- hash_before = hash.dup
- described_class.from_hash(client, hash)
- expect(hash).to eq(hash_before)
- end
-
- it 'initializes with a form-urlencoded key/value string' do
- kvform = "access_token=#{token}&expires_at=#{Time.now.to_i + 200}&foo=bar"
- target = described_class.from_kvform(client, kvform)
- assert_initialized_token(target)
- end
-
- context 'with options' do
- subject(:target) { described_class.new(client, token, **options) }
-
- context 'with body mode' do
- let(:mode) { :body }
- let(:options) { {param_name: 'foo', header_format: 'Bearer %', mode: mode} }
-
- it 'sets options' do
- expect(target.options[:param_name]).to eq('foo')
- expect(target.options[:header_format]).to eq('Bearer %')
- expect(target.options[:mode]).to eq(mode)
- end
- end
-
- context 'with header mode' do
- let(:mode) { :header }
- let(:options) { {headers: {}, mode: mode} }
-
- it 'sets options' do
- expect(target.options[:headers]).to be_nil
- expect(target.options[:mode]).to eq(mode)
- end
- end
-
- context 'with query mode' do
- let(:mode) { :query }
- let(:options) { {params: {}, param_name: 'foo', mode: mode} }
-
- it 'sets options' do
- expect(target.options[:param_name]).to eq('foo')
- expect(target.options[:params]).to be_nil
- expect(target.options[:mode]).to eq(mode)
- end
- end
-
- context 'with invalid mode' do
- let(:mode) { :this_is_bad }
- let(:options) { {mode: mode} }
-
- it 'does not raise on initialize' do
- block_is_expected.not_to raise_error
- end
-
- context 'with request' do
- subject(:request) { target.post('/token/header') }
-
- it 'raises' do
- block_is_expected.to raise_error("invalid :mode option of #{mode}")
- end
- end
-
- context 'with client.options[:raise_errors] = true' do
- let(:mode) { :this_is_bad }
- let(:options) { {mode: mode, raise_errors: true} }
-
- before do
- expect(client.options[:raise_errors]).to be(true)
- end
-
- context 'when there is a token' do
- it 'does not raise on initialize' do
- block_is_expected.not_to raise_error
- end
-
- context 'with request' do
- subject(:request) { target.post('/token/header') }
-
- it 'raises' do
- block_is_expected.to raise_error("invalid :mode option of #{mode}")
- end
- end
- end
-
- context 'when there is empty token' do
- let(:token) { '' }
-
- it 'raises on initialize' do
- block_is_expected.to raise_error(OAuth2::Error, '{:mode=>:this_is_bad, :raise_errors=>true}')
- end
- end
-
- context 'when there is nil token' do
- let(:token) { nil }
-
- it 'raises on initialize' do
- block_is_expected.to raise_error(OAuth2::Error, '{:mode=>:this_is_bad, :raise_errors=>true}')
- end
- end
- end
- end
-
- context 'with client.options[:raise_errors] = false' do
- let(:options) { {raise_errors: false} }
-
- before do
- expect(client.options[:raise_errors]).to be(false)
- end
-
- context 'when there is a token' do
- let(:token) { 'hurdygurdy' }
-
- it 'does not raise on initialize' do
- block_is_expected.not_to raise_error
- end
-
- it 'has token' do
- expect(target.token).to eq(token)
- end
-
- it 'has no refresh_token' do
- expect(target.refresh_token).to be_nil
- end
-
- context 'when there is refresh_token' do
- let(:options) { {raise_errors: false, refresh_token: 'zxcv'} }
-
- it 'does not raise on initialize' do
- block_is_expected.not_to raise_error
- end
-
- it 'has token' do
- expect(target.token).to eq(token)
- end
-
- it 'has refresh_token' do
- expect(target.refresh_token).to eq('zxcv')
- end
- end
- end
-
- context 'when there is empty token' do
- let(:token) { '' }
-
- context 'when there is no refresh_token' do
- it 'does not raise on initialize' do
- block_is_expected.not_to raise_error
- end
-
- it 'has no token' do
- expect(target.token).to eq('')
- end
-
- it 'has no refresh_token' do
- expect(target.refresh_token).to be_nil
- end
-
- context 'with warning for no token' do
- subject(:printed) do
- capture(:stderr) do
- target
- end
- end
-
- it 'warns on STDERR' do
- msg = <<-MSG.lstrip
- OAuth2::AccessToken has no token
- MSG
- expect(printed).to eq(msg)
- end
- end
- end
-
- context 'when there is refresh_token' do
- let(:options) { {raise_errors: false, refresh_token: 'qwer'} }
-
- it 'does not raise on initialize' do
- block_is_expected.not_to raise_error
- end
-
- it 'has no token' do
- expect(target.token).to eq('')
- end
-
- it 'has refresh_token' do
- expect(target.refresh_token).to eq('qwer')
- end
- end
- end
-
- context 'when there is nil token' do
- let(:token) { nil }
-
- context 'when there is no refresh_token' do
- it 'does not raise on initialize' do
- block_is_expected.not_to raise_error
- end
-
- it 'has no token' do
- expect(target.token).to eq('')
- end
-
- it 'has no refresh_token' do
- expect(target.refresh_token).to be_nil
- end
-
- context 'with warning for no token' do
- subject(:printed) do
- capture(:stderr) do
- target
- end
- end
-
- it 'warns on STDERR' do
- msg = <<-MSG.lstrip
- OAuth2::AccessToken has no token
- MSG
- expect(printed).to eq(msg)
- end
- end
- end
-
- context 'when there is refresh_token' do
- let(:options) { {raise_errors: false, refresh_token: 'asdf'} }
-
- it 'does not raise on initialize' do
- block_is_expected.not_to raise_error
- end
-
- it 'has no token' do
- expect(target.token).to eq('')
- end
-
- it 'has refresh_token' do
- expect(target.refresh_token).to eq('asdf')
- end
- end
- end
- end
-
- context 'with client.options[:raise_errors] = true' do
- let(:options) { {raise_errors: true} }
-
- before do
- expect(client.options[:raise_errors]).to be(true)
- end
-
- context 'when there is a token' do
- let(:token) { 'hurdygurdy' }
-
- it 'does not raise on initialize' do
- block_is_expected.not_to raise_error
- end
-
- it 'has token' do
- expect(target.token).to eq(token)
- end
-
- it 'has no refresh_token' do
- expect(target.refresh_token).to be_nil
- end
-
- context 'when there is refresh_token' do
- let(:options) { {raise_errors: true, refresh_token: 'zxcv'} }
-
- it 'does not raise on initialize' do
- block_is_expected.not_to raise_error
- end
-
- it 'has token' do
- expect(target.token).to eq(token)
- end
-
- it 'has refresh_token' do
- expect(target.refresh_token).to eq('zxcv')
- end
- end
- end
-
- context 'when there is empty token' do
- let(:token) { '' }
-
- context 'when there is no refresh_token' do
- it 'raises on initialize' do
- block_is_expected.to raise_error(OAuth2::Error, '{:raise_errors=>true}')
- end
- end
-
- context 'when there is refresh_token' do
- let(:options) { {raise_errors: true, refresh_token: 'qwer'} }
-
- it 'does not raise on initialize' do
- block_is_expected.not_to raise_error
- end
-
- it 'has no token' do
- expect(target.token).to eq('')
- end
-
- it 'has refresh_token' do
- expect(target.refresh_token).to eq('qwer')
- end
- end
- end
-
- context 'when there is nil token' do
- let(:token) { nil }
-
- context 'when there is no refresh_token' do
- it 'raises on initialize' do
- block_is_expected.to raise_error(OAuth2::Error, '{:raise_errors=>true}')
- end
- end
-
- context 'when there is refresh_token' do
- let(:options) { {raise_errors: true, refresh_token: 'asdf'} }
-
- it 'does not raise on initialize' do
- block_is_expected.not_to raise_error
- end
-
- it 'has no token' do
- expect(target.token).to eq('')
- end
-
- it 'has refresh_token' do
- expect(target.refresh_token).to eq('asdf')
- end
- end
- end
- end
- end
-
- it 'does not modify opts hash' do
- opts = {param_name: 'foo', header_format: 'Bearer %', mode: :body}
- opts_before = opts.dup
- described_class.new(client, token, opts)
- expect(opts).to eq(opts_before)
- end
-
- describe 'expires_at' do
- let(:expires_at) { 1_361_396_829 }
- let(:hash) do
- {
- :access_token => token,
- :expires_at => expires_at.to_s,
- 'foo' => 'bar',
- }
- end
-
- it 'initializes with an integer timestamp expires_at' do
- target = described_class.from_hash(client, hash.merge(expires_at: expires_at))
- assert_initialized_token(target)
- expect(target.expires_at).to eql(expires_at)
- end
-
- it 'initializes with a string timestamp expires_at' do
- target = described_class.from_hash(client, hash)
- assert_initialized_token(target)
- expect(target.expires_at).to eql(expires_at)
- end
-
- it 'initializes with a string time expires_at' do
- target = described_class.from_hash(client, hash.merge(expires_at: Time.at(expires_at).iso8601))
- assert_initialized_token(target)
- expect(target.expires_at).to eql(expires_at)
- end
- end
-
- describe 'expires_latency' do
- let(:expires_at) { 1_530_000_000 }
- let(:expires_in) { 100 }
- let(:expires_latency) { 10 }
- let(:hash) do
- {
- access_token: token,
- expires_latency: expires_latency,
- expires_in: expires_in,
- }
- end
-
- it 'sets it via options' do
- target = described_class.from_hash(client, hash.merge(expires_latency: expires_latency.to_s))
- expect(target.expires_latency).to eq expires_latency
- end
-
- it 'sets it nil by default' do
- hash.delete(:expires_latency)
- target = described_class.from_hash(client, hash)
- expect(target.expires_latency).to be_nil
- end
-
- it 'reduces expires_at by the given amount' do
- allow(Time).to receive(:now).and_return(expires_at)
- target = described_class.from_hash(client, hash)
- expect(target.expires_at).to eq(expires_at + expires_in - expires_latency)
- end
-
- it 'reduces expires_at by the given amount if expires_at is provided as option' do
- target = described_class.from_hash(client, hash.merge(expires_at: expires_at))
- expect(target.expires_at).to eq(expires_at - expires_latency)
- end
- end
- end
-
- describe '#request' do
- context 'with :mode => :header' do
- before do
- subject.options[:mode] = :header
- end
-
- VERBS.each do |verb|
- it "sends the token in the Authorization header for a #{verb.to_s.upcase} request" do
- expect(subject.post('/token/header').body).to include(token)
- end
- end
- end
-
- context 'with :mode => :query' do
- before do
- subject.options[:mode] = :query
- end
-
- VERBS.each do |verb|
- it "sends the token in the body for a #{verb.to_s.upcase} request" do
- expect(subject.post('/token/query').body).to eq(token)
- end
-
- it "sends a #{verb.to_s.upcase} request and options[:param_name] include [number]." do
- subject.options[:param_name] = 'auth[1]'
- expect(subject.__send__(verb, '/token/query_string').body).to include("auth[1]=#{token}")
- end
- end
- end
-
- context 'with :mode => :body' do
- before do
- subject.options[:mode] = :body
- end
-
- VERBS.each do |verb|
- it "sends the token in the body for a #{verb.to_s.upcase} request" do
- expect(subject.post('/token/body').body.split('=').last).to eq(token)
- end
-
- context 'when options[:param_name] include [number]' do
- it "sends a #{verb.to_s.upcase} request when body is a hash" do
- subject.options[:param_name] = 'auth[1]'
- expect(subject.__send__(verb, '/token/body', body: {hi: 'there'}).body).to include("auth%5B1%5D=#{token}")
- end
-
- it "sends a #{verb.to_s.upcase} request when body is overridden as string" do
- subject.options[:param_name] = 'snoo[1]'
- expect(subject.__send__(verb, '/token/body', body: 'hi_there').body).to include("hi_there&snoo[1]=#{token}")
- end
- end
- end
- end
-
- context 'params include [number]' do
- VERBS.each do |verb|
- it "sends #{verb.to_s.upcase} correct query" do
- expect(subject.__send__(verb, '/token/query_string', params: {'foo[bar][1]' => 'val'}).body).to include('foo[bar][1]=val')
- end
- end
- end
- end
-
- describe '#expires?' do
- it 'is false if there is no expires_at' do
- expect(described_class.new(client, token)).not_to be_expires
- end
-
- it 'is true if there is an expires_in' do
- expect(described_class.new(client, token, refresh_token: 'abaca', expires_in: 600)).to be_expires
- end
-
- it 'is true if there is an expires_at' do
- expect(described_class.new(client, token, refresh_token: 'abaca', expires_in: Time.now.getutc.to_i + 600)).to be_expires
- end
- end
-
- describe '#expired?' do
- it 'is false if there is no expires_in or expires_at' do
- expect(described_class.new(client, token)).not_to be_expired
- end
-
- it 'is false if expires_in is in the future' do
- expect(described_class.new(client, token, refresh_token: 'abaca', expires_in: 10_800)).not_to be_expired
- end
-
- it 'is true if expires_at is in the past' do
- access = described_class.new(client, token, refresh_token: 'abaca', expires_in: 600)
- @now = Time.now + 10_800
- allow(Time).to receive(:now).and_return(@now)
- expect(access).to be_expired
- end
-
- it 'is true if expires_at is now' do
- @now = Time.now
- access = described_class.new(client, token, refresh_token: 'abaca', expires_at: @now.to_i)
- allow(Time).to receive(:now).and_return(@now)
- expect(access).to be_expired
- end
- end
-
- describe '#refresh' do
- let(:options) { {access_token_class: access_token_class} }
- let(:access_token_class) { NewAccessToken }
- let(:access) do
- described_class.new(client, token, refresh_token: 'abaca',
- expires_in: 600,
- param_name: 'o_param',
- access_token_class: access_token_class)
- end
- let(:new_access) do
- NewAccessToken.new(client, token, refresh_token: 'abaca')
- end
-
- before do
- custom_class = Class.new(described_class) do
- def self.from_hash(client, hash)
- new(client, hash.delete('access_token'), hash)
- end
-
- def self.contains_token?(hash)
- hash.key?('refresh_token')
- end
- end
-
- stub_const('NewAccessToken', custom_class)
- end
-
- context 'without refresh_token' do
- subject(:no_refresh) { no_access.refresh }
-
- let(:no_access) do
- described_class.new(client, token, refresh_token: nil,
- expires_in: 600,
- param_name: 'o_param',
- access_token_class: access_token_class)
- end
-
- it 'raises when no refresh_token' do
- block_is_expected.to raise_error('A refresh_token is not available')
- end
- end
-
- it 'returns a refresh token with appropriate values carried over' do
- refreshed = access.refresh
- expect(access.client).to eq(refreshed.client)
- expect(access.options[:param_name]).to eq(refreshed.options[:param_name])
- end
-
- it 'returns a refresh token of the same access token class' do
- refreshed = new_access.refresh!
- expect(new_access.class).to eq(refreshed.class)
- end
-
- context 'with a nil refresh_token in the response' do
- let(:refresh_body) { JSON.dump(access_token: 'refreshed_foo', expires_in: 600, refresh_token: nil) }
-
- it 'copies the refresh_token from the original token' do
- refreshed = access.refresh
-
- expect(refreshed.refresh_token).to eq(access.refresh_token)
- end
- end
-
- context 'with a not-nil refresh_token in the response' do
- let(:refresh_body) { JSON.dump(access_token: 'refreshed_foo', expires_in: 600, refresh_token: 'qerwer') }
-
- it 'copies the refresh_token from the original token' do
- refreshed = access.refresh
-
- expect(refreshed.token).to eq('refreshed_foo')
- expect(refreshed.refresh_token).to eq('qerwer')
- end
- end
-
- context 'with a not-nil, not camel case, refresh_token in the response' do
- let(:refresh_body) { JSON.dump(accessToken: 'refreshed_foo', expires_in: 600, refreshToken: 'qerwer') }
-
- it 'copies the refresh_token from the original token' do
- refreshed = access.refresh
-
- expect(refreshed.token).to eq('refreshed_foo')
- expect(refreshed.refresh_token).to eq('qerwer')
- end
- end
-
- context 'with a custom access_token_class' do
- let(:access_token_class) { NewAccessToken }
-
- it 'returns a refresh token of NewAccessToken' do
- refreshed = access.refresh!
-
- expect(new_access.class).to eq(refreshed.class)
- end
- end
- end
-
- describe '#to_hash' do
- it 'return a hash equals to the hash used to initialize access token' do
- hash = {:access_token => token, :refresh_token => 'foobar', :expires_at => Time.now.to_i + 200, 'foo' => 'bar'}
- access_token = described_class.from_hash(client, hash.clone)
- expect(access_token.to_hash).to eq(hash)
- end
- end
-end
diff --git a/spec/oauth2/authenticator_spec.rb b/spec/oauth2/authenticator_spec.rb
deleted file mode 100644
index 4f06d306..00000000
--- a/spec/oauth2/authenticator_spec.rb
+++ /dev/null
@@ -1,126 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe OAuth2::Authenticator do
- subject do
- described_class.new(client_id, client_secret, mode)
- end
-
- let(:client_id) { 'foo' }
- let(:client_secret) { 'bar' }
- let(:mode) { :undefined }
-
- it 'raises NotImplementedError for unknown authentication mode' do
- expect { subject.apply({}) }.to raise_error(NotImplementedError)
- end
-
- describe '#apply' do
- context 'with parameter-based authentication' do
- let(:mode) { :request_body }
-
- it 'adds client_id and client_secret to params' do
- output = subject.apply({})
- expect(output).to eq('client_id' => 'foo', 'client_secret' => 'bar')
- end
-
- context 'when client_id nil' do
- let(:client_id) { nil }
-
- it 'ignores client_id, but adds client_secret to params' do
- output = subject.apply({})
- expect(output).to eq('client_secret' => 'bar')
- end
- end
-
- it 'does not overwrite existing credentials' do
- input = {'client_secret' => 's3cr3t'}
- output = subject.apply(input)
- expect(output).to eq('client_id' => 'foo', 'client_secret' => 's3cr3t')
- end
-
- it 'preserves other parameters' do
- input = {'state' => '42', :headers => {'A' => 'b'}}
- output = subject.apply(input)
- expect(output).to eq(
- 'client_id' => 'foo',
- 'client_secret' => 'bar',
- 'state' => '42',
- :headers => {'A' => 'b'}
- )
- end
-
- context 'passing nil secret' do
- let(:client_secret) { nil }
-
- it 'does not set nil client_secret' do
- output = subject.apply({})
- expect(output).to eq('client_id' => 'foo')
- end
- end
-
- context 'using tls client authentication' do
- let(:mode) { :tls_client_auth }
-
- it 'does not add client_secret' do
- output = subject.apply({})
- expect(output).to eq('client_id' => 'foo')
- end
- end
-
- context 'using private key jwt authentication' do
- let(:mode) { :private_key_jwt }
-
- it 'does not include client_id or client_secret' do
- output = subject.apply({})
- expect(output).to eq({})
- end
- end
- end
-
- context 'using tls_client_auth' do
- let(:mode) { :tls_client_auth }
-
- context 'when client_id present' do
- let(:client_id) { 'foobar' }
-
- it 'adds client_id to params' do
- output = subject.apply({})
- expect(output).to eq('client_id' => 'foobar')
- end
- end
-
- context 'when client_id nil' do
- let(:client_id) { nil }
-
- it 'ignores client_id for params' do
- output = subject.apply({})
- expect(output).to eq({})
- end
- end
- end
-
- context 'with Basic authentication' do
- let(:mode) { :basic_auth }
- let(:header) { "Basic #{Base64.strict_encode64("#{client_id}:#{client_secret}")}" }
-
- it 'encodes credentials in headers' do
- output = subject.apply({})
- expect(output).to eq(headers: {'Authorization' => header})
- end
-
- it 'does not overwrite existing credentials' do
- input = {headers: {'Authorization' => 'Bearer abc123'}}
- output = subject.apply(input)
- expect(output).to eq(headers: {'Authorization' => 'Bearer abc123'})
- end
-
- it 'does not overwrite existing params or headers' do
- input = {'state' => '42', :headers => {'A' => 'b'}}
- output = subject.apply(input)
- expect(output).to eq(
- 'state' => '42',
- :headers => {'A' => 'b', 'Authorization' => header}
- )
- end
- end
- end
-end
diff --git a/spec/oauth2/client_spec.rb b/spec/oauth2/client_spec.rb
deleted file mode 100644
index 57539b1b..00000000
--- a/spec/oauth2/client_spec.rb
+++ /dev/null
@@ -1,948 +0,0 @@
-# coding: utf-8
-# frozen_string_literal: true
-
-require 'nkf'
-
-RSpec.describe OAuth2::Client do
- subject(:instance) do
- described_class.new('abc', 'def', {site: 'https://api.example.com'}.merge(options)) do |builder|
- builder.adapter :test do |stub|
- stub.get('/success') { |_env| [200, {'Content-Type' => 'text/awesome'}, 'yay'] }
- stub.get('/reflect') { |env| [200, {}, env[:body]] }
- stub.post('/reflect') { |env| [200, {}, env[:body]] }
- stub.get('/unauthorized') { |_env| [401, {'Content-Type' => 'application/json'}, JSON.dump(error: error_value, error_description: error_description_value)] }
- stub.get('/conflict') { |_env| [409, {'Content-Type' => 'text/plain'}, 'not authorized'] }
- stub.get('/redirect') { |_env| [302, {'Content-Type' => 'text/plain', 'location' => '/success'}, ''] }
- stub.get('/redirect_no_loc') { |_env| [302, {'Content-Type' => 'text/plain'}, ''] }
- stub.post('/redirect') { |_env| [303, {'Content-Type' => 'text/plain', 'location' => '/reflect'}, ''] }
- stub.get('/error') { |_env| [500, {'Content-Type' => 'text/plain'}, 'unknown error'] }
- stub.get('/empty_get') { |_env| [204, {}, nil] }
- stub.get('/different_encoding') { |_env| [500, {'Content-Type' => 'application/json'}, NKF.nkf('-We', JSON.dump(error: error_value, error_description: '∞'))] }
- stub.get('/ascii_8bit_encoding') { |_env| [500, {'Content-Type' => 'application/json'}, JSON.dump(error: 'invalid_request', error_description: 'é').force_encoding('ASCII-8BIT')] }
- stub.get('/unhandled_status') { |_env| [600, {}, nil] }
- end
- end
- end
-
- let!(:error_value) { 'invalid_token' }
- let!(:error_description_value) { 'bad bad token' }
- let(:options) { {} }
-
- describe '#initialize' do
- it 'assigns id and secret' do
- expect(subject.id).to eq('abc')
- expect(subject.secret).to eq('def')
- end
-
- it 'assigns site from the options hash' do
- expect(subject.site).to eq('https://api.example.com')
- end
-
- it 'assigns Faraday::Connection#host' do
- expect(subject.connection.host).to eq('api.example.com')
- end
-
- it 'leaves Faraday::Connection#ssl unset' do
- expect(subject.connection.ssl).to be_empty
- end
-
- it 'is able to pass a block to configure the connection' do
- builder = double('builder')
-
- allow(Faraday).to receive(:new).and_yield(builder)
- allow(builder).to receive(:response)
-
- expect(builder).to receive(:adapter).with(:test)
-
- described_class.new('abc', 'def') do |client|
- client.adapter :test
- end.connection
- end
-
- it 'defaults raise_errors to true' do
- expect(subject.options[:raise_errors]).to be true
- end
-
- it 'allows true/false for raise_errors option' do
- client = described_class.new('abc', 'def', site: 'https://api.example.com', raise_errors: false)
- expect(client.options[:raise_errors]).to be false
- client = described_class.new('abc', 'def', site: 'https://api.example.com', raise_errors: true)
- expect(client.options[:raise_errors]).to be true
- end
-
- it 'allows override of raise_errors option' do
- client = described_class.new('abc', 'def', site: 'https://api.example.com', raise_errors: true) do |builder|
- builder.adapter :test do |stub|
- stub.get('/notfound') { |_env| [404, {}, nil] }
- end
- end
- expect(client.options[:raise_errors]).to be true
- expect { client.request(:get, '/notfound') }.to raise_error(OAuth2::Error)
- response = client.request(:get, '/notfound', raise_errors: false)
- expect(response.status).to eq(404)
- end
-
- it 'allows get/post for access_token_method option' do
- client = described_class.new('abc', 'def', site: 'https://api.example.com', access_token_method: :get)
- expect(client.options[:access_token_method]).to eq(:get)
- client = described_class.new('abc', 'def', site: 'https://api.example.com', access_token_method: :post)
- expect(client.options[:access_token_method]).to eq(:post)
- end
-
- it 'does not mutate the opts hash argument' do
- opts = {site: 'http://example.com/'}
- opts2 = opts.dup
- described_class.new 'abc', 'def', opts
- expect(opts).to eq(opts2)
- end
- end
-
- describe '#site=(val)' do
- subject(:site) { instance.site = new_site }
-
- let(:options) do
- {site: 'https://example.com/blog'}
- end
- let(:new_site) { 'https://example.com/sharpie' }
-
- it 'sets site' do
- block_is_expected.to change(instance, :site).from('https://example.com/blog').to('https://example.com/sharpie')
- end
-
- context 'with connection' do
- before do
- instance.connection
- end
-
- it 'allows connection to reset with new url prefix' do
- block_is_expected.to change { instance.connection.url_prefix }.from(URI('https://example.com/blog')).to(URI('https://example.com/sharpie'))
- end
- end
- end
-
- %w[authorize token].each do |url_type|
- describe ":#{url_type}_url option" do
- it "defaults to a path of /oauth/#{url_type}" do
- expect(subject.send("#{url_type}_url")).to eq("https://api.example.com/oauth/#{url_type}")
- end
-
- it "is settable via the :#{url_type}_url option" do
- subject.options[:"#{url_type}_url"] = '/oauth/custom'
- expect(subject.send("#{url_type}_url")).to eq('https://api.example.com/oauth/custom')
- end
-
- it 'allows a different host than the site' do
- subject.options[:"#{url_type}_url"] = 'https://api.foo.com/oauth/custom'
- expect(subject.send("#{url_type}_url")).to eq('https://api.foo.com/oauth/custom')
- end
-
- context 'when a URL with path is used in the site' do
- let(:options) do
- {site: 'https://example.com/blog'}
- end
-
- it 'generates an authorization URL relative to the site' do
- expect(subject.send("#{url_type}_url")).to eq("https://example.com/blog/oauth/#{url_type}")
- end
- end
- end
- end
-
- describe ':redirect_uri option' do
- let(:auth_code_params) do
- {
- 'client_id' => 'abc',
- 'client_secret' => 'def',
- 'code' => 'code',
- 'grant_type' => 'authorization_code',
- }
- end
-
- context 'when blank' do
- it 'there is no redirect_uri param added to authorization URL' do
- expect(subject.authorize_url('a' => 'b')).to eq('https://api.example.com/oauth/authorize?a=b')
- end
-
- it 'does not add the redirect_uri param to the auth_code token exchange request' do
- client = described_class.new('abc', 'def', site: 'https://api.example.com', auth_scheme: :request_body) do |builder|
- builder.adapter :test do |stub|
- stub.post('/oauth/token', auth_code_params) do
- [200, {'Content-Type' => 'application/json'}, '{"access_token":"token"}']
- end
- end
- end
- client.auth_code.get_token('code')
- end
- end
-
- context 'when set' do
- before { subject.options[:redirect_uri] = 'https://site.com/oauth/callback' }
-
- it 'adds the redirect_uri param to authorization URL' do
- expect(subject.authorize_url('a' => 'b')).to eq('https://api.example.com/oauth/authorize?a=b&redirect_uri=https%3A%2F%2Fsite.com%2Foauth%2Fcallback')
- end
-
- it 'adds the redirect_uri param to the auth_code token exchange request' do
- client = described_class.new('abc', 'def', redirect_uri: 'https://site.com/oauth/callback', site: 'https://api.example.com', auth_scheme: :request_body) do |builder|
- builder.adapter :test do |stub|
- stub.post('/oauth/token', auth_code_params.merge('redirect_uri' => 'https://site.com/oauth/callback')) do
- [200, {'Content-Type' => 'application/json'}, '{"access_token":"token"}']
- end
- end
- end
- client.auth_code.get_token('code')
- end
- end
-
- describe 'custom headers' do
- context 'string key headers' do
- it 'adds the custom headers to request' do
- client = described_class.new('abc', 'def', site: 'https://api.example.com', auth_scheme: :request_body) do |builder|
- builder.adapter :test do |stub|
- stub.post('/oauth/token') do |env|
- expect(env.request_headers).to include('CustomHeader' => 'CustomHeader')
- [200, {'Content-Type' => 'application/json'}, '{"access_token":"token"}']
- end
- end
- end
- header_params = {'headers' => {'CustomHeader' => 'CustomHeader'}}
- client.auth_code.get_token('code', header_params)
- end
- end
-
- context 'symbol key headers' do
- it 'adds the custom headers to request' do
- client = described_class.new('abc', 'def', site: 'https://api.example.com', auth_scheme: :request_body) do |builder|
- builder.adapter :test do |stub|
- stub.post('/oauth/token') do |env|
- expect(env.request_headers).to include('CustomHeader' => 'CustomHeader')
- [200, {'Content-Type' => 'application/json'}, '{"access_token":"token"}']
- end
- end
- end
- header_params = {headers: {'CustomHeader' => 'CustomHeader'}}
- client.auth_code.get_token('code', header_params)
- end
- end
-
- context 'string key custom headers with basic auth' do
- it 'adds the custom headers to request' do
- client = described_class.new('abc', 'def', site: 'https://api.example.com') do |builder|
- builder.adapter :test do |stub|
- stub.post('/oauth/token') do |env|
- expect(env.request_headers).to include('CustomHeader' => 'CustomHeader')
- [200, {'Content-Type' => 'application/json'}, '{"access_token":"token"}']
- end
- end
- end
- header_params = {'headers' => {'CustomHeader' => 'CustomHeader'}}
- client.auth_code.get_token('code', header_params)
- end
- end
-
- context 'symbol key custom headers with basic auth' do
- it 'adds the custom headers to request' do
- client = described_class.new('abc', 'def', site: 'https://api.example.com') do |builder|
- builder.adapter :test do |stub|
- stub.post('/oauth/token') do |env|
- expect(env.request_headers).to include('CustomHeader' => 'CustomHeader')
- [200, {'Content-Type' => 'application/json'}, '{"access_token":"token"}']
- end
- end
- end
- header_params = {headers: {'CustomHeader' => 'CustomHeader'}}
- client.auth_code.get_token('code', header_params)
- end
- end
- end
- end
-
- describe '#connection' do
- context 'when debugging' do
- include_context 'with stubbed env'
- before do
- stub_env('OAUTH_DEBUG' => debug_value)
- end
-
- context 'when OAUTH_DEBUG=true' do
- let(:debug_value) { 'true' }
-
- it 'smoothly handles successive requests' do
- silence_all do
- # first request (always goes smoothly)
- subject.request(:get, '/success')
- end
-
- expect do
- # second request (used to throw Faraday::RackBuilder::StackLocked)
- subject.request(:get, '/success')
- end.not_to raise_error
- end
-
- it 'prints both request and response bodies to STDOUT' do
- printed = capture(:stdout) do
- subject.request(:get, '/success')
- subject.request(:get, '/reflect', body: 'this is magical')
- end
- expect(printed).to match 'request: GET https://api.example.com/success'
- expect(printed).to match 'response: Content-Type:'
- expect(printed).to match 'response: yay'
- expect(printed).to match 'request: this is magical'
- expect(printed).to match 'response: this is magical'
- end
- end
-
- context 'when OAUTH_DEBUG=false' do
- let(:debug_value) { 'false' }
-
- it 'smoothly handles successive requests' do
- silence_all do
- # first request (always goes smoothly)
- subject.request(:get, '/success')
- end
-
- expect do
- # second request (used to throw Faraday::RackBuilder::StackLocked)
- subject.request(:get, '/success')
- end.not_to raise_error
- end
-
- it 'prints nothing to STDOUT' do
- printed = capture(:stdout) do
- subject.request(:get, '/success')
- subject.request(:get, '/reflect', body: 'this is magical')
- end
- expect(printed).to eq ''
- end
- end
- end
- end
-
- describe '#request' do
- it 'works with a null response body' do
- expect(subject.request(:get, 'empty_get').body).to eq('')
- end
-
- it 'returns on a successful response' do
- response = subject.request(:get, '/success')
- expect(response.body).to eq('yay')
- expect(response.status).to eq(200)
- expect(response.headers).to eq('Content-Type' => 'text/awesome')
- end
-
- context 'with ENV' do
- include_context 'with stubbed env'
- context 'when OAUTH_DEBUG=true' do
- before do
- stub_env('OAUTH_DEBUG' => 'true')
- end
-
- it 'outputs to $stdout when OAUTH_DEBUG=true' do
- output = capture(:stdout) do
- subject.request(:get, '/success')
- end
- logs = [
- 'request: GET https://api.example.com/success',
- 'response: Status 200',
- 'response: Content-Type: "text/awesome"',
- ]
- expect(output).to include(*logs)
- end
- end
- end
-
- it 'posts a body' do
- response = subject.request(:post, '/reflect', body: 'foo=bar')
- expect(response.body).to eq('foo=bar')
- end
-
- it 'follows redirects properly' do
- response = subject.request(:get, '/redirect')
- expect(response.body).to eq('yay')
- expect(response.status).to eq(200)
- expect(response.headers).to eq('Content-Type' => 'text/awesome')
- expect(response.response.env.url.to_s).to eq('https://api.example.com/success')
- end
-
- it 'redirects using GET on a 303' do
- response = subject.request(:post, '/redirect', body: 'foo=bar')
- expect(response.body).to be_empty
- expect(response.status).to eq(200)
- expect(response.response.env.url.to_s).to eq('https://api.example.com/reflect')
- end
-
- it 'raises an error if a redirect has no Location header' do
- expect { subject.request(:get, '/redirect_no_loc') }.to raise_error(OAuth2::Error, 'Got 302 status code, but no Location header was present')
- end
-
- it 'obeys the :max_redirects option' do
- max_redirects = subject.options[:max_redirects]
- subject.options[:max_redirects] = 0
- response = subject.request(:get, '/redirect')
- expect(response.status).to eq(302)
- subject.options[:max_redirects] = max_redirects
- end
-
- it 'returns if raise_errors is false' do
- subject.options[:raise_errors] = false
- response = subject.request(:get, '/unauthorized')
-
- expect(response.status).to eq(401)
- expect(response.headers).to eq('Content-Type' => 'application/json')
- end
-
- %w[/unauthorized /conflict /error /different_encoding /ascii_8bit_encoding].each do |error_path|
- it "raises OAuth2::Error on error response to path #{error_path}" do
- pending_for(engine: 'jruby', reason: 'https://github.com/jruby/jruby/issues/4921') if error_path == '/different_encoding'
- expect { subject.request(:get, error_path) }.to raise_error(OAuth2::Error)
- end
- end
-
- it 're-encodes response body in the error message' do
- expect { subject.request(:get, '/ascii_8bit_encoding') }.to raise_error do |ex|
- expect(ex.message).to eq("invalid_request: é\n{\"error\":\"invalid_request\",\"error_description\":\"��\"}")
- expect(ex.message.encoding.name).to eq('UTF-8')
- end
- end
-
- it 'parses OAuth2 standard error response' do
- expect { subject.request(:get, '/unauthorized') }.to raise_error do |ex|
- expect(ex.code).to eq(error_value)
- expect(ex.description).to eq(error_description_value)
- expect(ex.to_s).to match(/#{error_value}/)
- expect(ex.to_s).to match(/#{error_description_value}/)
- end
- end
-
- it 'provides the response in the Exception' do
- expect { subject.request(:get, '/error') }.to raise_error do |ex|
- expect(ex.response).not_to be_nil
- expect(ex.to_s).to match(/unknown error/)
- end
- end
-
- it 'informs about unhandled status code' do
- expect { subject.request(:get, '/unhandled_status') }.to raise_error do |ex|
- expect(ex.response).not_to be_nil
- expect(ex.to_s).to match(/Unhandled status code value of 600/)
- end
- end
-
- context 'when errors are raised by Faraday' do
- let(:connection) { instance_double(Faraday::Connection, build_url: double) }
-
- before do
- allow(connection).to(
- receive(:run_request).and_raise(faraday_exception)
- )
- allow(subject).to receive(:connection).and_return(connection) # rubocop:disable RSpec/SubjectStub
- end
-
- shared_examples 'failed connection handler' do
- it 'rescues the exception' do
- expect { subject.request(:get, '/redirect') }.to raise_error do |e|
- expect(e.class).to eq(expected_exception)
- expect(e.message).to eq(faraday_exception.message)
- end
- end
- end
-
- context 'with Faraday::ConnectionFailed' do
- let(:faraday_exception) { Faraday::ConnectionFailed.new('fail') }
- let(:expected_exception) { OAuth2::ConnectionError }
-
- it_behaves_like 'failed connection handler'
- end
-
- context 'with Faraday::TimeoutError' do
- let(:faraday_exception) { Faraday::TimeoutError.new('timeout') }
- let(:expected_exception) { OAuth2::TimeoutError }
-
- it_behaves_like 'failed connection handler'
- end
- end
- end
-
- describe '#get_token' do
- it 'returns a configured AccessToken' do
- client = stubbed_client do |stub|
- stub.post('/oauth/token') do
- [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')]
- end
- end
-
- token = client.get_token({})
- expect(token).to be_a OAuth2::AccessToken
- expect(token.token).to eq('the-token')
- end
-
- context 'when parse: :automatic' do
- it 'returns a configured AccessToken' do
- client = stubbed_client do |stub|
- stub.post('/oauth/token') do
- [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')]
- end
- end
-
- token = client.get_token(parse: :automatic)
- expect(token).to be_a OAuth2::AccessToken
- expect(token.token).to eq('the-token')
- end
- end
-
- context 'when parse: :xml but response is JSON' do
- it 'returns a configured AccessToken' do
- client = stubbed_client do |stub|
- stub.post('/oauth/token') do
- [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')]
- end
- end
-
- expect { client.get_token(parse: :xml) }.to raise_error(
- MultiXml::ParseError,
- 'The document "{\"access_token\":\"the-token\"}" does not have a valid root'
- )
- end
- end
-
- context 'when parse is fuzzed' do
- it 'returns a configured AccessToken' do
- client = stubbed_client do |stub|
- stub.post('/oauth/token') do
- [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')]
- end
- end
-
- token = client.get_token(parse: 'random')
- expect(token).to be_a OAuth2::AccessToken
- expect(token.token).to eq('the-token')
- end
- end
-
- context 'when parse is correct' do
- it 'returns a configured AccessToken' do
- client = stubbed_client do |stub|
- stub.post('/oauth/token') do
- [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')]
- end
- end
-
- token = client.get_token(parse: :json)
- expect(token).to be_a OAuth2::AccessToken
- expect(token.token).to eq('the-token')
- end
- end
-
- context 'when snaky is falsy, but response is snaky' do
- it 'returns a configured AccessToken' do
- client = stubbed_client do |stub|
- stub.post('/oauth/token') do
- [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')]
- end
- end
-
- token = client.get_token(snaky: false)
- expect(token).to be_a OAuth2::AccessToken
- expect(token.token).to eq('the-token')
- expect(token.response.parsed.to_h).to eq('access_token' => 'the-token')
- end
- end
-
- context 'when snaky is falsy, but response is not snaky' do
- it 'returns a configured AccessToken' do
- client = stubbed_client do |stub|
- stub.post('/oauth/token') do
- [200, {'Content-Type' => 'application/json'}, JSON.dump('accessToken' => 'the-token')]
- end
- end
-
- token = client.get_token({snaky: false}, {param_name: 'accessToken'})
- expect(token).to be_a OAuth2::AccessToken
- expect(token.token).to eq('the-token')
- expect(token.response.parsed.to_h).to eq('accessToken' => 'the-token')
- end
- end
-
- it 'authenticates with request parameters' do
- client = stubbed_client(auth_scheme: :request_body) do |stub|
- stub.post('/oauth/token', 'client_id' => 'abc', 'client_secret' => 'def') do |_env|
- [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')]
- end
- end
- client.get_token({})
- end
-
- it 'authenticates with Basic auth' do
- client = stubbed_client(auth_scheme: :basic_auth) do |stub|
- stub.post('/oauth/token') do |env|
- raise Faraday::Adapter::Test::Stubs::NotFound unless env[:request_headers]['Authorization'] == OAuth2::Authenticator.encode_basic_auth('abc', 'def')
-
- [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')]
- end
- end
- client.get_token({})
- end
-
- it 'authenticates with JSON' do
- client = stubbed_client(auth_scheme: :basic_auth) do |stub|
- stub.post('/oauth/token') do |env|
- [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')]
- end
- end
- client.get_token(headers: {'Content-Type' => 'application/json'})
- end
-
- it 'sets the response object on the access token' do
- client = stubbed_client do |stub|
- stub.post('/oauth/token') do
- [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')]
- end
- end
-
- token = client.get_token({})
- expect(token.response).to be_a OAuth2::Response
- expect(token.response.parsed).to eq('access_token' => 'the-token')
- end
-
- context 'when the :raise_errors flag is set to false' do
- let(:body) { nil }
- let(:status_code) { 500 }
- let(:content_type) { 'application/json' }
- let(:client) do
- stubbed_client(raise_errors: false) do |stub|
- stub.post('/oauth/token') do
- [status_code, {'Content-Type' => content_type}, body]
- end
- end
- end
-
- context 'when the request body is nil' do
- subject(:get_token) { client.get_token({}) }
-
- it 'raises error JSON::ParserError' do
- block_is_expected { get_token }.to raise_error(JSON::ParserError)
- end
-
- context 'when extract_access_token raises an exception' do
- let(:status_code) { 200 }
- let(:extract_proc) { proc { |client, hash| raise ArgumentError } }
-
- it 'returns a nil :access_token' do
- expect(client.get_token({}, {}, extract_proc)).to eq(nil)
- end
- end
- end
-
- context 'when status code is 200' do
- let(:status_code) { 200 }
-
- context 'when the request body is not nil' do
- let(:body) { JSON.dump('access_token' => 'the-token') }
-
- it 'returns the parsed :access_token from body' do
- token = client.get_token({})
- expect(token.response).to be_a OAuth2::Response
- expect(token.response.parsed).to eq('access_token' => 'the-token')
- end
- end
-
- context 'when Content-Type is not JSON' do
- let(:content_type) { 'text/plain' }
- let(:body) { 'hello world' }
-
- it 'returns the parsed :access_token from body' do
- expect(client.get_token({})).to be_nil
- end
- end
- end
- end
-
- describe 'with custom access_token_class option' do
- let(:options) { {access_token_class: CustomAccessToken} }
- let(:payload) { {'custom_token' => 'the-token'} }
- let(:content_type) { 'application/json' }
- let(:client) do
- stubbed_client(options) do |stub|
- stub.post('/oauth/token') do
- [200, {'Content-Type' => content_type}, JSON.dump(payload)]
- end
- end
- end
-
- before do
- custom_class = Class.new(OAuth2::AccessToken) do
- attr_accessor :response
-
- def self.from_hash(client, hash)
- new(client, hash.delete('custom_token'))
- end
-
- def self.contains_token?(hash)
- hash.key?('custom_token')
- end
- end
-
- stub_const('CustomAccessToken', custom_class)
- end
-
- it 'returns the parsed :custom_token from body' do
- client.get_token({})
- end
-
- context 'when the :raise_errors flag is set to true' do
- let(:options) { {access_token_class: CustomAccessToken, raise_errors: true} }
- let(:payload) { {} }
-
- it 'raises an error' do
- expect { client.get_token({}) }.to raise_error(OAuth2::Error)
- end
-
- context 'when the legacy extract_access_token' do
- let(:extract_access_token) do
- proc do |client, hash|
- token = hash['data']['access_token']
- OAuth2::AccessToken.new(client, token, hash)
- end
- end
- let(:options) { {raise_errors: true} }
- let(:payload) { {} }
-
- it 'raises an error' do
- expect { client.get_token({}, {}, extract_access_token) }.to raise_error(OAuth2::Error)
- end
- end
- end
-
- context 'when status code is 200' do
- let(:status_code) { 200 }
-
- context 'when the request body is blank' do
- let(:payload) { {} }
-
- it 'raises an error' do
- expect { client.get_token({}) }.to raise_error(OAuth2::Error)
- end
- end
-
- context 'when Content-Type is not JSON' do
- let(:content_type) { 'text/plain' }
- let(:body) { 'hello world' }
-
- it 'raises an error' do
- expect { client.get_token({}) }.to raise_error(OAuth2::Error)
- end
- end
- end
-
- context 'when access token instance responds to response=' do
- let(:options) { {access_token_class: CustomAccessToken, raise_errors: false} }
-
- it 'sets response' do
- expect(client.get_token({}).response).to be_a(OAuth2::Response)
- end
- end
-
- context 'when request has a block' do
- subject(:request) do
- client.get_token({}) do |req|
- raise 'Block is executing'
- end
- end
-
- let(:options) { {access_token_class: CustomAccessToken, raise_errors: false} }
-
- it 'sets response' do
- block_is_expected.to raise_error('Block is executing')
- end
- end
- end
-
- describe 'abnormal custom access_token_class option' do
- let(:payload) { {'custom_token' => 'the-token'} }
- let(:content_type) { 'application/json' }
- let(:client) do
- stubbed_client(options) do |stub|
- stub.post('/oauth/token') do
- [200, {'Content-Type' => content_type}, JSON.dump(payload)]
- end
- end
- end
-
- before do
- custom_class = Class.new do
- def initialize(client, hash)
- end
-
- def self.from_hash(client, hash)
- new(client, hash.delete('custom_token'))
- end
-
- def self.contains_token?(hash)
- hash.key?('custom_token')
- end
- end
-
- stub_const('StrangeAccessToken', custom_class)
- end
-
- context 'when the :raise_errors flag is set to true' do
- let(:options) { {access_token_class: StrangeAccessToken, raise_errors: true} }
- let(:payload) { {} }
-
- it 'raises an error' do
- expect { client.get_token({}) }.to raise_error(OAuth2::Error)
- end
- end
-
- context 'when access token instance does not responds to response=' do
- let(:options) { {access_token_class: StrangeAccessToken} }
- let(:payload) { {'custom_token' => 'the-token'} }
-
- it 'sets response' do
- token_access = client.get_token({})
- expect(token_access).to be_a(StrangeAccessToken)
- expect(token_access).not_to respond_to(:response=)
- expect(token_access).not_to respond_to(:response)
- end
- end
-
- context 'when request has a block' do
- subject(:request) do
- client.get_token({}) do |req|
- raise 'Block is executing'
- end
- end
-
- let(:options) { {access_token_class: StrangeAccessToken} }
-
- it 'sets response' do
- block_is_expected.to raise_error('Block is executing')
- end
- end
- end
-
- describe 'with extract_access_token option' do
- let(:client) do
- stubbed_client(extract_access_token: extract_access_token) do |stub|
- stub.post('/oauth/token') do
- [200, {'Content-Type' => 'application/json'}, JSON.dump('data' => {'access_token' => 'the-token'})]
- end
- end
- end
-
- let(:extract_access_token) do
- proc do |client, hash|
- token = hash['data']['access_token']
- OAuth2::AccessToken.new(client, token, hash)
- end
- end
-
- it 'returns a configured AccessToken' do
- token = client.get_token({})
- expect(token).to be_a OAuth2::AccessToken
- expect(token.token).to eq('the-token')
- end
-
- context 'with deprecation' do
- subject(:printed) do
- capture(:stderr) do
- client.get_token({})
- end
- end
-
- it 'warns on STDERR' do
- msg = <<-MSG.lstrip
- OAuth2::Client#initialize argument `extract_access_token` will be removed in oauth2 v3. Refactor to use `access_token_class`.
- MSG
- expect(printed).to eq(msg)
- end
-
- context 'on request' do
- subject(:printed) do
- capture(:stderr) do
- client.get_token({}, {}, extract_access_token)
- end
- end
-
- let(:client) do
- stubbed_client do |stub|
- stub.post('/oauth/token') do
- [200, {'Content-Type' => 'application/json'}, JSON.dump('data' => {'access_token' => 'the-token'})]
- end
- end
- end
-
- it 'warns on STDERR' do
- msg = <<-MSG.lstrip
- OAuth2::Client#get_token argument `extract_access_token` will be removed in oauth2 v3. Refactor to use `access_token_class` on #initialize.
- MSG
- expect(printed).to eq(msg)
- end
- end
- end
- end
-
- it 'forwards given token parameters' do
- client = stubbed_client(auth_scheme: :request_body) do |stub|
- stub.post('/oauth/token', 'arbitrary' => 'parameter', 'client_id' => 'abc', 'client_secret' => 'def') do |_env|
- [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')]
- end
- end
- client.get_token({'arbitrary' => 'parameter'}) # rubocop:disable Style/BracesAroundHashParameters
- end
-
- context 'when token_method is set to post_with_query_string' do
- it 'uses the http post method and passes parameters in the query string' do
- client = stubbed_client(token_method: :post_with_query_string) do |stub|
- stub.post('/oauth/token?state=abc123') do |_env|
- [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')]
- end
- end
- client.get_token({'state' => 'abc123'}) # rubocop:disable Style/BracesAroundHashParameters
- end
- end
-
- def stubbed_client(params = {}, &stubs)
- params = {site: 'https://api.example.com'}.merge(params)
- OAuth2::Client.new('abc', 'def', params) do |builder|
- builder.adapter :test, &stubs
- end
- end
- end
-
- it 'instantiates an HTTP Method with this client' do
- expect(subject.http_method).to be_kind_of(Symbol)
- end
-
- it 'instantiates an AuthCode strategy with this client' do
- expect(subject.auth_code).to be_kind_of(OAuth2::Strategy::AuthCode)
- end
-
- it 'instantiates an Implicit strategy with this client' do
- expect(subject.implicit).to be_kind_of(OAuth2::Strategy::Implicit)
- end
-
- context 'with SSL options' do
- subject do
- cli = described_class.new('abc', 'def', site: 'https://api.example.com', ssl: {ca_file: 'foo.pem'})
- cli.connection = Faraday.new(cli.site, cli.options[:connection_opts]) do |b|
- b.adapter :test
- end
- cli
- end
-
- it 'passes the SSL options along to Faraday::Connection#ssl' do
- expect(subject.connection.ssl.fetch(:ca_file)).to eq('foo.pem')
- end
- end
-
- context 'without a connection-configuration block' do
- subject do
- described_class.new('abc', 'def', site: 'https://api.example.com')
- end
-
- it 'applies default faraday middleware to the connection' do
- expect(subject.connection.builder.handlers).to include(Faraday::Request::UrlEncoded)
- end
- end
-end
diff --git a/spec/oauth2/error_spec.rb b/spec/oauth2/error_spec.rb
deleted file mode 100644
index fb1ed492..00000000
--- a/spec/oauth2/error_spec.rb
+++ /dev/null
@@ -1,645 +0,0 @@
-# encoding: UTF-8
-# frozen_string_literal: true
-
-class StirredHash < Hash
- def to_str
- '{"hello":"� Cool � StirredHash"}'
- end
-end
-
-class XmledString < String
- XML = '
-
-
-� Cool � XmledString
-
-
-'.freeze
- def to_str
- XML
- end
-end
-
-RSpec.describe OAuth2::Error do
- subject { described_class.new(response) }
-
- let(:response) do
- raw_response = Faraday::Response.new(
- status: 418,
- response_headers: response_headers,
- body: response_body
- )
-
- OAuth2::Response.new(raw_response)
- end
-
- let(:response_headers) { {'Content-Type' => 'application/json'} }
- let(:response_body) { {text: 'Coffee brewing failed'}.to_json }
-
- it 'sets the response object to #response on self' do
- error = described_class.new(response)
- expect(error.response).to equal(response)
- end
-
- describe 'attr_readers' do
- it 'has code' do
- expect(subject).to respond_to(:code)
- end
-
- it 'has description' do
- expect(subject).to respond_to(:description)
- end
-
- it 'has response' do
- expect(subject).to respond_to(:response)
- end
- end
-
- context 'when the response is parsed' do
- let(:response_body) { response_hash.to_json }
- let(:response_hash) { {text: 'Coffee brewing failed'} }
-
- context 'when the response has an error and error_description' do
- before do
- response_hash['error_description'] = 'Short and stout'
- response_hash['error'] = 'i_am_a_teapot'
- end
-
- it 'sets the code attribute' do
- expect(subject.code).to eq('i_am_a_teapot')
- end
-
- it 'sets the description attribute' do
- expect(subject.description).to eq('Short and stout')
- end
-
- it 'prepends to the error message with a return character' do
- expect(subject.message.each_line.to_a).to eq(
- [
- "i_am_a_teapot: Short and stout\n",
- '{"text":"Coffee brewing failed","error_description":"Short and stout","error":"i_am_a_teapot"}',
- ]
- )
- end
-
- context 'when the response needs to be encoded' do
- let(:response_body) { JSON.dump(response_hash).force_encoding('ASCII-8BIT') }
-
- context 'with invalid characters present' do
- before do
- response_body.gsub!('stout', "\255 invalid \255")
- end
-
- it 'replaces them' do
- # The skip can be removed once support for < 2.1 is dropped.
- encoding = {reason: 'encode/scrub only works as of Ruby 2.1'}
- skip_for(encoding.merge(engine: 'jruby'))
- # See https://bibwild.wordpress.com/2013/03/12/removing-illegal-bytes-for-encoding-in-ruby-1-9-strings/
-
- raise 'Invalid characters not replaced' unless subject.message.include?('� invalid �')
- # This will fail if {:invalid => replace} is not passed into `encode`
- end
- end
-
- context 'with undefined characters present' do
- before do
- response_hash['error_description'] += ": 'A magical voyage of tea 🍵'"
- end
-
- it 'replaces them' do
- raise 'Undefined characters not replaced' unless subject.message.include?('tea �')
- # This will fail if {:undef => replace} is not passed into `encode`
- end
- end
- end
-
- context 'when the response is not an encodable thing' do
- let(:response_headers) { {'Content-Type' => 'who knows'} }
- let(:response_body) { {text: 'Coffee brewing failed'} }
-
- before do
- expect(response_body).not_to respond_to(:encode)
- # i.e. a Ruby hash
- end
-
- it 'does not try to encode the message string' do
- expect(subject.message).to eq(response_body.to_s)
- end
- end
-
- context 'when using :json parser with non-encodable data' do
- let(:response_headers) { {'Content-Type' => 'application/hal+json'} }
- let(:response_body) do
- StirredHash.new(
- "_links": {
- "self": {"href": '/orders/523'},
- "warehouse": {"href": '/warehouse/56'},
- "invoice": {"href": '/invoices/873'},
- },
- "currency": 'USD',
- "status": 'shipped',
- "total": 10.20
- )
- end
-
- before do
- expect(response_body).not_to respond_to(:force_encoding)
- expect(response_body).to respond_to(:to_str)
- end
-
- it 'does not force encode the message' do
- expect(subject.message).to eq('{"hello":"� Cool � StirredHash"}')
- end
- end
-
- context 'when using :xml parser' do
- let(:response_headers) { {'Content-Type' => 'text/xml'} }
- let(:response_body) do
- XmledString.new(XmledString::XML)
- end
-
- before do
- expect(response_body).to respond_to(:to_str)
- end
-
- it 'parses the XML' do
- expect(subject.message).to eq(XmledString::XML)
- end
- end
-
- context 'when using :xml parser with non-String-like thing' do
- let(:response_headers) { {'Content-Type' => 'text/xml'} }
- let(:response_body) { {hello: :world} }
-
- before do
- expect(response_body).not_to respond_to(:to_str)
- end
-
- it 'just returns the thing if it can' do
- expect(subject.message).to eq('{:hello=>:world}')
- end
- end
- end
-
- it 'sets the code attribute to nil' do
- expect(subject.code).to be_nil
- end
-
- it 'sets the description attribute' do
- expect(subject.description).to be_nil
- end
-
- context 'when there is no error description' do
- before do
- expect(response_hash).not_to have_key('error')
- expect(response_hash).not_to have_key('error_description')
- end
-
- it 'does not prepend anything to the message' do
- expect(subject.message.lines.count).to eq(1)
- expect(subject.message).to eq '{"text":"Coffee brewing failed"}'
- end
-
- it 'does not set code' do
- expect(subject.code).to be_nil
- end
-
- it 'does not set description' do
- expect(subject.description).to be_nil
- end
- end
-
- context 'when there is code (error)' do
- before do
- response_hash['error_description'] = 'Short and stout'
- response_hash['error'] = 'i_am_a_teapot'
- response_hash['status'] = '418'
- end
-
- it 'prepends to the error message with a return character' do
- expect(subject.message.each_line.to_a).to eq(
- [
- "i_am_a_teapot: Short and stout\n",
- {
- "text": 'Coffee brewing failed',
- "error_description": 'Short and stout',
- "error": 'i_am_a_teapot',
- "status": '418',
- }.to_json,
- ]
- )
- end
-
- context 'when the response needs to be encoded' do
- let(:response_body) { JSON.dump(response_hash).force_encoding('ASCII-8BIT') }
-
- context 'with invalid characters present' do
- before do
- response_body.gsub!('stout', "\255 invalid \255")
- end
-
- it 'replaces them' do
- # The skip can be removed once support for < 2.1 is dropped.
- encoding = {reason: 'encode/scrub only works as of Ruby 2.1'}
- skip_for(encoding.merge(engine: 'jruby'))
- # See https://bibwild.wordpress.com/2013/03/12/removing-illegal-bytes-for-encoding-in-ruby-1-9-strings/
-
- raise 'Invalid characters not replaced' unless subject.message.include?('� invalid �')
- # This will fail if {:invalid => replace} is not passed into `encode`
- end
- end
-
- context 'with undefined characters present' do
- before do
- response_hash['error_description'] += ": 'A magical voyage of tea 🍵'"
- end
-
- it 'replaces them' do
- raise 'Undefined characters not replaced' unless subject.message.include?('tea �')
- # This will fail if {:undef => replace} is not passed into `encode`
- end
- end
- end
-
- context 'when the response is not an encodable thing' do
- let(:response_headers) { {'Content-Type' => 'who knows'} }
- let(:response_body) { {text: 'Coffee brewing failed'} }
-
- before do
- expect(response_body).not_to respond_to(:encode)
- # i.e. a Ruby hash
- end
-
- it 'does not try to encode the message string' do
- expect(subject.message).to eq(response_body.to_s)
- end
- end
-
- it 'sets the code attribute' do
- expect(subject.code).to eq('i_am_a_teapot')
- end
-
- it 'sets the description attribute' do
- expect(subject.description).to eq('Short and stout')
- end
- end
-
- context 'when there is code (error) but no error_description' do
- before do
- response_hash.delete('error_description')
- response_hash['error'] = 'i_am_a_teapot'
- response_hash['status'] = '418'
- end
-
- it 'prepends to the error message with a return character' do
- expect(subject.message.each_line.to_a).to eq(
- [
- "i_am_a_teapot: \n",
- {
- "text": 'Coffee brewing failed',
- "error": 'i_am_a_teapot',
- "status": '418',
- }.to_json,
- ]
- )
- end
-
- context 'when the response needs to be encoded' do
- let(:response_body) { JSON.dump(response_hash).force_encoding('ASCII-8BIT') }
-
- context 'with invalid characters present' do
- before do
- response_body.gsub!('brewing', "\255 invalid \255")
- end
-
- it 'replaces them' do
- # The skip can be removed once support for < 2.1 is dropped.
- encoding = {reason: 'encode/scrub only works as of Ruby 2.1'}
- skip_for(encoding.merge(engine: 'jruby'))
- # See https://bibwild.wordpress.com/2013/03/12/removing-illegal-bytes-for-encoding-in-ruby-1-9-strings/
-
- raise 'Invalid characters not replaced' unless subject.message.include?('� invalid �')
- # This will fail if {:invalid => replace} is not passed into `encode`
- end
- end
- end
-
- context 'when the response is not an encodable thing' do
- let(:response_headers) { {'Content-Type' => 'who knows'} }
- let(:response_body) { {text: 'Coffee brewing failed'} }
-
- before do
- expect(response_body).not_to respond_to(:encode)
- # i.e. a Ruby hash
- end
-
- it 'does not try to encode the message string' do
- expect(subject.message).to eq(response_body.to_s)
- end
- end
-
- it 'sets the code attribute from error' do
- expect(subject.code).to eq('i_am_a_teapot')
- end
-
- it 'does not set the description attribute' do
- expect(subject.description).to be_nil
- end
- end
-
- context 'when there is error_description but no code (error)' do
- before do
- response_hash['error_description'] = 'Short and stout'
- response_hash.delete('error')
- end
-
- it 'prepends to the error message with a return character' do
- expect(subject.message.each_line.to_a).to eq(
- [
- "Short and stout\n",
- {
- "text": 'Coffee brewing failed',
- "error_description": 'Short and stout',
- }.to_json,
- ]
- )
- end
-
- context 'when the response needs to be encoded' do
- let(:response_body) { JSON.dump(response_hash).force_encoding('ASCII-8BIT') }
-
- context 'with invalid characters present' do
- before do
- response_body.gsub!('stout', "\255 invalid \255")
- end
-
- it 'replaces them' do
- # The skip can be removed once support for < 2.1 is dropped.
- encoding = {reason: 'encode/scrub only works as of Ruby 2.1'}
- skip_for(encoding.merge(engine: 'jruby'))
- # See https://bibwild.wordpress.com/2013/03/12/removing-illegal-bytes-for-encoding-in-ruby-1-9-strings/
-
- raise 'Invalid characters not replaced' unless subject.message.include?('� invalid �')
- # This will fail if {:invalid => replace} is not passed into `encode`
- end
- end
-
- context 'with undefined characters present' do
- before do
- response_hash['error_description'] += ": 'A magical voyage of tea 🍵'"
- end
-
- it 'replaces them' do
- raise 'Undefined characters not replaced' unless subject.message.include?('tea �')
- # This will fail if {:undef => replace} is not passed into `encode`
- end
- end
- end
-
- context 'when the response is not an encodable thing' do
- let(:response_headers) { {'Content-Type' => 'who knows'} }
- let(:response_body) { {text: 'Coffee brewing failed'} }
-
- before do
- expect(response_body).not_to respond_to(:encode)
- # i.e. a Ruby hash
- end
-
- it 'does not try to encode the message string' do
- expect(subject.message).to eq(response_body.to_s)
- end
- end
-
- it 'sets the code attribute' do
- expect(subject.code).to be_nil
- end
-
- it 'sets the description attribute' do
- expect(subject.description).to eq('Short and stout')
- end
- end
- end
-
- context 'when the response is simple hash, not parsed' do
- subject { described_class.new(response_hash) }
-
- let(:response_hash) { {text: 'Coffee brewing failed'} }
-
- it 'sets the code attribute to nil' do
- expect(subject.code).to be_nil
- end
-
- it 'sets the description attribute' do
- expect(subject.description).to be_nil
- end
-
- context 'when the response has an error and error_description' do
- before do
- response_hash['error_description'] = 'Short and stout'
- response_hash['error'] = 'i_am_a_teapot'
- end
-
- it 'sets the code attribute' do
- expect(subject.code).to eq('i_am_a_teapot')
- end
-
- it 'sets the description attribute' do
- expect(subject.description).to eq('Short and stout')
- end
-
- it 'prepends to the error message with a return character' do
- expect(subject.message.each_line.to_a).to eq(
- [
- "i_am_a_teapot: Short and stout\n",
- '{:text=>"Coffee brewing failed", "error_description"=>"Short and stout", "error"=>"i_am_a_teapot"}',
- ]
- )
- end
-
- context 'when using :xml parser with non-String-like thing' do
- let(:response_headers) { {'Content-Type' => 'text/xml'} }
- let(:response_hash) { {hello: :world} }
-
- before do
- expect(response_hash).not_to respond_to(:to_str)
- end
-
- it 'just returns whatever it can' do
- expect(subject.message).to eq("i_am_a_teapot: Short and stout\n{:hello=>:world, \"error_description\"=>\"Short and stout\", \"error\"=>\"i_am_a_teapot\"}")
- end
- end
- end
-
- context 'when using :xml parser with non-String-like thing' do
- let(:response_headers) { {'Content-Type' => 'text/xml'} }
- let(:response_hash) { {hello: :world} }
-
- before do
- expect(response_hash).not_to respond_to(:to_str)
- end
-
- it 'just returns the thing if it can' do
- expect(subject.message).to eq('{:hello=>:world}')
- end
- end
-
- context 'when there is no error description' do
- before do
- expect(response_hash).not_to have_key('error')
- expect(response_hash).not_to have_key('error_description')
- end
-
- it 'does not prepend anything to the message' do
- expect(subject.message.lines.count).to eq(1)
- expect(subject.message).to eq '{:text=>"Coffee brewing failed"}'
- end
-
- it 'does not set code' do
- expect(subject.code).to be_nil
- end
-
- it 'does not set description' do
- expect(subject.description).to be_nil
- end
- end
-
- context 'when there is code (error)' do
- before do
- response_hash['error_description'] = 'Short and stout'
- response_hash['error'] = 'i_am_a_teapot'
- response_hash['status'] = '418'
- end
-
- it 'prepends to the error message with a return character' do
- expect(subject.message.each_line.to_a).to eq(
- [
- "i_am_a_teapot: Short and stout\n",
- '{:text=>"Coffee brewing failed", "error_description"=>"Short and stout", "error"=>"i_am_a_teapot", "status"=>"418"}',
- ]
- )
- end
-
- it 'sets the code attribute' do
- expect(subject.code).to eq('i_am_a_teapot')
- end
-
- it 'sets the description attribute' do
- expect(subject.description).to eq('Short and stout')
- end
- end
-
- context 'when there is code (error) but no error_description' do
- before do
- response_hash.delete('error_description')
- response_hash['error'] = 'i_am_a_teapot'
- response_hash['status'] = '418'
- end
-
- it 'sets the code attribute from error' do
- expect(subject.code).to eq('i_am_a_teapot')
- end
-
- it 'does not set the description attribute' do
- expect(subject.description).to be_nil
- end
-
- it 'prepends to the error message with a return character' do
- expect(subject.message.each_line.to_a).to eq(
- [
- "i_am_a_teapot: \n",
- '{:text=>"Coffee brewing failed", "error"=>"i_am_a_teapot", "status"=>"418"}',
- ]
- )
- end
- end
-
- context 'when there is error_description but no code (error)' do
- before do
- response_hash['error_description'] = 'Short and stout'
- response_hash.delete('error')
- end
-
- it 'prepends to the error message with a return character' do
- expect(subject.message.each_line.to_a).to eq(
- [
- "Short and stout\n",
- '{:text=>"Coffee brewing failed", "error_description"=>"Short and stout"}',
- ]
- )
- end
-
- context 'when the response is not an encodable thing' do
- let(:response_headers) { {'Content-Type' => 'who knows'} }
- let(:response_hash) { {text: 'Coffee brewing failed'} }
-
- before do
- expect(response_hash).not_to respond_to(:encode)
- # i.e. a Ruby hash
- end
-
- it 'does not try to encode the message string' do
- expect(subject.message).to eq("Short and stout\n{:text=>\"Coffee brewing failed\", \"error_description\"=>\"Short and stout\"}")
- end
- end
-
- it 'sets the code attribute' do
- expect(subject.code).to be_nil
- end
-
- it 'sets the description attribute' do
- expect(subject.description).to eq('Short and stout')
- end
- end
- end
-
- context 'when the response is not a hash, not parsed' do
- subject { described_class.new(response_thing) }
-
- let(:response_thing) { [200, 'Success'] }
-
- it 'sets the code attribute to nil' do
- expect(subject.code).to be_nil
- end
-
- it 'sets the description attribute' do
- expect(subject.description).to be_nil
- end
-
- it 'sets the body attribute' do
- expect(subject.body).to eq(response_thing)
- end
-
- it 'sets the response attribute' do
- expect(subject.response).to eq(response_thing)
- end
- end
-
- context 'when the response does not parse to a hash' do
- let(:response_headers) { {'Content-Type' => 'text/html'} }
- let(:response_body) { 'Hello, I am a teapot' }
-
- before do
- expect(response.parsed).not_to be_a(Hash)
- end
-
- it 'does not do anything to the message' do
- expect(subject.message.lines.count).to eq(1)
- expect(subject.message).to eq(response_body)
- end
-
- it 'does not set code' do
- expect(subject.code).to be_nil
- end
-
- it 'does not set description' do
- expect(subject.description).to be_nil
- end
- end
-
- describe 'parsing json' do
- it 'does not blow up' do
- expect { subject.to_json }.not_to raise_error
- expect { subject.response.to_json }.not_to raise_error
- end
- end
-end
diff --git a/spec/oauth2/response_spec.rb b/spec/oauth2/response_spec.rb
deleted file mode 100644
index 4ceb228f..00000000
--- a/spec/oauth2/response_spec.rb
+++ /dev/null
@@ -1,345 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe OAuth2::Response do
- subject { described_class.new(raw_response) }
-
- let(:raw_response) { Faraday::Response.new(status: status, response_headers: headers, body: body) }
- let(:status) { 200 }
- let(:headers) { {'foo' => 'bar'} }
- let(:body) { 'foo' }
-
- describe '#initialize' do
- it 'returns the status, headers and body' do
- expect(subject.headers).to eq(headers)
- expect(subject.status).to eq(status)
- expect(subject.body).to eq(body)
- end
- end
-
- describe '.register_parser' do
- let(:response) do
- double('response', headers: {'Content-Type' => 'application/foo-bar'},
- status: 200,
- body: 'baz')
- end
-
- before do
- described_class.register_parser(:foobar, ['application/foo-bar']) do |body|
- "foobar #{body}"
- end
- end
-
- it 'adds to the content types and parsers' do
- expect(described_class.send(:class_variable_get, :@@parsers).keys).to include(:foobar)
- expect(described_class.send(:class_variable_get, :@@content_types).keys).to include('application/foo-bar')
- end
-
- it 'is able to parse that content type automatically' do
- expect(described_class.new(response).parsed).to eq('foobar baz')
- end
- end
-
- describe '#content_type' do
- context 'when headers are blank' do
- let(:headers) { nil }
-
- it 'returns nil' do
- expect(subject.content_type).to be_nil
- end
- end
-
- context 'when content-type is not present' do
- let(:headers) { {'a fuzzy' => 'fuzzer'} }
-
- it 'returns empty string' do
- expect(subject.content_type).to eq('')
- end
- end
-
- context 'when content-type is present' do
- let(:headers) { {'Content-Type' => 'application/x-www-form-urlencoded'} }
-
- it 'returns the content type header contents' do
- expect(subject.content_type).to eq('application/x-www-form-urlencoded')
- end
- end
- end
-
- describe '#parsed' do
- subject(:parsed) do
- headers = {'Content-Type' => content_type}
- response = double('response', headers: headers, body: body)
- instance = described_class.new(response)
- instance.parsed
- end
-
- shared_examples_for 'parsing JSON-like' do
- it 'has num keys' do
- expect(parsed.keys.size).to eq(6)
- end
-
- it 'parses string' do
- expect(parsed['foo']).to eq('bar')
- expect(parsed.key('bar')).to eq('foo')
- end
-
- it 'parses non-zero number' do
- expect(parsed['answer']).to eq(42)
- expect(parsed.key(42)).to eq('answer')
- end
-
- it 'parses nil as NilClass' do
- expect(parsed['krill']).to be_nil
- expect(parsed.key(nil)).to eq('krill')
- end
-
- it 'parses zero as number' do
- expect(parsed['zero']).to eq(0)
- expect(parsed.key(0)).to eq('zero')
- end
-
- it 'parses false as FalseClass' do
- expect(parsed['malign']).to be(false)
- expect(parsed.key(false)).to eq('malign')
- end
-
- it 'parses false as TrueClass' do
- expect(parsed['shine']).to be(true)
- expect(parsed.key(true)).to eq('shine')
- end
- end
-
- context 'when application/json' do
- let(:content_type) { 'application/json' }
- let(:body) { JSON.dump(foo: 'bar', answer: 42, krill: nil, zero: 0, malign: false, shine: true) }
-
- it_behaves_like 'parsing JSON-like'
- end
-
- context 'when application/Json' do
- let(:content_type) { 'application/Json' }
- let(:body) { JSON.dump(foo: 'bar', answer: 42, krill: nil, zero: 0, malign: false, shine: true) }
-
- it_behaves_like 'parsing JSON-like'
- end
-
- context 'when application/hal+json' do
- let(:content_type) { 'application/hal+json' }
- let(:body) { JSON.dump(foo: 'bar', answer: 42, krill: nil, zero: 0, malign: false, shine: true) }
-
- it_behaves_like 'parsing JSON-like'
- end
-
- context 'when application/x-www-form-urlencoded' do
- let(:content_type) { 'application/x-www-form-urlencoded' }
- let(:body) { 'foo=bar&answer=42&krill=&zero=0&malign=false&shine=true' }
-
- it 'has num keys' do
- expect(parsed.keys.size).to eq(6)
- end
-
- it 'parses string' do
- expect(parsed['foo']).to eq('bar')
- expect(parsed.key('bar')).to eq('foo')
- end
-
- it 'parses non-zero number as string' do
- expect(parsed['answer']).to eq('42')
- expect(parsed.key('42')).to eq('answer')
- end
-
- it 'parses nil as empty string' do
- expect(parsed['krill']).to eq('')
- expect(parsed.key('')).to eq('krill')
- end
-
- it 'parses zero as string' do
- expect(parsed['zero']).to eq('0')
- expect(parsed.key('0')).to eq('zero')
- end
-
- it 'parses false as string' do
- expect(parsed['malign']).to eq('false')
- expect(parsed.key('false')).to eq('malign')
- end
-
- it 'parses true as string' do
- expect(parsed['shine']).to eq('true')
- expect(parsed.key('true')).to eq('shine')
- end
- end
-
- it 'parses application/vnd.collection+json body' do
- headers = {'Content-Type' => 'application/vnd.collection+json'}
- body = JSON.dump(collection: {})
- response = double('response', headers: headers, body: body)
- subject = described_class.new(response)
- expect(subject.parsed.keys.size).to eq(1)
- end
-
- it 'parses application/vnd.api+json body' do
- headers = {'Content-Type' => 'application/vnd.api+json'}
- body = JSON.dump(collection: {})
- response = double('response', headers: headers, body: body)
- subject = described_class.new(response)
- expect(subject.parsed.keys.size).to eq(1)
- end
-
- it 'parses application/problem+json body' do
- headers = {'Content-Type' => 'application/problem+json'}
- body = JSON.dump(type: 'https://tools.ietf.org/html/rfc7231#section-6.5.4', title: 'Not Found')
- response = double('response', headers: headers, body: body)
- subject = described_class.new(response)
- expect(subject.parsed.keys.size).to eq(2)
- expect(subject.parsed['type']).to eq('https://tools.ietf.org/html/rfc7231#section-6.5.4')
- expect(subject.parsed['title']).to eq('Not Found')
- end
-
- it "doesn't try to parse other content-types" do
- headers = {'Content-Type' => 'text/html'}
- body = ''
-
- response = double('response', headers: headers, body: body)
-
- expect(JSON).not_to receive(:parse)
- expect(Rack::Utils).not_to receive(:parse_query)
-
- subject = described_class.new(response)
- expect(subject.parsed).to be_nil
- end
-
- it "doesn't parse bodies which have previously been parsed" do
- headers = {'Content-Type' => 'application/json'}
- body = {foo: 'bar', answer: 42, krill: nil, zero: 0, malign: false, shine: true}
-
- response = double('response', headers: headers, body: body)
-
- expect(JSON).not_to receive(:parse)
- expect(Rack::Utils).not_to receive(:parse_query)
-
- subject = described_class.new(response)
- expect(subject.parsed.keys.size).to eq(6)
- end
-
- it 'snakecases json keys when parsing' do
- headers = {'Content-Type' => 'application/json'}
- body = JSON.dump('accessToken' => 'bar', 'MiGever' => 'Ani')
- response = double('response', headers: headers, body: body)
- subject = described_class.new(response)
- expect(subject.parsed.keys.size).to eq(2)
- expect(subject.parsed['access_token']).to eq('bar')
- expect(subject.parsed['mi_gever']).to eq('Ani')
- end
-
- context 'when not snaky' do
- it 'does not snakecase json keys when parsing' do
- headers = {'Content-Type' => 'application/json'}
- body = JSON.dump('accessToken' => 'bar', 'MiGever' => 'Ani')
- response = double('response', headers: headers, body: body)
- subject = described_class.new(response, snaky: false)
- expect(subject.parsed.keys.size).to eq(2)
- expect(subject.parsed['accessToken']).to eq('bar')
- expect(subject.parsed['MiGever']).to eq('Ani')
- expect(subject.parsed['access_token']).to be_nil
- expect(subject.parsed['mi_gever']).to be_nil
- end
- end
-
- it 'supports registered parsers with arity == 0; passing nothing' do
- described_class.register_parser(:arity_0, []) do
- 'a-ok'
- end
-
- headers = {'Content-Type' => 'text/html'}
- body = ''
- response = double('response', headers: headers, body: body)
-
- subject = described_class.new(response, parse: :arity_0)
-
- expect(subject.parsed).to eq('a-ok')
- end
-
- it 'supports registered parsers with arity == 2; passing body and response' do
- headers = {'Content-Type' => 'text/html'}
- body = ''
- response = double('response', headers: headers, body: body)
-
- described_class.register_parser(:arity_2, []) do |passed_body, passed_response|
- expect(passed_body).to eq(body)
- expect(passed_response).to eq(response)
-
- 'a-ok'
- end
-
- subject = described_class.new(response, parse: :arity_2)
-
- expect(subject.parsed).to eq('a-ok')
- end
-
- it 'supports registered parsers with arity > 2; passing body and response' do
- headers = {'Content-Type' => 'text/html'}
- body = ''
- response = double('response', headers: headers, body: body)
-
- described_class.register_parser(:arity_3, []) do |passed_body, passed_response, *args|
- expect(passed_body).to eq(body)
- expect(passed_response).to eq(response)
- expect(args).to eq([])
-
- 'a-ok'
- end
-
- subject = described_class.new(response, parse: :arity_3)
-
- expect(subject.parsed).to eq('a-ok')
- end
-
- it 'supports directly passed parsers' do
- headers = {'Content-Type' => 'text/html'}
- body = ''
- response = double('response', headers: headers, body: body)
-
- subject = described_class.new(response, parse: -> { 'a-ok' })
-
- expect(subject.parsed).to eq('a-ok')
- end
-
- it 'supports no parsing' do
- headers = {'Content-Type' => 'text/html'}
- body = ''
- response = double('response', headers: headers, body: body)
-
- subject = described_class.new(response, parse: false)
-
- expect(subject.parsed).to eq(nil)
- end
- end
-
- context 'with xml parser registration' do
- it 'tries to load multi_xml.rb and use it' do
- expect(described_class.send(:class_variable_get, :@@parsers)[:xml]).not_to be_nil
- end
-
- it 'is able to parse xml' do
- headers = {'Content-Type' => 'text/xml'}
- body = 'baz'
-
- response = double('response', headers: headers, body: body)
- expect(described_class.new(response).parsed).to eq('foo' => {'bar' => 'baz'})
- end
-
- it 'is able to parse application/xml' do
- headers = {'Content-Type' => 'application/xml'}
- body = 'baz'
-
- response = double('response', headers: headers, body: body)
- expect(described_class.new(response).parsed).to eq('foo' => {'bar' => 'baz'})
- end
- end
-
- describe 'converting to json' do
- it 'does not blow up' do
- expect { subject.to_json }.not_to raise_error
- end
- end
-end
diff --git a/spec/oauth2/snaky_hash_spec.rb b/spec/oauth2/snaky_hash_spec.rb
deleted file mode 100644
index 32bf0c33..00000000
--- a/spec/oauth2/snaky_hash_spec.rb
+++ /dev/null
@@ -1,163 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe OAuth2::SnakyHash do
- subject(:instance) { described_class.new }
-
- describe '.build' do
- context 'when build from hash' do
- subject { described_class.new('AccessToken' => '1') }
-
- it 'create correct snake hash' do
- expect(subject).to be_a(described_class)
- expect(subject['AccessToken']).to eq('1')
- expect(subject['access_token']).to eq('1')
- end
- end
-
- context 'when build from snake_hash' do
- subject do
- h = described_class.new
- h['AccessToken'] = '1'
-
- described_class.new(h)
- end
-
- it 'create correct snake hash' do
- expect(subject).to be_a(described_class)
- expect(subject['AccessToken']).to eq('1')
- expect(subject['access_token']).to eq('1')
- end
- end
- end
-
- describe 'assign and query' do
- it 'returns assigned value with camel key' do
- subject['AccessToken'] = '1'
-
- expect(subject['AccessToken']).to eq('1')
- expect(subject['access_token']).to eq('1')
- end
-
- it 'returns assigned value with snake key' do
- subject['access_token'] = '1'
-
- expect(subject['AccessToken']).to eq('1')
- expect(subject['access_token']).to eq('1')
- end
-
- it 'overwrite by alternate key' do
- subject['AccessToken'] = '1'
-
- expect(subject['AccessToken']).to eq('1')
- expect(subject['access_token']).to eq('1')
-
- subject['access_token'] = '2'
-
- expect(subject['AccessToken']).to eq('2')
- expect(subject['access_token']).to eq('2')
- end
- end
-
- describe '#to_h' do
- context 'when nil' do
- it 'can be converted to empty hash' do
- expect(instance.to_h).to eq({})
- end
- end
-
- context 'when empty' do
- subject(:instance) { described_class.new(original) }
-
- let(:original) { {} }
-
- it 'can be converted to empty hash' do
- expect(instance.to_h).to eq({})
- end
- end
-
- context 'when not empty' do
- subject(:instance) { described_class.new(original) }
-
- let(:original) { {'a' => 'b', 'bTo' => 'aDo', 'v_rt' => 1, yy_yy: 'yy_yy', :LuLu => :CRays} }
-
- it 'converts to snake hash' do
- expect(instance.to_h).to eq('a' => 'b', 'b_to' => 'aDo', 'lu_lu' => :CRays, 'v_rt' => 1, 'yy_yy' => 'yy_yy')
- end
- end
- end
-
- describe '#fetch' do
- context 'when Camel case key' do
- subject { described_class.new('AccessToken' => '1') }
-
- it 'return correct token' do
- expect(subject.fetch('access_token')).to eq('1')
- end
- end
-
- context 'when Camel case key with down-cased first letter' do
- subject { described_class.new('accessToken' => '1') }
-
- it 'return correct token' do
- expect(subject.fetch('access_token')).to eq('1')
- end
- end
-
- context 'when snake case key' do
- subject { described_class.new('access_token' => '1') }
-
- it 'return correct token' do
- expect(subject.fetch('access_token')).to eq('1')
- end
- end
-
- context 'when missing any key' do
- subject { described_class.new }
-
- it 'raise KeyError with key' do
- pending_for(engine: 'jruby', versions: '3.1.0', reason: 'https://github.com/jruby/jruby/issues/7112')
- expect do
- subject.fetch('access_token')
- end.to raise_error(KeyError, /access_token/)
- end
-
- it 'return default value' do
- expect(subject.fetch('access_token', 'default')).to eq('default')
- end
- end
- end
-
- describe '#key?' do
- context 'when Camel case key' do
- subject { described_class.new('AccessToken' => '1') }
-
- it 'return true' do
- expect(subject.key?('access_token')).to be(true)
- end
- end
-
- context 'when Camel case key with down-cased first letter' do
- subject { described_class.new('accessToken' => '1') }
-
- it 'return true' do
- expect(subject.key?('access_token')).to be(true)
- end
- end
-
- context 'when snake case key' do
- subject { described_class.new('access_token' => '1') }
-
- it 'return true' do
- expect(subject.key?('access_token')).to be(true)
- end
- end
-
- context 'when missing any key' do
- subject { described_class.new }
-
- it 'return false' do
- expect(subject.key?('access_token')).to be(false)
- end
- end
- end
-end
diff --git a/spec/oauth2/strategy/assertion_spec.rb b/spec/oauth2/strategy/assertion_spec.rb
deleted file mode 100644
index 43e498eb..00000000
--- a/spec/oauth2/strategy/assertion_spec.rb
+++ /dev/null
@@ -1,265 +0,0 @@
-# frozen_string_literal: true
-
-require 'openssl'
-require 'jwt'
-
-RSpec.describe OAuth2::Strategy::Assertion do
- let(:client_assertion) { client.assertion }
-
- let(:client) do
- cli = OAuth2::Client.new('abc', 'def', site: 'http://api.example.com', auth_scheme: auth_scheme)
- cli.connection = Faraday.new(cli.site, cli.options[:connection_opts]) do |b|
- b.request :url_encoded
- b.adapter :test do |stub|
- stub.post('/oauth/token') do |token_request|
- @request_body = Rack::Utils.parse_nested_query(token_request.body).transform_keys(&:to_sym)
-
- case @response_format
- when 'formencoded'
- [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, 'expires_in=600&access_token=salmon&refresh_token=trout']
- when 'json'
- [200, {'Content-Type' => 'application/json'}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}']
- else
- raise 'Please define @response_format to choose a response content type!'
- end
- end
- end
- end
- cli
- end
-
- let(:auth_scheme) { :request_body }
-
- describe '#authorize_url' do
- it 'raises NotImplementedError' do
- expect { client_assertion.authorize_url }.to raise_error(NotImplementedError)
- end
- end
-
- describe '#get_token' do
- let(:algorithm) { 'HS256' }
- let(:key) { 'arowana' }
- let(:timestamp) { Time.now.to_i }
- let(:claims) do
- {
- iss: 'carp@example.com',
- scope: 'https://oauth.example.com/auth/flounder',
- aud: 'https://sturgeon.example.com/oauth2/token',
- exp: timestamp + 3600,
- iat: timestamp,
- sub: '12345',
- custom_claim: 'ling cod',
- }
- end
-
- before do
- @response_format = 'json'
- end
-
- describe 'assembling a JWT assertion' do
- let(:jwt) do
- payload, header = JWT.decode(@request_body[:assertion], key, true, algorithm: algorithm)
- {payload: payload, header: header}
- end
-
- let(:payload) { jwt[:payload] }
- let(:header) { jwt[:header] }
-
- shared_examples_for 'encodes the JWT' do
- it 'indicates algorithm in the header' do
- expect(header).not_to be_nil
- expect(header['alg']).to eq(algorithm)
- end
-
- it 'has claims' do
- expect(payload).not_to be_nil
- expect(payload.keys).to match_array(%w[iss scope aud exp iat sub custom_claim])
- payload.each do |key, claim|
- expect(claims[key.to_sym]).to eq(claim)
- end
- end
- end
-
- context 'when encoding as HS256' do
- let(:algorithm) { 'HS256' }
- let(:key) { 'super_secret!' }
-
- before do
- client_assertion.get_token(claims, algorithm: algorithm, key: key)
- raise 'No request made!' if @request_body.nil?
- end
-
- it_behaves_like 'encodes the JWT'
-
- context 'with real key' do
- let(:key) { '1883be842495c3b58f68ca71fbf1397fbb9ed2fdf8990f8404a25d0a1b995943' }
-
- it_behaves_like 'encodes the JWT'
- end
- end
-
- context 'when encoding as RS256' do
- let(:algorithm) { 'RS256' }
- let(:key) { OpenSSL::PKey::RSA.new(1024) }
-
- before do
- client_assertion.get_token(claims, algorithm: algorithm, key: key)
- raise 'No request made!' if @request_body.nil?
- end
-
- it_behaves_like 'encodes the JWT'
-
- context 'with private key' do
- let(:private_key_file) { 'spec/fixtures/RS256/jwtRS256.key' }
- let(:password) { '' }
- let(:key) { OpenSSL::PKey::RSA.new(File.read(private_key_file), password) }
-
- it_behaves_like 'encodes the JWT'
- end
- end
-
- context 'with bad encoding params' do
- let(:encoding_opts) { {algorithm: algorithm, key: key} }
-
- describe 'non-supported algorithms' do
- let(:algorithm) { 'the blockchain' }
- let(:key) { 'machine learning' }
-
- it 'raises NotImplementedError' do
- # this behavior is handled by the JWT gem, but this should make sure it is consistent
- expect { client_assertion.get_token(claims, encoding_opts) }.to raise_error(NotImplementedError)
- end
- end
-
- describe 'of a wrong object type' do
- let(:encoding_opts) { 'the cloud' }
-
- it 'raises ArgumentError' do
- expect { client_assertion.get_token(claims, encoding_opts) }.to raise_error(ArgumentError, /encoding_opts/)
- end
- end
-
- describe 'missing encoding_opts[:algorithm]' do
- before do
- encoding_opts.delete(:algorithm)
- end
-
- it 'raises ArgumentError' do
- expect { client_assertion.get_token(claims, encoding_opts) }.to raise_error(ArgumentError, /encoding_opts/)
- end
- end
-
- describe 'missing encoding_opts[:key]' do
- before do
- encoding_opts.delete(:key)
- end
-
- it 'raises ArgumentError' do
- expect { client_assertion.get_token(claims, encoding_opts) }.to raise_error(ArgumentError, /encoding_opts/)
- end
- end
- end
- end
-
- describe 'POST request parameters' do
- context 'when using :auth_scheme => :request_body' do
- let(:auth_scheme) { :request_body }
-
- it 'includes assertion and grant_type, along with the client parameters' do
- client_assertion.get_token(claims, algorithm: algorithm, key: key)
- expect(@request_body).not_to be_nil
- expect(@request_body.keys).to match_array(%i[assertion grant_type client_id client_secret])
- expect(@request_body[:grant_type]).to eq('urn:ietf:params:oauth:grant-type:jwt-bearer')
- expect(@request_body[:assertion]).to be_a(String)
- expect(@request_body[:client_id]).to eq('abc')
- expect(@request_body[:client_secret]).to eq('def')
- end
-
- it 'includes other params via request_options' do
- client_assertion.get_token(claims, {algorithm: algorithm, key: key}, {scope: 'dover sole'})
- expect(@request_body).not_to be_nil
- expect(@request_body.keys).to match_array(%i[assertion grant_type scope client_id client_secret])
- expect(@request_body[:grant_type]).to eq('urn:ietf:params:oauth:grant-type:jwt-bearer')
- expect(@request_body[:assertion]).to be_a(String)
- expect(@request_body[:scope]).to eq('dover sole')
- expect(@request_body[:client_id]).to eq('abc')
- expect(@request_body[:client_secret]).to eq('def')
- end
- end
-
- context 'when using :auth_scheme => :basic_auth' do
- let(:auth_scheme) { :basic_auth }
-
- it 'includes assertion and grant_type by default' do
- client_assertion.get_token(claims, algorithm: algorithm, key: key)
- expect(@request_body).not_to be_nil
- expect(@request_body.keys).to match_array(%i[assertion grant_type])
- expect(@request_body[:grant_type]).to eq('urn:ietf:params:oauth:grant-type:jwt-bearer')
- expect(@request_body[:assertion]).to be_a(String)
- end
-
- it 'includes other params via request_options' do
- client_assertion.get_token(claims, {algorithm: algorithm, key: key}, {scope: 'dover sole'})
- expect(@request_body).not_to be_nil
- expect(@request_body.keys).to match_array(%i[assertion grant_type scope])
- expect(@request_body[:grant_type]).to eq('urn:ietf:params:oauth:grant-type:jwt-bearer')
- expect(@request_body[:assertion]).to be_a(String)
- expect(@request_body[:scope]).to eq('dover sole')
- end
- end
- end
-
- describe 'returning the response' do
- let(:access_token) { client_assertion.get_token(claims, {algorithm: algorithm, key: key}, {}, response_opts) }
- let(:response_opts) { {} }
-
- %w[json formencoded].each do |mode|
- context "when the content type is #{mode}" do
- before do
- @response_format = mode
- end
-
- it 'returns an AccessToken' do
- expect(access_token).to be_an(OAuth2::AccessToken)
- end
-
- it 'returns AccessToken with same Client' do
- expect(access_token.client).to eq(client)
- end
-
- it 'returns AccessToken with #token' do
- expect(access_token.token).to eq('salmon')
- end
-
- it 'returns AccessToken with #expires_in' do
- expect(access_token.expires_in).to eq(600)
- end
-
- it 'returns AccessToken with #expires_at' do
- expect(access_token.expires_at).not_to be_nil
- end
-
- it 'sets AccessToken#refresh_token to nil' do
- expect(access_token.refresh_token).to eq('trout')
- end
-
- context 'with custom response_opts' do
- let(:response_opts) { {'custom_token_option' => 'mackerel'} }
-
- it 'passes them into the token params' do
- expect(access_token.params).to eq(response_opts)
- end
- end
-
- context 'when no custom opts are passed in' do
- let(:response_opts) { {} }
-
- it 'does not set any params by default' do
- expect(access_token.params).to eq({})
- end
- end
- end
- end
- end
- end
-end
diff --git a/spec/oauth2/strategy/auth_code_spec.rb b/spec/oauth2/strategy/auth_code_spec.rb
deleted file mode 100644
index fcc7be3b..00000000
--- a/spec/oauth2/strategy/auth_code_spec.rb
+++ /dev/null
@@ -1,154 +0,0 @@
-# encoding: utf-8
-# frozen_string_literal: true
-
-RSpec.describe OAuth2::Strategy::AuthCode do
- subject { client.auth_code }
-
- let(:code) { 'sushi' }
- let(:kvform_token) { 'expires_in=600&access_token=salmon&refresh_token=trout&extra_param=steve' }
- let(:facebook_token) { kvform_token.gsub('_in', '') }
- let(:json_token) { JSON.dump(expires_in: 600, access_token: 'salmon', refresh_token: 'trout', extra_param: 'steve') }
- let(:redirect_uri) { 'http://example.com/redirect_uri' }
- let(:microsoft_token) { 'id_token=i_am_MSFT' }
-
- let(:client) do
- OAuth2::Client.new('abc', 'def', site: 'http://api.example.com') do |builder|
- builder.adapter :test do |stub|
- stub.get("/oauth/token?client_id=abc&code=#{code}&grant_type=authorization_code") do |_env|
- case @mode
- when 'formencoded'
- [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token]
- when 'json'
- [200, {'Content-Type' => 'application/json'}, json_token]
- when 'from_facebook'
- [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, facebook_token]
- when 'from_microsoft'
- [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, microsoft_token]
- else raise ArgumentError, "Bad @mode: #{@mode}"
- end
- end
- stub.post('/oauth/token', 'client_id' => 'abc', 'client_secret' => 'def', 'code' => 'sushi', 'grant_type' => 'authorization_code') do |_env|
- case @mode
- when 'formencoded'
- [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token]
- when 'json'
- [200, {'Content-Type' => 'application/json'}, json_token]
- when 'from_facebook'
- [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, facebook_token]
- else raise ArgumentError, "Bad @mode: #{@mode}"
- end
- end
- stub.post('/oauth/token', 'client_id' => 'abc', 'client_secret' => 'def', 'code' => 'sushi', 'grant_type' => 'authorization_code', 'redirect_uri' => redirect_uri) do |_env|
- [200, {'Content-Type' => 'application/json'}, json_token]
- end
- end
- end
- end
-
- describe '#authorize_url' do
- it 'includes the client_id' do
- expect(subject.authorize_url).to include('client_id=abc')
- end
-
- it 'includes the type' do
- expect(subject.authorize_url).to include('response_type=code')
- end
-
- it 'does not include the client_secret' do
- expect(subject.authorize_url).not_to include('client_secret=def')
- end
-
- it 'raises an error if the client_secret is passed in' do
- expect { subject.authorize_url(client_secret: 'def') }.to raise_error(ArgumentError)
- end
-
- it 'raises an error if the client_secret is passed in with string keys' do
- expect { subject.authorize_url('client_secret' => 'def') }.to raise_error(ArgumentError)
- end
-
- it 'includes passed in options' do
- cb = 'http://myserver.local/oauth/callback'
- expect(subject.authorize_url(redirect_uri: cb)).to include("redirect_uri=#{CGI.escape(cb)}")
- end
- end
-
- describe '#get_token (with dynamic redirect_uri)' do
- before do
- @mode = 'json'
- client.options[:token_method] = :post
- client.options[:auth_scheme] = :request_body
- client.options[:redirect_uri] = redirect_uri
- end
-
- it 'includes redirect_uri once in the request parameters' do
- expect { subject.get_token(code, redirect_uri: redirect_uri) }.not_to raise_error
- end
- end
-
- describe '#get_token (handling utf-8 data)' do
- let(:json_token) { JSON.dump(expires_in: 600, access_token: 'salmon', refresh_token: 'trout', extra_param: 'André') }
-
- before do
- @mode = 'json'
- client.options[:token_method] = :post
- client.options[:auth_scheme] = :request_body
- end
-
- it 'does not raise an error' do
- expect { subject.get_token(code) }.not_to raise_error
- end
-
- it 'does not create an error instance' do
- expect(OAuth2::Error).not_to receive(:new)
-
- subject.get_token(code)
- end
- end
-
- describe '#get_token' do
- it "doesn't treat an OpenID Connect token with only an id_token (like from Microsoft) as invalid" do
- @mode = 'from_microsoft'
- client.options[:token_method] = :get
- client.options[:auth_scheme] = :request_body
- @access = subject.get_token(code)
- expect(@access.token).to eq('i_am_MSFT')
- end
- end
-
- %w[json formencoded from_facebook].each do |mode|
- %i[get post].each do |verb|
- describe "#get_token (#{mode}, access_token_method=#{verb}" do
- before do
- @mode = mode
- client.options[:token_method] = verb
- client.options[:auth_scheme] = :request_body
- @access = subject.get_token(code)
- end
-
- it 'returns AccessToken with same Client' do
- expect(@access.client).to eq(client)
- end
-
- it 'returns AccessToken with #token' do
- expect(@access.token).to eq('salmon')
- end
-
- it 'returns AccessToken with #refresh_token' do
- expect(@access.refresh_token).to eq('trout')
- end
-
- it 'returns AccessToken with #expires_in' do
- expect(@access.expires_in).to eq(600)
- end
-
- it 'returns AccessToken with #expires_at' do
- expect(@access.expires_at).to be_kind_of(Integer)
- end
-
- it 'returns AccessToken with params accessible via []' do
- expect(@access['extra_param']).to eq('steve')
- end
- end
- end
- end
-end
diff --git a/spec/oauth2/strategy/base_spec.rb b/spec/oauth2/strategy/base_spec.rb
deleted file mode 100644
index 33b98389..00000000
--- a/spec/oauth2/strategy/base_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe OAuth2::Strategy::Base do
- it 'initializes with a Client' do
- expect { described_class.new(OAuth2::Client.new('abc', 'def')) }.not_to raise_error
- end
-end
diff --git a/spec/oauth2/strategy/client_credentials_spec.rb b/spec/oauth2/strategy/client_credentials_spec.rb
deleted file mode 100644
index b9480098..00000000
--- a/spec/oauth2/strategy/client_credentials_spec.rb
+++ /dev/null
@@ -1,97 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe OAuth2::Strategy::ClientCredentials do
- subject { client.client_credentials }
-
- let(:kvform_token) { 'expires_in=600&access_token=salmon&refresh_token=trout' }
- let(:json_token) { '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}' }
-
- let(:client) do
- OAuth2::Client.new('abc', 'def', site: 'http://api.example.com') do |builder|
- builder.adapter :test do |stub|
- stub.post('/oauth/token', 'grant_type' => 'client_credentials') do |env|
- client_id, client_secret = Base64.decode64(env[:request_headers]['Authorization'].split(' ', 2)[1]).split(':', 2)
- (client_id == 'abc' && client_secret == 'def') || raise(Faraday::Adapter::Test::Stubs::NotFound)
- @last_headers = env[:request_headers]
- case @mode
- when 'formencoded'
- [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token]
- when 'json'
- [200, {'Content-Type' => 'application/json'}, json_token]
- else raise ArgumentError, "Bad @mode: #{@mode}"
- end
- end
- stub.post('/oauth/token', 'client_id' => 'abc', 'client_secret' => 'def', 'grant_type' => 'client_credentials') do |_env|
- case @mode
- when 'formencoded'
- [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token]
- when 'json'
- [200, {'Content-Type' => 'application/json'}, json_token]
- else raise ArgumentError, "Bad @mode: #{@mode}"
- end
- end
- end
- end
- end
-
- describe '#authorize_url' do
- it 'raises NotImplementedError' do
- expect { subject.authorize_url }.to raise_error(NotImplementedError)
- end
- end
-
- %w[json formencoded].each do |mode|
- %i[basic_auth request_body].each do |auth_scheme|
- describe "#get_token (#{mode}) (#{auth_scheme})" do
- before do
- @mode = mode
- client.options[:auth_scheme] = auth_scheme
- @access = subject.get_token
- end
-
- it 'returns AccessToken with same Client' do
- expect(@access.client).to eq(client)
- end
-
- it 'returns AccessToken with #token' do
- expect(@access.token).to eq('salmon')
- end
-
- it 'returns AccessToken without #refresh_token' do
- expect(@access.refresh_token).to eq('trout')
- end
-
- it 'returns AccessToken with #expires_in' do
- expect(@access.expires_in).to eq(600)
- end
-
- it 'returns AccessToken with #expires_at' do
- expect(@access.expires_at).not_to be_nil
- end
- end
- end
- end
-
- describe '#get_token (with extra header parameters)' do
- before do
- @mode = 'json'
- @access = subject.get_token(headers: {'X-Extra-Header' => 'wow'})
- end
-
- it 'sends the header correctly.' do
- expect(@last_headers['X-Extra-Header']).to eq('wow')
- end
- end
-
- describe '#get_token (with option overriding response)' do
- before do
- @mode = 'json'
- @access = subject.get_token({}, {'refresh_token' => 'guppy'})
- end
-
- it 'override is applied' do
- expect(@access.token).to eq('salmon')
- expect(@access.refresh_token).to eq('guppy')
- end
- end
-end
diff --git a/spec/oauth2/strategy/implicit_spec.rb b/spec/oauth2/strategy/implicit_spec.rb
deleted file mode 100644
index 18588fea..00000000
--- a/spec/oauth2/strategy/implicit_spec.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe OAuth2::Strategy::Implicit do
- subject { client.implicit }
-
- let(:client) { OAuth2::Client.new('abc', 'def', site: 'http://api.example.com') }
-
- describe '#authorize_url' do
- it 'includes the client_id' do
- expect(subject.authorize_url).to include('client_id=abc')
- end
-
- it 'includes the type' do
- expect(subject.authorize_url).to include('response_type=token')
- end
-
- it 'does not include the client_secret' do
- expect(subject.authorize_url).not_to include('client_secret=def')
- end
-
- it 'raises an error if the client_secret is passed in' do
- expect { subject.authorize_url(client_secret: 'def') }.to raise_error(ArgumentError)
- end
-
- it 'raises an error if the client_secret is passed in with string keys' do
- expect { subject.authorize_url('client_secret' => 'def') }.to raise_error(ArgumentError)
- end
-
- it 'includes passed in options' do
- cb = 'http://myserver.local/oauth/callback'
- expect(subject.authorize_url(redirect_uri: cb)).to include("redirect_uri=#{CGI.escape(cb)}")
- end
- end
-
- describe '#get_token' do
- it 'raises NotImplementedError' do
- expect { subject.get_token }.to raise_error(NotImplementedError)
- end
- end
-end
diff --git a/spec/oauth2/strategy/password_spec.rb b/spec/oauth2/strategy/password_spec.rb
deleted file mode 100644
index 040b6455..00000000
--- a/spec/oauth2/strategy/password_spec.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe OAuth2::Strategy::Password do
- subject { client.password }
-
- let(:client) do
- cli = OAuth2::Client.new('abc', 'def', site: 'http://api.example.com')
- cli.connection = Faraday.new(cli.site, cli.options[:connection_opts]) do |b|
- b.request :url_encoded
- b.adapter :test do |stub|
- stub.post('/oauth/token') do |_env|
- case @mode
- when 'formencoded'
- [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, 'expires_in=600&access_token=salmon&refresh_token=trout']
- when 'json'
- [200, {'Content-Type' => 'application/json'}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}']
- else raise ArgumentError, "Bad @mode: #{@mode}"
- end
- end
- end
- end
- cli
- end
-
- describe '#authorize_url' do
- it 'raises NotImplementedError' do
- expect { subject.authorize_url }.to raise_error(NotImplementedError)
- end
- end
-
- %w[json formencoded].each do |mode|
- describe "#get_token (#{mode})" do
- before do
- @mode = mode
- @access = subject.get_token('username', 'password')
- end
-
- it 'returns AccessToken with same Client' do
- expect(@access.client).to eq(client)
- end
-
- it 'returns AccessToken with #token' do
- expect(@access.token).to eq('salmon')
- end
-
- it 'returns AccessToken with #refresh_token' do
- expect(@access.refresh_token).to eq('trout')
- end
-
- it 'returns AccessToken with #expires_in' do
- expect(@access.expires_in).to eq(600)
- end
-
- it 'returns AccessToken with #expires_at' do
- expect(@access.expires_at).not_to be_nil
- end
- end
- end
-end
diff --git a/spec/oauth2/version_spec.rb b/spec/oauth2/version_spec.rb
deleted file mode 100644
index 250a3039..00000000
--- a/spec/oauth2/version_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe OAuth2::Version do
- it 'has a version number' do
- expect(described_class).not_to be_nil
- end
-
- it 'can be a string' do
- expect(described_class.to_s).to be_a(String)
- end
-
- it 'allows Constant access' do
- expect(described_class::VERSION).to be_a(String)
- end
-
- it 'is greater than 0.1.0' do
- expect(Gem::Version.new(described_class) > Gem::Version.new('0.1.0')).to be(true)
- end
-
- it 'major version is an integer' do
- expect(described_class.major).to be_a(Integer)
- end
-
- it 'minor version is an integer' do
- expect(described_class.minor).to be_a(Integer)
- end
-
- it 'patch version is an integer' do
- expect(described_class.patch).to be_a(Integer)
- end
-
- it 'returns a Hash' do
- expect(described_class.to_h.keys).to match_array(%i[major minor patch pre])
- end
-
- it 'returns an Array' do
- expect(described_class.to_a).to be_a(Array)
- end
-end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
deleted file mode 100644
index a770c36d..00000000
--- a/spec/spec_helper.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-# frozen_string_literal: true
-
-# ensure test env
-ENV['RACK_ENV'] = 'test'
-
-# Third Party Libraries
-require 'rspec'
-require 'rspec/stubbed_env'
-require 'silent_stream'
-require 'addressable/uri'
-require 'rspec/pending_for'
-require 'rspec/block_is_expected'
-
-# Extensions
-require 'ext/backports'
-
-DEBUG = ENV['DEBUG'] == 'true'
-
-ruby_version = Gem::Version.new(RUBY_VERSION)
-minimum_version = ->(version, engine = 'ruby') { ruby_version >= Gem::Version.new(version) && RUBY_ENGINE == engine }
-actual_version = lambda do |major, minor|
- actual = Gem::Version.new(ruby_version)
- major == actual.segments[0] && minor == actual.segments[1] && RUBY_ENGINE == 'ruby'
-end
-debugging = minimum_version.call('2.7') && DEBUG
-RUN_COVERAGE = minimum_version.call('2.6') && (ENV['COVER_ALL'] || ENV['CI_CODECOV'] || ENV['CI'].nil?)
-ALL_FORMATTERS = actual_version.call(2, 7) && (ENV['COVER_ALL'] || ENV['CI_CODECOV'] || ENV['CI'])
-
-if DEBUG
- if debugging
- require 'byebug'
- elsif minimum_version.call('2.7', 'jruby')
- require 'pry-debugger-jruby'
- end
-end
-
-if RUN_COVERAGE
- require 'simplecov' # Config file `.simplecov` is run immediately when simplecov loads
- require 'codecov'
- require 'simplecov-json'
- require 'simplecov-lcov'
- require 'simplecov-cobertura'
- # This will override the formatter set in .simplecov
- if ALL_FORMATTERS
- SimpleCov::Formatter::LcovFormatter.config do |c|
- c.report_with_single_file = true
- c.single_report_path = 'coverage/lcov.info'
- end
-
- SimpleCov.formatters = [
- SimpleCov::Formatter::HTMLFormatter,
- SimpleCov::Formatter::CoberturaFormatter, # XML for Jenkins
- SimpleCov::Formatter::LcovFormatter,
- SimpleCov::Formatter::JSONFormatter, # For CodeClimate
- SimpleCov::Formatter::Codecov, # For CodeCov
- ]
- end
-end
-
-# This gem
-require 'oauth2'
-
-# Library Configs
-require 'config/multi_xml'
-require 'config/faraday'
-
-# RSpec Configs
-require 'config/rspec/rspec_core'
-require 'config/rspec/silent_stream'
-
-VERBS = %i[get post put delete patch].freeze