oauth2 2.0.18 → 2.0.22

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,48 +1,10 @@
1
- | 📍 NOTE |
2
- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
3
- | RubyGems (the [GitHub org][rubygems-org], not the website) [suffered][draper-security] a [hostile takeover][ellen-takeover] in September 2025. |
4
- | Ultimately [4 maintainers][simi-removed] were [hard removed][martin-removed] and a reason has been given for only 1 of those, while 2 others resigned in protest. |
5
- | It is a [complicated story][draper-takeover] which is difficult to [parse quickly][draper-lies]. |
6
- | I'm adding notes like this to gems because I [don't condone theft][draper-theft] of repositories or gems from their rightful owners. |
7
- | If a similar theft happened with my repos/gems, I'd hope some would stand up for me. |
8
- | Disenfranchised former-maintainers have started [gem.coop][gem-coop]. |
9
- | Once available I will publish there exclusively; unless RubyCentral makes amends with the community. |
10
- | The ["Technology for Humans: Joel Draper"][reinteractive-podcast] podcast episode by [reinteractive][reinteractive] is the most cogent summary I'm aware of. |
11
- | See [here][gem-naming], [here][gem-coop] and [here][martin-ann] for more info on what comes next. |
12
- | What I'm doing: A (WIP) proposal for [bundler/gem scopes][gem-scopes], and a (WIP) proposal for a federated [gem server][gem-server]. |
13
-
14
- [rubygems-org]: https://github.com/rubygems/
15
- [draper-security]: https://joel.drapper.me/p/ruby-central-security-measures/
16
- [draper-takeover]: https://joel.drapper.me/p/ruby-central-takeover/
17
- [ellen-takeover]: https://pup-e.com/blog/goodbye-rubygems/
18
- [simi-removed]: https://www.reddit.com/r/ruby/s/gOk42POCaV
19
- [martin-removed]: https://bsky.app/profile/martinemde.com/post/3m3occezxxs2q
20
- [draper-lies]: https://joel.drapper.me/p/ruby-central-fact-check/
21
- [draper-theft]: https://joel.drapper.me/p/ruby-central/
22
- [reinteractive]: https://reinteractive.com/ruby-on-rails
23
- [gem-coop]: https://gem.coop
24
- [gem-naming]: https://github.com/gem-coop/gem.coop/issues/12
25
- [martin-ann]: https://martinemde.com/2025/10/05/announcing-gem-coop.html
26
- [gem-scopes]: https://github.com/galtzo-floss/bundle-namespace
27
- [gem-server]: https://github.com/galtzo-floss/gem-server
28
- [reinteractive-podcast]: https://youtu.be/_H4qbtC5qzU?si=BvuBU90R2wAqD2E6
29
-
30
- [![Galtzo FLOSS Logo by Aboling0, CC BY-SA 4.0][🖼️galtzo-i]][🖼️galtzo-discord] [![ruby-lang Logo, Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5][🖼️ruby-lang-i]][🖼️ruby-lang] [![oauth2 Logo by Chris Messina, CC BY-SA 3.0][🖼️oauth2-i]][🖼️oauth2]
31
-
32
- [🖼️galtzo-i]: https://logos.galtzo.com/assets/images/galtzo-floss/avatar-192px.svg
33
- [🖼️galtzo-discord]: https://discord.gg/3qme4XHNKN
34
- [🖼️ruby-lang-i]: https://logos.galtzo.com/assets/images/ruby-lang/avatar-192px.svg
35
- [🖼️ruby-lang]: https://www.ruby-lang.org/
36
- [🖼️oauth2-i]: https://logos.galtzo.com/assets/images/oauth/oauth2/avatar-192px.svg
37
- [🖼️oauth2]: https://github.com/ruby-oauth/oauth2
38
-
39
- # 🔐 OAuth 2.0 Authorization Framework
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>
40
2
 
41
- ⭐️ including OAuth 2.1 draft spec & OpenID Connect (OIDC)
3
+ # 🔐 OAuth2
42
4
 
43
- [![Version][👽versioni]][👽version] [![GitHub tag (latest SemVer)][⛳️tag-img]][⛳️tag] [![License: MIT][📄license-img]][📄license-ref] [![Downloads Rank][👽dl-ranki]][👽dl-rank] [![Open Source Helpers][👽oss-helpi]][👽oss-help] [![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 JRuby][🚎10-j-wfi]][🚎10-j-wf] [![Deps Locked][🚎13-🔒️-wfi]][🚎13-🔒️-wf] [![Deps Unlocked][🚎14-🔓️-wfi]][🚎14-🔓️-wf] [![CI Supported][🚎6-s-wfi]][🚎6-s-wf] [![CI Legacy][🚎4-lg-wfi]][🚎4-lg-wf] [![CI Unsupported][🚎7-us-wfi]][🚎7-us-wf] [![CI Ancient][🚎1-an-wfi]][🚎1-an-wf] [![CI Test Coverage][🚎2-cov-wfi]][🚎2-cov-wf] [![CI Style][🚎5-st-wfi]][🚎5-st-wf] [![CodeQL][🖐codeQL-img]][🖐codeQL] [![Apache SkyWalking Eyes License Compatibility Check][🚎15-🪪-wfi]][🚎15-🪪-wf]
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]
44
6
 
45
- `if ci_badges.map(&:color).detect { it != "green"}` ☝️ [let me know][🖼️galtzo-discord], as I may have missed the [discord notification][🖼️galtzo-discord].
7
+ `if ci_badges.map(&:color).detect { it != "green"}` ☝️ [let me know][✉️discord-invite], as I may have missed the [discord notification][✉️discord-invite].
46
8
 
47
9
  ---
48
10
 
@@ -50,13 +12,20 @@
50
12
 
51
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 on Polar][🖇polar-img]][🖇polar] [![Donate at ko-fi.com][🖇kofi-img]][🖇kofi]
52
14
 
53
- ## 🌻 Synopsis
15
+ <details>
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).
19
+
20
+ </details>
21
+
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>
54
23
 
55
24
  OAuth 2.0 is the industry-standard protocol for authorization.
56
- OAuth 2.0 focuses on client developer simplicity while providing specific authorization flows for web applications,
57
- desktop applications, mobile phones, and living room devices.
58
25
  This is a RubyGem for implementing OAuth 2.0 clients (not servers) in Ruby applications.
59
26
 
27
+ ⭐️ including OAuth 2.1 draft spec & OpenID Connect (OIDC)
28
+
60
29
  ### Quick Examples
61
30
 
62
31
  <details markdown="1">
@@ -75,13 +44,14 @@ curl --request POST \
75
44
  NOTE: In the ruby version below, certain params are passed to the `get_token` call, instead of the client creation.
76
45
 
77
46
  ```ruby
78
- OAuth2::Client.new(
47
+ client = OAuth2::Client.new(
79
48
  "REDMOND_CLIENT_ID", # client_id
80
49
  "REDMOND_CLIENT_SECRET", # client_secret
81
50
  auth_scheme: :request_body, # Other modes are supported: :basic_auth, :tls_client_auth, :private_key_jwt
82
51
  token_url: "oauth2/token", # relative path, except with leading `/`, then absolute path
83
- site: "https://login.microsoftonline.com/REDMOND_REDACTED",
84
- ). # The base path for token_url when it is relative
52
+ site: "https://login.microsoftonline.com/REDMOND_REDACTED"
53
+ )
54
+ client.
85
55
  client_credentials. # There are many other types to choose from!
86
56
  get_token(resource: "REDMOND_RESOURCE_UUID")
87
57
  ```
@@ -147,51 +117,75 @@ Notes
147
117
 
148
118
  </details>
149
119
 
150
- If it seems like you are in the wrong place, you might try one of these:
120
+ ### Alternatives
121
+
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):
124
+
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. |
151
134
 
152
- * [OAuth 2.0 Spec][oauth2-spec]
153
- * [doorkeeper gem][doorkeeper-gem] for OAuth 2.0 server/provider implementation.
154
- * [oauth sibling gem][sibling-gem] for OAuth 1.0a implementations in Ruby.
135
+ See also the [OAuth 2.0 Spec][oauth2-spec], the [OpenID Connect Spec][oidc-spec], and the [MCP Auth Spec][mcp-auth-spec].
155
136
 
156
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
157
140
  [sibling-gem]: https://gitlab.com/ruby-oauth/oauth
141
+ [sibling-mcp-gem]: https://github.com/ruby-oauth/oauth2-mcp
158
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
159
148
 
160
149
  ## 💡 Info you can shake a stick at
161
150
 
162
- | Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] |
151
+ | Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] |
163
152
  |-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
164
- | Works with JRuby | ![JRuby 9.1 Compat][💎jruby-9.1i] ![JRuby 9.2 Compat][💎jruby-9.2i] ![JRuby 9.3 Compat][💎jruby-9.3i] <br/> [![JRuby 9.4 Compat][💎jruby-9.4i]][🚎10-j-wf] [![JRuby 10.0 Compat][💎jruby-c-i]][🚎11-c-wf] [![JRuby HEAD Compat][💎jruby-headi]][🚎3-hd-wf] |
165
- | Works with Truffle Ruby | ![Truffle Ruby 22.3 Compat][💎truby-22.3i] ![Truffle Ruby 23.0 Compat][💎truby-23.0i] ![Truffle Ruby 23.1 Compat][💎truby-23.1i] <br/> [![Truffle Ruby 24.1 Compat][💎truby-c-i]][🚎11-c-wf] |
166
- | Works with MRI Ruby 3 | [![Ruby 3.0 Compat][💎ruby-3.0i]][🚎4-lg-wf] [![Ruby 3.1 Compat][💎ruby-3.1i]][🚎6-s-wf] [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎6-s-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎6-s-wf] [![Ruby 3.4 Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf] |
167
- | Works with MRI Ruby 2 | ![Ruby 2.2 Compat][💎ruby-2.2i] <br/> [![Ruby 2.3 Compat][💎ruby-2.3i]][🚎1-an-wf] [![Ruby 2.4 Compat][💎ruby-2.4i]][🚎1-an-wf] [![Ruby 2.5 Compat][💎ruby-2.5i]][🚎1-an-wf] [![Ruby 2.6 Compat][💎ruby-2.6i]][🚎7-us-wf] [![Ruby 2.7 Compat][💎ruby-2.7i]][🚎7-us-wf] |
168
- | 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] |
169
- | 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] |
170
- | 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] |
171
- | Compliance | [![License: MIT][📄license-img]][📄license-ref] [![Compatible with Apache Software Projects: Verified by SkyWalking Eyes][📄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] |
172
- | 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] |
173
- | 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] |
174
- | `...` 💖 | [![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] |
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 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 current Compat][💎truby-c-i]][🚎9-t-wf]|
155
+ | Works with MRI Ruby 4 | [![Ruby 4.0 Compat][💎ruby-4.0i]][🚎11-c-wf] [![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] |
175
165
 
176
166
  ### Compatibility
177
167
 
178
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.
179
173
 
180
- | 🚚 _Amazing_ test matrix was brought to you by | 🔎 appraisal2 🔎 and the color 💚 green 💚 |
174
+ | 🚚 _Amazing_ test matrix was brought to you by | 🔎 appraisal2 🔎 and the color 💚 green 💚 |
181
175
  |------------------------------------------------|--------------------------------------------------------|
182
- | 👟 Check it out! | ✨ [github.com/appraisal-rb/appraisal2][💎appraisal2] ✨ |
176
+ | 👟 Check it out! | ✨ [github.com/appraisal-rb/appraisal2][💎appraisal2] ✨ |
183
177
 
184
178
  ### Federated DVCS
185
179
 
186
180
  <details markdown="1">
187
- <summary>Find this repo on federated forges (Coming soon!)</summary>
181
+ <summary>Find this repo on federated forges (Coming soon!)</summary>
188
182
 
189
- | Federated [DVCS][💎d-in-dvcs] Repository | Status | Issues | PRs | Wiki | CI | Discussions |
183
+ | Federated [DVCS][💎d-in-dvcs] Repository | Status | Issues | PRs | Wiki | CI | Discussions |
190
184
  |-------------------------------------------------|-----------------------------------------------------------------------|---------------------------|--------------------------|---------------------------|--------------------------|------------------------------|
191
- | 🧪 [ruby-oauth/oauth2 on GitLab][📜src-gl] | The Truth | [💚][🤝gl-issues] | [💚][🤝gl-pulls] | [💚][📜gl-wiki] | 🐭 Tiny Matrix | ➖ |
192
- | 🧊 [ruby-oauth/oauth2 on CodeBerg][📜src-cb] | An Ethical Mirror ([Donate][🤝cb-donate]) | [💚][🤝cb-issues] | [💚][🤝cb-pulls] | ➖ | ⭕️ No Matrix | ➖ |
193
- | 🐙 [ruby-oauth/oauth2 on GitHub][📜src-gh] | Another Mirror | [💚][🤝gh-issues] | [💚][🤝gh-pulls] | [💚][📜gh-wiki] | 💯 Full Matrix | [💚][gh-discussions] |
194
- | 🎮️ [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] |
185
+ | 🧪 [ruby-oauth/oauth2 on GitLab][📜src-gl] | The Truth | [💚][🤝gl-issues] | [💚][🤝gl-pulls] | [💚][📜gl-wiki] | 🐭 Tiny Matrix | ➖ |
186
+ | 🧊 [ruby-oauth/oauth2 on CodeBerg][📜src-cb] | An Ethical Mirror ([Donate][🤝cb-donate]) | [💚][🤝cb-issues] | [💚][🤝cb-pulls] | ➖ | ⭕️ No Matrix | ➖ |
187
+ | 🐙 [ruby-oauth/oauth2 on GitHub][📜src-gh] | Another Mirror | [💚][🤝gh-issues] | [💚][🤝gh-pulls] | [💚][📜gh-wiki] | 💯 Full Matrix | [💚][gh-discussions] |
188
+ | 🎮️ [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] |
195
189
 
196
190
  </details>
197
191
 
@@ -202,7 +196,7 @@ Compatible with MRI Ruby 2.2.0+, and concordant releases of JRuby, and TruffleRu
202
196
  Available as part of the Tidelift Subscription.
203
197
 
204
198
  <details markdown="1">
205
- <summary>Need enterprise-level guarantees?</summary>
199
+ <summary>Need enterprise-level guarantees?</summary>
206
200
 
207
201
  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.
208
202
 
@@ -234,143 +228,46 @@ If bundler is not being used to manage dependencies, install the gem by executin
234
228
  gem install oauth2
235
229
  ```
236
230
 
237
- ### 🔒 Secure Installation
238
-
239
- <details markdown="1">
240
- <summary>For Medium or High Security Installations</summary>
241
-
242
- This gem is cryptographically signed, and has verifiable [SHA-256 and SHA-512][💎SHA_checksums] checksums by
243
- [stone_checksums][💎stone_checksums]. Be sure the gem you install hasn’t been tampered with
244
- by following the instructions below.
245
-
246
- Add my public key (if you haven’t already, expires 2045-04-29) as a trusted certificate:
247
-
248
- ```console
249
- gem cert --add <(curl -Ls https://raw.github.com/galtzo-floss/certs/main/pboling.pem)
250
- ```
251
-
252
- You only need to do that once. Then proceed to install with:
253
-
254
- ```console
255
- gem install oauth2 -P MediumSecurity
256
- ```
257
-
258
- The `MediumSecurity` trust profile will verify signed gems, but allow the installation of unsigned dependencies.
259
-
260
- This is necessary because not all of `oauth2`’s dependencies are signed, so we cannot use `HighSecurity`.
261
-
262
- If you want to up your security game full-time:
263
-
264
- ```console
265
- bundle config set --global trust-policy MediumSecurity
266
- ```
267
-
268
- `MediumSecurity` instead of `HighSecurity` is necessary if not all the gems you use are signed.
269
-
270
- NOTE: Be prepared to track down certs for signed gems and add them the same way you added mine.
271
-
272
- </details>
273
-
274
- ## What is new for v2.0?
275
-
276
- - Works with Ruby versions >= 2.2
277
- - Drop support for the expired MAC Draft (all versions)
278
- - Support IETF rfc7515 JSON Web Signature - JWS (since v2.0.12)
279
- - Support JWT `kid` for key discovery and management
280
- - Support IETF rfc7523 JWT Bearer Tokens (since v2.0.0)
281
- - Support IETF rfc7231 Relative Location in Redirect (since v2.0.0)
282
- - Support IETF rfc6749 Don't set oauth params when nil (since v2.0.0)
283
- - Support IETF rfc7009 Token Revocation (since v2.0.10, updated in v2.0.13 to support revocation via URL-encoded parameters)
284
- - 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)
285
- - 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`
286
- - Adds option to `OAuth2::Client#get_token`:
287
- - `:access_token_class` (`AccessToken`); user specified class to use for all calls to `get_token`
288
- - Adds option to `OAuth2::AccessToken#initialize`:
289
- - `:expires_latency` (`nil`); number of seconds by which AccessToken validity will be reduced to offset latency
290
- - By default, keys are transformed to snake case.
291
- - Original keys will still work as previously, in most scenarios, thanks to [snaky_hash][snaky_hash] gem.
292
- - However, this is a _breaking_ change if you rely on `response.parsed.to_h` to retain the original case, and the original wasn't snake case, as the keys in the result will be snake case.
293
- - As of version 2.0.4 you can turn key transformation off with the `snaky: false` option.
294
- - By default, the `:auth_scheme` is now `:basic_auth` (instead of `:request_body`)
295
- - Third-party strategies and gems may need to be updated if a provider was requiring client id/secret in the request body
296
- - [... A lot more](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md#200-2022-06-21-tag)
297
-
298
- [snaky_hash]: https://gitlab.com/ruby-oauth/snaky_hash
299
-
300
- ## Compatibility
301
-
302
- Targeted ruby compatibility is non-EOL versions of Ruby, currently 3.2, 3.3, and 3.4.
303
- Compatibility is further distinguished as "Best Effort Support" or "Incidental Support" for older versions of Ruby.
304
- This gem will install on Ruby versions >= v2.2 for 2.x releases.
305
- See `1-4-stable` branch for older rubies.
306
-
307
- <details markdown="1">
308
- <summary>Ruby Engine Compatibility Policy</summary>
309
-
310
- This gem is tested against MRI, JRuby, and Truffleruby.
311
- Each of those has varying versions that target a specific version of MRI Ruby.
312
- This gem should work in the just-listed Ruby engines according to the targeted MRI compatibility in the table below.
313
- If you would like to add support for additional engines,
314
- see [gemfiles/README.md](gemfiles/README.md), then submit a PR to the correct maintenance branch as according to the table below.
315
-
316
- </details>
317
-
318
- <details markdown="1">
319
- <summary>Ruby Version Compatibility Policy</summary>
320
-
321
- If something doesn't work on one of these interpreters, it's a bug.
322
-
323
- This library may inadvertently work (or seem to work) on other Ruby
324
- implementations; however, support will only be provided for the versions listed
325
- above.
326
-
327
- If you would like this library to support another Ruby version, you may
328
- volunteer to be a maintainer. Being a maintainer entails making sure all tests
329
- run and pass on that implementation. When something breaks on your
330
- implementation, you will be responsible for providing patches in a timely
331
- fashion. If critical issues for a particular implementation exist at the time
332
- of a major release, support for that Ruby version may be dropped.
333
-
334
- </details>
335
-
336
- | | Ruby OAuth2 Version | Maintenance Branch | Targeted Support | Best Effort Support | Incidental Support |
337
- |:----|---------------------|--------------------|----------------------|-------------------------|------------------------------|
338
- | 1️⃣ | 2.0.x | `main` | 3.2, 3.3, 3.4 | 2.5, 2.6, 2.7, 3.0, 3.1 | 2.2, 2.3, 2.4 |
339
- | 2️⃣ | 1.4.x | `1-4-stable` | 3.2, 3.3, 3.4 | 2.5, 2.6, 2.7, 3.0, 3.1 | 1.9, 2.0, 2.1, 2.2, 2.3, 2.4 |
340
- | 3️⃣ | older | N/A | Best of luck to you! | Please upgrade! | |
341
-
342
- NOTE: The 1.4 series will only receive critical security updates.
343
- See [SECURITY.md][🔐security] and [IRP.md][🔐irp].
344
-
345
231
  ## ⚙️ Configuration
346
232
 
347
- You can turn on additional warnings.
233
+ Global settings for the library:
348
234
 
349
235
  ```ruby
350
236
  OAuth2.configure do |config|
351
- # Turn on a warning like:
352
- # OAuth2::AccessToken.from_hash: `hash` contained more than one 'token' key
353
237
  config.silence_extra_tokens_warning = false # default: true
354
- # Set to true if you want to also show warnings about no tokens
355
- config.silence_no_tokens_warning = false # default: true,
238
+ config.silence_no_tokens_warning = false # default: true
356
239
  end
357
240
  ```
358
241
 
359
- The "extra tokens" problem comes from ambiguity in the spec about which token is the right token.
360
- Some OAuth 2.0 standards legitimately have multiple tokens.
361
- You may need to subclass `OAuth2::AccessToken`, or write your own custom alternative to it, and pass it in.
362
- Specify your custom class with the `access_token_class` option.
242
+ Filtering-related settings:
363
243
 
364
- If you only need one token, you can, as of v2.0.10,
365
- specify the exact token name you want to extract via the `OAuth2::AccessToken` using
366
- the `token_name` option.
244
+ ```ruby
245
+ OAuth2.configure do |config|
246
+ config.filtered_label = "[REDACTED]" # default: "[FILTERED]"
247
+ config.filtered_debug_keys += ["client_assertion"]
248
+ end
249
+ ```
367
250
 
368
- You'll likely need to do some source diving.
369
- This gem has 100% test coverage for lines and branches, so the specs are a great place to look for ideas.
370
- If you have time and energy, please contribute to the documentation!
251
+ - `filtered_label` controls the placeholder used when sensitive values are filtered from inspected objects and debug logging output.
252
+ - `filtered_debug_keys` controls which key names have their values redacted from debug logging output when `OAUTH_DEBUG=true`.
253
+ - Debug logging remains opt-in and should still be used cautiously in production environments.
371
254
 
372
255
  ## 🔧 Basic Usage
373
256
 
257
+ ### Client Initialization Options
258
+
259
+ `OAuth2::Client.new` accepts several options:
260
+
261
+ - `:site`: The base URL for the OAuth 2.0 provider.
262
+ - `:authorize_url`: The authorization endpoint (default: `"oauth/authorize"`).
263
+ - `:token_url`: The token endpoint (default: `"oauth/token"`).
264
+ - `:auth_scheme`: The authentication scheme (`:basic_auth`, `:request_body`, `:tls_client_auth`, `:private_key_jwt`). Default is `:basic_auth`.
265
+ - `:connection_opts`: Options for the underlying Faraday connection (timeouts, proxy, etc.).
266
+ - `:raise_errors`: Whether to raise `OAuth2::Error` on 400+ responses (default: `true`).
267
+
268
+ <details markdown="1">
269
+ <summary><em>authorize_url</em> and <em>token_url</em></summary>
270
+
374
271
  ### `authorize_url` and `token_url` are on site root (Just Works!)
375
272
 
376
273
  ```ruby
@@ -407,7 +304,7 @@ client = OAuth2::Client.new(
407
304
  "client_secret",
408
305
  site: "https://example.org/nested/directory/on/your/server",
409
306
  authorize_url: "/jaunty/authorize/",
410
- token_url: "/stirrups/access_token",
307
+ token_url: "/stirrups/access_token"
411
308
  )
412
309
  # => #<OAuth2::Client:0x00000001204c8288 @id="client_id", @secret="client_sec...
413
310
  client.auth_code.authorize_url(redirect_uri: "http://localhost:8080/oauth2/callback")
@@ -416,6 +313,25 @@ client.class.name
416
313
  # => OAuth2::Client
417
314
  ```
418
315
 
316
+ </details>
317
+
318
+ ### Advanced Initializers
319
+
320
+ ```ruby
321
+ client = OAuth2::Client.new(id, secret, site: site) do |faraday|
322
+ faraday.request(:url_encoded)
323
+ faraday.adapter(:net_http_persistent)
324
+ end
325
+ ```
326
+
327
+ ### AccessToken Features
328
+
329
+ Instances of `OAuth2::AccessToken` handle request signing and token expiration.
330
+
331
+ - **Snake Case & Indifferent Access**: `response.parsed` returns a `SnakyHash` allowing access via string/symbol and snake_case keys even if the provider returns CamelCase.
332
+ - **Auto-Refresh**: You can manually check `token.expired?` and call `token.refresh`.
333
+ - **Serialization**: Persist tokens using `token.to_hash` and restore via `OAuth2::AccessToken.from_hash(client, hash)`.
334
+
419
335
  ### snake_case and indifferent access in Response#parsed
420
336
 
421
337
  ```ruby
@@ -553,27 +469,49 @@ ENV["OAUTH_DEBUG"] = "true"
553
469
  By default, debug output will go to `$stdout`. This can be overridden when
554
470
  initializing your OAuth2::Client.
555
471
 
472
+ Sensitive values are filtered from debug logging output using:
473
+
474
+ - `OAuth2.config[:filtered_label]`
475
+ - `OAuth2.config[:filtered_debug_keys]`
476
+
477
+ Debug logging remains opt-in and should still be used cautiously in production environments.
478
+
556
479
  ```ruby
557
480
  require "oauth2"
558
481
  client = OAuth2::Client.new(
559
482
  "client_id",
560
483
  "client_secret",
561
484
  site: "https://example.org",
562
- logger: Logger.new("example.log", "weekly"),
485
+ logger: Logger.new("example.log", "weekly")
563
486
  )
564
487
  ```
565
488
 
566
489
  </details>
567
490
 
491
+ ### Request Target Trust Boundaries
492
+
493
+ This gem supports request flows that can involve absolute URLs in addition to relative paths.
494
+ That flexibility can expand trust boundaries when a token-bearing client is asked to send requests
495
+ to caller-provided targets.
496
+
497
+ Practical guidance:
498
+
499
+ - prefer relative paths where practical
500
+ - do not pass untrusted absolute URLs into token-bearing clients
501
+ - validate or allowlist request targets at the application layer today if your deployment has strict trust-boundary requirements
502
+
503
+ This release line does not yet enforce same-host or allowlist request policy automatically.
504
+ If stricter outbound request controls are needed, they should currently be implemented by the calling application.
505
+
568
506
  ### OAuth2::Response
569
507
 
570
508
  The `AccessToken` methods `#get`, `#post`, `#put` and `#delete` and the generic `#request`
571
- will return an instance of the #OAuth2::Response class.
509
+ will return an instance of the `OAuth2::Response` class.
572
510
 
573
511
  This instance contains a `#parsed` method that will parse the response body and
574
512
  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
575
- the body is a JSON object. It will return an Array if the body is a JSON
576
- array. Otherwise, it will return the original body string.
513
+ the body is a JSON object. It will return an Array if the body is a JSON
514
+ array. Otherwise, it will return the original body string.
577
515
 
578
516
  The original response body, headers, and status can be accessed via their
579
517
  respective methods.
@@ -615,16 +553,22 @@ Response instance will contain the `OAuth2::Error` instance.
615
553
 
616
554
  ### Authorization Grants
617
555
 
618
- Note on OAuth 2.1 (draft):
556
+ Currently, the Authorization Code, Implicit, Resource Owner Password Credentials, Client Credentials, and Assertion
557
+ authentication grant types have helper strategy classes that simplify client
558
+ use. They are available via the [`#auth_code`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/auth_code.rb),
559
+ [`#implicit`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/implicit.rb),
560
+ [`#password`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/password.rb),
561
+ [`#client_credentials`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/client_credentials.rb), and
562
+ [`#assertion`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/assertion.rb) methods respectively.
563
+
564
+ #### OAuth 2.1 (draft) Note:
619
565
 
620
- - 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.
621
- - Redirect URIs must be compared using exact string matching by the Authorization Server.
622
- - The Implicit grant (response_type=token) and the 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.
623
- - Bearer tokens in the query string are omitted due to security risks; prefer Authorization header usage.
624
- - Refresh tokens for public clients must either be sender-constrained (e.g., DPoP/MTLS) or one-time use.
625
- - The definitions of public and confidential clients are simplified to refer only to whether the client has credentials.
566
+ - **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.
567
+ - **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.
568
+ - **Redirect URIs** must be compared using exact string matching by the Authorization Server.
626
569
 
627
- References:
570
+ <details markdown="1">
571
+ <summary>OAuth 2.1 (draft) References</summary>
628
572
 
629
573
  - OAuth 2.1 draft: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13
630
574
  - Aaron Parecki: https://aaronparecki.com/2019/12/12/21/its-time-for-oauth-2-dot-1
@@ -633,13 +577,7 @@ References:
633
577
  - Video: https://www.youtube.com/watch?v=g_aVPdwBTfw
634
578
  - Differences overview: https://fusionauth.io/learn/expert-advice/oauth/differences-between-oauth-2-oauth-2-1/
635
579
 
636
- Currently, the Authorization Code, Implicit, Resource Owner Password Credentials, Client Credentials, and Assertion
637
- authentication grant types have helper strategy classes that simplify client
638
- use. They are available via the [`#auth_code`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/auth_code.rb),
639
- [`#implicit`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/implicit.rb),
640
- [`#password`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/password.rb),
641
- [`#client_credentials`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/client_credentials.rb), and
642
- [`#assertion`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/assertion.rb) methods respectively.
580
+ </details>
643
581
 
644
582
  These aren't full examples, but demonstrative of the differences between usage for each strategy.
645
583
 
@@ -681,431 +619,14 @@ access = client.auth_code.get_token("code_value", redirect_uri: "http://localhos
681
619
  You can always use the `#request` method on the `OAuth2::Client` instance to make
682
620
  requests for tokens for any Authentication grant type.
683
621
 
684
- ## 📘 Comprehensive Usage
685
-
686
- ### Common Flows (end-to-end)
687
-
688
- - Authorization Code (server-side web app):
689
-
690
- ```ruby
691
- require "oauth2"
692
- client = OAuth2::Client.new(
693
- ENV["CLIENT_ID"],
694
- ENV["CLIENT_SECRET"],
695
- site: "https://provider.example.com",
696
- redirect_uri: "https://my.app.example.com/oauth/callback",
697
- )
698
-
699
- # Step 1: redirect user to consent
700
- state = SecureRandom.hex(16)
701
- auth_url = client.auth_code.authorize_url(scope: "openid profile email", state: state)
702
- # redirect_to auth_url
703
-
704
- # Step 2: handle the callback
705
- # params[:code], params[:state]
706
- raise "state mismatch" unless params[:state] == state
707
- access = client.auth_code.get_token(params[:code])
708
-
709
- # Step 3: call APIs
710
- profile = access.get("/api/v1/me").parsed
711
- ```
712
-
713
- - Client Credentials (machine-to-machine):
714
-
715
- ```ruby
716
- client = OAuth2::Client.new(ENV["CLIENT_ID"], ENV["CLIENT_SECRET"], site: "https://provider.example.com")
717
- access = client.client_credentials.get_token(audience: "https://api.example.com")
718
- resp = access.get("/v1/things")
719
- ```
720
-
721
- - Resource Owner Password (legacy; avoid when possible):
722
-
723
- ```ruby
724
- access = client.password.get_token("jdoe", "s3cret", scope: "read")
725
- ```
726
-
727
- #### Examples
728
-
729
- <details markdown="1">
730
- <summary>JHipster UAA (Spring Cloud) password grant example (legacy; avoid when possible)</summary>
731
-
732
- ```ruby
733
- # This converts a Postman/Net::HTTP multipart token request to oauth2 gem usage.
734
- # JHipster UAA typically exposes the token endpoint at /uaa/oauth/token.
735
- # The original snippet included:
736
- # - Basic Authorization header for the client (web_app:changeit)
737
- # - X-XSRF-TOKEN header from a cookie (some deployments require it)
738
- # - grant_type=password with username/password and client_id
739
- # Using oauth2 gem, you don't need to build multipart bodies; the gem sends
740
- # application/x-www-form-urlencoded as required by RFC 6749.
741
-
742
- require "oauth2"
743
-
744
- client = OAuth2::Client.new(
745
- "web_app", # client_id
746
- "changeit", # client_secret
747
- site: "http://localhost:8080/uaa",
748
- token_url: "/oauth/token", # absolute under site (or "oauth/token" relative)
749
- auth_scheme: :basic_auth, # sends HTTP Basic Authorization header
750
- )
751
-
752
- # If your UAA requires an XSRF header for the token call, provide it as a header.
753
- # Often this is not required for token endpoints, but if your gateway enforces it,
754
- # obtain the value from the XSRF-TOKEN cookie and pass it here.
755
- xsrf_token = ENV["X_XSRF_TOKEN"] # e.g., pulled from a prior set-cookie value
756
-
757
- access = client.password.get_token(
758
- "admin", # username
759
- "admin", # password
760
- headers: xsrf_token ? {"X-XSRF-TOKEN" => xsrf_token} : {},
761
- # JHipster commonly also accepts/needs the client_id in the body; include if required:
762
- # client_id: "web_app",
763
- )
764
-
765
- puts access.token
766
- puts access.to_hash # full token response
767
- ```
768
-
769
- Notes:
770
-
771
- - Resource Owner Password Credentials (ROPC) is deprecated in OAuth 2.1 and discouraged. Prefer Authorization Code + PKCE.
772
- - If your deployment strictly demands the X-XSRF-TOKEN header, first fetch it from an endpoint that sets the XSRF-TOKEN cookie (often "/" or a login page) and pass it to headers.
773
- - For Basic auth, auth_scheme: :basic_auth handles the Authorization header; you do not need to base64-encode manually.
774
-
775
- </details>
776
-
777
- ### Instagram API (verb‑dependent token mode)
778
-
779
- Providers like Instagram require the access token to be sent differently depending on the HTTP verb:
780
-
781
- - GET requests: token must be in the query string (?access_token=...)
782
- - POST/DELETE requests: token must be in the Authorization header (Bearer ...)
783
-
784
- Since v2.0.15, you can configure an AccessToken with a verb‑dependent mode. The gem will choose how to send the token based on the request method.
785
-
786
- Example: exchanging and refreshing long‑lived Instagram tokens, and making API calls
787
-
788
- ```ruby
789
- require "oauth2"
790
-
791
- # NOTE: Users authenticate via Facebook Login to obtain a short‑lived user token (not shown here).
792
- # See Facebook Login docs for obtaining the initial short‑lived token.
793
-
794
- client = OAuth2::Client.new(nil, nil, site: "https://graph.instagram.com")
795
-
796
- # Start with a short‑lived token you already obtained via Facebook Login
797
- short_lived = OAuth2::AccessToken.new(
798
- client,
799
- ENV["IG_SHORT_LIVED_TOKEN"],
800
- # Key part: verb‑dependent mode
801
- mode: {get: :query, post: :header, delete: :header},
802
- )
803
-
804
- # 1) Exchange for a long‑lived token (Instagram requires GET with access_token in query)
805
- # Endpoint: GET https://graph.instagram.com/access_token
806
- # Params: grant_type=ig_exchange_token, client_secret=APP_SECRET
807
- exchange = short_lived.get(
808
- "/access_token",
809
- params: {
810
- grant_type: "ig_exchange_token",
811
- client_secret: ENV["IG_APP_SECRET"],
812
- # access_token param will be added automatically by the AccessToken (mode => :query for GET)
813
- },
814
- )
815
- long_lived_token_value = exchange.parsed["access_token"]
816
-
817
- long_lived = OAuth2::AccessToken.new(
818
- client,
819
- long_lived_token_value,
820
- mode: {get: :query, post: :header, delete: :header},
821
- )
822
-
823
- # 2) Refresh the long‑lived token (Instagram uses GET with token in query)
824
- # Endpoint: GET https://graph.instagram.com/refresh_access_token
825
- refresh_resp = long_lived.get(
826
- "/refresh_access_token",
827
- params: {grant_type: "ig_refresh_token"},
828
- )
829
- long_lived = OAuth2::AccessToken.new(
830
- client,
831
- refresh_resp.parsed["access_token"],
832
- mode: {get: :query, post: :header, delete: :header},
833
- )
834
-
835
- # 3) Typical API GET request (token in query automatically)
836
- me = long_lived.get("/me", params: {fields: "id,username"}).parsed
837
-
838
- # 4) Example POST (token sent via Bearer header automatically)
839
- # Note: Replace the path/params with a real Instagram Graph API POST you need,
840
- # such as publishing media via the Graph API endpoints.
841
- # long_lived.post("/me/media", body: {image_url: "https://...", caption: "hello"})
842
- ```
843
-
844
- Tips:
845
-
846
- - Avoid query‑string bearer tokens unless required by your provider. Instagram explicitly requires it for `GET` requests.
847
- - If you need a custom rule, you can pass a `Proc` for `mode`, e.g. `mode: ->(verb) { verb == :get ? :query : :header }`.
848
-
849
- ### Refresh Tokens
850
-
851
- When the server issues a refresh_token, you can refresh manually or implement an auto-refresh wrapper.
852
-
853
- - Manual refresh:
854
-
855
- ```ruby
856
- if access.expired?
857
- access = access.refresh
858
- end
859
- ```
860
-
861
- - Auto-refresh wrapper pattern:
862
-
863
- ```ruby
864
- class AutoRefreshingToken
865
- def initialize(token_provider, store: nil)
866
- @token = token_provider
867
- @store = store # e.g., something that responds to read/write for token data
868
- end
869
-
870
- def with(&blk)
871
- tok = ensure_fresh!
872
- blk ? blk.call(tok) : tok
873
- rescue OAuth2::Error => e
874
- # If a 401 suggests token invalidation, try one refresh and retry once
875
- if e.response && e.response.status == 401 && @token.refresh_token
876
- @token = @token.refresh
877
- @store.write(@token.to_hash) if @store
878
- retry
879
- end
880
- raise
881
- end
882
-
883
- private
884
-
885
- def ensure_fresh!
886
- if @token.expired? && @token.refresh_token
887
- @token = @token.refresh
888
- @store.write(@token.to_hash) if @store
889
- end
890
- @token
891
- end
892
- end
893
-
894
- # usage
895
- keeper = AutoRefreshingToken.new(access)
896
- keeper.with { |tok| tok.get("/v1/protected") }
897
- ```
898
-
899
- Persist the token across processes using `AccessToken#to_hash` and `AccessToken.from_hash(client, hash)`.
900
-
901
- ### Token Revocation (RFC 7009)
902
-
903
- You can revoke either the access token or the refresh token.
904
-
905
- ```ruby
906
- # Revoke the current access token
907
- access.revoke(token_type_hint: :access_token)
908
-
909
- # Or explicitly revoke the refresh token (often also invalidates associated access tokens)
910
- access.revoke(token_type_hint: :refresh_token)
911
- ```
912
-
913
- ### Client Configuration Tips
914
-
915
- #### Mutual TLS (mTLS) client authentication
916
-
917
- Some providers require OAuth requests (including the token request and subsequent API calls) to be sender‑constrained using mutual TLS (mTLS). With this gem, you enable mTLS by providing a client certificate/private key to Faraday via connection_opts.ssl and, if your provider requires it for client authentication, selecting the tls_client_auth auth_scheme.
918
-
919
- Example using PEM files (certificate and key):
920
-
921
- ```ruby
922
- require "oauth2"
923
- require "openssl"
924
-
925
- client = OAuth2::Client.new(
926
- ENV.fetch("CLIENT_ID"),
927
- ENV.fetch("CLIENT_SECRET"),
928
- site: "https://example.com",
929
- authorize_url: "/oauth/authorize/",
930
- token_url: "/oauth/token/",
931
- auth_scheme: :tls_client_auth, # if your AS requires mTLS-based client authentication
932
- connection_opts: {
933
- ssl: {
934
- client_cert: OpenSSL::X509::Certificate.new(File.read("localhost.pem")),
935
- client_key: OpenSSL::PKey::RSA.new(File.read("localhost-key.pem")),
936
- # Optional extras, uncomment as needed:
937
- # ca_file: "/path/to/ca-bundle.pem", # custom CA(s)
938
- # verify: true # enable server cert verification (recommended)
939
- },
940
- },
941
- )
942
-
943
- # Example token request (any grant type can be used). The mTLS handshake
944
- # will occur automatically on HTTPS calls using the configured cert/key.
945
- access = client.client_credentials.get_token
946
-
947
- # Subsequent resource requests will also use mTLS on HTTPS endpoints of `site`:
948
- resp = access.get("/v1/protected")
949
- ```
950
-
951
- Notes:
952
-
953
- - Files must contain the appropriate PEMs. The private key may be encrypted; if so, pass a password to `OpenSSL::PKey::RSA.new(File.read(path), ENV["KEY_PASSWORD"])`.
954
- - If your certificate and key are in a PKCS#12/PFX bundle, you can load them like:
955
- - `p12 = OpenSSL::PKCS12.new(File.read("client.p12"), ENV["P12_PASSWORD"])`
956
- - `client_cert = p12.certificate; client_key = p12.key`
957
- - Server trust:
958
- - If your environment does not have system CAs, specify `ca_file` or `ca_path` inside the `ssl:` hash.
959
- - Keep `verify: true` in production. Set `verify: false` only for local testing.
960
- - Faraday adapter: Any adapter that supports Ruby’s OpenSSL should work. `net_http` (default) and `net_http_persistent` are common choices.
961
- - Scope of mTLS: The SSL client cert is applied to any HTTPS request made by this client (token and resource requests) to the configured site base URL (and absolute URLs you call with the same client).
962
- - OIDC tie-in: Some OPs require tls_client_auth at the token endpoint per OIDC/OAuth specifications. That is enabled via `auth_scheme: :tls_client_auth` as shown above.
963
-
964
- #### Authentication schemes for the token request
965
-
966
- ```ruby
967
- OAuth2::Client.new(
968
- id,
969
- secret,
970
- site: "https://provider.example.com",
971
- auth_scheme: :basic_auth, # default. Alternatives: :request_body, :tls_client_auth, :private_key_jwt
972
- )
973
- ```
974
-
975
- #### Faraday connection, timeouts, proxy, custom adapter/middleware:
976
-
977
- ```ruby
978
- client = OAuth2::Client.new(
979
- id,
980
- secret,
981
- site: "https://provider.example.com",
982
- connection_opts: {
983
- request: {open_timeout: 5, timeout: 15},
984
- proxy: ENV["HTTPS_PROXY"],
985
- ssl: {verify: true},
986
- },
987
- ) do |faraday|
988
- faraday.request(:url_encoded)
989
- # faraday.response :logger, Logger.new($stdout) # see OAUTH_DEBUG below
990
- faraday.adapter(:net_http_persistent) # or any Faraday adapter you need
991
- end
992
- ```
993
-
994
- ##### Using flat query params (`Faraday::FlatParamsEncoder`)
995
-
996
- Some APIs expect repeated key parameters to be sent as flat params rather than arrays. Faraday provides `FlatParamsEncoder` for this purpose. You can configure the oauth2 client to use it when building requests.
997
-
998
- ```ruby
999
- require "faraday"
1000
-
1001
- client = OAuth2::Client.new(
1002
- id,
1003
- secret,
1004
- site: "https://api.example.com",
1005
- # Pass Faraday connection options to make FlatParamsEncoder the default
1006
- connection_opts: {
1007
- request: {params_encoder: Faraday::FlatParamsEncoder},
1008
- },
1009
- ) do |faraday|
1010
- faraday.request(:url_encoded)
1011
- faraday.adapter(:net_http)
1012
- end
1013
-
1014
- access = client.client_credentials.get_token
1015
-
1016
- # Example of a GET with two flat filter params (not an array):
1017
- # Results in: ?filter=order.clientCreatedTime%3E1445006997000&filter=order.clientCreatedTime%3C1445611797000
1018
- resp = access.get(
1019
- "/v1/orders",
1020
- params: {
1021
- # Provide the values as an array; FlatParamsEncoder expands them as repeated keys
1022
- filter: [
1023
- "order.clientCreatedTime>1445006997000",
1024
- "order.clientCreatedTime<1445611797000",
1025
- ],
1026
- },
1027
- )
1028
- ```
1029
-
1030
- If you instead need to build a raw Faraday connection yourself, the equivalent configuration is:
1031
-
1032
- ```ruby
1033
- conn = Faraday.new("https://api.example.com", request: {params_encoder: Faraday::FlatParamsEncoder})
1034
- ```
1035
-
1036
- #### Redirection
1037
-
1038
- The library follows up to `max_redirects` (default 5).
1039
- You can override per-client via `options[:max_redirects]`.
1040
-
1041
- ### Handling Responses and Errors
1042
-
1043
- - Parsing:
1044
-
1045
- ```ruby
1046
- resp = access.get("/v1/thing")
1047
- resp.status # Integer
1048
- resp.headers # Hash
1049
- resp.body # String
1050
- resp.parsed # SnakyHash::StringKeyed or Array when JSON array
1051
- ```
1052
-
1053
- - Error handling:
1054
-
1055
- ```ruby
1056
- begin
1057
- access.get("/v1/forbidden")
1058
- rescue OAuth2::Error => e
1059
- e.code # OAuth2 error code (when present)
1060
- e.description # OAuth2 error description (when present)
1061
- e.response # OAuth2::Response (full access to status/headers/body)
1062
- end
1063
- ```
1064
-
1065
- - Disable raising on 4xx/5xx to inspect the response yourself:
1066
-
1067
- ```ruby
1068
- client = OAuth2::Client.new(id, secret, site: site, raise_errors: false)
1069
- res = client.request(:get, "/v1/maybe-errors")
1070
- if res.status == 429
1071
- sleep res.headers["retry-after"].to_i
1072
- end
1073
- ```
1074
-
1075
- ### Making Raw Token Requests
1076
-
1077
- If a provider requires non-standard parameters or headers, you can call `client.get_token` directly:
1078
-
1079
- ```ruby
1080
- access = client.get_token({
1081
- grant_type: "client_credentials",
1082
- audience: "https://api.example.com",
1083
- headers: {"X-Custom" => "value"},
1084
- parse: :json, # override parsing
1085
- })
1086
- ```
1087
-
1088
- ### OpenID Connect (OIDC) Notes
1089
-
1090
- - If the token response includes an `id_token` (a JWT), this gem surfaces it but does not validate the signature. Use a JWT library and your provider's JWKs to verify it.
1091
- - For private_key_jwt client authentication, provide `auth_scheme: :private_key_jwt` and ensure your key configuration matches the provider requirements.
1092
- - See [OIDC.md](OIDC.md) for a more complete OIDC overview, example, and links to the relevant specifications.
1093
-
1094
- ### Debugging
1095
-
1096
- - Set environment variable `OAUTH_DEBUG=true` to enable verbose Faraday logging (uses the client-provided logger).
1097
- - To mirror a working curl request, ensure you set the same auth scheme, params, and content type. The Quick Example at the top shows a curl-to-ruby translation.
1098
-
1099
- ---
1100
-
1101
622
  ## 🦷 FLOSS Funding
1102
623
 
1103
624
  While ruby-oauth tools are free software and will always be, the project would benefit immensely from some funding.
1104
625
  Raising a monthly budget of... "dollars" would make the project more sustainable.
1105
626
 
1106
627
  We welcome both individual and corporate sponsors! We also offer a
1107
- wide array of funding channels to account for your preferences
1108
- (although currently [Open Collective][🖇osc] is our preferred funding platform).
628
+ wide array of funding channels to account for your preferences.
629
+ Currently, [Open Collective][🖇osc] is our preferred funding platform.
1109
630
 
1110
631
  **If you're working in a company that's making significant use of ruby-oauth tools we'd
1111
632
  appreciate it if you suggest to your company to become a ruby-oauth sponsor.**
@@ -1117,7 +638,7 @@ You can support the development of ruby-oauth tools via
1117
638
  [Open Collective][🖇osc]
1118
639
  and [Tidelift][🏙️entsup-tidelift].
1119
640
 
1120
- | 📍 NOTE |
641
+ | 📍 NOTE |
1121
642
  |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
1122
643
  | 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. |
1123
644
 
@@ -1145,7 +666,7 @@ No sponsors yet. Be the first!
1145
666
 
1146
667
  ### Another way to support open-source
1147
668
 
1148
- 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).
669
+ 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).
1149
670
 
1150
671
  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`.
1151
672
 
@@ -1157,15 +678,12 @@ I’m developing a new library, [floss_funding][🖇floss-funding-gem], designed
1157
678
 
1158
679
  ## 🔐 Security
1159
680
 
1160
- To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security).
1161
- Tidelift will coordinate the fix and disclosure.
1162
-
1163
- For more see [SECURITY.md][🔐security], [THREAT_MODEL.md][🔐threat-model], and [IRP.md][🔐irp].
681
+ See [SECURITY.md][🔐security].
1164
682
 
1165
683
  ## 🤝 Contributing
1166
684
 
1167
685
  If you need some ideas of where to help, you could work on adding more code coverage,
1168
- or if it is already 💯 (see [below](#code-coverage)) check [reek](REEK), [issues][🤝gh-issues], or [PRs][🤝gh-pulls],
686
+ or if it is already 💯 (see [below](#code-coverage)) check [issues][🤝gh-issues] or [PRs][🤝gh-pulls],
1169
687
  or use the gem and think about how it could be better.
1170
688
 
1171
689
  We [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] so if you make changes, remember to update it.
@@ -1178,12 +696,17 @@ See [CONTRIBUTING.md][🤝contributing].
1178
696
 
1179
697
  ### Code Coverage
1180
698
 
699
+ <details markdown="1">
700
+ <summary>Coverage service badges</summary>
701
+
1181
702
  [![Coverage Graph][🏀codecov-g]][🏀codecov]
1182
703
 
1183
704
  [![Coveralls Test Coverage][🏀coveralls-img]][🏀coveralls]
1184
705
 
1185
706
  [![QLTY Test Coverage][🏀qlty-covi]][🏀qlty-cov]
1186
707
 
708
+ </details>
709
+
1187
710
  ### 🪇 Code of Conduct
1188
711
 
1189
712
  Everyone interacting with this project's codebases, issue trackers,
@@ -1198,13 +721,13 @@ Made with [contributors-img][🖐contrib-rocks].
1198
721
  Also see GitLab Contributors: [https://gitlab.com/ruby-oauth/oauth2/-/graphs/main][🚎contributors-gl]
1199
722
 
1200
723
  <details>
1201
- <summary>⭐️ Star History</summary>
724
+ <summary>⭐️ Star History</summary>
1202
725
 
1203
- <a href="https://star-history.com/#ruby-oauth/oauth2&Date">
726
+ <a href="https://star-history.com/ruby-oauth/oauth2&Date">
1204
727
  <picture>
1205
- <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=ruby-oauth/oauth2&type=Date&theme=dark" />
1206
- <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=ruby-oauth/oauth2&type=Date" />
1207
- <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=ruby-oauth/oauth2&type=Date" />
728
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=ruby-oauth/oauth2&type=Date&theme=dark" />
729
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=ruby-oauth/oauth2&type=Date" />
730
+ <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=ruby-oauth/oauth2&type=Date" />
1208
731
  </picture>
1209
732
  </a>
1210
733
 
@@ -1212,19 +735,8 @@ Also see GitLab Contributors: [https://gitlab.com/ruby-oauth/oauth2/-/graphs/mai
1212
735
 
1213
736
  ## 📌 Versioning
1214
737
 
1215
- This Library adheres to [![Semantic Versioning 2.0.0][📌semver-img]][📌semver].
1216
- Violations of this scheme should be reported as bugs.
1217
- Specifically, if a minor or patch version is released that breaks backward compatibility,
1218
- a new version should be immediately released that restores compatibility.
1219
- Breaking changes to the public API will only be introduced with new major versions.
1220
-
1221
- > dropping support for a platform is both obviously and objectively a breaking change <br/>
1222
- >—Jordan Harband ([@ljharb](https://github.com/ljharb), maintainer of SemVer) [in SemVer issue 716][📌semver-breaking]
1223
-
1224
- I understand that policy doesn't work universally ("exceptions to every rule!"),
1225
- but it is the policy here.
1226
- As such, in many cases it is good to specify a dependency on this library using
1227
- the [Pessimistic Version Constraint][📌pvc] with two digits of precision.
738
+ This library follows [![Semantic Versioning 2.0.0][📌semver-img]][📌semver] for its public API where practical.
739
+ For most applications, prefer the [Pessimistic Version Constraint][📌pvc] with two digits of precision.
1228
740
 
1229
741
  For example:
1230
742
 
@@ -1235,8 +747,8 @@ spec.add_dependency("oauth2", "~> 2.0")
1235
747
  <details markdown="1">
1236
748
  <summary>📌 Is "Platform Support" part of the public API? More details inside.</summary>
1237
749
 
1238
- SemVer should, IMO, but doesn't explicitly, say that dropping support for specific Platforms
1239
- is a *breaking change* to an API, and for that reason the bike shedding is endless.
750
+ Dropping support for a platform can be a breaking change for affected users.
751
+ If a release changes supported platforms, it should be called out clearly in the changelog and versioned with that impact in mind.
1240
752
 
1241
753
  To get a better understanding of how SemVer is intended to work over a project's lifetime,
1242
754
  read this article from the creator of SemVer:
@@ -1250,25 +762,118 @@ See [CHANGELOG.md][📌changelog] for a list of releases.
1250
762
  ## 📄 License
1251
763
 
1252
764
  The gem is available as open source under the terms of
1253
- the [MIT License][📄license] [![License: MIT][📄license-img]][📄license-ref].
1254
- See [LICENSE.txt][📄license] for the official [Copyright Notice][📄copyright-notice-explainer].
765
+ the [MIT](MIT.md) [![License: MIT][📄license-img]][📄license-ref].
1255
766
 
1256
767
  ### © Copyright
1257
768
 
1258
- <ul>
1259
- <li>
1260
- Copyright (c) 2017 – 2025 Peter H. Boling, of
1261
- <a href="https://discord.gg/3qme4XHNKN">
1262
- Galtzo.com
1263
- <picture>
1264
- <img src="https://logos.galtzo.com/assets/images/galtzo-floss/avatar-128px-blank.svg" alt="Galtzo.com Logo (Wordless) by Aboling0, CC BY-SA 4.0" width="24">
1265
- </picture>
1266
- </a>, and oauth2 contributors.
1267
- </li>
1268
- <li>
1269
- Copyright (c) 2011 - 2013 Michael Bleigh and Intridea, Inc.
1270
- </li>
1271
- </ul>
769
+ See [LICENSE.md][📄license] for the official copyright notice.
770
+
771
+ <details markdown="1">
772
+ <summary>Copyright holders</summary>
773
+
774
+ - Copyright (c) 2010-2014, 2016-2017 Erik Michaels-Ober
775
+ - Copyright (c) 2010 Jeremy Kemper
776
+ - Copyright (c) 2010-2011 Michael Bleigh
777
+ - Copyright (c) 2010-2011 Paul Walker
778
+ - Copyright (c) 2010 rick
779
+ - Copyright (c) 2010 Tim Habermaas
780
+ - Copyright (c) 2010 Wynn Netherland
781
+ - Copyright (c) 2011 Alexander Lang
782
+ - Copyright (c) 2011 Alexander Lang
783
+ - Copyright (c) 2011 Greg Spurrier
784
+ - Copyright (c) 2011 Jay Adkisson
785
+ - Copyright (c) 2011 Luke Saunders
786
+ - Copyright (c) 2011 Paul Walker
787
+ - Copyright (c) 2011 Simon Gate
788
+ - Copyright (c) 2012 Bas Vodde
789
+ - Copyright (c) 2012 Damian Janowski
790
+ - Copyright (c) 2012 Daniël van de Burgt
791
+ - Copyright (c) 2012 Dorren Chen
792
+ - Copyright (c) 2012 Igor Sales
793
+ - Copyright (c) 2012 Leigh Caplan
794
+ - Copyright (c) 2012 Michael Andrews
795
+ - Copyright (c) 2012 Omer Rauchwerger
796
+ - Copyright (c) 2012 Saverio Trioni
797
+ - Copyright (c) 2012 Trent Ogren
798
+ - Copyright (c) 2012 Vsevolod Romashov
799
+ - Copyright (c) 2013 Antonio Tapiador del Dujo
800
+ - Copyright (c) 2013 Eduardo Gurgel
801
+ - Copyright (c) 2013 Geostellar Developer
802
+ - Copyright (c) 2013-2014, 2018 Niels Ganser
803
+ - Copyright (c) 2013 Rainux Luo
804
+ - Copyright (c) 2013 Taylor Hedberg
805
+ - Copyright (c) 2013 Tim Clem
806
+ - Copyright (c) 2014 Dave Stevens
807
+ - Copyright (c) 2014 Ellis Berner
808
+ - Copyright (c) 2014 Frank Macreery
809
+ - Copyright (c) 2014 Michael Bleigh
810
+ - Copyright (c) 2014 Olivier Lacan
811
+ - Copyright (c) 2014 Peter Souter
812
+ - Copyright (c) 2014 Ryan Williams
813
+ - Copyright (c) 2015 Andrew Cantino and Jeff Moore
814
+ - Copyright (c) 2015 Thomas Walpole
815
+ - Copyright (c) 2016 Bo Jeanes
816
+ - Copyright (c) 2016 Cody Cutrer
817
+ - Copyright (c) 2016 Edward Rudd
818
+ - Copyright (c) 2016 Lawrence Oluyede
819
+ - Copyright (c) 2016 Linus Pettersson
820
+ - Copyright (c) 2016 Motoshi Nishihira
821
+ - Copyright (c) 2017 Adrian Setyadi
822
+ - Copyright (c) 2017-2018 Benjamin Quorning
823
+ - Copyright (c) 2017 Christoph Petschnig
824
+ - Copyright (c) 2017, 2022 Nathaniel Bibler
825
+ - Copyright (c) 2017 Oleg
826
+ - Copyright (c) 2017 Samuel Cochran
827
+ - Copyright (c) 2017 tetsuya
828
+ - Copyright (c) 2017 Yury Velikanau
829
+ - Copyright (c) 2018 Alex Kowalczuk
830
+ - Copyright (c) 2018 asm__
831
+ - Copyright (c) 2018 David Christensen
832
+ - Copyright (c) 2018 fossabot
833
+ - Copyright (c) 2018 Jeff Moore
834
+ - Copyright (c) 2018 Jeff Moore
835
+ - Copyright (c) 2018 Jonathan del Strother
836
+ - Copyright (c) 2018 Joseph Page
837
+ - Copyright (c) 2018 Joseph Page
838
+ - Copyright (c) 2018 Lomey
839
+ - Copyright (c) 2018 Markus Bengts
840
+ - Copyright (c) 2018 Mathias Klippinge
841
+ - Copyright (c) 2018 nikz
842
+ - Copyright (c) 2018-2019, 2021-2022, 2024-2026 Peter H. Boling
843
+ - Copyright (c) 2019 Daniel Fockler
844
+ - Copyright (c) 2019 Elliot Crosby-McCullough
845
+ - Copyright (c) 2019 João Paulo
846
+ - Copyright (c) 2019 Orien Madgwick
847
+ - Copyright (c) 2019 Ryan T. Hosford
848
+ - Copyright (c) 2019 Tom Corley
849
+ - Copyright (c) 2020 anvox
850
+ - Copyright (c) 2020 Jesse Cotton
851
+ - Copyright (c) 2020 Olle Jonsson
852
+ - Copyright (c) 2020 Stephen Reid
853
+ - Copyright (c) 2021 Anders Carling
854
+ - Copyright (c) 2021 dobon
855
+ - Copyright (c) 2021 Jan Zaydowicz
856
+ - Copyright (c) 2021 Nicholas Palaniuk
857
+ - Copyright (c) 2021-2022 Stan Hu
858
+ - Copyright (c) 2022 Benjamin Quorning
859
+ - Copyright (c) 2022 Bouke van der Bijl
860
+ - Copyright (c) 2022 nov
861
+ - Copyright (c) 2022 Rick Selby
862
+ - Copyright (c) 2022 Ryo Takahashi
863
+ - Copyright (c) 2023 Jessie Young
864
+ - Copyright (c) 2023 Карим Гимадеев
865
+ - Copyright (c) 2024 Aboling0
866
+ - Copyright (c) 2024 Elise Wood
867
+ - Copyright (c) 2024 Manuel van Rijn
868
+ - Copyright (c) 2025-2026 Annibelle Boling
869
+ - Copyright (c) 2025 Mark James
870
+ - Copyright (c) 2025 Mridang Agarwalla
871
+ - Copyright (c) 2025 Sasa Rosic
872
+ - Copyright (c) 2026 Jonathan Grinstead
873
+ - Copyright (c) 2026 kain
874
+ - Copyright (c) 2026 StepSecurity Bot
875
+
876
+ </details>
1272
877
 
1273
878
  ## 🤑 A request for help
1274
879
 
@@ -1289,6 +894,8 @@ To say "thanks!" ☝️ Join the Discord or 👇️ send money.
1289
894
 
1290
895
  ### Please give the project a star ⭐ ♥.
1291
896
 
897
+ 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.
898
+
1292
899
  Thanks for RTFM. ☺️
1293
900
 
1294
901
  [⛳liberapay-img]: https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay&color=a51611&style=flat
@@ -1311,7 +918,7 @@ Thanks for RTFM. ☺️
1311
918
  [🖇polar-img]: https://img.shields.io/badge/polar-donate-a51611.svg?style=flat
1312
919
  [🖇polar]: https://polar.sh/pboling
1313
920
  [🖇kofi-img]: https://img.shields.io/badge/ko--fi-%E2%9C%93-a51611.svg?style=flat
1314
- [🖇kofi]: https://ko-fi.com/O5O86SNP4
921
+ [🖇kofi]: https://ko-fi.com/pboling
1315
922
  [🖇patreon-img]: https://img.shields.io/badge/patreon-donate-a51611.svg?style=flat
1316
923
  [🖇patreon]: https://patreon.com/galtzo
1317
924
  [🖇buyme-small-img]: https://img.shields.io/badge/buy_me_a_coffee-%E2%9C%93-a51611.svg?style=flat
@@ -1327,22 +934,19 @@ Thanks for RTFM. ☺️
1327
934
  [✉️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
1328
935
  [✉️ruby-friends]: https://app.daily.dev/squads/rubyfriends
1329
936
 
1330
- [⛳gg-discussions]: https://groups.google.com/g/oauth-ruby
1331
- [⛳gg-discussions-img]: https://img.shields.io/badge/google-group-0093D0.svg?style=for-the-badge&logo=google&logoColor=orange
1332
-
1333
937
  [✇bundle-group-pattern]: https://gist.github.com/pboling/4564780
1334
938
  [⛳️gem-namespace]: https://github.com/ruby-oauth/oauth2
1335
939
  [⛳️namespace-img]: https://img.shields.io/badge/namespace-OAuth2-3C2D2D.svg?style=square&logo=ruby&logoColor=white
1336
940
  [⛳️gem-name]: https://bestgems.org/gems/oauth2
1337
941
  [⛳️name-img]: https://img.shields.io/badge/name-oauth2-3C2D2D.svg?style=square&logo=rubygems&logoColor=red
1338
942
  [⛳️tag-img]: https://img.shields.io/github/tag/ruby-oauth/oauth2.svg
1339
- [⛳️tag]: http://github.com/ruby-oauth/oauth2/releases
943
+ [⛳️tag]: https://github.com/ruby-oauth/oauth2/releases
1340
944
  [🚂maint-blog]: http://www.railsbling.com/tags/oauth2
1341
945
  [🚂maint-blog-img]: https://img.shields.io/badge/blog-railsbling-0093D0.svg?style=for-the-badge&logo=rubyonrails&logoColor=orange
1342
946
  [🚂maint-contact]: http://www.railsbling.com/contact
1343
947
  [🚂maint-contact-img]: https://img.shields.io/badge/Contact-Maintainer-0093D0.svg?style=flat&logo=rubyonrails&logoColor=red
1344
948
  [💖🖇linkedin]: http://www.linkedin.com/in/peterboling
1345
- [💖🖇linkedin-img]: https://img.shields.io/badge/PeterBoling-LinkedIn-0B66C2?style=flat&logo=newjapanprowrestling
949
+ [💖🖇linkedin-img]: https://img.shields.io/badge/LinkedIn-Profile-0B66C2?style=flat&logo=newjapanprowrestling
1346
950
  [💖✌️wellfound]: https://wellfound.com/u/peter-boling
1347
951
  [💖✌️wellfound-img]: https://img.shields.io/badge/peter--boling-orange?style=flat&logo=wellfound
1348
952
  [💖💲crunchbase]: https://www.crunchbase.com/person/peter-boling
@@ -1371,7 +975,7 @@ Thanks for RTFM. ☺️
1371
975
  [💁🏼‍♂️peterboling]: http://www.peterboling.com
1372
976
  [🚂railsbling]: http://www.railsbling.com
1373
977
  [📜src-gl-img]: https://img.shields.io/badge/GitLab-FBA326?style=for-the-badge&logo=Gitlab&logoColor=orange
1374
- [📜src-gl]: https://gitlab.com/ruby-oauth/oauth2/
978
+ [📜src-gl]: https://gitlab.com/ruby-oauth/oauth2
1375
979
  [📜src-cb-img]: https://img.shields.io/badge/CodeBerg-4893CC?style=for-the-badge&logo=CodeBerg&logoColor=blue
1376
980
  [📜src-cb]: https://codeberg.org/ruby-oauth/oauth2
1377
981
  [📜src-gh-img]: https://img.shields.io/badge/GitHub-238636?style=for-the-badge&logo=Github&logoColor=green
@@ -1380,12 +984,10 @@ Thanks for RTFM. ☺️
1380
984
  [📜docs-head-rd-img]: https://img.shields.io/badge/YARD_on_Galtzo.com-HEAD-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white
1381
985
  [📜gl-wiki]: https://gitlab.com/ruby-oauth/oauth2/-/wikis/home
1382
986
  [📜gh-wiki]: https://github.com/ruby-oauth/oauth2/wiki
1383
- [📜gl-wiki-img]: https://img.shields.io/badge/wiki-examples-943CD2.svg?style=for-the-badge&logo=gitlab&logoColor=white
1384
- [📜gh-wiki-img]: https://img.shields.io/badge/wiki-examples-943CD2.svg?style=for-the-badge&logo=github&logoColor=white
987
+ [📜gl-wiki-img]: https://img.shields.io/badge/wiki-gitlab-943CD2.svg?style=for-the-badge&logo=gitlab&logoColor=white
988
+ [📜gh-wiki-img]: https://img.shields.io/badge/wiki-github-943CD2.svg?style=for-the-badge&logo=github&logoColor=white
1385
989
  [👽dl-rank]: https://bestgems.org/gems/oauth2
1386
990
  [👽dl-ranki]: https://img.shields.io/gem/rd/oauth2.svg
1387
- [👽oss-help]: https://www.codetriage.com/ruby-oauth/oauth2
1388
- [👽oss-helpi]: https://www.codetriage.com/ruby-oauth/oauth2/badges/users.svg
1389
991
  [👽version]: https://bestgems.org/gems/oauth2
1390
992
  [👽versioni]: https://img.shields.io/gem/v/oauth2.svg
1391
993
  [🏀qlty-mnt]: https://qlty.sh/gh/ruby-oauth/projects/oauth2
@@ -1396,32 +998,37 @@ Thanks for RTFM. ☺️
1396
998
  [🏀codecovi]: https://codecov.io/gh/ruby-oauth/oauth2/graph/badge.svg
1397
999
  [🏀coveralls]: https://coveralls.io/github/ruby-oauth/oauth2?branch=main
1398
1000
  [🏀coveralls-img]: https://coveralls.io/repos/github/ruby-oauth/oauth2/badge.svg?branch=main
1399
- [🖐codeQL]: https://github.com/ruby-oauth/oauth2/security/code-scanning
1400
- [🖐codeQL-img]: https://github.com/ruby-oauth/oauth2/actions/workflows/codeql-analysis.yml/badge.svg
1401
- [🚎1-an-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ancient.yml
1402
- [🚎1-an-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/ancient.yml/badge.svg
1001
+ [🚎ruby-2.4-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ruby-2.4.yml
1002
+ [🚎ruby-2.5-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ruby-2.5.yml
1003
+ [🚎ruby-2.6-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ruby-2.6.yml
1004
+ [🚎ruby-2.7-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ruby-2.7.yml
1005
+ [🚎ruby-3.0-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ruby-3.0.yml
1006
+ [🚎ruby-3.1-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ruby-3.1.yml
1007
+ [🚎ruby-3.2-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ruby-3.2.yml
1008
+ [🚎ruby-3.3-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ruby-3.3.yml
1009
+ [🚎ruby-3.4-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ruby-3.4.yml
1010
+ [🚎jruby-9.2-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/jruby-9.2.yml
1011
+ [🚎jruby-9.3-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/jruby-9.3.yml
1012
+ [🚎jruby-9.4-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/jruby-9.4.yml
1013
+ [🚎truby-22.3-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/truffleruby-22.3.yml
1014
+ [🚎truby-23.0-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/truffleruby-23.0.yml
1015
+ [🚎truby-23.1-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/truffleruby-23.1.yml
1016
+ [🚎truby-24.2-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/truffleruby-24.2.yml
1017
+ [🚎truby-25.0-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/truffleruby-25.0.yml
1403
1018
  [🚎2-cov-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/coverage.yml
1404
1019
  [🚎2-cov-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/coverage.yml/badge.svg
1405
1020
  [🚎3-hd-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/heads.yml
1406
1021
  [🚎3-hd-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/heads.yml/badge.svg
1407
- [🚎4-lg-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/legacy.yml
1408
- [🚎4-lg-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/legacy.yml/badge.svg
1409
1022
  [🚎5-st-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/style.yml
1410
1023
  [🚎5-st-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/style.yml/badge.svg
1411
- [🚎6-s-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/supported.yml
1412
- [🚎6-s-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/supported.yml/badge.svg
1413
- [🚎7-us-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/unsupported.yml
1414
- [🚎7-us-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/unsupported.yml/badge.svg
1415
- [🚎8-ho-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/hoary.yml
1416
- [🚎8-ho-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/hoary.yml/badge.svg
1024
+ [🚎9-t-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/truffle.yml
1025
+ [🚎9-t-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/truffle.yml/badge.svg
1417
1026
  [🚎10-j-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/jruby.yml
1418
1027
  [🚎10-j-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/jruby.yml/badge.svg
1419
1028
  [🚎11-c-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/current.yml
1420
1029
  [🚎11-c-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/current.yml/badge.svg
1421
1030
  [🚎12-crh-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/dep-heads.yml
1422
1031
  [🚎12-crh-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/dep-heads.yml/badge.svg
1423
- [🚎13-cbs-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/caboose.yml
1424
- [🚎13-cbs-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/caboose.yml/badge.svg
1425
1032
  [🚎13-🔒️-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/locked_deps.yml
1426
1033
  [🚎13-🔒️-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/locked_deps.yml/badge.svg
1427
1034
  [🚎14-🔓️-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/unlocked_deps.yml
@@ -1429,7 +1036,7 @@ Thanks for RTFM. ☺️
1429
1036
  [🚎15-🪪-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/license-eye.yml
1430
1037
  [🚎15-🪪-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/license-eye.yml/badge.svg
1431
1038
  [💎ruby-2.2i]: https://img.shields.io/badge/Ruby-2.2_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white
1432
- [💎ruby-2.3i]: https://img.shields.io/badge/Ruby-2.3-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
1039
+ [💎ruby-2.3i]: https://img.shields.io/badge/Ruby-2.3_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white
1433
1040
  [💎ruby-2.4i]: https://img.shields.io/badge/Ruby-2.4-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
1434
1041
  [💎ruby-2.5i]: https://img.shields.io/badge/Ruby-2.5-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
1435
1042
  [💎ruby-2.6i]: https://img.shields.io/badge/Ruby-2.6-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
@@ -1438,16 +1045,18 @@ Thanks for RTFM. ☺️
1438
1045
  [💎ruby-3.1i]: https://img.shields.io/badge/Ruby-3.1-CC342D?style=for-the-badge&logo=ruby&logoColor=white
1439
1046
  [💎ruby-3.2i]: https://img.shields.io/badge/Ruby-3.2-CC342D?style=for-the-badge&logo=ruby&logoColor=white
1440
1047
  [💎ruby-3.3i]: https://img.shields.io/badge/Ruby-3.3-CC342D?style=for-the-badge&logo=ruby&logoColor=white
1048
+ [💎ruby-3.4i]: https://img.shields.io/badge/Ruby-3.4-CC342D?style=for-the-badge&logo=ruby&logoColor=white
1049
+ [💎ruby-4.0i]: https://img.shields.io/badge/Ruby-4.0-CC342D?style=for-the-badge&logo=ruby&logoColor=white
1441
1050
  [💎ruby-c-i]: https://img.shields.io/badge/Ruby-current-CC342D?style=for-the-badge&logo=ruby&logoColor=green
1442
1051
  [💎ruby-headi]: https://img.shields.io/badge/Ruby-HEAD-CC342D?style=for-the-badge&logo=ruby&logoColor=blue
1443
- [💎truby-22.3i]: https://img.shields.io/badge/Truffle_Ruby-22.3_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=pink
1444
- [💎truby-23.0i]: https://img.shields.io/badge/Truffle_Ruby-23.0_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=pink
1445
- [💎truby-23.1i]: https://img.shields.io/badge/Truffle_Ruby-23.1_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=pink
1052
+ [💎truby-22.3i]: https://img.shields.io/badge/Truffle_Ruby-22.3-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
1053
+ [💎truby-23.0i]: https://img.shields.io/badge/Truffle_Ruby-23.0-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
1054
+ [💎truby-23.1i]: https://img.shields.io/badge/Truffle_Ruby-23.1-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
1055
+ [💎truby-24.2i]: https://img.shields.io/badge/Truffle_Ruby-24.2-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
1056
+ [💎truby-25.0i]: https://img.shields.io/badge/Truffle_Ruby-25.0-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
1446
1057
  [💎truby-c-i]: https://img.shields.io/badge/Truffle_Ruby-current-34BCB1?style=for-the-badge&logo=ruby&logoColor=green
1447
- [💎truby-headi]: https://img.shields.io/badge/Truffle_Ruby-HEAD-34BCB1?style=for-the-badge&logo=ruby&logoColor=blue
1448
- [💎jruby-9.1i]: https://img.shields.io/badge/JRuby-9.1_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=red
1449
- [💎jruby-9.2i]: https://img.shields.io/badge/JRuby-9.2_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=red
1450
- [💎jruby-9.3i]: https://img.shields.io/badge/JRuby-9.3_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=red
1058
+ [💎jruby-9.2i]: https://img.shields.io/badge/JRuby-9.2-FBE742?style=for-the-badge&logo=ruby&logoColor=red
1059
+ [💎jruby-9.3i]: https://img.shields.io/badge/JRuby-9.3-FBE742?style=for-the-badge&logo=ruby&logoColor=red
1451
1060
  [💎jruby-9.4i]: https://img.shields.io/badge/JRuby-9.4-FBE742?style=for-the-badge&logo=ruby&logoColor=red
1452
1061
  [💎jruby-c-i]: https://img.shields.io/badge/JRuby-current-FBE742?style=for-the-badge&logo=ruby&logoColor=green
1453
1062
  [💎jruby-headi]: https://img.shields.io/badge/JRuby-HEAD-FBE742?style=for-the-badge&logo=ruby&logoColor=blue
@@ -1458,38 +1067,35 @@ Thanks for RTFM. ☺️
1458
1067
  [🤝cb-issues]: https://codeberg.org/ruby-oauth/oauth2/issues
1459
1068
  [🤝cb-pulls]: https://codeberg.org/ruby-oauth/oauth2/pulls
1460
1069
  [🤝cb-donate]: https://donate.codeberg.org/
1461
- [🤝contributing]: CONTRIBUTING.md
1462
- [🏀codecov-g]: https://codecov.io/gh/ruby-oauth/oauth2/graphs/tree.svg
1070
+ [🤝contributing]: https://github.com/ruby-oauth/oauth2/blob/main/CONTRIBUTING.md
1071
+ [🏀codecov-g]: https://codecov.io/gh/ruby-oauth/oauth2/graph/badge.svg
1463
1072
  [🖐contrib-rocks]: https://contrib.rocks
1464
1073
  [🖐contributors]: https://github.com/ruby-oauth/oauth2/graphs/contributors
1465
1074
  [🖐contributors-img]: https://contrib.rocks/image?repo=ruby-oauth/oauth2
1466
1075
  [🚎contributors-gl]: https://gitlab.com/ruby-oauth/oauth2/-/graphs/main
1467
- [🪇conduct]: CODE_OF_CONDUCT.md
1076
+ [🪇conduct]: https://github.com/ruby-oauth/oauth2/blob/main/CODE_OF_CONDUCT.md
1468
1077
  [🪇conduct-img]: https://img.shields.io/badge/Contributor_Covenant-2.1-259D6C.svg
1469
1078
  [📌pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint
1470
1079
  [📌semver]: https://semver.org/spec/v2.0.0.html
1471
1080
  [📌semver-img]: https://img.shields.io/badge/semver-2.0.0-259D6C.svg?style=flat
1472
1081
  [📌semver-breaking]: https://github.com/semver/semver/issues/716#issuecomment-869336139
1473
1082
  [📌major-versions-not-sacred]: https://tom.preston-werner.com/2022/05/23/major-version-numbers-are-not-sacred.html
1474
- [📌changelog]: CHANGELOG.md
1083
+ [📌changelog]: https://github.com/ruby-oauth/oauth2/blob/main/CHANGELOG.md
1475
1084
  [📗keep-changelog]: https://keepachangelog.com/en/1.0.0/
1476
1085
  [📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-34495e.svg?style=flat
1477
1086
  [📌gitmoji]: https://gitmoji.dev
1478
1087
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
1479
1088
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
1480
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.526-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1481
- [🔐security]: SECURITY.md
1089
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.542-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1090
+ [🔐security]: https://github.com/ruby-oauth/oauth2/blob/main/SECURITY.md
1482
1091
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
1483
- [🔐irp]: IRP.md
1484
- [🔐irp-img]: https://img.shields.io/badge/IRP-259D6C.svg?style=flat
1485
- [🔐threat-model]: THREAT_MODEL.md
1486
- [🔐threat-model-img]: https://img.shields.io/badge/threat-model-259D6C.svg?style=flat
1487
1092
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
1488
- [📄license]: LICENSE.txt
1489
- [📄license-ref]: https://opensource.org/licenses/MIT
1093
+ [📄license]: LICENSE.md
1094
+ [📄license-ref]: MIT.md
1490
1095
  [📄license-img]: https://img.shields.io/badge/License-MIT-259D6C.svg
1491
- [📄license-compat]: https://dev.to/galtzo/how-to-check-license-compatibility-41h0
1492
- [📄license-compat-img]: https://img.shields.io/badge/Apache_Compatible:_Category_A-%E2%9C%93-259D6C.svg?style=flat&logo=Apache
1096
+ [📄license-compat]: https://www.apache.org/legal/resolved.html#category-a
1097
+ [📄license-compat-img]: https://img.shields.io/badge/Apache_Compatible:_Category_A-✓-259D6C.svg?style=flat&logo=Apache
1098
+
1493
1099
  [📄ilo-declaration]: https://www.ilo.org/declaration/lang--en/index.htm
1494
1100
  [📄ilo-declaration-img]: https://img.shields.io/badge/ILO_Fundamental_Principles-✓-259D6C.svg?style=flat
1495
1101
  [🚎yard-current]: http://rubydoc.info/gems/oauth2
@@ -1502,12 +1108,13 @@ Thanks for RTFM. ☺️
1502
1108
  [💎appraisal2-img]: https://img.shields.io/badge/appraised_by-appraisal2-34495e.svg?plastic&logo=ruby&logoColor=white
1503
1109
  [💎d-in-dvcs]: https://railsbling.com/posts/dvcs/put_the_d_in_dvcs/
1504
1110
 
1505
- <details>
1506
- <summary>
1507
- rel="me" Social Proofs
1508
- </summary>
1509
-
1510
- <a rel="me" alt="Follow me on Ruby.social" href="https://ruby.social/@galtzo"><img src="https://img.shields.io/mastodon/follow/109447111526622197?domain=https://ruby.social&style=social&label=Follow%20@galtzo%20on%20Ruby.social"></a>
1511
- <a rel="me" alt="Follow me on FLOSS.social" href="https://floss.social/@galtzo"><img src="https://img.shields.io/mastodon/follow/110304921404405715?domain=https://floss.social&style=social&label=Follow%20@galtzo%20on%20Floss.social"></a>
1512
-
1513
- </details>
1111
+ <!-- kettle-jem:metadata:start -->
1112
+ | Field | Value |
1113
+ |---|---|
1114
+ | Package | oauth2 |
1115
+ | Description | 🔐 A Ruby wrapper for the OAuth 2.0 Authorization Framework, including the OAuth 2.1 draft spec, and OpenID Connect (OIDC) |
1116
+ | Homepage | https://github.com/ruby-oauth/oauth2 |
1117
+ | Source | https://github.com/ruby-oauth/oauth2/tree/v2.0.21 |
1118
+ | License | `MIT` |
1119
+ | Funding | https://github.com/sponsors/pboling, https://issuehunt.io/u/pboling, https://ko-fi.com/pboling, https://liberapay.com/pboling/donate, https://opencollective.com/ruby-oauth, https://patreon.com/galtzo, https://polar.sh/pboling, https://thanks.dev/u/gh/pboling, https://tidelift.com/funding/github/rubygems/oauth2, https://www.buymeacoffee.com/pboling |
1120
+ <!-- kettle-jem:metadata:end -->