oauth2 1.4.11 → 2.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -1,356 +1,561 @@
1
- <p align="center">
2
- <a href="http://oauth.net/2/" target="_blank" rel="noopener">
3
- <img src="https://github.com/oauth-xx/oauth2/raw/main/docs/images/logo/oauth2-logo-124px.png?raw=true" alt="OAuth 2.0 Logo by Chris Messina, CC BY-SA 3.0">
4
- </a>
5
- <a href="https://www.ruby-lang.org/" target="_blank" rel="noopener">
6
- <img width="124px" src="https://github.com/oauth-xx/oauth2/raw/main/docs/images/logo/ruby-logo-198px.svg?raw=true" alt="Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5">
7
- </a>
8
- </p>
1
+ <a href="https://github.com/ruby-oauth"><img alt="ruby-oauth Logo by Aboling0, CC BY-SA 4.0" src="https://logos.galtzo.com/assets/images/ruby-oauth/avatar-128px.svg" width="14%" align="right"/></a>
9
2
 
10
- ## What
3
+ # 🔐 OAuth2
11
4
 
12
- OAuth 2.0 is the industry-standard protocol for authorization.
13
- OAuth 2.0 focuses on client developer simplicity while providing specific authorization flows for web applications,
14
- desktop applications, mobile phones, and living room devices.
15
- This is a RubyGem for implementing OAuth 2.0 clients and servers in Ruby applications.
16
- See the sibling `oauth` gem for OAuth 1.0 implementations in Ruby.
5
+ [![Version][👽versioni]][👽version] [![GitHub tag (latest SemVer)][⛳️tag-img]][⛳️tag] [![License: MIT][📄license-img]][📄license] [![Downloads Rank][👽dl-ranki]][👽dl-rank] [![CodeCov Test Coverage][🏀codecovi]][🏀codecov] [![Coveralls Test Coverage][🏀coveralls-img]][🏀coveralls] [![QLTY Test Coverage][🏀qlty-covi]][🏀qlty-cov] [![QLTY Maintainability][🏀qlty-mnti]][🏀qlty-mnt] [![CI Heads][🚎3-hd-wfi]][🚎3-hd-wf] [![CI Runtime Dependencies @ HEAD][🚎12-crh-wfi]][🚎12-crh-wf] [![CI Current][🚎11-c-wfi]][🚎11-c-wf] [![CI Truffle Ruby][🚎9-t-wfi]][🚎9-t-wf] [![CI JRuby][🚎10-j-wfi]][🚎10-j-wf] [![Deps Locked][🚎13-🔒️-wfi]][🚎13-🔒️-wf] [![Deps Unlocked][🚎14-🔓️-wfi]][🚎14-🔓️-wf] [![CI Test Coverage][🚎2-cov-wfi]][🚎2-cov-wf] [![CI Style][🚎5-st-wfi]][🚎5-st-wf] [![Apache SkyWalking Eyes License Compatibility Check][🚎15-🪪-wfi]][🚎15-🪪-wf]
17
6
 
18
- ⚠️⚠️⚠️ **_WARNING_**: You are viewing the `README` of the
19
- [supported-only-for-critical-enterprise-security-issues](#oauth2-for-enterprise) `1-4-stable`
20
- branch. Please do not use this, and instead upgrade to version 2! ⚠️⚠️⚠️
7
+ `if ci_badges.map(&:color).detect { it != "green"}` ☝️ [let me know][✉️discord-invite], as I may have missed the [discord notification][✉️discord-invite].
21
8
 
22
- No further releases of 1.x series are planned! [Version 2](https://gitlab.com/oauth-xx/oauth2/#what-is-new-for-v20) has *tons* of improvements!
9
+ ---
23
10
 
24
- If you must continue using 1.4.x please consider purchasing an open source security maintenance contract from [Tidelift][tidelift-ref].
11
+ `if ci_badges.map(&:color).all? { it == "green"}` 👇️ send money so I can do more of this. FLOSS maintenance is now my full-time job.
25
12
 
26
- ---
13
+ [![OpenCollective Backers][🖇osc-backers-i]][🖇osc-backers] [![OpenCollective Sponsors][🖇osc-sponsors-i]][🖇osc-sponsors] [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal] [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate at ko-fi.com][🖇kofi-img]][🖇kofi]
27
14
 
28
- * [OAuth 2.0 Spec][oauth2-spec]
29
- * [OAuth 1.0 sibling gem][sibling-gem]
15
+ <details markdown="1">
16
+ <summary>👣 How will this project approach the September 2025 hostile takeover of RubyGems? 🚑️</summary>
17
+
18
+ I've summarized my thoughts in [this blog post](https://dev.to/galtzo/hostile-takeover-of-rubygems-my-thoughts-5hlo).
30
19
 
31
- [oauth2-spec]: https://oauth.net/2/
32
- [sibling-gem]: https://gitlab.com/oauth-xx/oauth
33
-
34
- ## Release Documentation
35
-
36
- <details>
37
- <summary>1.4.x Readmes</summary>
38
-
39
- | Version | Release Date | Readme |
40
- |---------|--------------|-------------------------------------------------------------|
41
- | 1.4.11 | Sep 16, 2022 | https://gitlab.com/oauth-xx/oauth2/-/blob/v1.4.11/README.md |
42
- | 1.4.10 | Jul 1, 2022 | https://gitlab.com/oauth-xx/oauth2/-/blob/v1.4.10/README.md |
43
- | 1.4.9 | Feb 20, 2022 | https://gitlab.com/oauth-xx/oauth2/-/blob/v1.4.9/README.md |
44
- | 1.4.8 | Feb 18, 2022 | https://gitlab.com/oauth-xx/oauth2/-/blob/v1.4.8/README.md |
45
- | 1.4.7 | Mar 19, 2021 | https://gitlab.com/oauth-xx/oauth2/-/blob/v1.4.7/README.md |
46
- | 1.4.6 | Mar 19, 2021 | https://gitlab.com/oauth-xx/oauth2/-/blob/v1.4.6/README.md |
47
- | 1.4.5 | Mar 18, 2021 | https://gitlab.com/oauth-xx/oauth2/-/blob/v1.4.5/README.md |
48
- | 1.4.4 | Feb 12, 2020 | https://gitlab.com/oauth-xx/oauth2/-/blob/v1.4.4/README.md |
49
- | 1.4.3 | Jan 29, 2020 | https://gitlab.com/oauth-xx/oauth2/-/blob/v1.4.3/README.md |
50
- | 1.4.2 | Oct 1, 2019 | https://gitlab.com/oauth-xx/oauth2/-/blob/v1.4.2/README.md |
51
- | 1.4.1 | Oct 13, 2018 | https://gitlab.com/oauth-xx/oauth2/-/blob/v1.4.1/README.md |
52
- | 1.4.0 | Jun 9, 2017 | https://gitlab.com/oauth-xx/oauth2/-/blob/v1.4.0/README.md |
53
20
  </details>
54
21
 
55
- <details>
56
- <summary>1.3.x Readmes</summary>
22
+ ## 🌻 Synopsis <a href="https://discord.gg/3qme4XHNKN"><img alt="Galtzo FLOSS Logo by Aboling0, CC BY-SA 4.0" src="https://logos.galtzo.com/assets/images/galtzo-floss/avatar-128px.svg" width="8%" align="right"/></a> <a href="https://ruby-toolbox.com"><img alt="ruby-lang Logo, Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5" src="https://logos.galtzo.com/assets/images/ruby-lang/avatar-128px.svg" width="8%" align="right"/></a>
23
+
24
+ OAuth 2.0 is the industry-standard protocol for authorization.
25
+ This is a RubyGem for implementing OAuth 2.0 clients (not servers) in Ruby applications.
26
+
27
+ ⭐️ including OAuth 2.1 draft spec & OpenID Connect (OIDC)
28
+
29
+ ### Quick Examples
30
+
31
+ <details markdown="1">
32
+ <summary>Convert the following `curl` command into a token request using this gem...</summary>
33
+
34
+ ```shell
35
+ curl --request POST \
36
+ --url 'https://login.microsoftonline.com/REDMOND_REDACTED/oauth2/token' \
37
+ --header 'content-type: application/x-www-form-urlencoded' \
38
+ --data grant_type=client_credentials \
39
+ --data client_id=REDMOND_CLIENT_ID \
40
+ --data client_secret=REDMOND_CLIENT_SECRET \
41
+ --data resource=REDMOND_RESOURCE_UUID
42
+ ```
43
+
44
+ NOTE: In the ruby version below, certain params are passed to the `get_token` call, instead of the client creation.
45
+
46
+ ```ruby
47
+ client = OAuth2::Client.new(
48
+ "REDMOND_CLIENT_ID", # client_id
49
+ "REDMOND_CLIENT_SECRET", # client_secret
50
+ auth_scheme: :request_body, # Other modes are supported: :basic_auth, :tls_client_auth, :private_key_jwt
51
+ token_url: "oauth2/token", # relative path, except with leading `/`, then absolute path
52
+ site: "https://login.microsoftonline.com/REDMOND_REDACTED"
53
+ )
54
+ client.
55
+ client_credentials. # There are many other types to choose from!
56
+ get_token(resource: "REDMOND_RESOURCE_UUID")
57
+ ```
58
+
59
+ NOTE: `header` - The content type specified in the `curl` is already the default!
57
60
 
58
- | Version | Release Date | Readme |
59
- |----------|--------------|----------------------------------------------------------|
60
- | 1.3.1 | Mar 3, 2017 | https://gitlab.com/oauth-xx/oauth2/-/blob/v1.3.1/README.md |
61
- | 1.3.0 | Dec 27, 2016 | https://gitlab.com/oauth-xx/oauth2/-/blob/v1.3.0/README.md |
62
61
  </details>
63
62
 
64
- <details>
65
- <summary>&le;= 1.2.x Readmes (2016 and before)</summary>
63
+ <details markdown="1">
64
+ <summary>Complete E2E single file script against mock-oauth2-server</summary>
65
+
66
+ - E2E example uses [navikt/mock-oauth2-server](https://github.com/navikt/mock-oauth2-server), which was added in v2.0.11
67
+ - E2E example does not ship with the released gem, so clone the source to play with it.
68
+
69
+ ```console
70
+ docker compose -f docker-compose-ssl.yml up -d --wait
71
+ ruby examples/e2e.rb
72
+ # If your machine is slow or Docker pulls are cold, increase the wait:
73
+ E2E_WAIT_TIMEOUT=120 ruby examples/e2e.rb
74
+ # The mock server serves HTTP on 8080; the example points to http://localhost:8080 by default.
75
+ ```
76
+
77
+ The output should be something like this:
78
+
79
+ ```console
80
+ ➜ ruby examples/e2e.rb
81
+ Access token (truncated): eyJraWQiOiJkZWZhdWx0...
82
+ userinfo status: 200
83
+ userinfo body: {"sub" => "demo-sub", "aud" => ["demo-aud"], "nbf" => 1757816758000, "iss" => "http://localhost:8080/default", "exp" => 1757820358000, "iat" => 1757816758000, "jti" => "d63b97a7-ebe5-4dea-93e6-d542caba6104"}
84
+ E2E complete
85
+ ```
86
+
87
+ Make sure to shut down the mock server when you are done:
88
+
89
+ ```console
90
+ docker compose -f docker-compose-ssl.yml down
91
+ ```
92
+
93
+ Troubleshooting: validate connectivity to the mock server
94
+
95
+ - Check container status and port mapping:
96
+ - `docker compose -f docker-compose-ssl.yml ps`
97
+ - From the host, try the discovery URL directly (this is what the example uses by default):
98
+ - `curl -v http://localhost:8080/default/.well-known/openid-configuration`
99
+ - If that fails immediately, also try: `curl -v --connect-timeout 2 http://127.0.0.1:8080/default/.well-known/openid-configuration`
100
+ - From inside the container (to distinguish container vs. host networking):
101
+ - `docker exec -it oauth2-mock-oauth2-server-1 curl -v http://127.0.0.1:8080/default/.well-known/openid-configuration`
102
+ - Simple TCP probe from the host:
103
+ - `nc -vz localhost 8080 # or: ruby -rsocket -e 'TCPSocket.new("localhost",8080).close; puts "tcp ok"'`
104
+ - Inspect which host port 8080 is bound to (should be 8080):
105
+ - `docker inspect -f '{{ (index (index .NetworkSettings.Ports "8080/tcp") 0).HostPort }}' oauth2-mock-oauth2-server-1`
106
+ - Look at server logs for readiness/errors:
107
+ - `docker logs -n 200 oauth2-mock-oauth2-server-1`
108
+ - On Linux, ensure nothing else is bound to 8080 and that firewall/SELinux aren’t blocking:
109
+ - `ss -ltnp | grep :8080`
110
+
111
+ Notes
112
+
113
+ - Discovery URL pattern is: `http://localhost:8080/<realm>/.well-known/openid-configuration`, where `<realm>` defaults to `default`.
114
+ - You can change these with env vars when running the example:
115
+ - `E2E_ISSUER_BASE` (default: http://localhost:8080)
116
+ - `E2E_REALM` (default: default)
66
117
 
67
- | Version | Release Date | Readme |
68
- |----------|--------------|----------------------------------------------------------|
69
- | 1.2.0 | Jun 30, 2016 | https://gitlab.com/oauth-xx/oauth2/-/blob/v1.2.0/README.md |
70
- | 1.1.0 | Jan 30, 2016 | https://gitlab.com/oauth-xx/oauth2/-/blob/v1.1.0/README.md |
71
- | 1.0.0 | May 23, 2014 | https://gitlab.com/oauth-xx/oauth2/-/blob/v1.0.0/README.md |
72
- | < 1.0.0 | Find here | https://gitlab.com/oauth-xx/oauth2/-/tags |
73
118
  </details>
74
119
 
75
- ## Status
76
-
77
- <!--
78
- Numbering rows and badges in each row as a visual "database" lookup,
79
- as the table is extremely dense, and it can be very difficult to find anything
80
- Putting one on each row here, to document the emoji that should be used, and for ease of copy/paste.
81
-
82
- row #s:
83
- 1️⃣
84
- 2️⃣
85
- 3️⃣
86
- 4️⃣
87
- 5️⃣
88
- 6️⃣
89
- 7️⃣
90
-
91
- badge #s:
92
- ⛳️
93
- 🖇
94
- 🏘
95
- 🚎
96
- 🖐
97
- 🧮
98
- 📗
99
-
100
- appended indicators:
101
- ♻️ - URL needs to be updated from SASS integration. Find / Replace is insufficient.
102
- -->
103
-
104
- | | Project | bundle add oauth2 |
105
- |:----|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
106
- | 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] |
107
- | 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] |
108
- | 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] |
109
- | 4️⃣ | testing | [![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] |
110
- | 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] |
111
- | 6️⃣ | resources | [![Discussion][⛳gg-discussions-img]][⛳gg-discussions] [![Get help on Codementor][🖇codementor-img]][🖇codementor] [![Chat][🏘chat-img]][🏘chat] [![Blog][🚎blog-img]][🚎blog] [![Blog][🖐wiki-img]][🖐wiki] |
112
- | 7️⃣ | spread 💖 | [![Liberapay Patrons][⛳liberapay-img]][⛳liberapay] [![Sponsor Me][🖇sponsor-img]][🖇sponsor] [![Tweet @ Peter][🏘tweet-img]][🏘tweet] [🌏][aboutme] [👼][angelme] [💻][coderme] |
113
-
114
- <!--
115
- The link tokens in the following sections should be kept ordered by the row and badge numbering scheme
116
- -->
117
-
118
- <!-- 1️⃣ name, license, docs -->
119
- [⛳️gem]: https://rubygems.org/gems/oauth2
120
- [⛳️name-img]: https://img.shields.io/badge/name-oauth2-brightgreen.svg?style=flat
121
- [🖇src-license]: https://opensource.org/licenses/MIT
122
- [🖇src-license-img]: https://img.shields.io/badge/License-MIT-green.svg
123
- [🏘fossa]: https://app.fossa.io/projects/git%2Bgithub.com%2Foauth-xx%2Foauth2?ref=badge_shield
124
- [🏘fossa-img]: https://app.fossa.io/api/projects/git%2Bgithub.com%2Foauth-xx%2Foauth2.svg?type=shield
125
- [🚎yard]: https://www.rubydoc.info/github/oauth-xx/oauth2
126
- [🚎yard-img]: https://img.shields.io/badge/documentation-rubydoc-brightgreen.svg?style=flat
127
- [🖐inch-ci-img]: http://inch-ci.org/github/oauth-xx/oauth2.png
128
-
129
- <!-- 2️⃣ version & activity -->
130
- [⛳️version-img]: http://img.shields.io/gem/v/oauth2.svg
131
- [🖇DL-total-img]: https://img.shields.io/gem/dt/oauth2.svg
132
- [🏘DL-rank-img]: https://img.shields.io/gem/rt/oauth2.svg
133
- [🚎src-home]: https://gitlab.com/oauth-xx/oauth2/
134
- [🚎src-home-img]: https://img.shields.io/badge/source-gitlab-blue.svg?style=flat
135
-
136
- <!-- 3️⃣ maintenance & linting -->
137
- [⛳cclim-maint]: https://codeclimate.com/github/oauth-xx/oauth2/maintainability
138
- [⛳cclim-maint-img♻️]: https://api.codeclimate.com/v1/badges/688c612528ff90a46955/maintainability
139
- [🖇triage-help]: https://www.codetriage.com/oauth-xx/oauth2
140
- [🖇triage-help-img]: https://www.codetriage.com/oauth-xx/oauth2/badges/users.svg
141
- [🏘depfu♻️]: https://depfu.com/github/oauth-xx/oauth2?project_id=4445
142
- [🏘depfu-img♻️]: https://badges.depfu.com/badges/6d34dc1ba682bbdf9ae2a97848241743/count.svg
143
- [🚎contributors]: https://gitlab.com/oauth-xx/oauth2/-/graphs/main
144
- [🚎contributors-img]: https://img.shields.io/github/contributors-anon/oauth-xx/oauth2
145
- [🖐style-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/style.yml
146
- [🖐style-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/style.yml/badge.svg
147
- [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
148
- [🧮kloc-img]: https://img.shields.io/tokei/lines/github.com/oauth-xx/oauth2
149
-
150
- <!-- 4️⃣ testing -->
151
- [🏘sup-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/supported.yml
152
- [🏘sup-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/supported.yml/badge.svg
153
- [🚎heads-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/heads.yml
154
- [🚎heads-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/heads.yml/badge.svg
155
- [🖐uns-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/unsupported.yml
156
- [🖐uns-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/unsupported.yml/badge.svg
157
- [🧮mac-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/macos.yml
158
- [🧮mac-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/macos.yml/badge.svg
159
- [📗win-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/windows.yml
160
- [📗win-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/windows.yml/badge.svg
161
-
162
- <!-- 5️⃣ coverage & security -->
163
- [⛳cclim-cov]: https://codeclimate.com/github/oauth-xx/oauth2/test_coverage
164
- [⛳cclim-cov-img♻️]: https://api.codeclimate.com/v1/badges/688c612528ff90a46955/test_coverage
165
- [🖇codecov-img♻️]: https://codecov.io/gh/oauth-xx/oauth2/branch/1-4-stable/graph/badge.svg?token=bNqSzNiuo2
166
- [🖇codecov]: https://codecov.io/gh/oauth-xx/oauth2
167
- [🏘coveralls]: https://coveralls.io/github/oauth-xx/oauth2?branch=1-4-stable
168
- [🏘coveralls-img]: https://coveralls.io/repos/github/oauth-xx/oauth2/badge.svg?branch=1-4-stable
169
- [🚎sec-pol]: https://gitlab.com/oauth-xx/oauth2/-/blob/main/SECURITY.md
170
- [🚎sec-pol-img]: https://img.shields.io/badge/security-policy-brightgreen.svg?style=flat
171
- [🖐codeQL]: https://github.com/oauth-xx/oauth2/security/code-scanning
172
- [🖐codeQL-img]: https://github.com/oauth-xx/oauth2/actions/workflows/codeql-analysis.yml/badge.svg
173
- [🧮cov-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/coverage.yml
174
- [🧮cov-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/coverage.yml/badge.svg
175
-
176
- <!-- 6️⃣ resources -->
177
- [⛳gg-discussions]: https://groups.google.com/g/oauth-ruby
178
- [⛳gg-discussions-img]: https://img.shields.io/badge/google-group-purple.svg?style=flat
179
- [🖇codementor]: https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github
180
- [🖇codementor-img]: https://cdn.codementor.io/badges/get_help_github.svg
181
- [🏘chat]: https://gitter.im/oauth-xx/oauth2
182
- [🏘chat-img]: https://img.shields.io/gitter/room/oauth-xx/oauth2.svg
183
- [🚎blog]: http://www.railsbling.com/tags/oauth2/
184
- [🚎blog-img]: https://img.shields.io/badge/blog-railsbling-brightgreen.svg?style=flat
185
- [🖐wiki]: https://gitlab.com/oauth-xx/oauth2/-/wikis/home
186
- [🖐wiki-img]: https://img.shields.io/badge/wiki-examples-brightgreen.svg?style=flat
187
-
188
- <!-- 7️⃣ spread 💖 -->
189
- [⛳liberapay-img]: https://img.shields.io/liberapay/patrons/pboling.svg?logo=liberapay
190
- [⛳liberapay]: https://liberapay.com/pboling/donate
191
- [🖇sponsor-img]: https://img.shields.io/badge/sponsor-pboling.svg?style=social&logo=github
192
- [🖇sponsor]: https://github.com/sponsors/pboling
193
- [🏘tweet-img]: https://img.shields.io/twitter/follow/galtzo.svg?style=social&label=Follow
194
- [🏘tweet]: http://twitter.com/galtzo
120
+ ### Alternatives
195
121
 
196
- <!-- Maintainer Contact Links -->
197
- [railsbling]: http://www.railsbling.com
198
- [peterboling]: http://www.peterboling.com
199
- [aboutme]: https://about.me/peter.boling
200
- [angelme]: https://angel.co/peter-boling
201
- [coderme]:http://coderwall.com/pboling
122
+ This gem is a low-level OAuth 2.0 **client** (it talks _to_ an authorization server to obtain and use tokens).
123
+ If that isn't quite what you need, one of the following libraries may be a better fit (the first row is this gem for comparison):
202
124
 
203
- ## Installation
125
+ | Library | Role | When to use it |
126
+ |-------------------------------------------------------------------|------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
127
+ | **this gem** ([oauth2][📜src-gh]) | OAuth 2.0 / 2.1 + OIDC **client** | You are calling an OAuth 2.0 API, or signing in against a provider, and want a small, dependency-light, spec-faithful client with fine-grained control over the request/response cycle. |
128
+ | [oauth2-mcp][sibling-mcp-gem] | OAuth 2.1 MCP Auth client **and** server | Focused, spec-faithful, MCP Auth client with fine-grained control over the request/response cycle. Rack/Roda compatible clients and servers. Built on top of this gem, [oauth2][📜src-gh]. |
129
+ | [omniauth][omniauth-gem] + [omniauth-oauth2][omniauth-oauth2-gem] | "Log in with…" **client** (Rack) | You primarily want user _authentication_ ("Log in with GitHub/GitLab/Google") wired into a Rack/Rails app via a strategy, rather than driving the token flow yourself. Built on top of this gem, [oauth2][📜src-gh]. |
130
+ | [openid_connect][openid-connect-gem] | OpenID Connect client & server | You need full [OpenID Connect][oidc-spec] (ID-token validation, discovery, userinfo, etc.) with batteries included. Maintained by [@nov][nov]. |
131
+ | [rack-oauth2][rack-oauth2-gem] | OAuth 2.0 client **and** server | You want lower-level Rack primitives, need both client and server pieces, or are building on top of `openid_connect`. Maintained by [@nov][nov]. |
132
+ | [doorkeeper][doorkeeper-gem] | OAuth 2.0 **server / provider** | You want to _be_ the authorization server — issuing tokens to other apps — in a Rails/Grape/Sinatra application, rather than acting as a client. |
133
+ | [oauth][sibling-gem] | OAuth **1.0a** client & server | The provider you integrate with only speaks the older OAuth 1.0a protocol. This is our sibling gem. |
204
134
 
205
- Install the gem and add to the application's Gemfile by executing:
135
+ See also the [OAuth 2.0 Spec][oauth2-spec], the [OpenID Connect Spec][oidc-spec], and the [MCP Auth Spec][mcp-auth-spec].
206
136
 
207
- $ bundle add oauth2
137
+ [oauth2-spec]: https://oauth.net/2/
138
+ [oidc-spec]: https://openid.net/specs/openid-connect-core-1_0.html
139
+ [mcp-auth-spec]: https://modelcontextprotocol.io/specification/draft/basic/authorization
140
+ [sibling-gem]: https://gitlab.com/ruby-oauth/oauth
141
+ [sibling-mcp-gem]: https://github.com/ruby-oauth/oauth2-mcp
142
+ [doorkeeper-gem]: https://github.com/doorkeeper-gem/doorkeeper
143
+ [omniauth-gem]: https://github.com/omniauth/omniauth
144
+ [omniauth-oauth2-gem]: https://github.com/omniauth/omniauth-oauth2
145
+ [openid-connect-gem]: https://github.com/nov/openid_connect
146
+ [rack-oauth2-gem]: https://github.com/nov/rack-oauth2
147
+ [nov]: https://github.com/nov
148
+
149
+ ## 💡 Info you can shake a stick at
150
+
151
+ | Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] |
152
+ |-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
153
+ | Works with JRuby | [![JRuby 9.2 Compat][💎jruby-9.2i]][🚎jruby-9.2-wf] [![JRuby 9.3 Compat][💎jruby-9.3i]][🚎jruby-9.3-wf] <br/> [![JRuby 9.4 Compat][💎jruby-9.4i]][🚎jruby-9.4-wf] [![JRuby 10.0 Compat][💎jruby-10.0i]][🚎jruby-10.0-wf] [![JRuby current Compat][💎jruby-c-i]][🚎10-j-wf] [![JRuby HEAD Compat][💎jruby-headi]][🚎3-hd-wf]|
154
+ | Works with Truffle Ruby | [![Truffle Ruby 22.3 Compat][💎truby-22.3i]][🚎truby-22.3-wf] [![Truffle Ruby 23.0 Compat][💎truby-23.0i]][🚎truby-23.0-wf] [![Truffle Ruby 23.1 Compat][💎truby-23.1i]][🚎truby-23.1-wf] <br/> [![Truffle Ruby 24.2 Compat][💎truby-24.2i]][🚎truby-24.2-wf] [![Truffle Ruby 25.0 Compat][💎truby-25.0i]][🚎truby-25.0-wf] [![Truffle Ruby 33.0 Compat][💎truby-33.0i]][🚎truby-33.0-wf] [![Truffle Ruby current Compat][💎truby-c-i]][🚎9-t-wf] [![Truffle Ruby HEAD Compat][💎truby-headi]][🚎3-hd-wf]|
155
+ | Works with MRI Ruby 4 | [![Ruby current Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf]|
156
+ | Works with MRI Ruby 3 | [![Ruby 3.0 Compat][💎ruby-3.0i]][🚎ruby-3.0-wf] [![Ruby 3.1 Compat][💎ruby-3.1i]][🚎ruby-3.1-wf] [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎ruby-3.2-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎ruby-3.3-wf] [![Ruby 3.4 Compat][💎ruby-3.4i]][🚎ruby-3.4-wf]|
157
+ | Works with MRI Ruby 2 | ![Ruby 2.2 Compat][💎ruby-2.2i] ![Ruby 2.3 Compat][💎ruby-2.3i] <br/> [![Ruby 2.4 Compat][💎ruby-2.4i]][🚎ruby-2.4-wf] [![Ruby 2.5 Compat][💎ruby-2.5i]][🚎ruby-2.5-wf] [![Ruby 2.6 Compat][💎ruby-2.6i]][🚎ruby-2.6-wf] [![Ruby 2.7 Compat][💎ruby-2.7i]][🚎ruby-2.7-wf]|
158
+ | Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork] [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor] |
159
+ | Source | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on CodeBerg.org][📜src-cb-img]][📜src-cb] [![Source on Github.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc] |
160
+ | Documentation | [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![GitLab Wiki][📜gl-wiki-img]][📜gl-wiki] [![GitHub Wiki][📜gh-wiki-img]][📜gh-wiki] |
161
+ | Compliance | [![License: MIT][📄license-img]][📄license] [![Apache license compatibility: Category A][📄license-compat-img]][📄license-compat] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] |
162
+ | Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] [![Compatibility appraised by: appraisal2][💎appraisal2-img]][💎appraisal2] |
163
+ | Maintainer 🎖️ | [![Follow Me on LinkedIn][💖🖇linkedin-img]][💖🖇linkedin] [![Follow Me on Ruby.Social][💖🐘ruby-mast-img]][💖🐘ruby-mast] [![Follow Me on Bluesky][💖🦋bluesky-img]][💖🦋bluesky] [![Contact Maintainer][🚂maint-contact-img]][🚂maint-contact] [![My technical writing][💖💁🏼‍♂️devto-img]][💖💁🏼‍♂️devto] |
164
+ | `...` 💖 | [![Find Me on WellFound:][💖✌️wellfound-img]][💖✌️wellfound] [![Find Me on CrunchBase][💖💲crunchbase-img]][💖💲crunchbase] [![My LinkTree][💖🌳linktree-img]][💖🌳linktree] [![More About Me][💖💁🏼‍♂️aboutme-img]][💖💁🏼‍♂️aboutme] [🧊][💖🧊berg] [🐙][💖🐙hub] [🛖][💖🛖hut] [🧪][💖🧪lab] |
165
+
166
+ ### Compatibility
167
+
168
+ Compatible with MRI Ruby 2.2.0+, and concordant releases of JRuby, and TruffleRuby.
169
+ CI workflows and Appraisals are generated for MRI Ruby 2.4+.
170
+ This test floor is configured by `ruby.test_minimum` in `.kettle-jem.yml` and
171
+ may be higher than the gem's runtime compatibility floor when legacy Rubies are
172
+ not practical for the current toolchain.
173
+
174
+ <a href="https://github.com/kettle-dev"><img alt="kettle-dev Logo by Aboling0, CC BY-SA 4.0" src="https://logos.galtzo.com/assets/images/kettle-dev/avatar-128px.svg" width="14%" align="right"/></a>
175
+
176
+ The _amazing_ test matrix is powered by the kettle-dev stack.
177
+
178
+ <details markdown="1">
179
+ <summary>How kettle-dev manages complexity in tests</summary>
180
+
181
+ | Gem | Source | Role | Daily download rank |
182
+ |-----|--------|------|---------------------|
183
+ | [appraisal2](https://bestgems.org/gems/appraisal2) | [GitHub](https://github.com/appraisal-rb/appraisal2) | multi-dependency Appraisal matrix generation | [![Daily download rank for appraisal2](https://img.shields.io/gem/rd/appraisal2.svg?style=flat-square)](https://bestgems.org/gems/appraisal2) |
184
+ | [appraisal2-rubocop](https://bestgems.org/gems/appraisal2-rubocop) | [GitHub](https://github.com/appraisal-rb/appraisal2-rubocop) | RuboCop Appraisal generator integration | [![Daily download rank for appraisal2-rubocop](https://img.shields.io/gem/rd/appraisal2-rubocop.svg?style=flat-square)](https://bestgems.org/gems/appraisal2-rubocop) |
185
+ | [kettle-dev](https://bestgems.org/gems/kettle-dev) | [GitHub](https://github.com/kettle-dev/kettle-dev) | development, release, and CI workflow tooling | [![Daily download rank for kettle-dev](https://img.shields.io/gem/rd/kettle-dev.svg?style=flat-square)](https://bestgems.org/gems/kettle-dev) |
186
+ | [kettle-jem](https://bestgems.org/gems/kettle-jem) | [GitHub](https://github.com/kettle-dev/kettle-jem) | Appraisals & CI workflow templates | [![Daily download rank for kettle-jem](https://img.shields.io/gem/rd/kettle-jem.svg?style=flat-square)](https://bestgems.org/gems/kettle-jem) |
187
+ | [kettle-soup-cover](https://bestgems.org/gems/kettle-soup-cover) | [GitHub](https://github.com/kettle-dev/kettle-soup-cover) | SimpleCov coverage policy and reporting | [![Daily download rank for kettle-soup-cover](https://img.shields.io/gem/rd/kettle-soup-cover.svg?style=flat-square)](https://bestgems.org/gems/kettle-soup-cover) |
188
+ | [kettle-test](https://bestgems.org/gems/kettle-test) | [GitHub](https://github.com/kettle-dev/kettle-test) | standard test runner and coverage harness | [![Daily download rank for kettle-test](https://img.shields.io/gem/rd/kettle-test.svg?style=flat-square)](https://bestgems.org/gems/kettle-test) |
189
+ | [rubocop-lts](https://bestgems.org/gems/rubocop-lts) | [GitHub](https://github.com/rubocop-lts/rubocop-lts) | Ruby-version-aware linting | [![Daily download rank for rubocop-lts](https://img.shields.io/gem/rd/rubocop-lts.svg?style=flat-square)](https://bestgems.org/gems/rubocop-lts) |
190
+ | [turbo_tests2](https://bestgems.org/gems/turbo_tests2) | [GitHub](https://github.com/galtzo-floss/turbo_tests2) | parallel test execution | [![Daily download rank for turbo_tests2](https://img.shields.io/gem/rd/turbo_tests2.svg?style=flat-square)](https://bestgems.org/gems/turbo_tests2) |
208
191
 
209
- If bundler is not being used to manage dependencies, install the gem by executing:
192
+ </details>
210
193
 
211
- $ gem install oauth2
194
+ ### Federated DVCS
212
195
 
213
- ## OAuth2 for Enterprise
196
+ <details markdown="1">
197
+ <summary>Find this repo on federated forges (Coming soon!)</summary>
214
198
 
215
- Available as part of the Tidelift Subscription.
199
+ | Federated [DVCS][💎d-in-dvcs] Repository | Status | Issues | PRs | Wiki | CI | Discussions |
200
+ |-------------------------------------------------|-----------------------------------------------------------------------|---------------------------|--------------------------|---------------------------|--------------------------|------------------------------|
201
+ | 🧪 [ruby-oauth/oauth2 on GitLab][📜src-gl] | The Truth | [💚][🤝gl-issues] | [💚][🤝gl-pulls] | [💚][📜gl-wiki] | 🐭 Tiny Matrix | ➖ |
202
+ | 🧊 [ruby-oauth/oauth2 on CodeBerg][📜src-cb] | An Ethical Mirror ([Donate][🤝cb-donate]) | [💚][🤝cb-issues] | [💚][🤝cb-pulls] | ➖ | ⭕️ No Matrix | ➖ |
203
+ | 🐙 [ruby-oauth/oauth2 on GitHub][📜src-gh] | Another Mirror | [💚][🤝gh-issues] | [💚][🤝gh-pulls] | [💚][📜gh-wiki] | 💯 Full Matrix | [💚][gh-discussions] |
204
+ | 🎮️ [Discord Server][✉️discord-invite] | [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] | [Let's][✉️discord-invite] | [talk][✉️discord-invite] | [about][✉️discord-invite] | [this][✉️discord-invite] | [library!][✉️discord-invite] |
216
205
 
217
- 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]
218
-
219
- [tidelift-ref]: https://tidelift.com/subscription/pkg/rubygems-oauth2?utm_source=rubygems-oauth2&utm_medium=referral&utm_campaign=enterprise
220
-
221
- ## Security contact information
222
-
223
- To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security).
224
- Tidelift will coordinate the fix and disclosure.
225
-
226
- For more see [SECURITY.md][🚎sec-pol].
227
-
228
- ## Why should you upgrade to version v2.0?
229
-
230
- - Officially support Ruby versions >= 2.7
231
- - Unofficially support Ruby versions >= 2.5
232
- - Incidentally support Ruby versions >= 2.2
233
- - Drop support for the expired MAC Draft (all versions)
234
- - Support IETF rfc7523 JWT Bearer Tokens
235
- - Support IETF rfc7231 Relative Location in Redirect
236
- - Support IETF rfc6749 Don't set oauth params when nil
237
- - 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)
238
- - 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`
239
- - Adds new option to `OAuth2::Client#get_token`:
240
- - `:access_token_class` (`AccessToken`); user specified class to use for all calls to `get_token`
241
- - Adds new option to `OAuth2::AccessToken#initialize`:
242
- - `:expires_latency` (`nil`); number of seconds by which AccessToken validity will be reduced to offset latency
243
- - By default, keys are transformed to camel case.
244
- - Original keys will still work as previously, in most scenarios, thanks to `rash_alt` gem.
245
- - However, this is a _breaking_ change if you rely on `response.parsed.to_h`, as the keys in the result will be camel case.
246
- - As of version 2.0.4 you can turn key transformation off with the `snaky: false` option.
247
- - By default, the `:auth_scheme` is now `:basic_auth` (instead of `:request_body`)
248
- - Third-party strategies and gems may need to be updated if a provider was requiring client id/secret in the request body
249
- - [... A lot more](https://gitlab.com/oauth-xx/oauth2/-/blob/main/CHANGELOG.md#2.0.0)
250
-
251
- ## Compatibility
252
-
253
- Targeted ruby compatibility is 2.7, 3.0 and 3.1. Compatibility is further distinguished by
254
- supported and unsupported versions of Ruby.
255
- This gem will work with Ruby versions back to 1.9, though it remains unsupported.
256
- Ruby is limited to 1.9+ in the gemspec for the 1.4.x series and is be 2.2+ for next major version releases (see `master` branch).
257
-
258
- <details>
259
- <summary>Ruby Engine Compatibility Policy</summary>
260
-
261
- This gem is tested against MRI, JRuby, and Truffleruby.
262
- Each of those has varying versions that target a specific version of MRI Ruby.
263
- This gem should work in the just-listed Ruby engines according to the targeted MRI compatibility in the table below.
264
- If you would like to add support for additional engines,
265
- see `gemfiles/README.md`, then submit a PR to the correct maintenance branch as according to the table below.
266
206
  </details>
267
207
 
268
- <details>
269
- <summary>Ruby Version Compatibility Policy</summary>
208
+ [gh-discussions]: https://github.com/ruby-oauth/oauth2/discussions
209
+
210
+ ### Enterprise Support [![Tidelift](https://tidelift.com/badges/package/rubygems/oauth2)](https://tidelift.com/subscription/pkg/rubygems-oauth2?utm_source=rubygems-oauth2&utm_medium=referral&utm_campaign=readme)
211
+
212
+ Available as part of the Tidelift Subscription.
213
+
214
+ <details markdown="1">
215
+ <summary>Need enterprise-level guarantees?</summary>
216
+
217
+ The maintainers of this 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.
270
218
 
271
- If something doesn't work on one of these interpreters, it's a bug.
219
+ [![Get help from me on Tidelift][🏙️entsup-tidelift-img]][🏙️entsup-tidelift]
272
220
 
273
- This library may inadvertently work (or seem to work) on other Ruby
274
- implementations, however support will only be provided for the versions listed
275
- above.
221
+ - 💡Subscribe for support guarantees covering _all_ your FLOSS dependencies
222
+ - 💡Tidelift is part of [Sonar][🏙️entsup-tidelift-sonar]
223
+ - 💡Tidelift pays maintainers to maintain the software you depend on!<br/>📊`@`Pointy Haired Boss: An [enterprise support][🏙️entsup-tidelift] subscription is "[never gonna let you down][🧮kloc]", and *supports* open source maintainers
224
+
225
+ Alternatively:
226
+
227
+ - [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite]
228
+ - [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork]
229
+ - [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor]
276
230
 
277
- If you would like this library to support another Ruby version, you may
278
- volunteer to be a maintainer. Being a maintainer entails making sure all tests
279
- run and pass on that implementation. When something breaks on your
280
- implementation, you will be responsible for providing patches in a timely
281
- fashion. If critical issues for a particular implementation exist at the time
282
- of a major release, support for that Ruby version may be dropped.
283
231
  </details>
284
232
 
285
- | | Ruby OAuth2 Version | Maintenance Branch | Supported Officially | Supported Unofficially | Supported Incidentally |
286
- |:----|---------------------|--------------------|-------------------------|------------------------|------------------------|
287
- | 1️⃣ | 2.0.x | `main` | 2.7, 3.0, 3.1 | 2.5, 2.6 | 2.2, 2.3, 2.4 |
288
- | 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 |
289
- | 3️⃣ | older | N/A | Best of luck to you! | Please upgrade! | |
233
+ ## ✨ Installation
290
234
 
291
- NOTE: The 1.4 series will only receive critical security updates.
292
- See [SECURITY.md][🚎sec-pol]
235
+ Install the gem and add to the application's Gemfile by executing:
293
236
 
294
- ## Usage Examples
237
+ ```console
238
+ bundle add oauth2
239
+ ```
240
+
241
+ If bundler is not being used to manage dependencies, install the gem by executing:
242
+
243
+ ```console
244
+ gem install oauth2
245
+ ```
246
+
247
+ ## ⚙️ Configuration
248
+
249
+ Global settings for the library:
250
+
251
+ ```ruby
252
+ OAuth2.configure do |config|
253
+ config.silence_extra_tokens_warning = false # default: true
254
+ config.silence_no_tokens_warning = false # default: true
255
+ end
256
+ ```
257
+
258
+ Filtering-related settings:
295
259
 
296
260
  ```ruby
297
- require 'oauth2'
298
- client = OAuth2::Client.new('client_id', 'client_secret', :site => 'https://example.org')
261
+ OAuth2.configure do |config|
262
+ config.filtered_label = "[REDACTED]" # default: "[FILTERED]"
263
+ config.filtered_debug_keys += ["client_assertion"]
264
+ end
265
+ ```
266
+
267
+ - `filtered_label` controls the placeholder used when sensitive values are filtered from inspected objects and debug logging output.
268
+ - `filtered_debug_keys` controls which key names have their values redacted from debug logging output when `OAUTH_DEBUG=true`.
269
+ - Debug logging remains opt-in and should still be used cautiously in production environments.
270
+
271
+ ## 🔧 Basic Usage
299
272
 
300
- client.auth_code.authorize_url(:redirect_uri => 'http://localhost:8080/oauth2/callback')
301
- # => "https://example.org/oauth/authorization?response_type=code&client_id=client_id&redirect_uri=http://localhost:8080/oauth2/callback"
273
+ ### Client Initialization Options
302
274
 
303
- token = client.auth_code.get_token('authorization_code_value', :redirect_uri => 'http://localhost:8080/oauth2/callback', :headers => {'Authorization' => 'Basic some_password'})
304
- response = token.get('/api/resource', :params => {'query_foo' => 'bar'})
275
+ `OAuth2::Client.new` accepts several options:
276
+
277
+ - `:site`: The base URL for the OAuth 2.0 provider.
278
+ - `:authorize_url`: The authorization endpoint (default: `"oauth/authorize"`).
279
+ - `:token_url`: The token endpoint (default: `"oauth/token"`).
280
+ - `:auth_scheme`: The authentication scheme (`:basic_auth`, `:request_body`, `:tls_client_auth`, `:private_key_jwt`). Default is `:basic_auth`.
281
+ - `:connection_opts`: Options for the underlying Faraday connection (timeouts, proxy, etc.).
282
+ - `:raise_errors`: Whether to raise `OAuth2::Error` on 400+ responses (default: `true`).
283
+
284
+ <details markdown="1">
285
+ <summary><em>authorize_url</em> and <em>token_url</em></summary>
286
+
287
+ ### `authorize_url` and `token_url` are on site root (Just Works!)
288
+
289
+ ```ruby
290
+ require "oauth2"
291
+ client = OAuth2::Client.new("client_id", "client_secret", site: "https://example.org")
292
+ # => #<OAuth2::Client:0x00000001204c8288 @id="client_id", @secret="client_sec...
293
+ client.auth_code.authorize_url(redirect_uri: "http://localhost:8080/oauth2/callback")
294
+ # => "https://example.org/oauth/authorize?client_id=client_id&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Foauth2%2Fcallback&response_type=code"
295
+
296
+ access = client.auth_code.get_token("authorization_code_value", redirect_uri: "http://localhost:8080/oauth2/callback", headers: {"Authorization" => "Basic some_password"})
297
+ response = access.get("/api/resource", params: {"query_foo" => "bar"})
305
298
  response.class.name
306
299
  # => OAuth2::Response
307
300
  ```
308
301
 
309
- <details>
310
- <summary>Debugging</summary>
302
+ ### Relative `authorize_url` and `token_url` (Not on site root, Just Works!)
303
+
304
+ In the 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.
305
+
306
+ ```ruby
307
+ client = OAuth2::Client.new("client_id", "client_secret", site: "https://example.org/nested/directory/on/your/server")
308
+ # => #<OAuth2::Client:0x00000001204c8288 @id="client_id", @secret="client_sec...
309
+ client.auth_code.authorize_url(redirect_uri: "http://localhost:8080/oauth2/callback")
310
+ # => "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"
311
+ ```
312
+
313
+ ### Customize `authorize_url` and `token_url`
314
+
315
+ You can specify custom URLs for authorization and access token, and when using a leading `/` they will _not be relative_, as shown below:
316
+
317
+ ```ruby
318
+ client = OAuth2::Client.new(
319
+ "client_id",
320
+ "client_secret",
321
+ site: "https://example.org/nested/directory/on/your/server",
322
+ authorize_url: "/jaunty/authorize/",
323
+ token_url: "/stirrups/access_token"
324
+ )
325
+ # => #<OAuth2::Client:0x00000001204c8288 @id="client_id", @secret="client_sec...
326
+ client.auth_code.authorize_url(redirect_uri: "http://localhost:8080/oauth2/callback")
327
+ # => "https://example.org/jaunty/authorize/?client_id=client_id&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Foauth2%2Fcallback&response_type=code"
328
+ client.class.name
329
+ # => OAuth2::Client
330
+ ```
331
+
332
+ </details>
333
+
334
+ ### Advanced Initializers
335
+
336
+ ```ruby
337
+ client = OAuth2::Client.new(id, secret, site: site) do |faraday|
338
+ faraday.request(:url_encoded)
339
+ faraday.adapter(:net_http_persistent)
340
+ end
341
+ ```
342
+
343
+ ### AccessToken Features
344
+
345
+ Instances of `OAuth2::AccessToken` handle request signing and token expiration.
346
+
347
+ - **Snake Case & Indifferent Access**: `response.parsed` returns a `SnakyHash` allowing access via string/symbol and snake_case keys even if the provider returns CamelCase.
348
+ - **Auto-Refresh**: You can manually check `token.expired?` and call `token.refresh`.
349
+ - **Serialization**: Persist tokens using `token.to_hash` and restore via `OAuth2::AccessToken.from_hash(client, hash)`.
350
+
351
+ ### snake_case and indifferent access in Response#parsed
352
+
353
+ ```ruby
354
+ response = access.get("/api/resource", params: {"query_foo" => "bar"})
355
+ # Even if the actual response is CamelCase. it will be made available as snaky:
356
+ JSON.parse(response.body) # => {"accessToken"=>"aaaaaaaa", "additionalData"=>"additional"}
357
+ response.parsed # => {"access_token"=>"aaaaaaaa", "additional_data"=>"additional"}
358
+ response.parsed.access_token # => "aaaaaaaa"
359
+ response.parsed[:access_token] # => "aaaaaaaa"
360
+ response.parsed.additional_data # => "additional"
361
+ response.parsed[:additional_data] # => "additional"
362
+ response.parsed.class.name # => SnakyHash::StringKeyed (from snaky_hash gem)
363
+ ```
364
+
365
+ #### Serialization
366
+
367
+ As of v2.0.11, if you need to serialize the parsed result, you can!
368
+
369
+ There are two ways to do this, globally, or discretely. The discrete way is recommended.
370
+
371
+ ##### Global Serialization Config
372
+
373
+ Globally configure `SnakyHash::StringKeyed` to use the serializer. Put this in your code somewhere reasonable (like an initializer for Rails).
374
+
375
+ ```ruby
376
+ SnakyHash::StringKeyed.class_eval do
377
+ extend SnakyHash::Serializer
378
+ end
379
+ ```
380
+
381
+ ##### Discrete Serialization Config
382
+
383
+ Discretely configure a custom Snaky Hash class to use the serializer.
384
+
385
+ ```ruby
386
+ class MySnakyHash < SnakyHash::StringKeyed
387
+ # Give this hash class `dump` and `load` abilities!
388
+ extend SnakyHash::Serializer
389
+ end
390
+
391
+ # And tell your client to use the custom class in each call:
392
+ client = OAuth2::Client.new("client_id", "client_secret", site: "https://example.org/oauth2")
393
+ token = client.get_token({snaky_hash_klass: MySnakyHash})
394
+ ```
395
+
396
+ ##### Serialization Extensions
397
+
398
+ These extensions work regardless of whether you used the global or discrete config above.
399
+
400
+ There are a few hacks you may need in your class to support Ruby < 2.4.2 or < 2.6.
401
+ They are likely not needed if you are on a newer Ruby.
402
+ Expand the examples below, or the [ruby-oauth/snaky_hash](https://gitlab.com/ruby-oauth/snaky_hash) gem,
403
+ or [response_spec.rb](https://github.com/ruby-oauth/oauth2/blob/main/spec/oauth2/response_spec.rb), for more ideas, especially if you need to study the hacks for older Rubies.
404
+
405
+ <details markdown="1">
406
+ <summary>See Examples</summary>
407
+
408
+ ```ruby
409
+ class MySnakyHash < SnakyHash::StringKeyed
410
+ # Give this hash class `dump` and `load` abilities!
411
+ extend SnakyHash::Serializer
412
+
413
+ #### Serialization Extentions
414
+ #
415
+ # Act on the non-hash values (including the values of hashes) as they are dumped to JSON
416
+ # In other words, this retains nested hashes, and only the deepest leaf nodes become bananas.
417
+ # WARNING: This is a silly example!
418
+ dump_value_extensions.add(:to_fruit) do |value|
419
+ "banana" # => Make values "banana" on dump
420
+ end
421
+
422
+ # Act on the non-hash values (including the values of hashes) as they are loaded from the JSON dump
423
+ # In other words, this retains nested hashes, and only the deepest leaf nodes become ***.
424
+ # WARNING: This is a silly example!
425
+ load_value_extensions.add(:to_stars) do |value|
426
+ "***" # Turn dumped bananas into *** when they are loaded
427
+ end
428
+
429
+ # Act on the entire hash as it is prepared for dumping to JSON
430
+ # WARNING: This is a silly example!
431
+ dump_hash_extensions.add(:to_cheese) do |value|
432
+ if value.is_a?(Hash)
433
+ value.transform_keys do |key|
434
+ split = key.split("_")
435
+ first_word = split[0]
436
+ key.sub(first_word, "cheese")
437
+ end
438
+ else
439
+ value
440
+ end
441
+ end
442
+
443
+ # Act on the entire hash as it is loaded from the JSON dump
444
+ # WARNING: This is a silly example!
445
+ load_hash_extensions.add(:to_pizza) do |value|
446
+ if value.is_a?(Hash)
447
+ res = klass.new
448
+ value.keys.each_with_object(res) do |key, result|
449
+ split = key.split("_")
450
+ last_word = split[-1]
451
+ new_key = key.sub(last_word, "pizza")
452
+ result[new_key] = value[key]
453
+ end
454
+ res
455
+ else
456
+ value
457
+ end
458
+ end
459
+ end
460
+ ```
461
+
462
+ </details>
463
+
464
+ #### Prefer camelCase over snake_case? => snaky: false
465
+
466
+ ```ruby
467
+ response = access.get("/api/resource", params: {"query_foo" => "bar"}, snaky: false)
468
+ JSON.parse(response.body) # => {"accessToken"=>"aaaaaaaa", "additionalData"=>"additional"}
469
+ response.parsed # => {"accessToken"=>"aaaaaaaa", "additionalData"=>"additional"}
470
+ response.parsed["accessToken"] # => "aaaaaaaa"
471
+ response.parsed["additionalData"] # => "additional"
472
+ response.parsed.class.name # => Hash (just, regular old Hash)
473
+ ```
474
+
475
+ <details markdown="1">
476
+ <summary>Debugging & Logging</summary>
311
477
 
312
- Set an environment variable, however you would [normally do that](https://github.com/bkeepers/dotenv).
478
+ Set an environment variable as per usual (e.g. with [dotenv](https://github.com/bkeepers/dotenv)).
313
479
 
314
480
  ```ruby
315
481
  # will log both request and response, including bodies
316
- ENV['OAUTH_DEBUG'] = 'true'
482
+ ENV["OAUTH_DEBUG"] = "true"
317
483
  ```
318
484
 
319
485
  By default, debug output will go to `$stdout`. This can be overridden when
320
486
  initializing your OAuth2::Client.
321
487
 
488
+ Sensitive values are filtered from debug logging output using:
489
+
490
+ - `OAuth2.config[:filtered_label]`
491
+ - `OAuth2.config[:filtered_debug_keys]`
492
+
493
+ Debug logging remains opt-in and should still be used cautiously in production environments.
494
+
322
495
  ```ruby
323
- require 'oauth2'
496
+ require "oauth2"
324
497
  client = OAuth2::Client.new(
325
- 'client_id',
326
- 'client_secret',
327
- :site => 'https://example.org',
328
- :logger => Logger.new('example.log', 'weekly')
498
+ "client_id",
499
+ "client_secret",
500
+ site: "https://example.org",
501
+ logger: Logger.new("example.log", "weekly")
329
502
  )
330
503
  ```
504
+
331
505
  </details>
332
506
 
333
- ## OAuth2::Response
507
+ ### Request Target Trust Boundaries
508
+
509
+ This gem supports request flows that can involve absolute URLs in addition to relative paths.
510
+ That flexibility can expand trust boundaries when a token-bearing client is asked to send requests
511
+ to caller-provided targets.
512
+
513
+ Practical guidance:
514
+
515
+ - prefer relative paths where practical
516
+ - do not pass untrusted absolute URLs into token-bearing clients
517
+ - validate or allowlist request targets at the application layer today if your deployment has strict trust-boundary requirements
518
+
519
+ This release line does not yet enforce same-host or allowlist request policy automatically.
520
+ If stricter outbound request controls are needed, they should currently be implemented by the calling application.
521
+
522
+ ### OAuth2::Response
334
523
 
335
524
  The `AccessToken` methods `#get`, `#post`, `#put` and `#delete` and the generic `#request`
336
- will return an instance of the #OAuth2::Response class.
525
+ will return an instance of the `OAuth2::Response` class.
337
526
 
338
527
  This instance contains a `#parsed` method that will parse the response body and
339
- return a Hash if the `Content-Type` is `application/x-www-form-urlencoded` or if
340
- the body is a JSON object. It will return an Array if the body is a JSON
341
- array. Otherwise, it will return the original body string.
528
+ return a Hash-like [`SnakyHash::StringKeyed`](https://gitlab.com/ruby-oauth/snaky_hash/-/blob/main/lib/snaky_hash/string_keyed.rb) if the `Content-Type` is `application/x-www-form-urlencoded` or if
529
+ the body is a JSON object. It will return an Array if the body is a JSON
530
+ array. Otherwise, it will return the original body string.
342
531
 
343
532
  The original response body, headers, and status can be accessed via their
344
533
  respective methods.
345
534
 
346
- ## OAuth2::AccessToken
535
+ ### OAuth2::AccessToken
347
536
 
348
537
  If you have an existing Access Token for a user, you can initialize an instance
349
538
  using various class methods including the standard new, `from_hash` (if you have
350
539
  a hash of the values), or `from_kvform` (if you have an
351
540
  `application/x-www-form-urlencoded` encoded string of the values).
352
541
 
353
- ## OAuth2::Error
542
+ Options (since v2.0.x unless noted):
543
+
544
+ - `expires_latency` (Integer | nil): Seconds to subtract from expires_in when computing #expired? to offset latency.
545
+ - `token_name` (String | Symbol | nil): When multiple token-like fields exist in responses, select the field name to use as the access token (since v2.0.10).
546
+ - `mode` (Symbol | Proc | Hash): Controls how the token is transmitted on requests made via this AccessToken instance.
547
+ - `:header` — Send as Authorization: Bearer <token> header (default and preferred by OAuth 2.1 draft guidance).
548
+ - `:query` — Send as access_token query parameter (discouraged in general, but required by some providers).
549
+ - Verb-dependent (since v2.0.15): Provide either:
550
+ - a `Proc` taking `|verb|` and returning `:header` or `:query`, or
551
+ - a `Hash` with verb symbols as keys, for example `{get: :query, post: :header, delete: :header}`.
552
+
553
+ Note: Verb-dependent mode supports providers like Instagram that require query mode for `GET` and header mode for `POST`/`DELETE`
554
+
555
+ - Verb-dependent mode via `Proc` was added in v2.0.15
556
+ - Verb-dependent mode via `Hash` was added in v2.0.16
557
+
558
+ ### OAuth2::Error
354
559
 
355
560
  On 400+ status code responses, an `OAuth2::Error` will be raised. If it is a
356
561
  standard OAuth2 error response, the body will be parsed and `#code` and `#description` will contain the values provided from the error and
@@ -362,90 +567,571 @@ option on initialization of the client. In this case the `OAuth2::Response`
362
567
  instance will be returned as usual and on 400+ status code responses, the
363
568
  Response instance will contain the `OAuth2::Error` instance.
364
569
 
365
- ## Authorization Grants
570
+ ### Authorization Grants
366
571
 
367
- Currently the Authorization Code, Implicit, Resource Owner Password Credentials, Client Credentials, and Assertion
572
+ Currently, the Authorization Code, Implicit, Resource Owner Password Credentials, Client Credentials, and Assertion
368
573
  authentication grant types have helper strategy classes that simplify client
369
- use. They are available via the `#auth_code`, `#implicit`, `#password`, `#client_credentials`, and `#assertion` methods respectively.
574
+ use. They are available via the [`#auth_code`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/auth_code.rb),
575
+ [`#implicit`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/implicit.rb),
576
+ [`#password`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/password.rb),
577
+ [`#client_credentials`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/client_credentials.rb), and
578
+ [`#assertion`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/assertion.rb) methods respectively.
370
579
 
371
- ```ruby
372
- auth_url = client.auth_code.authorize_url(:redirect_uri => 'http://localhost:8080/oauth/callback')
373
- token = client.auth_code.get_token('code_value', :redirect_uri => 'http://localhost:8080/oauth/callback')
580
+ #### OAuth 2.1 (draft) Note:
374
581
 
375
- auth_url = client.implicit.authorize_url(:redirect_uri => 'http://localhost:8080/oauth/callback')
376
- # get the token params in the callback and
377
- token = OAuth2::AccessToken.from_kvform(client, query_string)
582
+ - **PKCE** is required for all OAuth clients using the authorization code flow (especially public clients). Implement PKCE in your app when required by your provider. See RFC 7636 and RFC 8252.
583
+ - **Implicit grant** (response_type=token) and **Resource Owner Password Credentials grant** are omitted from OAuth 2.1; they remain here for OAuth 2.0 compatibility but should be avoided for new apps.
584
+ - **Redirect URIs** must be compared using exact string matching by the Authorization Server.
585
+
586
+ <details markdown="1">
587
+ <summary>OAuth 2.1 (draft) References</summary>
588
+
589
+ - OAuth 2.1 draft: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13
590
+ - Aaron Parecki: https://aaronparecki.com/2019/12/12/21/its-time-for-oauth-2-dot-1
591
+ - FusionAuth: https://fusionauth.io/blog/2020/04/15/whats-new-in-oauth-2-1
592
+ - Okta: https://developer.okta.com/blog/2019/12/13/oauth-2-1-how-many-rfcs
593
+ - Video: https://www.youtube.com/watch?v=g_aVPdwBTfw
594
+ - Differences overview: https://fusionauth.io/learn/expert-advice/oauth/differences-between-oauth-2-oauth-2-1/
378
595
 
379
- token = client.password.get_token('username', 'password')
596
+ </details>
597
+
598
+ These aren't full examples, but demonstrative of the differences between usage for each strategy.
380
599
 
381
- token = client.client_credentials.get_token
600
+ ```ruby
601
+ auth_url = client.auth_code.authorize_url(redirect_uri: "http://localhost:8080/oauth/callback")
602
+ access = client.auth_code.get_token("code_value", redirect_uri: "http://localhost:8080/oauth/callback")
382
603
 
383
- token = client.assertion.get_token(assertion_params)
604
+ auth_url = client.implicit.authorize_url(redirect_uri: "http://localhost:8080/oauth/callback")
605
+ # get the token params in the callback and
606
+ access = OAuth2::AccessToken.from_kvform(client, query_string)
607
+
608
+ access = client.password.get_token("username", "password")
609
+
610
+ access = client.client_credentials.get_token
611
+
612
+ # Client Assertion Strategy
613
+ # see: https://tools.ietf.org/html/rfc7523
614
+ claimset = {
615
+ iss: "http://localhost:3001",
616
+ aud: "http://localhost:8080/oauth2/token",
617
+ sub: "me@example.com",
618
+ exp: Time.now.utc.to_i + 3600,
619
+ }
620
+ assertion_params = [claimset, "HS256", "secret_key"]
621
+ access = client.assertion.get_token(assertion_params)
622
+
623
+ # The `access` (i.e. access token) is then used like so:
624
+ access.token # actual access_token string, if you need it somewhere
625
+ access.get("/api/stuff") # making api calls with access token
384
626
  ```
385
627
 
386
628
  If you want to specify additional headers to be sent out with the
387
629
  request, add a 'headers' hash under 'params':
388
630
 
389
631
  ```ruby
390
- token = client.auth_code.get_token('code_value', :redirect_uri => 'http://localhost:8080/oauth/callback', :headers => {'Some' => 'Header'})
632
+ access = client.auth_code.get_token("code_value", redirect_uri: "http://localhost:8080/oauth/callback", headers: {"Some" => "Header"})
391
633
  ```
392
634
 
393
635
  You can always use the `#request` method on the `OAuth2::Client` instance to make
394
636
  requests for tokens for any Authentication grant type.
395
637
 
396
- ## Versioning
638
+ ## 🦷 FLOSS Funding
639
+
640
+ While ruby-oauth tools are free software and will always be, the project would benefit immensely from some funding.
641
+ Raising a monthly budget of... "dollars" would make the project more sustainable.
642
+
643
+ We welcome both individual and corporate sponsors! We also offer a
644
+ wide array of funding channels to account for your preferences.
645
+ Currently, [Open Collective][🖇osc] is our preferred funding platform.
646
+
647
+ **If you're working in a company that's making significant use of ruby-oauth tools we'd
648
+ appreciate it if you suggest to your company to become a ruby-oauth sponsor.**
649
+
650
+ You can support the development of ruby-oauth tools via
651
+ [GitHub Sponsors][🖇sponsor],
652
+ [Liberapay][⛳liberapay],
653
+ [PayPal][🖇paypal],
654
+ [Open Collective][🖇osc]
655
+ and [Tidelift][🏙️entsup-tidelift].
397
656
 
398
- This library aims to adhere to [Semantic Versioning 2.0.0][semver].
399
- Violations of this scheme should be reported as bugs. Specifically,
400
- if a minor or patch version is released that breaks backward
401
- compatibility, a new version should be immediately released that
402
- restores compatibility. Breaking changes to the public API will
403
- only be introduced with new major versions.
657
+ | 📍 NOTE |
658
+ |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
659
+ | If doing a sponsorship in the form of donation is problematic for your company <br/> from an accounting standpoint, we'd recommend the use of Tidelift, <br/> where you can get a support-like subscription instead. |
404
660
 
405
- As a result of this policy, you can (and should) specify a
406
- dependency on this gem using the [Pessimistic Version Constraint][pvc] with two digits of precision.
661
+ ### Open Collective for Individuals
662
+
663
+ Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/ruby-oauth#backer)]
664
+
665
+ NOTE: [kettle-readme-backers][kettle-readme-backers] updates this list every day, automatically.
666
+
667
+ <!-- OPENCOLLECTIVE-INDIVIDUALS:START -->
668
+ No backers yet. Be the first!
669
+ <!-- OPENCOLLECTIVE-INDIVIDUALS:END -->
670
+
671
+ ### Open Collective for Organizations
672
+
673
+ Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor](https://opencollective.com/ruby-oauth#sponsor)]
674
+
675
+ NOTE: [kettle-readme-backers][kettle-readme-backers] updates this list every day, automatically.
676
+
677
+ <!-- OPENCOLLECTIVE-ORGANIZATIONS:START -->
678
+ No sponsors yet. Be the first!
679
+ <!-- OPENCOLLECTIVE-ORGANIZATIONS:END -->
680
+
681
+ [kettle-readme-backers]: https://github.com/ruby-oauth/oauth2/blob/main/bin/kettle-readme-backers
682
+
683
+ ### Another way to support open-source
684
+
685
+ I’m driven by a passion to foster a thriving open-source community – a space where people can tackle complex problems, no matter how small. Revitalizing libraries that have fallen into disrepair, and building new libraries focused on solving real-world challenges, are my passions. I was recently affected by layoffs, and the tech jobs market is unwelcoming. I’m reaching out here because your support would significantly aid my efforts to provide for my family, and my farm (11 🐔 chickens, 2 🐶 dogs, 3 🐰 rabbits, 8 🐈‍ cats).
686
+
687
+ If you work at a company that uses my work, please encourage them to support me as a corporate sponsor. My work on gems you use might show up in `bundle fund`.
688
+
689
+ I’m developing a new library, [floss_funding][🖇floss-funding-gem], designed to empower open-source developers like myself to get paid for the work we do, in a sustainable way. Please give it a look.
690
+
691
+ **[Floss-Funding.dev][🖇floss-funding.dev]: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags**
692
+
693
+ [![OpenCollective Backers][🖇osc-backers-i]][🖇osc-backers] [![OpenCollective Sponsors][🖇osc-sponsors-i]][🖇osc-sponsors] [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal] [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate to my FLOSS efforts at ko-fi.com][🖇kofi-img]][🖇kofi]
694
+
695
+ ## 🔐 Security
696
+
697
+ See [SECURITY.md][🔐security].
698
+
699
+ ## 🤝 Contributing
700
+
701
+ If you need some ideas of where to help, you could work on adding more code coverage,
702
+ or if it is already 💯 (see [below](#code-coverage)) check [issues][🤝gh-issues] or [PRs][🤝gh-pulls],
703
+ or use the gem and think about how it could be better.
704
+
705
+ We [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] so if you make changes, remember to update it.
706
+
707
+ See [CONTRIBUTING.md][🤝contributing] for more detailed instructions.
708
+
709
+ ### 🚀 Release Instructions
710
+
711
+ See [CONTRIBUTING.md][🤝contributing].
712
+
713
+ ### Code Coverage
714
+
715
+ <details markdown="1">
716
+ <summary>Coverage service badges</summary>
717
+
718
+ [![Coverage Graph][🏀codecov-g]][🏀codecov]
719
+
720
+ [![Coveralls Test Coverage][🏀coveralls-img]][🏀coveralls]
721
+
722
+ [![QLTY Test Coverage][🏀qlty-covi]][🏀qlty-cov]
723
+
724
+ </details>
725
+
726
+ ### 🪇 Code of Conduct
727
+
728
+ Everyone interacting with this project's codebases, issue trackers,
729
+ chat rooms and mailing lists agrees to follow the [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct].
730
+
731
+ ## 🌈 Contributors
732
+
733
+ [![Contributors][🖐contributors-img]][🖐contributors]
734
+
735
+ Made with [contributors-img][🖐contrib-rocks].
736
+
737
+ Also see GitLab Contributors: [https://gitlab.com/ruby-oauth/oauth2/-/graphs/main][🚎contributors-gl]
738
+
739
+ <details markdown="1">
740
+ <summary>⭐️ Star History</summary>
741
+
742
+ <a href="https://star-history.com/ruby-oauth/oauth2&Date">
743
+ <picture>
744
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=ruby-oauth/oauth2&type=Date&theme=dark" />
745
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=ruby-oauth/oauth2&type=Date" />
746
+ <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=ruby-oauth/oauth2&type=Date" />
747
+ </picture>
748
+ </a>
749
+
750
+ </details>
751
+
752
+ ## 📌 Versioning
753
+
754
+ This library follows [![Semantic Versioning 2.0.0][📌semver-img]][📌semver] for its public API where practical.
755
+ For most applications, prefer the [Pessimistic Version Constraint][📌pvc] with two digits of precision.
407
756
 
408
757
  For example:
409
758
 
410
759
  ```ruby
411
- spec.add_dependency 'oauth2', '~> 1.4'
760
+ spec.add_dependency("oauth2", "~> 2.0")
412
761
  ```
413
762
 
414
- [semver]: http://semver.org/
415
- [pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint
763
+ <details markdown="1">
764
+ <summary>📌 Is "Platform Support" part of the public API? More details inside.</summary>
416
765
 
417
- ## License
766
+ Dropping support for a platform can be a breaking change for affected users.
767
+ If a release changes supported platforms, it should be called out clearly in the changelog and versioned with that impact in mind.
418
768
 
419
- [![License: MIT][🖇src-license-img]][🖇src-license]
769
+ To get a better understanding of how SemVer is intended to work over a project's lifetime,
770
+ read this article from the creator of SemVer:
420
771
 
421
- - Copyright (c) 2011-2013 Michael Bleigh and Intridea, Inc.
422
- - Copyright (c) 2017-2022 [oauth-xx organization][oauth-xx]
423
- - See [LICENSE][license] for details.
772
+ - ["Major Version Numbers are Not Sacred"][📌major-versions-not-sacred]
424
773
 
425
- [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Foauth-xx%2Foauth2.svg?type=large)][fossa2]
774
+ </details>
426
775
 
427
- [license]: https://gitlab.com/oauth-xx/oauth2/-/blob/main/LICENSE
428
- [oauth-xx]: https://gitlab.com/oauth-xx
429
- [fossa2]: https://app.fossa.io/projects/git%2Bgithub.com%2Foauth-xx%2Foauth2?ref=badge_large
776
+ See [CHANGELOG.md][📌changelog] for a list of releases.
777
+
778
+ ## 📄 License
779
+
780
+ The gem is available as open source under the terms of
781
+ the [MIT](MIT.md) [![License: MIT][📄license-img]][📄license-ref].
782
+
783
+ ### © Copyright
784
+
785
+ See [LICENSE.md][📄license] for the official copyright notice.
786
+
787
+ <details markdown="1">
788
+ <summary>Copyright holders</summary>
789
+
790
+ - Copyright (c) 2010-2014, 2016-2017 Erik Michaels-Ober
791
+ - Copyright (c) 2010 Jeremy Kemper
792
+ - Copyright (c) 2010-2011 Michael Bleigh
793
+ - Copyright (c) 2010-2011 Paul Walker
794
+ - Copyright (c) 2010 rick
795
+ - Copyright (c) 2010 Tim Habermaas
796
+ - Copyright (c) 2010 Wynn Netherland
797
+ - Copyright (c) 2011 Alexander Lang
798
+ - Copyright (c) 2011 Alexander Lang
799
+ - Copyright (c) 2011 Greg Spurrier
800
+ - Copyright (c) 2011 Jay Adkisson
801
+ - Copyright (c) 2011 Luke Saunders
802
+ - Copyright (c) 2011 Paul Walker
803
+ - Copyright (c) 2011 Simon Gate
804
+ - Copyright (c) 2012 Bas Vodde
805
+ - Copyright (c) 2012 Damian Janowski
806
+ - Copyright (c) 2012 Daniël van de Burgt
807
+ - Copyright (c) 2012 Dorren Chen
808
+ - Copyright (c) 2012 Igor Sales
809
+ - Copyright (c) 2012 Leigh Caplan
810
+ - Copyright (c) 2012 Michael Andrews
811
+ - Copyright (c) 2012 Omer Rauchwerger
812
+ - Copyright (c) 2012 Saverio Trioni
813
+ - Copyright (c) 2012 Trent Ogren
814
+ - Copyright (c) 2012 Vsevolod Romashov
815
+ - Copyright (c) 2013 Antonio Tapiador del Dujo
816
+ - Copyright (c) 2013 Eduardo Gurgel
817
+ - Copyright (c) 2013 Geostellar Developer
818
+ - Copyright (c) 2013-2014, 2018 Niels Ganser
819
+ - Copyright (c) 2013 Rainux Luo
820
+ - Copyright (c) 2013 Taylor Hedberg
821
+ - Copyright (c) 2013 Tim Clem
822
+ - Copyright (c) 2014 Dave Stevens
823
+ - Copyright (c) 2014 Ellis Berner
824
+ - Copyright (c) 2014 Frank Macreery
825
+ - Copyright (c) 2014 Michael Bleigh
826
+ - Copyright (c) 2014 Olivier Lacan
827
+ - Copyright (c) 2014 Peter Souter
828
+ - Copyright (c) 2014 Ryan Williams
829
+ - Copyright (c) 2015 Andrew Cantino and Jeff Moore
830
+ - Copyright (c) 2015 Thomas Walpole
831
+ - Copyright (c) 2016 Bo Jeanes
832
+ - Copyright (c) 2016 Cody Cutrer
833
+ - Copyright (c) 2016 Edward Rudd
834
+ - Copyright (c) 2016 Lawrence Oluyede
835
+ - Copyright (c) 2016 Linus Pettersson
836
+ - Copyright (c) 2016 Motoshi Nishihira
837
+ - Copyright (c) 2017 Adrian Setyadi
838
+ - Copyright (c) 2017-2018 Benjamin Quorning
839
+ - Copyright (c) 2017 Christoph Petschnig
840
+ - Copyright (c) 2017, 2022 Nathaniel Bibler
841
+ - Copyright (c) 2017 Oleg
842
+ - Copyright (c) 2017 Samuel Cochran
843
+ - Copyright (c) 2017 tetsuya
844
+ - Copyright (c) 2017 Yury Velikanau
845
+ - Copyright (c) 2018 Alex Kowalczuk
846
+ - Copyright (c) 2018 asm__
847
+ - Copyright (c) 2018 David Christensen
848
+ - Copyright (c) 2018 fossabot
849
+ - Copyright (c) 2018 Jeff Moore
850
+ - Copyright (c) 2018 Jeff Moore
851
+ - Copyright (c) 2018 Jonathan del Strother
852
+ - Copyright (c) 2018 Joseph Page
853
+ - Copyright (c) 2018 Joseph Page
854
+ - Copyright (c) 2018 Lomey
855
+ - Copyright (c) 2018 Markus Bengts
856
+ - Copyright (c) 2018 Mathias Klippinge
857
+ - Copyright (c) 2018 nikz
858
+ - Copyright (c) 2018-2019, 2021-2022, 2024-2026 Peter H. Boling
859
+ - Copyright (c) 2019 Daniel Fockler
860
+ - Copyright (c) 2019 Elliot Crosby-McCullough
861
+ - Copyright (c) 2019 João Paulo
862
+ - Copyright (c) 2019 Orien Madgwick
863
+ - Copyright (c) 2019 Ryan T. Hosford
864
+ - Copyright (c) 2019 Tom Corley
865
+ - Copyright (c) 2020 anvox
866
+ - Copyright (c) 2020 Jesse Cotton
867
+ - Copyright (c) 2020 Olle Jonsson
868
+ - Copyright (c) 2020 Stephen Reid
869
+ - Copyright (c) 2021 Anders Carling
870
+ - Copyright (c) 2021 dobon
871
+ - Copyright (c) 2021 Jan Zaydowicz
872
+ - Copyright (c) 2021 Nicholas Palaniuk
873
+ - Copyright (c) 2021-2022 Stan Hu
874
+ - Copyright (c) 2022 Benjamin Quorning
875
+ - Copyright (c) 2022 Bouke van der Bijl
876
+ - Copyright (c) 2022 nov
877
+ - Copyright (c) 2022 Rick Selby
878
+ - Copyright (c) 2022 Ryo Takahashi
879
+ - Copyright (c) 2023 Jessie Young
880
+ - Copyright (c) 2023 Карим Гимадеев
881
+ - Copyright (c) 2024 Aboling0
882
+ - Copyright (c) 2024 Elise Wood
883
+ - Copyright (c) 2024 Manuel van Rijn
884
+ - Copyright (c) 2025-2026 Annibelle Boling
885
+ - Copyright (c) 2025 Mark James
886
+ - Copyright (c) 2025 Mridang Agarwalla
887
+ - Copyright (c) 2025 Sasa Rosic
888
+ - Copyright (c) 2026 Jonathan Grinstead
889
+ - Copyright (c) 2026 kain
890
+ - Copyright (c) 2026 Rob Zolkos
891
+ - Copyright (c) 2026 StepSecurity Bot
430
892
 
431
- ## Development
893
+ </details>
432
894
 
433
- 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.
895
+ ## 🤑 A request for help
434
896
 
435
- 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).
897
+ Maintainers have teeth and need to pay their dentists.
898
+ After getting laid off in an RIF in March, and encountering difficulty finding a new one,
899
+ I began spending most of my time building open source tools.
900
+ I'm hoping to be able to pay for my kids' health insurance this month,
901
+ so if you value the work I am doing, I need your support.
902
+ Please consider sponsoring me or the project.
436
903
 
437
- ## Contributing
904
+ To join the community or get help 👇️ Join the Discord.
438
905
 
439
- See [CONTRIBUTING.md][contributing]
906
+ [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite]
440
907
 
441
- [contributing]: https://gitlab.com/oauth-xx/oauth2/-/blob/main/CONTRIBUTING.md
908
+ To say "thanks!" ☝️ Join the Discord or 👇️ send money.
442
909
 
443
- ## Contributors
910
+ [![Sponsor ruby-oauth/oauth2 on Open Source Collective][🖇osc-all-bottom-img]][🖇osc] 💌 [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal]
444
911
 
445
- [![Contributors](https://contrib.rocks/image?repo=oauth-xx/oauth2)]("https://gitlab.com/oauth-xx/oauth2/-/graphs/main")
912
+ ### Please give the project a star ⭐ ♥.
446
913
 
447
- Made with [contributors-img](https://contrib.rocks).
914
+ Many parts of this project are actively managed by a [kettle-jem](https://github.com/structuredmerge/structuredmerge-ruby/tree/main/gems/kettle-jem) smart template utilizing [StructuredMerge.org](https://structuredmerge.org) merge contracts.
448
915
 
449
- ## Code of Conduct
916
+ Thanks for RTFM. ☺️
450
917
 
451
- Everyone interacting in the OAuth2 project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://gitlab.com/oauth-xx/oauth2/-/blob/main/CODE_OF_CONDUCT.md).
918
+ [⛳liberapay-img]: https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay&color=a51611&style=flat
919
+ [⛳liberapay-bottom-img]: https://img.shields.io/liberapay/goal/pboling.svg?style=for-the-badge&logo=liberapay&color=a51611
920
+ [⛳liberapay]: https://liberapay.com/pboling/donate
921
+ [🖇osc-all-img]: https://img.shields.io/opencollective/all/ruby-oauth
922
+ [🖇osc-sponsors-img]: https://img.shields.io/opencollective/sponsors/ruby-oauth
923
+ [🖇osc-backers-img]: https://img.shields.io/opencollective/backers/ruby-oauth
924
+ [🖇osc-backers]: https://opencollective.com/ruby-oauth#backer
925
+ [🖇osc-backers-i]: https://opencollective.com/ruby-oauth/backers/badge.svg?style=flat
926
+ [🖇osc-sponsors]: https://opencollective.com/ruby-oauth#sponsor
927
+ [🖇osc-sponsors-i]: https://opencollective.com/ruby-oauth/sponsors/badge.svg?style=flat
928
+ [🖇osc-all-bottom-img]: https://img.shields.io/opencollective/all/ruby-oauth?style=for-the-badge
929
+ [🖇osc-sponsors-bottom-img]: https://img.shields.io/opencollective/sponsors/ruby-oauth?style=for-the-badge
930
+ [🖇osc-backers-bottom-img]: https://img.shields.io/opencollective/backers/ruby-oauth?style=for-the-badge
931
+ [🖇osc]: https://opencollective.com/ruby-oauth
932
+ [🖇sponsor-img]: https://img.shields.io/badge/Sponsor_Me!-pboling.svg?style=social&logo=github
933
+ [🖇sponsor-bottom-img]: https://img.shields.io/badge/Sponsor_Me!-pboling-blue?style=for-the-badge&logo=github
934
+ [🖇sponsor]: https://github.com/sponsors/pboling
935
+ [🖇kofi-img]: https://img.shields.io/badge/ko--fi-%E2%9C%93-a51611.svg?style=flat
936
+ [🖇kofi]: https://ko-fi.com/pboling
937
+ [🖇buyme-small-img]: https://img.shields.io/badge/buy_me_a_coffee-%E2%9C%93-a51611.svg?style=flat
938
+ [🖇buyme-img]: https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20latte&emoji=&slug=pboling&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff
939
+ [🖇buyme]: https://www.buymeacoffee.com/pboling
940
+ [🖇paypal-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=flat&logo=paypal
941
+ [🖇paypal-bottom-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=for-the-badge&logo=paypal&color=0A0A0A
942
+ [🖇paypal]: https://www.paypal.com/paypalme/peterboling
943
+ [🖇floss-funding.dev]: https://floss-funding.dev
944
+ [🖇floss-funding-gem]: https://github.com/galtzo-floss/floss_funding
945
+ [✉️discord-invite]: https://discord.gg/3qme4XHNKN
946
+ [✉️discord-invite-img-ftb]: https://img.shields.io/discord/1373797679469170758?style=for-the-badge&logo=discord
947
+ [✉️ruby-friends-img]: https://img.shields.io/badge/daily.dev-%F0%9F%92%8E_Ruby_Friends-0A0A0A?style=for-the-badge&logo=dailydotdev&logoColor=white
948
+ [✉️ruby-friends]: https://app.daily.dev/squads/rubyfriends
949
+
950
+ [✇bundle-group-pattern]: https://gist.github.com/pboling/4564780
951
+ [⛳️gem-namespace]: https://github.com/ruby-oauth/oauth2
952
+ [⛳️namespace-img]: https://img.shields.io/badge/namespace-OAuth2-3C2D2D.svg?style=square&logo=ruby&logoColor=white
953
+ [⛳️gem-name]: https://bestgems.org/gems/oauth2
954
+ [⛳️name-img]: https://img.shields.io/badge/name-oauth2-3C2D2D.svg?style=square&logo=rubygems&logoColor=red
955
+ [⛳️tag-img]: https://img.shields.io/github/tag/ruby-oauth/oauth2.svg
956
+ [⛳️tag]: https://github.com/ruby-oauth/oauth2/releases
957
+ [🚂maint-blog]: http://www.railsbling.com/tags/oauth2
958
+ [🚂maint-blog-img]: https://img.shields.io/badge/blog-railsbling-0093D0.svg?style=for-the-badge&logo=rubyonrails&logoColor=orange
959
+ [🚂maint-contact]: http://www.railsbling.com/contact
960
+ [🚂maint-contact-img]: https://img.shields.io/badge/Contact-Maintainer-0093D0.svg?style=flat&logo=rubyonrails&logoColor=red
961
+ [💖🖇linkedin]: http://www.linkedin.com/in/peterboling
962
+ [💖🖇linkedin-img]: https://img.shields.io/badge/LinkedIn-Profile-0B66C2?style=flat&logo=newjapanprowrestling
963
+ [💖✌️wellfound]: https://wellfound.com/u/peter-boling
964
+ [💖✌️wellfound-img]: https://img.shields.io/badge/peter--boling-orange?style=flat&logo=wellfound
965
+ [💖💲crunchbase]: https://www.crunchbase.com/person/peter-boling
966
+ [💖💲crunchbase-img]: https://img.shields.io/badge/peter--boling-purple?style=flat&logo=crunchbase
967
+ [💖🐘ruby-mast]: https://ruby.social/@galtzo
968
+ [💖🐘ruby-mast-img]: https://img.shields.io/mastodon/follow/109447111526622197?domain=https://ruby.social&style=flat&logo=mastodon&label=Ruby%20@galtzo
969
+ [💖🦋bluesky]: https://bsky.app/profile/galtzo.com
970
+ [💖🦋bluesky-img]: https://img.shields.io/badge/@galtzo.com-0285FF?style=flat&logo=bluesky&logoColor=white
971
+ [💖🌳linktree]: https://linktr.ee/galtzo
972
+ [💖🌳linktree-img]: https://img.shields.io/badge/galtzo-purple?style=flat&logo=linktree
973
+ [💖💁🏼‍♂️devto]: https://dev.to/galtzo
974
+ [💖💁🏼‍♂️devto-img]: https://img.shields.io/badge/dev.to-0A0A0A?style=flat&logo=devdotto&logoColor=white
975
+ [💖💁🏼‍♂️aboutme]: https://about.me/peter.boling
976
+ [💖💁🏼‍♂️aboutme-img]: https://img.shields.io/badge/about.me-0A0A0A?style=flat&logo=aboutme&logoColor=white
977
+ [💖🧊berg]: https://codeberg.org/pboling
978
+ [💖🐙hub]: https://github.org/pboling
979
+ [💖🛖hut]: https://sr.ht/~galtzo/
980
+ [💖🧪lab]: https://gitlab.com/pboling
981
+ [👨🏼‍🏫expsup-upwork]: https://www.upwork.com/freelancers/~014942e9b056abdf86?mp_source=share
982
+ [👨🏼‍🏫expsup-upwork-img]: https://img.shields.io/badge/UpWork-13544E?style=for-the-badge&logo=Upwork&logoColor=white
983
+ [👨🏼‍🏫expsup-codementor]: https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github
984
+ [👨🏼‍🏫expsup-codementor-img]: https://img.shields.io/badge/CodeMentor-Get_Help-1abc9c?style=for-the-badge&logo=CodeMentor&logoColor=white
985
+ [🏙️entsup-tidelift]: https://tidelift.com/subscription/pkg/rubygems-oauth2?utm_source=rubygems-oauth2&utm_medium=referral&utm_campaign=readme
986
+ [🏙️entsup-tidelift-img]: https://img.shields.io/badge/Tidelift_and_Sonar-Enterprise_Support-FD3456?style=for-the-badge&logo=sonar&logoColor=white
987
+ [🏙️entsup-tidelift-sonar]: https://blog.tidelift.com/tidelift-joins-sonar
988
+ [💁🏼‍♂️peterboling]: http://www.peterboling.com
989
+ [🚂railsbling]: http://www.railsbling.com
990
+ [📜src-gl-img]: https://img.shields.io/badge/GitLab-FBA326?style=for-the-badge&logo=Gitlab&logoColor=orange
991
+ [📜src-gl]: https://gitlab.com/ruby-oauth/oauth2
992
+ [📜src-cb-img]: https://img.shields.io/badge/CodeBerg-4893CC?style=for-the-badge&logo=CodeBerg&logoColor=blue
993
+ [📜src-cb]: https://codeberg.org/ruby-oauth/oauth2
994
+ [📜src-gh-img]: https://img.shields.io/badge/GitHub-238636?style=for-the-badge&logo=Github&logoColor=green
995
+ [📜src-gh]: https://github.com/ruby-oauth/oauth2
996
+ [📜docs-cr-rd-img]: https://img.shields.io/badge/RubyDoc-Current_Release-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white
997
+ [📜docs-head-rd-img]: https://img.shields.io/badge/YARD_on_Galtzo.com-HEAD-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white
998
+ [📜gl-wiki]: https://gitlab.com/ruby-oauth/oauth2/-/wikis/home
999
+ [📜gh-wiki]: https://github.com/ruby-oauth/oauth2/wiki
1000
+ [📜gl-wiki-img]: https://img.shields.io/badge/wiki-gitlab-943CD2.svg?style=for-the-badge&logo=gitlab&logoColor=white
1001
+ [📜gh-wiki-img]: https://img.shields.io/badge/wiki-github-943CD2.svg?style=for-the-badge&logo=github&logoColor=white
1002
+ [👽dl-rank]: https://bestgems.org/gems/oauth2
1003
+ [👽dl-ranki]: https://img.shields.io/gem/rd/oauth2.svg
1004
+ [👽version]: https://bestgems.org/gems/oauth2
1005
+ [👽versioni]: https://img.shields.io/gem/v/oauth2.svg
1006
+ [🏀qlty-mnt]: https://qlty.sh/gh/ruby-oauth/projects/oauth2
1007
+ [🏀qlty-mnti]: https://qlty.sh/gh/ruby-oauth/projects/oauth2/maintainability.svg
1008
+ [🏀qlty-cov]: https://qlty.sh/gh/ruby-oauth/projects/oauth2/metrics/code?sort=coverageRating
1009
+ [🏀qlty-covi]: https://qlty.sh/gh/ruby-oauth/projects/oauth2/coverage.svg
1010
+ [🏀codecov]: https://codecov.io/gh/ruby-oauth/oauth2
1011
+ [🏀codecovi]: https://codecov.io/gh/ruby-oauth/oauth2/graph/badge.svg
1012
+ [🏀coveralls]: https://coveralls.io/github/ruby-oauth/oauth2?branch=main
1013
+ [🏀coveralls-img]: https://coveralls.io/repos/github/ruby-oauth/oauth2/badge.svg?branch=main
1014
+ [🚎ruby-2.4-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ruby-2.4.yml
1015
+ [🚎ruby-2.5-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ruby-2.5.yml
1016
+ [🚎ruby-2.6-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ruby-2.6.yml
1017
+ [🚎ruby-2.7-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ruby-2.7.yml
1018
+ [🚎ruby-3.0-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ruby-3.0.yml
1019
+ [🚎ruby-3.1-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ruby-3.1.yml
1020
+ [🚎ruby-3.2-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ruby-3.2.yml
1021
+ [🚎ruby-3.3-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ruby-3.3.yml
1022
+ [🚎ruby-3.4-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ruby-3.4.yml
1023
+ [🚎jruby-9.2-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/jruby-9.2.yml
1024
+ [🚎jruby-9.3-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/jruby-9.3.yml
1025
+ [🚎jruby-9.4-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/jruby-9.4.yml
1026
+ [🚎jruby-10.0-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/jruby-10.0.yml
1027
+ [🚎truby-22.3-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/truffleruby-22.3.yml
1028
+ [🚎truby-23.0-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/truffleruby-23.0.yml
1029
+ [🚎truby-23.1-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/truffleruby-23.1.yml
1030
+ [🚎truby-24.2-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/truffleruby-24.2.yml
1031
+ [🚎truby-25.0-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/truffleruby-25.0.yml
1032
+ [🚎truby-33.0-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/truffleruby-33.0.yml
1033
+ [🚎2-cov-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/coverage.yml
1034
+ [🚎2-cov-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/coverage.yml/badge.svg
1035
+ [🚎3-hd-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/heads.yml
1036
+ [🚎3-hd-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/heads.yml/badge.svg
1037
+ [🚎5-st-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/style.yml
1038
+ [🚎5-st-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/style.yml/badge.svg
1039
+ [🚎9-t-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/truffle.yml
1040
+ [🚎9-t-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/truffle.yml/badge.svg
1041
+ [🚎10-j-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/jruby.yml
1042
+ [🚎10-j-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/jruby.yml/badge.svg
1043
+ [🚎11-c-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/current.yml
1044
+ [🚎11-c-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/current.yml/badge.svg
1045
+ [🚎12-crh-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/dep-heads.yml
1046
+ [🚎12-crh-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/dep-heads.yml/badge.svg
1047
+ [🚎13-🔒️-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/locked_deps.yml
1048
+ [🚎13-🔒️-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/locked_deps.yml/badge.svg
1049
+ [🚎14-🔓️-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/unlocked_deps.yml
1050
+ [🚎14-🔓️-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/unlocked_deps.yml/badge.svg
1051
+ [🚎15-🪪-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/license-eye.yml
1052
+ [🚎15-🪪-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/license-eye.yml/badge.svg
1053
+ [💎ruby-2.2i]: https://img.shields.io/badge/Ruby-2.2_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white
1054
+ [💎ruby-2.3i]: https://img.shields.io/badge/Ruby-2.3_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white
1055
+ [💎ruby-2.4i]: https://img.shields.io/badge/Ruby-2.4-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
1056
+ [💎ruby-2.5i]: https://img.shields.io/badge/Ruby-2.5-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
1057
+ [💎ruby-2.6i]: https://img.shields.io/badge/Ruby-2.6-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
1058
+ [💎ruby-2.7i]: https://img.shields.io/badge/Ruby-2.7-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
1059
+ [💎ruby-3.0i]: https://img.shields.io/badge/Ruby-3.0-CC342D?style=for-the-badge&logo=ruby&logoColor=white
1060
+ [💎ruby-3.1i]: https://img.shields.io/badge/Ruby-3.1-CC342D?style=for-the-badge&logo=ruby&logoColor=white
1061
+ [💎ruby-3.2i]: https://img.shields.io/badge/Ruby-3.2-CC342D?style=for-the-badge&logo=ruby&logoColor=white
1062
+ [💎ruby-3.3i]: https://img.shields.io/badge/Ruby-3.3-CC342D?style=for-the-badge&logo=ruby&logoColor=white
1063
+ [💎ruby-3.4i]: https://img.shields.io/badge/Ruby-3.4-CC342D?style=for-the-badge&logo=ruby&logoColor=white
1064
+ [💎ruby-c-i]: https://img.shields.io/badge/Ruby-current-CC342D?style=for-the-badge&logo=ruby&logoColor=green
1065
+ [💎ruby-headi]: https://img.shields.io/badge/Ruby-HEAD-CC342D?style=for-the-badge&logo=ruby&logoColor=blue
1066
+ [💎truby-22.3i]: https://img.shields.io/badge/Truffle_Ruby-22.3-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
1067
+ [💎truby-23.0i]: https://img.shields.io/badge/Truffle_Ruby-23.0-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
1068
+ [💎truby-23.1i]: https://img.shields.io/badge/Truffle_Ruby-23.1-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
1069
+ [💎truby-24.2i]: https://img.shields.io/badge/Truffle_Ruby-24.2-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
1070
+ [💎truby-25.0i]: https://img.shields.io/badge/Truffle_Ruby-25.0-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
1071
+ [💎truby-33.0i]: https://img.shields.io/badge/Truffle_Ruby-33.0-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
1072
+ [💎truby-c-i]: https://img.shields.io/badge/Truffle_Ruby-current-34BCB1?style=for-the-badge&logo=ruby&logoColor=green
1073
+ [💎truby-headi]: https://img.shields.io/badge/Truffle_Ruby-HEAD-34BCB1?style=for-the-badge&logo=ruby&logoColor=blue
1074
+ [💎jruby-9.2i]: https://img.shields.io/badge/JRuby-9.2-FBE742?style=for-the-badge&logo=ruby&logoColor=red
1075
+ [💎jruby-9.3i]: https://img.shields.io/badge/JRuby-9.3-FBE742?style=for-the-badge&logo=ruby&logoColor=red
1076
+ [💎jruby-9.4i]: https://img.shields.io/badge/JRuby-9.4-FBE742?style=for-the-badge&logo=ruby&logoColor=red
1077
+ [💎jruby-10.0i]: https://img.shields.io/badge/JRuby-10.0-FBE742?style=for-the-badge&logo=ruby&logoColor=red
1078
+ [💎jruby-c-i]: https://img.shields.io/badge/JRuby-current-FBE742?style=for-the-badge&logo=ruby&logoColor=green
1079
+ [💎jruby-headi]: https://img.shields.io/badge/JRuby-HEAD-FBE742?style=for-the-badge&logo=ruby&logoColor=blue
1080
+ [🤝gh-issues]: https://github.com/ruby-oauth/oauth2/issues
1081
+ [🤝gh-pulls]: https://github.com/ruby-oauth/oauth2/pulls
1082
+ [🤝gl-issues]: https://gitlab.com/ruby-oauth/oauth2/-/issues
1083
+ [🤝gl-pulls]: https://gitlab.com/ruby-oauth/oauth2/-/merge_requests
1084
+ [🤝cb-issues]: https://codeberg.org/ruby-oauth/oauth2/issues
1085
+ [🤝cb-pulls]: https://codeberg.org/ruby-oauth/oauth2/pulls
1086
+ [🤝cb-donate]: https://donate.codeberg.org/
1087
+ [🤝contributing]: https://github.com/ruby-oauth/oauth2/blob/main/CONTRIBUTING.md
1088
+ [🏀codecov-g]: https://codecov.io/gh/ruby-oauth/oauth2/graph/badge.svg
1089
+ [🖐contrib-rocks]: https://contrib.rocks
1090
+ [🖐contributors]: https://github.com/ruby-oauth/oauth2/graphs/contributors
1091
+ [🖐contributors-img]: https://contrib.rocks/image?repo=ruby-oauth/oauth2
1092
+ [🚎contributors-gl]: https://gitlab.com/ruby-oauth/oauth2/-/graphs/main
1093
+ [🪇conduct]: https://github.com/ruby-oauth/oauth2/blob/main/CODE_OF_CONDUCT.md
1094
+ [🪇conduct-img]: https://img.shields.io/badge/Contributor_Covenant-2.1-259D6C.svg
1095
+ [📌pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint
1096
+ [📌semver]: https://semver.org/spec/v2.0.0.html
1097
+ [📌semver-img]: https://img.shields.io/badge/semver-2.0.0-259D6C.svg?style=flat
1098
+ [📌semver-breaking]: https://github.com/semver/semver/issues/716#issuecomment-869336139
1099
+ [📌major-versions-not-sacred]: https://tom.preston-werner.com/2022/05/23/major-version-numbers-are-not-sacred.html
1100
+ [📌changelog]: https://github.com/ruby-oauth/oauth2/blob/main/CHANGELOG.md
1101
+ [📗keep-changelog]: https://keepachangelog.com/en/1.0.0/
1102
+ [📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-34495e.svg?style=flat
1103
+ [📌gitmoji]: https://gitmoji.dev
1104
+ [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
1105
+ [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
1106
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.558-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1107
+ [🔐security]: https://github.com/ruby-oauth/oauth2/blob/main/SECURITY.md
1108
+ [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
1109
+ [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
1110
+ [📄license]: LICENSE.md
1111
+ [📄license-ref]: MIT.md
1112
+ [📄license-img]: https://img.shields.io/badge/License-MIT-259D6C.svg
1113
+ [📄license-compat]: https://www.apache.org/legal/resolved.html#category-a
1114
+ [📄license-compat-img]: https://img.shields.io/badge/Apache_Compatible:_Category_A-%E2%9C%93-259D6C.svg?style=flat&logo=Apache
1115
+
1116
+ [📄ilo-declaration]: https://www.ilo.org/declaration/lang--en/index.htm
1117
+ [📄ilo-declaration-img]: https://img.shields.io/badge/ILO_Fundamental_Principles-✓-259D6C.svg?style=flat
1118
+ [🚎yard-current]: http://rubydoc.info/gems/oauth2
1119
+ [🚎yard-head]: https://oauth2.galtzo.com
1120
+ [💎stone_checksums]: https://github.com/galtzo-floss/stone_checksums
1121
+ [💎SHA_checksums]: https://gitlab.com/ruby-oauth/oauth2/-/tree/main/checksums
1122
+ [💎rlts]: https://github.com/rubocop-lts/rubocop-lts
1123
+ [💎rlts-img]: https://img.shields.io/badge/code_style_&_linting-rubocop--lts-34495e.svg?plastic&logo=ruby&logoColor=white
1124
+ [💎appraisal2]: https://github.com/appraisal-rb/appraisal2
1125
+ [💎appraisal2-img]: https://img.shields.io/badge/appraised_by-appraisal2-34495e.svg?plastic&logo=ruby&logoColor=white
1126
+ [💎d-in-dvcs]: https://railsbling.com/posts/dvcs/put_the_d_in_dvcs/
1127
+
1128
+ <!-- kettle-jem:metadata:start -->
1129
+ | Field | Value |
1130
+ |---|---|
1131
+ | Package | oauth2 |
1132
+ | Description | 🔐 A Ruby wrapper for the OAuth 2.0 Authorization Framework, including the OAuth 2.1 draft spec, and OpenID Connect (OIDC) |
1133
+ | Homepage | https://github.com/ruby-oauth/oauth2 |
1134
+ | Source | https://github.com/ruby-oauth/oauth2 |
1135
+ | License | `MIT` |
1136
+ | Funding | https://github.com/sponsors/pboling, https://ko-fi.com/pboling, https://liberapay.com/pboling/donate, https://opencollective.com/ruby-oauth, https://thanks.dev/u/gh/pboling, https://tidelift.com/funding/github/rubygems/oauth2, https://www.buymeacoffee.com/pboling |
1137
+ <!-- kettle-jem:metadata:end -->