oauth2 2.0.12 → 2.0.14
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +396 -169
- data/CITATION.cff +20 -0
- data/CODE_OF_CONDUCT.md +24 -23
- data/CONTRIBUTING.md +103 -45
- data/FUNDING.md +77 -0
- data/LICENSE.txt +2 -2
- data/OIDC.md +158 -0
- data/README.md +844 -332
- data/REEK +0 -0
- data/RUBOCOP.md +71 -0
- data/lib/oauth2/access_token.rb +2 -0
- data/lib/oauth2/authenticator.rb +30 -1
- data/lib/oauth2/client.rb +7 -3
- data/lib/oauth2/error.rb +21 -3
- data/lib/oauth2/filtered_attributes.rb +21 -0
- data/lib/oauth2/strategy/auth_code.rb +10 -0
- data/lib/oauth2/strategy/implicit.rb +8 -0
- data/lib/oauth2/strategy/password.rb +8 -0
- data/lib/oauth2/version.rb +1 -1
- data/lib/oauth2.rb +36 -0
- data/sig/oauth2/access_token.rbs +25 -0
- data/sig/oauth2/authenticator.rbs +22 -0
- data/sig/oauth2/client.rbs +52 -0
- data/sig/oauth2/error.rbs +8 -0
- data/sig/oauth2/filtered_attributes.rbs +6 -0
- data/sig/oauth2/response.rbs +18 -0
- data/sig/oauth2/strategy.rbs +34 -0
- data/sig/oauth2/version.rbs +5 -0
- data/sig/oauth2.rbs +9 -0
- data.tar.gz.sig +0 -0
- metadata +72 -160
- metadata.gz.sig +0 -0
data/README.md
CHANGED
@@ -1,55 +1,97 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
1
|
+
[![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]
|
2
|
+
|
3
|
+
[🖼️oauth2-i]: https://logos.galtzo.com/assets/images/oauth/oauth2/avatar-192px.svg
|
4
|
+
[🖼️oauth2]: https://github.com/ruby-oauth/oauth2
|
5
|
+
[🖼️ruby-lang-i]: https://logos.galtzo.com/assets/images/ruby-lang/avatar-192px.svg
|
6
|
+
[🖼️ruby-lang]: https://www.ruby-lang.org/
|
7
|
+
[🖼️galtzo-i]: https://logos.galtzo.com/assets/images/galtzo-floss/avatar-192px.svg
|
8
|
+
[🖼️galtzo-discord]: https://discord.gg/3qme4XHNKN
|
9
|
+
|
10
|
+
# 🔐 OAuth 2.0 Authorization Framework
|
11
|
+
|
12
|
+
⭐️ including OAuth 2.1 draft spec & OpenID Connect (OIDC)
|
13
|
+
|
14
|
+
[![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] [![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 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 Caboose is an absolute WAGON][🚎13-cbs-wfi]][🚎13-cbs-wf] [![CI Test Coverage][🚎2-cov-wfi]][🚎2-cov-wf] [![CI Style][🚎5-st-wfi]][🚎5-st-wf] [![CodeQL][🖐codeQL-img]][🖐codeQL]
|
15
|
+
|
16
|
+
If ☝️ `ci_badges.map(&:color).detect { it != "green"}` [let me know][🖼️galtzo-discord], as I may have missed the [discord notification][🖼️galtzo-discord].
|
16
17
|
|
17
18
|
---
|
18
19
|
|
19
|
-
|
20
|
+
OTOH, 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.
|
21
|
+
|
22
|
+
[![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 to my FLOSS or refugee efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS or refugee efforts using Patreon][🖇patreon-img]][🖇patreon]
|
23
|
+
|
24
|
+
## 🌻 Synopsis
|
20
25
|
|
21
26
|
OAuth 2.0 is the industry-standard protocol for authorization.
|
22
27
|
OAuth 2.0 focuses on client developer simplicity while providing specific authorization flows for web applications,
|
23
28
|
desktop applications, mobile phones, and living room devices.
|
24
29
|
This is a RubyGem for implementing OAuth 2.0 clients (not servers) in Ruby applications.
|
25
30
|
|
26
|
-
|
27
|
-
|-----------------------------------------------|-------------------------------------------------------------------|---------------------------|--------------------------|---------------------------|--------------------------|------------------------------|
|
28
|
-
| 🧪 [oauth-xx/oauth2 on GitLab][📜src-gl] | The Truth | [💚][🤝gl-issues] | [💚][🤝gl-pulls] | [💚][📜wiki] | 🏀 Tiny Matrix | ➖ |
|
29
|
-
| 🧊 [oauth-xx/oauth2 on CodeBerg][📜src-cb] | An Ethical Mirror ([Donate][🤝cb-donate]) | ➖ | [💚][🤝cb-pulls] | ➖ | ⭕️ No Matrix | ➖ |
|
30
|
-
| 🐙 [oauth-xx/oauth2 on GitHub][📜src-gh] | A Dirty Mirror | [💚][🤝gh-issues] | [💚][🤝gh-pulls] | ➖ | 💯 Full Matrix | ➖ |
|
31
|
-
| 🤼 [OAuth Ruby Google Group][⛳gg-discussions] | "Active" | ➖ | ➖ | ➖ | ➖ | [💚][⛳gg-discussions] |
|
32
|
-
| 🎮️ [Discord Server][✉️discord-invite] | [![Live Chat on Discord][✉️discord-invite-img]][✉️discord-invite] | [Let's][✉️discord-invite] | [talk][✉️discord-invite] | [about][✉️discord-invite] | [this][✉️discord-invite] | [library!][✉️discord-invite] |
|
31
|
+
### Quick Example
|
33
32
|
|
34
|
-
|
33
|
+
<details>
|
34
|
+
<summary>Convert the following `curl` command into a token request using this gem...</summary>
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
```shell
|
37
|
+
curl --request POST \
|
38
|
+
--url 'https://login.microsoftonline.com/REDMOND_REDACTED/oauth2/token' \
|
39
|
+
--header 'content-type: application/x-www-form-urlencoded' \
|
40
|
+
--data grant_type=client_credentials \
|
41
|
+
--data client_id=REDMOND_CLIENT_ID \
|
42
|
+
--data client_secret=REDMOND_CLIENT_SECRET \
|
43
|
+
--data resource=REDMOND_RESOURCE_UUID
|
44
|
+
```
|
40
45
|
|
41
|
-
|
46
|
+
NOTE: In the ruby version below, certain params are passed to the `get_token` call, instead of the client creation.
|
42
47
|
|
43
|
-
|
44
|
-
|
48
|
+
```ruby
|
49
|
+
OAuth2::Client.new(
|
50
|
+
"REDMOND_CLIENT_ID", # client_id
|
51
|
+
"REDMOND_CLIENT_SECRET", # client_secret
|
52
|
+
auth_scheme: :request_body, # Other modes are supported: :basic_auth, :tls_client_auth, :private_key_jwt
|
53
|
+
token_url: "oauth2/token", # relative path, except with leading `/`, then absolute path
|
54
|
+
site: "https://login.microsoftonline.com/REDMOND_REDACTED",
|
55
|
+
). # The base path for token_url when it is relative
|
56
|
+
client_credentials. # There are many other types to choose from!
|
57
|
+
get_token(resource: "REDMOND_RESOURCE_UUID")
|
58
|
+
```
|
45
59
|
|
46
|
-
|
60
|
+
NOTE: `header` - The content type specified in the `curl` is already the default!
|
47
61
|
|
48
|
-
|
49
|
-
covering the latest patch for each of the following minor versions:
|
62
|
+
</details>
|
50
63
|
|
64
|
+
If it seems like you are in the wrong place, you might try one of these:
|
65
|
+
|
66
|
+
* [OAuth 2.0 Spec][oauth2-spec]
|
67
|
+
* [doorkeeper gem][doorkeeper-gem] for OAuth 2.0 server/provider implementation.
|
68
|
+
* [oauth sibling gem][sibling-gem] for OAuth 1.0a implementations in Ruby.
|
69
|
+
|
70
|
+
[oauth2-spec]: https://oauth.net/2/
|
71
|
+
[sibling-gem]: https://gitlab.com/ruby-oauth/oauth
|
72
|
+
[doorkeeper-gem]: https://github.com/doorkeeper-gem/doorkeeper
|
73
|
+
|
74
|
+
## 💡 Info you can shake a stick at
|
75
|
+
|
76
|
+
| Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] |
|
77
|
+
|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
78
|
+
| 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] |
|
79
|
+
| Works with Truffle Ruby | ![Truffle Ruby 22.3 Compat][💎truby-22.3i] ![Truffle Ruby 23.0 Compat][💎truby-23.0i] <br/> [![Truffle Ruby 23.1 Compat][💎truby-23.1i]][🚎9-t-wf] [![Truffle Ruby 24.1 Compat][💎truby-c-i]][🚎11-c-wf] |
|
80
|
+
| 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] |
|
81
|
+
| 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] |
|
82
|
+
| 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] |
|
83
|
+
| Documentation | [![Discussion][⛳gg-discussions-img]][⛳gg-discussions] [![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] [![Wiki][📜wiki-img]][📜wiki] |
|
84
|
+
| Compliance | [![License: MIT][📄license-img]][📄license-ref] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] |
|
85
|
+
| 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] |
|
86
|
+
| Support | [![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] |
|
87
|
+
| 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] |
|
88
|
+
| `...` 💖 | [![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] |
|
89
|
+
|
90
|
+
### Compatibility
|
91
|
+
|
92
|
+
* Operating Systems: Linux, MacOS, Windows
|
51
93
|
* MRI Ruby @ v2.3, v2.4, v2.5, v2.6, v2.7, v3.0, v3.1, v3.2, v3.3, v3.4, HEAD
|
52
|
-
|
94
|
+
* NOTE: This gem will still install on ruby v2.2, but vanilla GitHub Actions no longer supports testing against it, so YMMV.
|
53
95
|
* JRuby @ v9.2, v9.3, v9.4, v10.0, HEAD
|
54
96
|
* TruffleRuby @ v23.1, v24.1, HEAD
|
55
97
|
* gem `faraday` @ v0, v1, v2, HEAD ⏩️ [lostisland/faraday](https://github.com/lostisland/faraday)
|
@@ -57,33 +99,48 @@ covering the latest patch for each of the following minor versions:
|
|
57
99
|
* gem `logger` @ v1.2, v1.5, v1.7, HEAD ⏩️ [ruby/logger](https://github.com/ruby/logger)
|
58
100
|
* gem `multi_xml` @ v0.5, v0.6, v0.7, HEAD ⏩️ [sferik/multi_xml](https://github.com/sferik/multi_xml)
|
59
101
|
* gem `rack` @ v1.2, v1.6, v2, v3, HEAD ⏩️ [rack/rack](https://github.com/rack/rack)
|
60
|
-
* gem `snaky_hash` @ v2, HEAD ⏩️ [oauth
|
61
|
-
* gem `version_gem` @ v1, HEAD ⏩️ [oauth
|
102
|
+
* gem `snaky_hash` @ v2, HEAD ⏩️ [ruby-oauth/snaky_hash](https://gitlab.com/ruby-oauth/snaky_hash)
|
103
|
+
* gem `version_gem` @ v1, HEAD ⏩️ [ruby-oauth/version_gem](https://gitlab.com/ruby-oauth/version_gem)
|
62
104
|
|
63
|
-
The last two were extracted from this gem. They are part of the `oauth
|
105
|
+
The last two were extracted from this gem. They are part of the `ruby-oauth` org,
|
64
106
|
and are developed in tight collaboration with this gem.
|
65
107
|
|
66
108
|
Also, where reasonable, tested against the runtime dependencies of those dependencies:
|
67
109
|
|
68
110
|
* gem `hashie` @ v0, v1, v2, v3, v4, v5, HEAD ⏩️ [hashie/hashie](https://github.com/hashie/hashie)
|
69
111
|
|
112
|
+
#### Upgrading Runtime Gem Dependencies
|
113
|
+
|
114
|
+
This project sits underneath a large portion of the authorization systems on the internet.
|
115
|
+
According to GitHub's project tracking, which I believe only reports on public projects,
|
116
|
+
[100,000+ projects](https://github.com/ruby-oauth/oauth2/network/dependents), and
|
117
|
+
[500+ packages](https://github.com/ruby-oauth/oauth2/network/dependents?dependent_type=PACKAGE) depend on this project.
|
118
|
+
|
119
|
+
That means it is painful for the Ruby community when this gem forces updates to its runtime dependencies.
|
120
|
+
|
121
|
+
As a result, great care, and a lot of time, have been invested to ensure this gem is working with all the
|
122
|
+
leading versions per each minor version of Ruby of all the runtime dependencies it can install with.
|
123
|
+
|
124
|
+
What does that mean specifically for the runtime dependencies?
|
125
|
+
|
126
|
+
We have 100% test coverage of lines and branches, and this test suite runs across a large matrix
|
127
|
+
covering the latest patch for each of the following minor versions:
|
128
|
+
|
129
|
+
| 🚚 _Amazing_ test matrix was brought to you by | 🔎 appraisal2 🔎 |
|
130
|
+
|------------------------------------------------|--------------------------------------------------------------------------------------|
|
131
|
+
| 👟 Check it out! | ✨ [github.com/appraisal-rb/appraisal2](https://github.com/appraisal-rb/appraisal2) ✨ |
|
132
|
+
|
70
133
|
#### You should upgrade this gem with confidence\*.
|
71
134
|
|
72
135
|
- This gem follows a _strict & correct_ (according to the maintainer of SemVer; [more info][sv-pub-api]) interpretation of SemVer.
|
73
|
-
|
74
|
-
|
136
|
+
- Dropping support for **any** of the runtime dependency versions above will be a major version bump.
|
137
|
+
- If you aren't on one of the minor versions above, make getting there a priority.
|
75
138
|
- You should upgrade the dependencies of this gem with confidence\*.
|
76
139
|
- Please do upgrade, and then, when it goes smooth as butter [please sponsor me][🖇sponsor]. Thanks!
|
77
140
|
|
78
141
|
[sv-pub-api]: #-is-platform-support-part-of-the-public-api
|
79
142
|
|
80
|
-
\* MIT license;
|
81
|
-
|
82
|
-
| 🚚 Test matrix brought to you by | 🔎 appraisal++ |
|
83
|
-
|----------------------------------|-------------------------------------------------------------------------|
|
84
|
-
| Adds back support for old Rubies | ✨ [appraisal PR #250](https://github.com/thoughtbot/appraisal/pull/250) |
|
85
|
-
| Adds support for `eval_gemfile` | ✨ [appraisal PR #248](https://github.com/thoughtbot/appraisal/pull/248) |
|
86
|
-
| Please review | my PRs! |
|
143
|
+
\* MIT license; The only guarantees I make are for [enterprise support](#enterprise-support).
|
87
144
|
|
88
145
|
<details>
|
89
146
|
<summary>Standard Library Dependencies</summary>
|
@@ -96,67 +153,49 @@ The various versions of each are tested via the Ruby test matrix, along with wha
|
|
96
153
|
* time
|
97
154
|
* logger (removed from stdlib in Ruby 3.5 so added as runtime dependency in v2.0.10)
|
98
155
|
|
99
|
-
If you use a gem version it should work fine!
|
156
|
+
If you use a gem version of a core Ruby library it should work fine!
|
100
157
|
|
101
158
|
</details>
|
102
159
|
|
103
|
-
###
|
160
|
+
### Federated DVCS
|
104
161
|
|
105
|
-
|
162
|
+
<details>
|
163
|
+
<summary>Find this repo on other forges (Coming soon!)</summary>
|
106
164
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
--data resource=REDMOND_RESOURCE_UUID
|
115
|
-
```
|
165
|
+
| Federated [DVCS][💎d-in-dvcs] Repository | Status | Issues | PRs | Wiki | CI | Discussions |
|
166
|
+
|-----------------------------------------------|-----------------------------------------------------------------------|---------------------------|--------------------------|---------------------------|--------------------------|------------------------------|
|
167
|
+
| 🧪 [ruby-oauth/oauth2 on GitLab][📜src-gl] | The Truth | [💚][🤝gl-issues] | [💚][🤝gl-pulls] | [💚][📜wiki] | 🏀 Tiny Matrix | ➖ |
|
168
|
+
| 🧊 [ruby-oauth/oauth2 on CodeBerg][📜src-cb] | An Ethical Mirror ([Donate][🤝cb-donate]) | [💚][🤝cb-issues] | [💚][🤝cb-pulls] | ➖ | ⭕️ No Matrix | ➖ |
|
169
|
+
| 🐙 [ruby-oauth/oauth2 on GitHub][📜src-gh] | Another Mirror | [💚][🤝gh-issues] | [💚][🤝gh-pulls] | ➖ | 💯 Full Matrix | [💚][gh-discussions] |
|
170
|
+
| 🤼 [OAuth Ruby Google Group][⛳gg-discussions] | "Active" | ➖ | ➖ | ➖ | ➖ | [💚][⛳gg-discussions] |
|
171
|
+
| 🎮️ [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] |
|
116
172
|
|
117
|
-
|
173
|
+
</details>
|
118
174
|
|
119
|
-
|
120
|
-
OAuth2::Client.new(
|
121
|
-
"REDMOND_CLIENT_ID", # client_id
|
122
|
-
"REDMOND_CLIENT_SECRET", # client_secret
|
123
|
-
auth_scheme: :request_body, # Other modes are supported: :basic_auth, :tls_client_auth, :private_key_jwt
|
124
|
-
token_url: "oauth2/token", # relative path, except with leading `/`, then absolute path
|
125
|
-
site: "https://login.microsoftonline.com/REDMOND_REDACTED",
|
126
|
-
). # The base path for token_url when it is relative
|
127
|
-
client_credentials. # There are many other types to choose from!
|
128
|
-
get_token(resource: "REDMOND_RESOURCE_UUID")
|
129
|
-
```
|
175
|
+
[gh-discussions]: https://github.com/ruby-oauth/oauth2/discussions
|
130
176
|
|
131
|
-
|
177
|
+
### Enterprise Support [](https://tidelift.com/subscription/pkg/rubygems-oauth2?utm_source=rubygems-oauth2&utm_medium=referral&utm_campaign=readme)
|
132
178
|
|
133
|
-
|
134
|
-
One of these might be what you are looking for:
|
179
|
+
Available as part of the Tidelift Subscription.
|
135
180
|
|
136
|
-
|
137
|
-
|
138
|
-
* [oauth sibling gem][sibling-gem] for OAuth 1.0 implementations in Ruby.
|
181
|
+
<details>
|
182
|
+
<summary>Need enterprise-level guarantees?</summary>
|
139
183
|
|
140
|
-
|
141
|
-
[sibling-gem]: https://gitlab.com/oauth-xx/oauth
|
142
|
-
[doorkeeper-gem]: https://github.com/doorkeeper-gem/doorkeeper
|
184
|
+
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.
|
143
185
|
|
144
|
-
|
186
|
+
[![Get help from me on Tidelift][🏙️entsup-tidelift-img]][🏙️entsup-tidelift]
|
187
|
+
|
188
|
+
- 💡Subscribe for support guarantees covering _all_ your FLOSS dependencies
|
189
|
+
- 💡Tidelift is part of [Sonar][🏙️entsup-tidelift-sonar]
|
190
|
+
- 💡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
|
191
|
+
|
192
|
+
Alternatively:
|
193
|
+
|
194
|
+
- [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite]
|
195
|
+
- [![Get help from me on Upwork][👨🏼🏫expsup-upwork-img]][👨🏼🏫expsup-upwork]
|
196
|
+
- [![Get help from me on Codementor][👨🏼🏫expsup-codementor-img]][👨🏼🏫expsup-codementor]
|
145
197
|
|
146
|
-
|
147
|
-
|-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
148
|
-
| Works with JRuby | [![JRuby 9.2 Compat][💎jruby-9.2i]][🚎10-j-wf] [![JRuby 9.3 Compat][💎jruby-9.3i]][🚎10-j-wf] [![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] |
|
149
|
-
| Works with Truffle Ruby | [![Truffle Ruby 23.1 Compat][💎truby-23.1i]][🚎9-t-wf] [![Truffle Ruby 24.1 Compat][💎truby-c-i]][🚎11-c-wf] [![Truffle Ruby HEAD Compat][💎truby-headi]][🚎3-hd-wf] |
|
150
|
-
| 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] |
|
151
|
-
| Works with MRI Ruby 2 | [![Ruby 2.3 Compat][💎ruby-2.3i]][🚎13-cbs-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] |
|
152
|
-
| 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] |
|
153
|
-
| Documentation | [![Discussion][⛳gg-discussions-img]][⛳gg-discussions] [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![HEAD on RubyDoc.info][📜docs-head-rd-img]][🚎yard-head] [![BDFL Blog][🚂bdfl-blog-img]][🚂bdfl-blog] [![Wiki][📜wiki-img]][📜wiki] |
|
154
|
-
| Compliance | [![License: MIT][📄license-img]][📄license-ref] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] |
|
155
|
-
| Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] |
|
156
|
-
| Support | [![Live Chat on Discord][✉️discord-invite-img]][✉️discord-invite] [![Get help from me on Upwork][👨🏼🏫expsup-upwork-img]][👨🏼🏫expsup-upwork] [![Get help from me on Codementor][👨🏼🏫expsup-codementor-img]][👨🏼🏫expsup-codementor] |
|
157
|
-
| Enterprise Support | [![Get help from me on Tidelift][🏙️entsup-tidelift-img]][🏙️entsup-tidelift]<br/>💡Subscribe for support guarantees covering _all_ FLOSS dependencies!<br/>💡Tidelift is part of [Sonar][🏙️entsup-tidelift-sonar]!<br/>💡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! |
|
158
|
-
| Comrade BDFL 🎖️ | [![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 BDFL][🚂bdfl-contact-img]][🚂bdfl-contact] [![My technical writing][💖💁🏼♂️devto-img]][💖💁🏼♂️devto] |
|
159
|
-
| `...` 💖 | [![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] |
|
198
|
+
</details>
|
160
199
|
|
161
200
|
## 🚀 Release Documentation
|
162
201
|
|
@@ -167,6 +206,7 @@ One of these might be what you are looking for:
|
|
167
206
|
|
168
207
|
| Version | Release Date | CHANGELOG | README |
|
169
208
|
|---------|--------------|---------------------------------------|---------------------------------|
|
209
|
+
| 2.0.13 | 2025-08-30 | [v2.0.13 CHANGELOG][2.0.13-changelog] | [v2.0.13 README][2.0.13-readme] |
|
170
210
|
| 2.0.12 | 2025-05-31 | [v2.0.12 CHANGELOG][2.0.12-changelog] | [v2.0.12 README][2.0.12-readme] |
|
171
211
|
| 2.0.11 | 2025-05-23 | [v2.0.11 CHANGELOG][2.0.11-changelog] | [v2.0.11 README][2.0.11-readme] |
|
172
212
|
| 2.0.10 | 2025-05-17 | [v2.0.10 CHANGELOG][2.0.10-changelog] | [v2.0.10 README][2.0.10-readme] |
|
@@ -180,35 +220,38 @@ One of these might be what you are looking for:
|
|
180
220
|
| 2.0.2 | 2022-06-24 | [v2.0.2 CHANGELOG][2.0.2-changelog] | [v2.0.2 README][2.0.2-readme] |
|
181
221
|
| 2.0.1 | 2022-06-22 | [v2.0.1 CHANGELOG][2.0.1-changelog] | [v2.0.1 README][2.0.1-readme] |
|
182
222
|
| 2.0.0 | 2022-06-21 | [v2.0.0 CHANGELOG][2.0.0-changelog] | [v2.0.0 README][2.0.0-readme] |
|
223
|
+
|
183
224
|
</details>
|
184
225
|
|
185
|
-
[2.0.
|
186
|
-
[2.0.
|
187
|
-
[2.0.
|
188
|
-
[2.0.
|
189
|
-
[2.0.
|
190
|
-
[2.0.
|
191
|
-
[2.0.
|
192
|
-
[2.0.
|
193
|
-
[2.0.
|
194
|
-
[2.0.
|
195
|
-
[2.0.
|
196
|
-
[2.0.
|
197
|
-
[2.0.
|
198
|
-
|
199
|
-
|
200
|
-
[2.0.
|
201
|
-
[2.0.
|
202
|
-
[2.0.
|
203
|
-
[2.0.
|
204
|
-
[2.0.
|
205
|
-
[2.0.
|
206
|
-
[2.0.
|
207
|
-
[2.0.
|
208
|
-
[2.0.
|
209
|
-
[2.0.
|
210
|
-
[2.0.
|
211
|
-
[2.0.
|
226
|
+
[2.0.13-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#2013---2025-08-30
|
227
|
+
[2.0.12-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#2012---2025-05-31
|
228
|
+
[2.0.11-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#2011---2025-05-23
|
229
|
+
[2.0.10-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#2010---2025-05-17
|
230
|
+
[2.0.9-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#209---2022-09-16
|
231
|
+
[2.0.8-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#208---2022-09-01
|
232
|
+
[2.0.7-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#207---2022-08-22
|
233
|
+
[2.0.6-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#206---2022-07-13
|
234
|
+
[2.0.5-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#205---2022-07-07
|
235
|
+
[2.0.4-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#204---2022-07-01
|
236
|
+
[2.0.3-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#203---2022-06-28
|
237
|
+
[2.0.2-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#202---2022-06-24
|
238
|
+
[2.0.1-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#201---2022-06-22
|
239
|
+
[2.0.0-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#200---2022-06-21
|
240
|
+
|
241
|
+
[2.0.13-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v2.0.13/README.md
|
242
|
+
[2.0.12-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v2.0.12/README.md
|
243
|
+
[2.0.11-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v2.0.11/README.md
|
244
|
+
[2.0.10-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v2.0.10/README.md
|
245
|
+
[2.0.9-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v2.0.9/README.md
|
246
|
+
[2.0.8-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v2.0.8/README.md
|
247
|
+
[2.0.7-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v2.0.7/README.md
|
248
|
+
[2.0.6-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v2.0.6/README.md
|
249
|
+
[2.0.5-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v2.0.5/README.md
|
250
|
+
[2.0.4-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v2.0.4/README.md
|
251
|
+
[2.0.3-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v2.0.3/README.md
|
252
|
+
[2.0.2-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v2.0.2/README.md
|
253
|
+
[2.0.1-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v2.0.1/README.md
|
254
|
+
[2.0.0-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v2.0.0/README.md
|
212
255
|
|
213
256
|
### Older Releases
|
214
257
|
|
@@ -231,77 +274,86 @@ One of these might be what you are looking for:
|
|
231
274
|
| 1.4.0 | Jun 9, 2017 | [v1.4.0 CHANGELOG][1.4.0-changelog] | [v1.4.0 README][1.4.0-readme] |
|
232
275
|
</details>
|
233
276
|
|
234
|
-
[1.4.11-changelog]: https://gitlab.com/oauth
|
235
|
-
[1.4.10-changelog]: https://gitlab.com/oauth
|
236
|
-
[1.4.9-changelog]: https://gitlab.com/oauth
|
237
|
-
[1.4.8-changelog]: https://gitlab.com/oauth
|
238
|
-
[1.4.7-changelog]: https://gitlab.com/oauth
|
239
|
-
[1.4.6-changelog]: https://gitlab.com/oauth
|
240
|
-
[1.4.5-changelog]: https://gitlab.com/oauth
|
241
|
-
[1.4.4-changelog]: https://gitlab.com/oauth
|
242
|
-
[1.4.3-changelog]: https://gitlab.com/oauth
|
243
|
-
[1.4.2-changelog]: https://gitlab.com/oauth
|
244
|
-
[1.4.1-changelog]: https://gitlab.com/oauth
|
245
|
-
[1.4.0-changelog]: https://gitlab.com/oauth
|
246
|
-
|
247
|
-
[1.4.11-readme]: https://gitlab.com/oauth
|
248
|
-
[1.4.10-readme]: https://gitlab.com/oauth
|
249
|
-
[1.4.9-readme]: https://gitlab.com/oauth
|
250
|
-
[1.4.8-readme]: https://gitlab.com/oauth
|
251
|
-
[1.4.7-readme]: https://gitlab.com/oauth
|
252
|
-
[1.4.6-readme]: https://gitlab.com/oauth
|
253
|
-
[1.4.5-readme]: https://gitlab.com/oauth
|
254
|
-
[1.4.4-readme]: https://gitlab.com/oauth
|
255
|
-
[1.4.3-readme]: https://gitlab.com/oauth
|
256
|
-
[1.4.2-readme]: https://gitlab.com/oauth
|
257
|
-
[1.4.1-readme]: https://gitlab.com/oauth
|
258
|
-
[1.4.0-readme]: https://gitlab.com/oauth
|
277
|
+
[1.4.11-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#1411---2022-09-16
|
278
|
+
[1.4.10-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#1410---2022-07-01
|
279
|
+
[1.4.9-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#149---2022-02-20
|
280
|
+
[1.4.8-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#148---2022-02-18
|
281
|
+
[1.4.7-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#147---2021-03-19
|
282
|
+
[1.4.6-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#146---2021-03-19
|
283
|
+
[1.4.5-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#145---2021-03-18
|
284
|
+
[1.4.4-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#144---2020-02-12
|
285
|
+
[1.4.3-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#143---2020-01-29
|
286
|
+
[1.4.2-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#142---2019-10-01
|
287
|
+
[1.4.1-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#141---2018-10-13
|
288
|
+
[1.4.0-changelog]: https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md?ref_type=heads#140---2017-06-09
|
289
|
+
|
290
|
+
[1.4.11-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v1.4.11/README.md
|
291
|
+
[1.4.10-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v1.4.10/README.md
|
292
|
+
[1.4.9-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v1.4.9/README.md
|
293
|
+
[1.4.8-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v1.4.8/README.md
|
294
|
+
[1.4.7-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v1.4.7/README.md
|
295
|
+
[1.4.6-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v1.4.6/README.md
|
296
|
+
[1.4.5-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v1.4.5/README.md
|
297
|
+
[1.4.4-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v1.4.4/README.md
|
298
|
+
[1.4.3-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v1.4.3/README.md
|
299
|
+
[1.4.2-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v1.4.2/README.md
|
300
|
+
[1.4.1-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v1.4.1/README.md
|
301
|
+
[1.4.0-readme]: https://gitlab.com/ruby-oauth/oauth2/-/blob/v1.4.0/README.md
|
259
302
|
|
260
303
|
<details>
|
261
304
|
<summary>1.3.x Readmes</summary>
|
262
305
|
|
263
|
-
| Version
|
264
|
-
|
265
|
-
| 1.3.1
|
266
|
-
| 1.3.0
|
306
|
+
| Version | Release Date | Readme |
|
307
|
+
|---------|--------------|--------------------------------------------------------------|
|
308
|
+
| 1.3.1 | Mar 3, 2017 | https://gitlab.com/ruby-oauth/oauth2/-/blob/v1.3.1/README.md |
|
309
|
+
| 1.3.0 | Dec 27, 2016 | https://gitlab.com/ruby-oauth/oauth2/-/blob/v1.3.0/README.md |
|
310
|
+
|
267
311
|
</details>
|
268
312
|
|
269
313
|
<details>
|
270
314
|
<summary>≤= 1.2.x Readmes (2016 and before)</summary>
|
271
315
|
|
272
|
-
| Version
|
273
|
-
|
274
|
-
| 1.2.0
|
275
|
-
| 1.1.0
|
276
|
-
| 1.0.0
|
277
|
-
| < 1.0.0
|
316
|
+
| Version | Release Date | Readme |
|
317
|
+
|---------|--------------|--------------------------------------------------------------|
|
318
|
+
| 1.2.0 | Jun 30, 2016 | https://gitlab.com/ruby-oauth/oauth2/-/blob/v1.2.0/README.md |
|
319
|
+
| 1.1.0 | Jan 30, 2016 | https://gitlab.com/ruby-oauth/oauth2/-/blob/v1.1.0/README.md |
|
320
|
+
| 1.0.0 | May 23, 2014 | https://gitlab.com/ruby-oauth/oauth2/-/blob/v1.0.0/README.md |
|
321
|
+
| < 1.0.0 | Find here | https://gitlab.com/ruby-oauth/oauth2/-/tags |
|
322
|
+
|
278
323
|
</details>
|
279
324
|
|
280
325
|
## ✨ Installation
|
281
326
|
|
282
327
|
Install the gem and add to the application's Gemfile by executing:
|
283
328
|
|
284
|
-
|
329
|
+
```console
|
330
|
+
bundle add oauth2
|
331
|
+
```
|
285
332
|
|
286
333
|
If bundler is not being used to manage dependencies, install the gem by executing:
|
287
334
|
|
288
|
-
|
335
|
+
```console
|
336
|
+
gem install oauth2
|
337
|
+
```
|
289
338
|
|
290
339
|
### 🔒 Secure Installation
|
291
340
|
|
292
|
-
|
341
|
+
<details>
|
342
|
+
<summary>For Medium or High Security Installations</summary>
|
343
|
+
|
344
|
+
This gem is cryptographically signed, and has verifiable [SHA-256 and SHA-512][💎SHA_checksums] checksums by
|
293
345
|
[stone_checksums][💎stone_checksums]. Be sure the gem you install hasn’t been tampered with
|
294
346
|
by following the instructions below.
|
295
347
|
|
296
348
|
Add my public key (if you haven’t already, expires 2045-04-29) as a trusted certificate:
|
297
349
|
|
298
|
-
```
|
299
|
-
gem cert --add <(curl -Ls https://raw.github.com/
|
350
|
+
```console
|
351
|
+
gem cert --add <(curl -Ls https://raw.github.com/galtzo-floss/certs/main/pboling.pem)
|
300
352
|
```
|
301
353
|
|
302
354
|
You only need to do that once. Then proceed to install with:
|
303
355
|
|
304
|
-
```
|
356
|
+
```console
|
305
357
|
gem install oauth2 -P MediumSecurity
|
306
358
|
```
|
307
359
|
|
@@ -311,37 +363,24 @@ This is necessary because not all of `oauth2`’s dependencies are signed, so we
|
|
311
363
|
|
312
364
|
If you want to up your security game full-time:
|
313
365
|
|
314
|
-
```
|
366
|
+
```console
|
315
367
|
bundle config set --global trust-policy MediumSecurity
|
316
368
|
```
|
317
369
|
|
318
370
|
NOTE: Be prepared to track down certs for signed gems and add them the same way you added mine.
|
319
371
|
|
320
|
-
|
321
|
-
|
322
|
-
Available as part of the Tidelift Subscription.
|
323
|
-
|
324
|
-
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. [Learn more.][tidelift-ref]
|
325
|
-
|
326
|
-
[tidelift-ref]: https://tidelift.com/subscription/pkg/rubygems-oauth2?utm_source=rubygems-oauth2&utm_medium=referral&utm_campaign=enterprise
|
327
|
-
|
328
|
-
## Security contact information
|
329
|
-
|
330
|
-
To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security).
|
331
|
-
Tidelift will coordinate the fix and disclosure.
|
332
|
-
|
333
|
-
For more see [SECURITY.md][🔐security].
|
372
|
+
</details>
|
334
373
|
|
335
374
|
## What is new for v2.0?
|
336
375
|
|
337
376
|
- Works with Ruby versions >= 2.2
|
338
377
|
- Drop support for the expired MAC Draft (all versions)
|
339
378
|
- Support IETF rfc7515 JSON Web Signature - JWS (since v2.0.12)
|
340
|
-
|
379
|
+
- Support JWT `kid` for key discovery and management
|
341
380
|
- Support IETF rfc7523 JWT Bearer Tokens (since v2.0.0)
|
342
381
|
- Support IETF rfc7231 Relative Location in Redirect (since v2.0.0)
|
343
382
|
- Support IETF rfc6749 Don't set oauth params when nil (since v2.0.0)
|
344
|
-
- Support IETF rfc7009 Token Revocation (since v2.0.10)
|
383
|
+
- Support IETF rfc7009 Token Revocation (since v2.0.10, updated in v2.0.13 to support revocation via URL-encoded parameters)
|
345
384
|
- 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)
|
346
385
|
- 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`
|
347
386
|
- Adds option to `OAuth2::Client#get_token`:
|
@@ -349,14 +388,14 @@ For more see [SECURITY.md][🔐security].
|
|
349
388
|
- Adds option to `OAuth2::AccessToken#initialize`:
|
350
389
|
- `:expires_latency` (`nil`); number of seconds by which AccessToken validity will be reduced to offset latency
|
351
390
|
- By default, keys are transformed to snake case.
|
352
|
-
|
353
|
-
|
354
|
-
|
391
|
+
- Original keys will still work as previously, in most scenarios, thanks to [snaky_hash][snaky_hash] gem.
|
392
|
+
- 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.
|
393
|
+
- As of version 2.0.4 you can turn key transformation off with the `snaky: false` option.
|
355
394
|
- By default, the `:auth_scheme` is now `:basic_auth` (instead of `:request_body`)
|
356
|
-
|
357
|
-
- [... A lot more](https://gitlab.com/oauth
|
395
|
+
- Third-party strategies and gems may need to be updated if a provider was requiring client id/secret in the request body
|
396
|
+
- [... A lot more](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/CHANGELOG.md#200-2022-06-21-tag)
|
358
397
|
|
359
|
-
[snaky_hash]: https://gitlab.com/oauth
|
398
|
+
[snaky_hash]: https://gitlab.com/ruby-oauth/snaky_hash
|
360
399
|
|
361
400
|
## Compatibility
|
362
401
|
|
@@ -372,7 +411,7 @@ This gem is tested against MRI, JRuby, and Truffleruby.
|
|
372
411
|
Each of those has varying versions that target a specific version of MRI Ruby.
|
373
412
|
This gem should work in the just-listed Ruby engines according to the targeted MRI compatibility in the table below.
|
374
413
|
If you would like to add support for additional engines,
|
375
|
-
|
414
|
+
see [gemfiles/README.md](gemfiles/README.md), then submit a PR to the correct maintenance branch as according to the table below.
|
376
415
|
</details>
|
377
416
|
|
378
417
|
<details>
|
@@ -401,9 +440,7 @@ of a major release, support for that Ruby version may be dropped.
|
|
401
440
|
NOTE: The 1.4 series will only receive critical security updates.
|
402
441
|
See [SECURITY.md][🔐security].
|
403
442
|
|
404
|
-
##
|
405
|
-
|
406
|
-
### Global Configuration
|
443
|
+
## ⚙️ Configuration
|
407
444
|
|
408
445
|
You can turn on additional warnings.
|
409
446
|
|
@@ -430,6 +467,8 @@ You'll likely need to do some source diving.
|
|
430
467
|
This gem has 100% test coverage for lines and branches, so the specs are a great place to look for ideas.
|
431
468
|
If you have time and energy please contribute to the documentation!
|
432
469
|
|
470
|
+
## 🔧 Basic Usage
|
471
|
+
|
433
472
|
### `authorize_url` and `token_url` are on site root (Just Works!)
|
434
473
|
|
435
474
|
```ruby
|
@@ -495,32 +534,38 @@ As of v2.0.11, if you need to serialize the parsed result, you can!
|
|
495
534
|
|
496
535
|
There are two ways to do this, globally, or discretely. The discrete way is recommended.
|
497
536
|
|
498
|
-
|
537
|
+
##### Global Serialization Config
|
538
|
+
|
539
|
+
Globally configure `SnakyHash::StringKeyed` to use the serializer. Put this in your code somewhere reasonable (like an initializer for Rails).
|
499
540
|
|
500
|
-
|
541
|
+
```ruby
|
501
542
|
SnakyHash::StringKeyed.class_eval do
|
502
543
|
extend SnakyHash::Serializer
|
503
544
|
end
|
504
|
-
|
545
|
+
```
|
546
|
+
|
547
|
+
##### Discrete Serialization Config
|
505
548
|
|
506
|
-
|
549
|
+
Discretely configure a custom Snaky Hash class to use the serializer.
|
507
550
|
|
508
|
-
|
551
|
+
```ruby
|
509
552
|
class MySnakyHash < SnakyHash::StringKeyed
|
510
553
|
# Give this hash class `dump` and `load` abilities!
|
511
554
|
extend SnakyHash::Serializer
|
512
555
|
end
|
513
556
|
|
514
|
-
|
557
|
+
# And tell your client to use the custom class in each call:
|
515
558
|
client = OAuth2::Client.new("client_id", "client_secret", site: "https://example.org/oauth2")
|
516
559
|
token = client.get_token({snaky_hash_klass: MySnakyHash})
|
517
|
-
|
560
|
+
```
|
518
561
|
|
519
562
|
##### Serialization Extensions
|
520
563
|
|
564
|
+
These extensions work regardless of whether you used the global or discrete config above.
|
565
|
+
|
521
566
|
There are a few hacks you may need in your class to support Ruby < 2.4.2 or < 2.6.
|
522
567
|
They are likely not needed if you are on a newer Ruby.
|
523
|
-
See
|
568
|
+
See [response_spec.rb](https://github.com/ruby-oauth/oauth2/blob/main/spec/oauth2/response_spec.rb) if you need to study the hacks for older Rubies.
|
524
569
|
|
525
570
|
```ruby
|
526
571
|
class MySnakyHash < SnakyHash::StringKeyed
|
@@ -576,9 +621,9 @@ class MySnakyHash < SnakyHash::StringKeyed
|
|
576
621
|
end
|
577
622
|
```
|
578
623
|
|
579
|
-
See
|
624
|
+
See [response_spec.rb](https://github.com/ruby-oauth/oauth2/blob/main/spec/oauth2/response_spec.rb), or the [ruby-oauth/snaky_hash](https://gitlab.com/ruby-oauth/snaky_hash) gem for more ideas.
|
580
625
|
|
581
|
-
####
|
626
|
+
#### Prefer camelCase over snake_case? => snaky: false
|
582
627
|
|
583
628
|
```ruby
|
584
629
|
response = access.get("/api/resource", params: {"query_foo" => "bar"}, snaky: false)
|
@@ -619,7 +664,7 @@ The `AccessToken` methods `#get`, `#post`, `#put` and `#delete` and the generic
|
|
619
664
|
will return an instance of the #OAuth2::Response class.
|
620
665
|
|
621
666
|
This instance contains a `#parsed` method that will parse the response body and
|
622
|
-
return a Hash-like [`SnakyHash::StringKeyed`](https://gitlab.com/oauth
|
667
|
+
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
|
623
668
|
the body is a JSON object. It will return an Array if the body is a JSON
|
624
669
|
array. Otherwise, it will return the original body string.
|
625
670
|
|
@@ -647,13 +692,29 @@ Response instance will contain the `OAuth2::Error` instance.
|
|
647
692
|
|
648
693
|
### Authorization Grants
|
649
694
|
|
695
|
+
Note on OAuth 2.1 (draft):
|
696
|
+
- 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.
|
697
|
+
- Redirect URIs must be compared using exact string matching by the Authorization Server.
|
698
|
+
- 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.
|
699
|
+
- Bearer tokens in the query string are omitted due to security risks; prefer Authorization header usage.
|
700
|
+
- Refresh tokens for public clients must either be sender-constrained (e.g., DPoP/MTLS) or one-time use.
|
701
|
+
- The definitions of public and confidential clients are simplified to refer only to whether the client has credentials.
|
702
|
+
|
703
|
+
References:
|
704
|
+
- OAuth 2.1 draft: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13
|
705
|
+
- Aaron Parecki: https://aaronparecki.com/2019/12/12/21/its-time-for-oauth-2-dot-1
|
706
|
+
- FusionAuth: https://fusionauth.io/blog/2020/04/15/whats-new-in-oauth-2-1
|
707
|
+
- Okta: https://developer.okta.com/blog/2019/12/13/oauth-2-1-how-many-rfcs
|
708
|
+
- Video: https://www.youtube.com/watch?v=g_aVPdwBTfw
|
709
|
+
- Differences overview: https://fusionauth.io/learn/expert-advice/oauth/differences-between-oauth-2-oauth-2-1/
|
710
|
+
|
650
711
|
Currently, the Authorization Code, Implicit, Resource Owner Password Credentials, Client Credentials, and Assertion
|
651
712
|
authentication grant types have helper strategy classes that simplify client
|
652
|
-
use. They are available via the [`#auth_code`](https://gitlab.com/oauth
|
653
|
-
[`#implicit`](https://gitlab.com/oauth
|
654
|
-
[`#password`](https://gitlab.com/oauth
|
655
|
-
[`#client_credentials`](https://gitlab.com/oauth
|
656
|
-
[`#assertion`](https://gitlab.com/oauth
|
713
|
+
use. They are available via the [`#auth_code`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/auth_code.rb),
|
714
|
+
[`#implicit`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/implicit.rb),
|
715
|
+
[`#password`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/password.rb),
|
716
|
+
[`#client_credentials`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/client_credentials.rb), and
|
717
|
+
[`#assertion`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/assertion.rb) methods respectively.
|
657
718
|
|
658
719
|
These aren't full examples, but demonstrative of the differences between usage for each strategy.
|
659
720
|
```ruby
|
@@ -694,14 +755,414 @@ access = client.auth_code.get_token("code_value", redirect_uri: "http://localhos
|
|
694
755
|
You can always use the `#request` method on the `OAuth2::Client` instance to make
|
695
756
|
requests for tokens for any Authentication grant type.
|
696
757
|
|
758
|
+
## 📘 Comprehensive Usage
|
759
|
+
|
760
|
+
### Common Flows (end-to-end)
|
761
|
+
|
762
|
+
- Authorization Code (server-side web app):
|
763
|
+
|
764
|
+
```ruby
|
765
|
+
require "oauth2"
|
766
|
+
client = OAuth2::Client.new(
|
767
|
+
ENV["CLIENT_ID"],
|
768
|
+
ENV["CLIENT_SECRET"],
|
769
|
+
site: "https://provider.example.com",
|
770
|
+
redirect_uri: "https://my.app.example.com/oauth/callback",
|
771
|
+
)
|
772
|
+
|
773
|
+
# Step 1: redirect user to consent
|
774
|
+
state = SecureRandom.hex(16)
|
775
|
+
auth_url = client.auth_code.authorize_url(scope: "openid profile email", state: state)
|
776
|
+
# redirect_to auth_url
|
777
|
+
|
778
|
+
# Step 2: handle the callback
|
779
|
+
# params[:code], params[:state]
|
780
|
+
raise "state mismatch" unless params[:state] == state
|
781
|
+
access = client.auth_code.get_token(params[:code])
|
782
|
+
|
783
|
+
# Step 3: call APIs
|
784
|
+
profile = access.get("/api/v1/me").parsed
|
785
|
+
```
|
786
|
+
|
787
|
+
- Client Credentials (machine-to-machine):
|
788
|
+
|
789
|
+
```ruby
|
790
|
+
client = OAuth2::Client.new(ENV["CLIENT_ID"], ENV["CLIENT_SECRET"], site: "https://provider.example.com")
|
791
|
+
access = client.client_credentials.get_token(audience: "https://api.example.com")
|
792
|
+
resp = access.get("/v1/things")
|
793
|
+
```
|
794
|
+
|
795
|
+
- Resource Owner Password (legacy; avoid when possible):
|
796
|
+
|
797
|
+
```ruby
|
798
|
+
access = client.password.get_token("jdoe", "s3cret", scope: "read")
|
799
|
+
```
|
800
|
+
|
801
|
+
#### Examples
|
802
|
+
|
803
|
+
<details>
|
804
|
+
<summary>JHipster UAA (Spring Cloud) password grant example (legacy; avoid when possible)</summary>
|
805
|
+
|
806
|
+
```ruby
|
807
|
+
# This converts a Postman/Net::HTTP multipart token request to oauth2 gem usage.
|
808
|
+
# JHipster UAA typically exposes the token endpoint at /uaa/oauth/token.
|
809
|
+
# The original snippet included:
|
810
|
+
# - Basic Authorization header for the client (web_app:changeit)
|
811
|
+
# - X-XSRF-TOKEN header from a cookie (some deployments require it)
|
812
|
+
# - grant_type=password with username/password and client_id
|
813
|
+
# Using oauth2 gem, you don't need to build multipart bodies; the gem sends
|
814
|
+
# application/x-www-form-urlencoded as required by RFC 6749.
|
815
|
+
|
816
|
+
require "oauth2"
|
817
|
+
|
818
|
+
client = OAuth2::Client.new(
|
819
|
+
"web_app", # client_id
|
820
|
+
"changeit", # client_secret
|
821
|
+
site: "http://localhost:8080/uaa",
|
822
|
+
token_url: "/oauth/token", # absolute under site (or "oauth/token" relative)
|
823
|
+
auth_scheme: :basic_auth, # sends HTTP Basic Authorization header
|
824
|
+
)
|
825
|
+
|
826
|
+
# If your UAA requires an XSRF header for the token call, provide it as a header.
|
827
|
+
# Often this is not required for token endpoints, but if your gateway enforces it,
|
828
|
+
# obtain the value from the XSRF-TOKEN cookie and pass it here.
|
829
|
+
xsrf_token = ENV["X_XSRF_TOKEN"] # e.g., pulled from a prior set-cookie value
|
830
|
+
|
831
|
+
access = client.password.get_token(
|
832
|
+
"admin", # username
|
833
|
+
"admin", # password
|
834
|
+
headers: xsrf_token ? {"X-XSRF-TOKEN" => xsrf_token} : {},
|
835
|
+
# JHipster commonly also accepts/needs the client_id in the body; include if required:
|
836
|
+
# client_id: "web_app",
|
837
|
+
)
|
838
|
+
|
839
|
+
puts access.token
|
840
|
+
puts access.to_hash # full token response
|
841
|
+
```
|
842
|
+
|
843
|
+
Notes:
|
844
|
+
- Resource Owner Password Credentials (ROPC) is deprecated in OAuth 2.1 and discouraged. Prefer Authorization Code + PKCE.
|
845
|
+
- 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.
|
846
|
+
- For Basic auth, auth_scheme: :basic_auth handles the Authorization header; you do not need to base64-encode manually.
|
847
|
+
|
848
|
+
</details>
|
849
|
+
|
850
|
+
### Refresh Tokens
|
851
|
+
|
852
|
+
When the server issues a refresh_token, you can refresh manually or implement an auto-refresh wrapper.
|
853
|
+
|
854
|
+
- Manual refresh:
|
855
|
+
|
856
|
+
```ruby
|
857
|
+
if access.expired?
|
858
|
+
access = access.refresh
|
859
|
+
end
|
860
|
+
```
|
861
|
+
|
862
|
+
- Auto-refresh wrapper pattern:
|
863
|
+
|
864
|
+
```ruby
|
865
|
+
class AutoRefreshingToken
|
866
|
+
def initialize(token_provider, store: nil)
|
867
|
+
@token = token_provider
|
868
|
+
@store = store # e.g., something that responds to read/write for token data
|
869
|
+
end
|
870
|
+
|
871
|
+
def with(&blk)
|
872
|
+
tok = ensure_fresh!
|
873
|
+
blk ? blk.call(tok) : tok
|
874
|
+
rescue OAuth2::Error => e
|
875
|
+
# If a 401 suggests token invalidation, try one refresh and retry once
|
876
|
+
if e.response && e.response.status == 401 && @token.refresh_token
|
877
|
+
@token = @token.refresh
|
878
|
+
@store.write(@token.to_hash) if @store
|
879
|
+
retry
|
880
|
+
end
|
881
|
+
raise
|
882
|
+
end
|
883
|
+
|
884
|
+
private
|
885
|
+
|
886
|
+
def ensure_fresh!
|
887
|
+
if @token.expired? && @token.refresh_token
|
888
|
+
@token = @token.refresh
|
889
|
+
@store.write(@token.to_hash) if @store
|
890
|
+
end
|
891
|
+
@token
|
892
|
+
end
|
893
|
+
end
|
894
|
+
|
895
|
+
# usage
|
896
|
+
keeper = AutoRefreshingToken.new(access)
|
897
|
+
keeper.with { |tok| tok.get("/v1/protected") }
|
898
|
+
```
|
899
|
+
|
900
|
+
Persist the token across processes using `AccessToken#to_hash` and `AccessToken.from_hash(client, hash)`.
|
901
|
+
|
902
|
+
### Token Revocation (RFC 7009)
|
903
|
+
|
904
|
+
You can revoke either the access token or the refresh token.
|
905
|
+
|
906
|
+
```ruby
|
907
|
+
# Revoke the current access token
|
908
|
+
access.revoke(token_type_hint: :access_token)
|
909
|
+
|
910
|
+
# Or explicitly revoke the refresh token (often also invalidates associated access tokens)
|
911
|
+
access.revoke(token_type_hint: :refresh_token)
|
912
|
+
```
|
913
|
+
|
914
|
+
### Client Configuration Tips
|
915
|
+
|
916
|
+
#### Mutual TLS (mTLS) client authentication
|
917
|
+
|
918
|
+
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.
|
919
|
+
|
920
|
+
Example using PEM files (certificate and key):
|
921
|
+
|
922
|
+
```ruby
|
923
|
+
require "oauth2"
|
924
|
+
require "openssl"
|
925
|
+
|
926
|
+
client = OAuth2::Client.new(
|
927
|
+
ENV.fetch("CLIENT_ID"),
|
928
|
+
ENV.fetch("CLIENT_SECRET"),
|
929
|
+
site: "https://example.com",
|
930
|
+
authorize_url: "/oauth/authorize/",
|
931
|
+
token_url: "/oauth/token/",
|
932
|
+
auth_scheme: :tls_client_auth, # if your AS requires mTLS-based client authentication
|
933
|
+
connection_opts: {
|
934
|
+
ssl: {
|
935
|
+
client_cert: OpenSSL::X509::Certificate.new(File.read("localhost.pem")),
|
936
|
+
client_key: OpenSSL::PKey::RSA.new(File.read("localhost-key.pem")),
|
937
|
+
# Optional extras, uncomment as needed:
|
938
|
+
# ca_file: "/path/to/ca-bundle.pem", # custom CA(s)
|
939
|
+
# verify: true # enable server cert verification (recommended)
|
940
|
+
},
|
941
|
+
},
|
942
|
+
)
|
943
|
+
|
944
|
+
# Example token request (any grant type can be used). The mTLS handshake
|
945
|
+
# will occur automatically on HTTPS calls using the configured cert/key.
|
946
|
+
access = client.client_credentials.get_token
|
947
|
+
|
948
|
+
# Subsequent resource requests will also use mTLS on HTTPS endpoints of `site`:
|
949
|
+
resp = access.get("/v1/protected")
|
950
|
+
```
|
951
|
+
|
952
|
+
Notes:
|
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
|
+
## 🦷 FLOSS Funding
|
1102
|
+
|
1103
|
+
While ruby-oauth tools are free software and will always be, the project would benefit immensely from some funding.
|
1104
|
+
Raising a monthly budget of... "dollars" would make the project more sustainable.
|
1105
|
+
|
1106
|
+
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).
|
1109
|
+
|
1110
|
+
**If you're working in a company that's making significant use of ruby-oauth tools we'd
|
1111
|
+
appreciate it if you suggest to your company to become a ruby-oauth sponsor.**
|
1112
|
+
|
1113
|
+
You can support the development of ruby-oauth tools via
|
1114
|
+
[GitHub Sponsors][🖇sponsor],
|
1115
|
+
[Liberapay][⛳liberapay],
|
1116
|
+
[PayPal][🖇paypal],
|
1117
|
+
[Open Collective][🖇osc]
|
1118
|
+
and [Tidelift][🏙️entsup-tidelift].
|
1119
|
+
|
1120
|
+
| 📍 NOTE |
|
1121
|
+
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
1122
|
+
| 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
|
+
|
1124
|
+
### Open Collective for Individuals
|
1125
|
+
|
1126
|
+
<!-- OPENCOLLECTIVE-INDIVIDUALS:START -->
|
1127
|
+
No backers yet. Be the first!
|
1128
|
+
<!-- OPENCOLLECTIVE-INDIVIDUALS:END -->
|
1129
|
+
|
1130
|
+
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/ruby-oauth#backer)]
|
1131
|
+
|
1132
|
+
### Open Collective for Organizations
|
1133
|
+
|
1134
|
+
<!-- OPENCOLLECTIVE-ORGANIZATIONS:START -->
|
1135
|
+
No sponsors yet. Be the first!
|
1136
|
+
<!-- OPENCOLLECTIVE-ORGANIZATIONS:END -->
|
1137
|
+
|
1138
|
+
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)]
|
1139
|
+
|
1140
|
+
### Another way to support open-source
|
1141
|
+
|
1142
|
+
> How wonderful it is that nobody need wait a single moment before starting to improve the world.<br/>
|
1143
|
+
>—Anne Frank
|
1144
|
+
|
1145
|
+
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 — totaling 79 hours of FLOSS coding over just the past seven days, a pretty regular week for me. 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).
|
1146
|
+
|
1147
|
+
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`.
|
1148
|
+
|
1149
|
+
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.
|
1150
|
+
|
1151
|
+
**[Floss-Funding.dev][🖇floss-funding.dev]: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags**
|
1152
|
+
|
1153
|
+
[![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 to my FLOSS or refugee efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS or refugee efforts using Patreon][🖇patreon-img]][🖇patreon]
|
1154
|
+
|
697
1155
|
## 🔐 Security
|
698
1156
|
|
699
|
-
|
1157
|
+
To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security).
|
1158
|
+
Tidelift will coordinate the fix and disclosure.
|
1159
|
+
|
1160
|
+
For more see [SECURITY.md][🔐security].
|
700
1161
|
|
701
1162
|
## 🤝 Contributing
|
702
1163
|
|
703
1164
|
If you need some ideas of where to help, you could work on adding more code coverage,
|
704
|
-
or if it is already 💯 (see [below](#code-coverage)) check [issues][🤝gh-issues], or [PRs][🤝gh-pulls],
|
1165
|
+
or if it is already 💯 (see [below](#code-coverage)) check [reek](REEK), [issues][🤝gh-issues], or [PRs][🤝gh-pulls],
|
705
1166
|
or use the gem and think about how it could be better.
|
706
1167
|
|
707
1168
|
We [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] so if you make changes, remember to update it.
|
@@ -715,12 +1176,13 @@ See [CONTRIBUTING.md][🤝contributing].
|
|
715
1176
|
### Code Coverage
|
716
1177
|
|
717
1178
|
[![Coveralls Test Coverage][🔑coveralls-img]][🔑coveralls]
|
718
|
-
|
1179
|
+
|
1180
|
+
[![QLTY Test Coverage][🔑qlty-covi]][🔑qlty-cov]
|
719
1181
|
|
720
1182
|
### 🪇 Code of Conduct
|
721
1183
|
|
722
|
-
Everyone interacting
|
723
|
-
chat rooms and mailing lists
|
1184
|
+
Everyone interacting with this project's codebases, issue trackers,
|
1185
|
+
chat rooms and mailing lists agrees to follow the [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct].
|
724
1186
|
|
725
1187
|
## 🌈 Contributors
|
726
1188
|
|
@@ -728,18 +1190,21 @@ chat rooms and mailing lists is expected to follow the [![Contributor Covenant 2
|
|
728
1190
|
|
729
1191
|
Made with [contributors-img][🖐contrib-rocks].
|
730
1192
|
|
731
|
-
Also see GitLab Contributors: [https://gitlab.com/oauth
|
1193
|
+
Also see GitLab Contributors: [https://gitlab.com/ruby-oauth/oauth2/-/graphs/main][🚎contributors-gl]
|
732
1194
|
|
733
|
-
|
1195
|
+
<details>
|
1196
|
+
<summary>⭐️ Star History</summary>
|
734
1197
|
|
735
|
-
<a href="https://star-history.com/#oauth
|
1198
|
+
<a href="https://star-history.com/#ruby-oauth/oauth2&Date">
|
736
1199
|
<picture>
|
737
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=oauth
|
738
|
-
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=oauth
|
739
|
-
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=oauth
|
1200
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=ruby-oauth/oauth2&type=Date&theme=dark" />
|
1201
|
+
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=ruby-oauth/oauth2&type=Date" />
|
1202
|
+
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=ruby-oauth/oauth2&type=Date" />
|
740
1203
|
</picture>
|
741
1204
|
</a>
|
742
1205
|
|
1206
|
+
</details>
|
1207
|
+
|
743
1208
|
## 📌 Versioning
|
744
1209
|
|
745
1210
|
This Library adheres to [![Semantic Versioning 2.0.0][📌semver-img]][📌semver].
|
@@ -748,34 +1213,35 @@ Specifically, if a minor or patch version is released that breaks backward compa
|
|
748
1213
|
a new version should be immediately released that restores compatibility.
|
749
1214
|
Breaking changes to the public API will only be introduced with new major versions.
|
750
1215
|
|
751
|
-
|
1216
|
+
> dropping support for a platform is both obviously and objectively a breaking change <br/>
|
1217
|
+
>—Jordan Harband ([@ljharb](https://github.com/ljharb), maintainer of SemVer) [in SemVer issue 716][📌semver-breaking]
|
752
1218
|
|
753
|
-
|
1219
|
+
I understand that policy doesn't work universally ("exceptions to every rule!"),
|
1220
|
+
but it is the policy here.
|
1221
|
+
As such, in many cases it is good to specify a dependency on this library using
|
1222
|
+
the [Pessimistic Version Constraint][📌pvc] with two digits of precision.
|
754
1223
|
|
755
|
-
|
756
|
-
|
757
|
-
|
1224
|
+
For example:
|
1225
|
+
|
1226
|
+
```ruby
|
1227
|
+
spec.add_dependency("oauth2", "~> 2.0")
|
1228
|
+
```
|
758
1229
|
|
759
|
-
>
|
1230
|
+
<details>
|
1231
|
+
<summary>📌 Is "Platform Support" part of the public API? More details inside.</summary>
|
760
1232
|
|
761
|
-
|
1233
|
+
SemVer should, IMO, but doesn't explicitly, say that dropping support for specific Platforms
|
1234
|
+
is a *breaking change* to an API.
|
1235
|
+
It is obvious to many, but not all, and since the spec is silent, the bike shedding is endless.
|
762
1236
|
|
763
1237
|
To get a better understanding of how SemVer is intended to work over a project's lifetime,
|
764
1238
|
read this article from the creator of SemVer:
|
765
1239
|
|
766
1240
|
- ["Major Version Numbers are Not Sacred"][📌major-versions-not-sacred]
|
767
1241
|
|
768
|
-
|
769
|
-
you can (and should) specify a dependency on these libraries using
|
770
|
-
the [Pessimistic Version Constraint][📌pvc] with two digits of precision.
|
771
|
-
|
772
|
-
For example:
|
773
|
-
|
774
|
-
```ruby
|
775
|
-
spec.add_dependency("oauth2", "~> 2.0")
|
776
|
-
```
|
1242
|
+
</details>
|
777
1243
|
|
778
|
-
See [CHANGELOG.md][📌changelog] for list of releases.
|
1244
|
+
See [CHANGELOG.md][📌changelog] for a list of releases.
|
779
1245
|
|
780
1246
|
## 📄 License
|
781
1247
|
|
@@ -787,45 +1253,91 @@ See [LICENSE.txt][📄license] for the official [Copyright Notice][📄copyright
|
|
787
1253
|
|
788
1254
|
<ul>
|
789
1255
|
<li>
|
790
|
-
|
1256
|
+
Copyright (c) 2017–2025 Peter H. Boling, of
|
791
1257
|
<a href="https://discord.gg/3qme4XHNKN">
|
792
1258
|
Galtzo.com
|
793
1259
|
<picture>
|
794
|
-
<img src="https://
|
1260
|
+
<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">
|
795
1261
|
</picture>
|
796
|
-
</a>, and oauth2 contributors
|
1262
|
+
</a>, and oauth2 contributors.
|
797
1263
|
</li>
|
798
1264
|
<li>
|
799
|
-
Copyright (c) 2011
|
1265
|
+
Copyright (c) 2011-2013 Michael Bleigh and Intridea, Inc.
|
800
1266
|
</li>
|
801
1267
|
</ul>
|
802
1268
|
|
803
|
-
## 🤑
|
1269
|
+
## 🤑 A request for help
|
1270
|
+
|
1271
|
+
Maintainers have teeth and need to pay their dentists.
|
1272
|
+
After getting laid off in an RIF in March and filled with many dozens of rejections,
|
1273
|
+
I'm now spending ~60+ hours a week building open source tools.
|
1274
|
+
I'm hoping to be able to pay for my kids' health insurance this month,
|
1275
|
+
so if you value the work I am doing, I need your support.
|
1276
|
+
Please consider sponsoring me or the project.
|
1277
|
+
|
1278
|
+
To join the community or get help 👇️ Join the Discord.
|
1279
|
+
|
1280
|
+
[![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite]
|
1281
|
+
|
1282
|
+
To say "thanks for maintaining such a great tool" ☝️ Join the Discord or 👇️ send money.
|
1283
|
+
|
1284
|
+
[![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-img] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal-img]
|
804
1285
|
|
805
|
-
|
806
|
-
so perhaps you'll indulge me for another 20 seconds.
|
807
|
-
I maintain many dozens of gems, including this one,
|
808
|
-
because I want Ruby to be a great place for people to solve problems, big and small.
|
809
|
-
Please consider supporting my efforts via the giant yellow link below,
|
810
|
-
or one of the others at the head of this README.
|
1286
|
+
### Please give the project a star ⭐ ♥.
|
811
1287
|
|
812
|
-
|
1288
|
+
Thanks for RTFM. ☺️
|
1289
|
+
|
1290
|
+
[⛳liberapay-img]: https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay&color=a51611&style=flat
|
1291
|
+
[⛳liberapay-bottom-img]: https://img.shields.io/liberapay/goal/pboling.svg?style=for-the-badge&logo=liberapay&color=a51611
|
1292
|
+
[⛳liberapay]: https://liberapay.com/pboling/donate
|
1293
|
+
[🖇osc-all-img]: https://img.shields.io/opencollective/all/ruby-oauth
|
1294
|
+
[🖇osc-sponsors-img]: https://img.shields.io/opencollective/sponsors/ruby-oauth
|
1295
|
+
[🖇osc-backers-img]: https://img.shields.io/opencollective/backers/ruby-oauth
|
1296
|
+
[🖇osc-backers]: https://opencollective.com/ruby-oauth#backer
|
1297
|
+
[🖇osc-backers-i]: https://opencollective.com/ruby-oauth/backers/badge.svg?style=flat
|
1298
|
+
[🖇osc-sponsors]: https://opencollective.com/ruby-oauth#sponsor
|
1299
|
+
[🖇osc-sponsors-i]: https://opencollective.com/ruby-oauth/sponsors/badge.svg?style=flat
|
1300
|
+
[🖇osc-all-bottom-img]: https://img.shields.io/opencollective/all/ruby-oauth?style=for-the-badge
|
1301
|
+
[🖇osc-sponsors-bottom-img]: https://img.shields.io/opencollective/sponsors/ruby-oauth?style=for-the-badge
|
1302
|
+
[🖇osc-backers-bottom-img]: https://img.shields.io/opencollective/backers/ruby-oauth?style=for-the-badge
|
1303
|
+
[🖇osc]: https://opencollective.com/ruby-oauth
|
1304
|
+
[🖇sponsor-img]: https://img.shields.io/badge/Sponsor_Me!-pboling.svg?style=social&logo=github
|
1305
|
+
[🖇sponsor-bottom-img]: https://img.shields.io/badge/Sponsor_Me!-pboling-blue?style=for-the-badge&logo=github
|
1306
|
+
[🖇sponsor]: https://github.com/sponsors/pboling
|
1307
|
+
[🖇polar-img]: https://img.shields.io/badge/polar-donate-a51611.svg?style=flat
|
1308
|
+
[🖇polar]: https://polar.sh/pboling
|
1309
|
+
[🖇kofi-img]: https://img.shields.io/badge/ko--fi-✓-a51611.svg?style=flat
|
1310
|
+
[🖇kofi]: https://ko-fi.com/O5O86SNP4
|
1311
|
+
[🖇patreon-img]: https://img.shields.io/badge/patreon-donate-a51611.svg?style=flat
|
1312
|
+
[🖇patreon]: https://patreon.com/galtzo
|
1313
|
+
[🖇buyme-small-img]: https://img.shields.io/badge/buy_me_a_coffee-✓-a51611.svg?style=flat
|
1314
|
+
[🖇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
|
1315
|
+
[🖇buyme]: https://www.buymeacoffee.com/pboling
|
1316
|
+
[🖇paypal-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=flat&logo=paypal
|
1317
|
+
[🖇paypal-bottom-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=for-the-badge&logo=paypal&color=0A0A0A
|
1318
|
+
[🖇paypal]: https://www.paypal.com/paypalme/peterboling
|
1319
|
+
[🖇floss-funding.dev]: https://floss-funding.dev
|
1320
|
+
[🖇floss-funding-gem]: https://github.com/galtzo-floss/floss_funding
|
1321
|
+
[✉️discord-invite]: https://discord.gg/3qme4XHNKN
|
1322
|
+
[✉️discord-invite-img-ftb]: https://img.shields.io/discord/1373797679469170758?style=for-the-badge
|
813
1323
|
|
814
1324
|
[⛳gg-discussions]: https://groups.google.com/g/oauth-ruby
|
815
1325
|
[⛳gg-discussions-img]: https://img.shields.io/badge/google-group-0093D0.svg?style=for-the-badge&logo=google&logoColor=orange
|
816
1326
|
|
817
1327
|
[✇bundle-group-pattern]: https://gist.github.com/pboling/4564780
|
818
|
-
[⛳️gem-namespace]: https://github.com/oauth
|
819
|
-
[⛳️namespace-img]: https://img.shields.io/badge/namespace-OAuth2-
|
1328
|
+
[⛳️gem-namespace]: https://github.com/ruby-oauth/oauth2
|
1329
|
+
[⛳️namespace-img]: https://img.shields.io/badge/namespace-OAuth2-3C2D2D.svg?style=square&logo=ruby&logoColor=white
|
820
1330
|
[⛳️gem-name]: https://rubygems.org/gems/oauth2
|
821
|
-
[⛳️name-img]: https://img.shields.io/badge/name-oauth2-
|
822
|
-
[
|
823
|
-
[
|
824
|
-
[🚂
|
825
|
-
[🚂
|
1331
|
+
[⛳️name-img]: https://img.shields.io/badge/name-oauth2-3C2D2D.svg?style=square&logo=rubygems&logoColor=red
|
1332
|
+
[⛳️tag-img]: https://img.shields.io/github/tag/ruby-oauth/oauth2.svg
|
1333
|
+
[⛳️tag]: http://github.com/ruby-oauth/oauth2/releases
|
1334
|
+
[🚂maint-blog]: http://www.railsbling.com/tags/oauth2
|
1335
|
+
[🚂maint-blog-img]: https://img.shields.io/badge/blog-railsbling-0093D0.svg?style=for-the-badge&logo=rubyonrails&logoColor=orange
|
1336
|
+
[🚂maint-contact]: http://www.railsbling.com/contact
|
1337
|
+
[🚂maint-contact-img]: https://img.shields.io/badge/Contact-Maintainer-0093D0.svg?style=flat&logo=rubyonrails&logoColor=red
|
826
1338
|
[💖🖇linkedin]: http://www.linkedin.com/in/peterboling
|
827
1339
|
[💖🖇linkedin-img]: https://img.shields.io/badge/PeterBoling-LinkedIn-0B66C2?style=flat&logo=newjapanprowrestling
|
828
|
-
[💖✌️wellfound]: https://
|
1340
|
+
[💖✌️wellfound]: https://wellfound.com/u/peter-boling
|
829
1341
|
[💖✌️wellfound-img]: https://img.shields.io/badge/peter--boling-orange?style=flat&logo=wellfound
|
830
1342
|
[💖💲crunchbase]: https://www.crunchbase.com/person/peter-boling
|
831
1343
|
[💖💲crunchbase-img]: https://img.shields.io/badge/peter--boling-purple?style=flat&logo=crunchbase
|
@@ -847,78 +1359,68 @@ or one of the others at the head of this README.
|
|
847
1359
|
[👨🏼🏫expsup-upwork-img]: https://img.shields.io/badge/UpWork-13544E?style=for-the-badge&logo=Upwork&logoColor=white
|
848
1360
|
[👨🏼🏫expsup-codementor]: https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github
|
849
1361
|
[👨🏼🏫expsup-codementor-img]: https://img.shields.io/badge/CodeMentor-Get_Help-1abc9c?style=for-the-badge&logo=CodeMentor&logoColor=white
|
850
|
-
[🏙️entsup-tidelift]: https://tidelift.com/subscription
|
1362
|
+
[🏙️entsup-tidelift]: https://tidelift.com/subscription/pkg/rubygems-oauth2?utm_source=rubygems-oauth2&utm_medium=referral&utm_campaign=readme
|
851
1363
|
[🏙️entsup-tidelift-img]: https://img.shields.io/badge/Tidelift_and_Sonar-Enterprise_Support-FD3456?style=for-the-badge&logo=sonar&logoColor=white
|
852
1364
|
[🏙️entsup-tidelift-sonar]: https://blog.tidelift.com/tidelift-joins-sonar
|
853
1365
|
[💁🏼♂️peterboling]: http://www.peterboling.com
|
854
1366
|
[🚂railsbling]: http://www.railsbling.com
|
855
1367
|
[📜src-gl-img]: https://img.shields.io/badge/GitLab-FBA326?style=for-the-badge&logo=Gitlab&logoColor=orange
|
856
|
-
[📜src-gl]: https://gitlab.com/oauth
|
1368
|
+
[📜src-gl]: https://gitlab.com/ruby-oauth/oauth2/
|
857
1369
|
[📜src-cb-img]: https://img.shields.io/badge/CodeBerg-4893CC?style=for-the-badge&logo=CodeBerg&logoColor=blue
|
858
|
-
[📜src-cb]: https://codeberg.org/oauth
|
1370
|
+
[📜src-cb]: https://codeberg.org/ruby-oauth/oauth2
|
859
1371
|
[📜src-gh-img]: https://img.shields.io/badge/GitHub-238636?style=for-the-badge&logo=Github&logoColor=green
|
860
|
-
[📜src-gh]: https://github.com/oauth
|
1372
|
+
[📜src-gh]: https://github.com/ruby-oauth/oauth2
|
861
1373
|
[📜docs-cr-rd-img]: https://img.shields.io/badge/RubyDoc-Current_Release-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white
|
862
1374
|
[📜docs-head-rd-img]: https://img.shields.io/badge/YARD_on_Galtzo.com-HEAD-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white
|
863
|
-
[📜wiki]: https://gitlab.com/oauth
|
1375
|
+
[📜wiki]: https://gitlab.com/ruby-oauth/oauth2/-/wikis/home
|
864
1376
|
[📜wiki-img]: https://img.shields.io/badge/wiki-examples-943CD2.svg?style=for-the-badge&logo=Wiki&logoColor=white
|
865
1377
|
[👽dl-rank]: https://rubygems.org/gems/oauth2
|
866
1378
|
[👽dl-ranki]: https://img.shields.io/gem/rd/oauth2.svg
|
867
|
-
[👽oss-help]: https://www.codetriage.com/oauth
|
868
|
-
[👽oss-helpi]: https://www.codetriage.com/oauth
|
1379
|
+
[👽oss-help]: https://www.codetriage.com/ruby-oauth/oauth2
|
1380
|
+
[👽oss-helpi]: https://www.codetriage.com/ruby-oauth/oauth2/badges/users.svg
|
869
1381
|
[👽version]: https://rubygems.org/gems/oauth2
|
870
1382
|
[👽versioni]: https://img.shields.io/gem/v/oauth2.svg
|
871
|
-
[🔑qlty-mnt]: https://qlty.sh/gh/oauth
|
872
|
-
[🔑qlty-mnti
|
873
|
-
[🔑qlty-cov]: https://qlty.sh/gh/oauth
|
874
|
-
[🔑qlty-covi
|
875
|
-
[🔑codecov]: https://codecov.io/gh/oauth
|
876
|
-
[🔑codecovi
|
877
|
-
[🔑coveralls]: https://coveralls.io/github/oauth
|
878
|
-
[🔑coveralls-img]: https://coveralls.io/repos/github/oauth
|
879
|
-
[
|
880
|
-
[
|
881
|
-
[
|
882
|
-
[
|
883
|
-
[🚎
|
884
|
-
[🚎
|
885
|
-
[🚎
|
886
|
-
[🚎
|
887
|
-
[🚎
|
888
|
-
[🚎
|
889
|
-
[🚎
|
890
|
-
[🚎
|
891
|
-
[🚎
|
892
|
-
[🚎
|
893
|
-
[🚎
|
894
|
-
[🚎
|
895
|
-
[🚎
|
896
|
-
[🚎
|
897
|
-
[🚎
|
898
|
-
[🚎
|
899
|
-
[🚎
|
900
|
-
[🚎
|
901
|
-
[🚎
|
902
|
-
[🚎
|
903
|
-
[🚎
|
904
|
-
[🚎
|
905
|
-
[🚎
|
906
|
-
[🚎
|
907
|
-
[🚎13
|
908
|
-
[🚎13
|
909
|
-
[
|
910
|
-
[
|
911
|
-
[
|
912
|
-
[🖇sponsor]: https://github.com/sponsors/pboling
|
913
|
-
[🖇polar-img]: https://img.shields.io/badge/polar-donate-yellow.svg
|
914
|
-
[🖇polar]: https://polar.sh/pboling
|
915
|
-
[🖇kofi-img]: https://img.shields.io/badge/a_more_different_coffee-✓-yellow.svg
|
916
|
-
[🖇kofi]: https://ko-fi.com/O5O86SNP4
|
917
|
-
[🖇patreon-img]: https://img.shields.io/badge/patreon-donate-yellow.svg
|
918
|
-
[🖇patreon]: https://patreon.com/galtzo
|
919
|
-
[🖇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
|
920
|
-
[🖇buyme]: https://www.buymeacoffee.com/pboling
|
921
|
-
[🖇buyme-small-img]: https://img.shields.io/badge/buy_me_a_coffee-✓-yellow.svg?style=flat
|
1383
|
+
[🔑qlty-mnt]: https://qlty.sh/gh/ruby-oauth/projects/oauth2
|
1384
|
+
[🔑qlty-mnti]: https://qlty.sh/gh/ruby-oauth/projects/oauth2/maintainability.svg
|
1385
|
+
[🔑qlty-cov]: https://qlty.sh/gh/ruby-oauth/projects/oauth2/metrics/code?sort=coverageRating
|
1386
|
+
[🔑qlty-covi]: https://qlty.sh/gh/ruby-oauth/projects/oauth2/coverage.svg
|
1387
|
+
[🔑codecov]: https://codecov.io/gh/ruby-oauth/oauth2
|
1388
|
+
[🔑codecovi]: https://codecov.io/gh/ruby-oauth/oauth2/graph/badge.svg
|
1389
|
+
[🔑coveralls]: https://coveralls.io/github/ruby-oauth/oauth2?branch=main
|
1390
|
+
[🔑coveralls-img]: https://coveralls.io/repos/github/ruby-oauth/oauth2/badge.svg?branch=main
|
1391
|
+
[🖐codeQL]: https://github.com/ruby-oauth/oauth2/security/code-scanning
|
1392
|
+
[🖐codeQL-img]: https://github.com/ruby-oauth/oauth2/actions/workflows/codeql-analysis.yml/badge.svg
|
1393
|
+
[🚎1-an-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/ancient.yml
|
1394
|
+
[🚎1-an-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/ancient.yml/badge.svg
|
1395
|
+
[🚎2-cov-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/coverage.yml
|
1396
|
+
[🚎2-cov-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/coverage.yml/badge.svg
|
1397
|
+
[🚎3-hd-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/heads.yml
|
1398
|
+
[🚎3-hd-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/heads.yml/badge.svg
|
1399
|
+
[🚎4-lg-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/legacy.yml
|
1400
|
+
[🚎4-lg-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/legacy.yml/badge.svg
|
1401
|
+
[🚎5-st-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/style.yml
|
1402
|
+
[🚎5-st-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/style.yml/badge.svg
|
1403
|
+
[🚎6-s-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/supported.yml
|
1404
|
+
[🚎6-s-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/supported.yml/badge.svg
|
1405
|
+
[🚎7-us-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/unsupported.yml
|
1406
|
+
[🚎7-us-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/unsupported.yml/badge.svg
|
1407
|
+
[🚎8-ho-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/hoary.yml
|
1408
|
+
[🚎8-ho-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/hoary.yml/badge.svg
|
1409
|
+
[🚎9-t-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/truffle.yml
|
1410
|
+
[🚎9-t-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/truffle.yml/badge.svg
|
1411
|
+
[🚎10-j-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/jruby.yml
|
1412
|
+
[🚎10-j-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/jruby.yml/badge.svg
|
1413
|
+
[🚎11-c-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/current.yml
|
1414
|
+
[🚎11-c-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/current.yml/badge.svg
|
1415
|
+
[🚎12-crh-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/current-runtime-heads.yml
|
1416
|
+
[🚎12-crh-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/current-runtime-heads.yml/badge.svg
|
1417
|
+
[🚎13-cbs-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/caboose.yml
|
1418
|
+
[🚎13-cbs-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/caboose.yml/badge.svg
|
1419
|
+
[🚎13-🔒️-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/locked_deps.yml
|
1420
|
+
[🚎13-🔒️-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/locked_deps.yml/badge.svg
|
1421
|
+
[🚎14-🔓️-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/unlocked_deps.yml
|
1422
|
+
[🚎14-🔓️-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/unlocked_deps.yml/badge.svg
|
1423
|
+
[💎ruby-2.2i]: https://img.shields.io/badge/Ruby-2.2_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white
|
922
1424
|
[💎ruby-2.3i]: https://img.shields.io/badge/Ruby-2.3-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
|
923
1425
|
[💎ruby-2.4i]: https://img.shields.io/badge/Ruby-2.4-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
|
924
1426
|
[💎ruby-2.5i]: https://img.shields.io/badge/Ruby-2.5-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
|
@@ -930,30 +1432,30 @@ or one of the others at the head of this README.
|
|
930
1432
|
[💎ruby-3.3i]: https://img.shields.io/badge/Ruby-3.3-CC342D?style=for-the-badge&logo=ruby&logoColor=white
|
931
1433
|
[💎ruby-c-i]: https://img.shields.io/badge/Ruby-current-CC342D?style=for-the-badge&logo=ruby&logoColor=green
|
932
1434
|
[💎ruby-headi]: https://img.shields.io/badge/Ruby-HEAD-CC342D?style=for-the-badge&logo=ruby&logoColor=blue
|
933
|
-
[💎truby-22.3i]: https://img.shields.io/badge/Truffle_Ruby-22.
|
934
|
-
[💎truby-23.0i]: https://img.shields.io/badge/Truffle_Ruby-23.
|
1435
|
+
[💎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
|
1436
|
+
[💎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
|
935
1437
|
[💎truby-23.1i]: https://img.shields.io/badge/Truffle_Ruby-23.1-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
|
936
1438
|
[💎truby-c-i]: https://img.shields.io/badge/Truffle_Ruby-current-34BCB1?style=for-the-badge&logo=ruby&logoColor=green
|
937
1439
|
[💎truby-headi]: https://img.shields.io/badge/Truffle_Ruby-HEAD-34BCB1?style=for-the-badge&logo=ruby&logoColor=blue
|
938
|
-
[💎jruby-9.1i]: https://img.shields.io/badge/JRuby-9.
|
939
|
-
[💎jruby-9.2i]: https://img.shields.io/badge/JRuby-9.
|
940
|
-
[💎jruby-9.3i]: https://img.shields.io/badge/JRuby-9.
|
1440
|
+
[💎jruby-9.1i]: https://img.shields.io/badge/JRuby-9.1_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=red
|
1441
|
+
[💎jruby-9.2i]: https://img.shields.io/badge/JRuby-9.2_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=red
|
1442
|
+
[💎jruby-9.3i]: https://img.shields.io/badge/JRuby-9.3_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=red
|
941
1443
|
[💎jruby-9.4i]: https://img.shields.io/badge/JRuby-9.4-FBE742?style=for-the-badge&logo=ruby&logoColor=red
|
942
1444
|
[💎jruby-c-i]: https://img.shields.io/badge/JRuby-current-FBE742?style=for-the-badge&logo=ruby&logoColor=green
|
943
1445
|
[💎jruby-headi]: https://img.shields.io/badge/JRuby-HEAD-FBE742?style=for-the-badge&logo=ruby&logoColor=blue
|
944
|
-
[🤝gh-issues]: https://github.com/oauth
|
945
|
-
[🤝gh-pulls]: https://github.com/oauth
|
946
|
-
[🤝gl-issues]: https://gitlab.com/oauth
|
947
|
-
[🤝gl-pulls]: https://gitlab.com/oauth
|
948
|
-
[🤝cb-issues]: https://codeberg.org/oauth
|
949
|
-
[🤝cb-pulls]: https://codeberg.org/oauth
|
1446
|
+
[🤝gh-issues]: https://github.com/ruby-oauth/oauth2/issues
|
1447
|
+
[🤝gh-pulls]: https://github.com/ruby-oauth/oauth2/pulls
|
1448
|
+
[🤝gl-issues]: https://gitlab.com/ruby-oauth/oauth2/-/issues
|
1449
|
+
[🤝gl-pulls]: https://gitlab.com/ruby-oauth/oauth2/-/merge_requests
|
1450
|
+
[🤝cb-issues]: https://codeberg.org/ruby-oauth/oauth2/issues
|
1451
|
+
[🤝cb-pulls]: https://codeberg.org/ruby-oauth/oauth2/pulls
|
950
1452
|
[🤝cb-donate]: https://donate.codeberg.org/
|
951
1453
|
[🤝contributing]: CONTRIBUTING.md
|
952
|
-
[🔑codecov-g
|
1454
|
+
[🔑codecov-g]: https://codecov.io/gh/ruby-oauth/oauth2/graphs/tree.svg
|
953
1455
|
[🖐contrib-rocks]: https://contrib.rocks
|
954
|
-
[🖐contributors]: https://github.com/oauth
|
955
|
-
[🖐contributors-img]: https://contrib.rocks/image?repo=oauth
|
956
|
-
[🚎contributors-gl]: https://gitlab.com/oauth
|
1456
|
+
[🖐contributors]: https://github.com/ruby-oauth/oauth2/graphs/contributors
|
1457
|
+
[🖐contributors-img]: https://contrib.rocks/image?repo=ruby-oauth/oauth2
|
1458
|
+
[🚎contributors-gl]: https://gitlab.com/ruby-oauth/oauth2/-/graphs/main
|
957
1459
|
[🪇conduct]: CODE_OF_CONDUCT.md
|
958
1460
|
[🪇conduct-img]: https://img.shields.io/badge/Contributor_Covenant-2.1-259D6C.svg
|
959
1461
|
[📌pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint
|
@@ -967,7 +1469,7 @@ or one of the others at the head of this README.
|
|
967
1469
|
[📌gitmoji]:https://gitmoji.dev
|
968
1470
|
[📌gitmoji-img]:https://img.shields.io/badge/gitmoji_commits-%20😜%20😍-34495e.svg?style=flat-square
|
969
1471
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
970
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-0.
|
1472
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-0.519-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
971
1473
|
[🔐security]: SECURITY.md
|
972
1474
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
973
1475
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
@@ -978,13 +1480,13 @@ or one of the others at the head of this README.
|
|
978
1480
|
[📄ilo-declaration-img]: https://img.shields.io/badge/ILO_Fundamental_Principles-✓-259D6C.svg?style=flat
|
979
1481
|
[🚎yard-current]: http://rubydoc.info/gems/oauth2
|
980
1482
|
[🚎yard-head]: https://oauth2.galtzo.com
|
981
|
-
[💎stone_checksums]: https://github.com/
|
982
|
-
[💎SHA_checksums]: https://gitlab.com/oauth
|
1483
|
+
[💎stone_checksums]: https://github.com/galtzo-floss/stone_checksums
|
1484
|
+
[💎SHA_checksums]: https://gitlab.com/ruby-oauth/oauth2/-/tree/main/checksums
|
983
1485
|
[💎rlts]: https://github.com/rubocop-lts/rubocop-lts
|
984
1486
|
[💎rlts-img]: https://img.shields.io/badge/code_style_%26_linting-rubocop--lts-34495e.svg?plastic&logo=ruby&logoColor=white
|
1487
|
+
[💎appraisal2]: https://github.com/appraisal-rb/appraisal2
|
1488
|
+
[💎appraisal2-img]: https://img.shields.io/badge/appraised_by-appraisal2-34495e.svg?plastic&logo=ruby&logoColor=white
|
985
1489
|
[💎d-in-dvcs]: https://railsbling.com/posts/dvcs/put_the_d_in_dvcs/
|
986
|
-
[✉️discord-invite]: https://discord.gg/3qme4XHNKN
|
987
|
-
[✉️discord-invite-img]: https://img.shields.io/discord/1373797679469170758?style=for-the-badge
|
988
1490
|
|
989
1491
|
<details>
|
990
1492
|
<summary>
|
@@ -993,4 +1495,14 @@ or one of the others at the head of this README.
|
|
993
1495
|
|
994
1496
|
<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%3A%2F%2Fruby.social&style=social&label=Follow%20%40galtzo%20on%20Ruby.social"></a>
|
995
1497
|
<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%3A%2F%2Ffloss.social&style=social&label=Follow%20%40galtzo%20on%20Floss.social"></a>
|
1498
|
+
|
1499
|
+
</details>
|
1500
|
+
|
1501
|
+
|
1502
|
+
<details>
|
1503
|
+
<summary>Broken badges</summary>
|
1504
|
+
|
1505
|
+
[![Coverage Graph][🔑codecov-g]][🔑codecov]
|
1506
|
+
[![CodeCov Test Coverage][🔑codecovi]][🔑codecov]
|
1507
|
+
|
996
1508
|
</details>
|