oauth2 1.4.9 → 2.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +99 -23
- data/CONTRIBUTING.md +44 -0
- data/README.md +200 -82
- data/SECURITY.md +20 -0
- data/lib/oauth2/access_token.rb +29 -20
- data/lib/oauth2/authenticator.rb +9 -4
- data/lib/oauth2/client.rb +116 -79
- data/lib/oauth2/error.rb +27 -18
- data/lib/oauth2/response.rb +73 -22
- data/lib/oauth2/snaky_hash.rb +8 -0
- data/lib/oauth2/strategy/assertion.rb +63 -38
- data/lib/oauth2/strategy/auth_code.rb +12 -1
- data/lib/oauth2/strategy/implicit.rb +7 -0
- data/lib/oauth2/version.rb +1 -59
- data/lib/oauth2.rb +19 -1
- metadata +96 -77
- data/lib/oauth2/mac_token.rb +0 -130
- data/spec/fixtures/README.md +0 -11
- data/spec/fixtures/RS256/jwtRS256.key +0 -51
- data/spec/fixtures/RS256/jwtRS256.key.pub +0 -14
- data/spec/helper.rb +0 -33
- data/spec/oauth2/access_token_spec.rb +0 -218
- data/spec/oauth2/authenticator_spec.rb +0 -86
- data/spec/oauth2/client_spec.rb +0 -556
- data/spec/oauth2/mac_token_spec.rb +0 -122
- data/spec/oauth2/response_spec.rb +0 -96
- data/spec/oauth2/strategy/assertion_spec.rb +0 -113
- data/spec/oauth2/strategy/auth_code_spec.rb +0 -108
- data/spec/oauth2/strategy/base_spec.rb +0 -7
- data/spec/oauth2/strategy/client_credentials_spec.rb +0 -71
- data/spec/oauth2/strategy/implicit_spec.rb +0 -28
- data/spec/oauth2/strategy/password_spec.rb +0 -58
- data/spec/oauth2/version_spec.rb +0 -23
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
<p align="center">
|
2
|
-
<a href="http://oauth.net/2/" target="_blank" rel="noopener
|
2
|
+
<a href="http://oauth.net/2/" target="_blank" rel="noopener">
|
3
3
|
<img src="https://github.com/oauth-xx/oauth2/raw/master/docs/images/logo/oauth2-logo-124px.png?raw=true" alt="OAuth 2.0 Logo by Chris Messina, CC BY-SA 3.0">
|
4
4
|
</a>
|
5
|
-
<a href="https://www.ruby-lang.org/" target="_blank" rel="noopener
|
5
|
+
<a href="https://www.ruby-lang.org/" target="_blank" rel="noopener">
|
6
6
|
<img width="124px" src="https://github.com/oauth-xx/oauth2/raw/master/docs/images/logo/ruby-logo-198px.svg?raw=true" alt="Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5">
|
7
7
|
</a>
|
8
8
|
</p>
|
@@ -15,29 +15,37 @@ OAuth 2.0 focuses on client developer simplicity while providing specific author
|
|
15
15
|
This is a RubyGem for implementing OAuth 2.0 clients and servers in Ruby applications.
|
16
16
|
See the sibling `oauth` gem for OAuth 1.0 implementations in Ruby.
|
17
17
|
|
18
|
-
⚠️ **_WARNING_**: You are viewing the `README` of the soon-to-be-deprecated `1-4-stable`
|
19
|
-
branch which for version 1.4.x releases. Version 2.0 is coming! ⚠️
|
20
|
-
|
21
18
|
---
|
22
19
|
|
23
20
|
* [OAuth 2.0 Spec][oauth2-spec]
|
24
|
-
* [
|
25
|
-
* Help us finish release [![2.0.0 release milestone][next-milestone-pct-img]][next-milestone-pct] by submitting or reviewing PRs and issues.
|
26
|
-
* Oauth2 gem is _always_ looking for additional maintainers. See [#307][maintainers-discussion].
|
21
|
+
* [oauth sibling gem][sibling-gem] for OAuth 1.0 implementations in Ruby.
|
27
22
|
|
28
23
|
[oauth2-spec]: https://oauth.net/2/
|
29
24
|
[sibling-gem]: https://github.com/oauth-xx/oauth-ruby
|
30
|
-
[next-milestone-pct]: https://github.com/oauth-xx/oauth2/milestone/1
|
31
|
-
[next-milestone-pct-img]: https://img.shields.io/github/milestones/progress-percent/oauth-xx/oauth2/1
|
32
|
-
[maintainers-discussion]: https://github.com/oauth-xx/oauth2/issues/307
|
33
25
|
|
34
26
|
## Release Documentation
|
35
27
|
|
28
|
+
### Version 2.0.x
|
29
|
+
|
30
|
+
<details>
|
31
|
+
<summary>2.0.x Readmes</summary>
|
32
|
+
|
33
|
+
| Version | Release Date | Readme |
|
34
|
+
|---------|--------------|----------------------------------------------------------|
|
35
|
+
| 2.0.3 | 2022-06-28 | https://github.com/oauth-xx/oauth2/blob/v2.0.3/README.md |
|
36
|
+
| 2.0.2 | 2022-06-24 | https://github.com/oauth-xx/oauth2/blob/v2.0.2/README.md |
|
37
|
+
| 2.0.1 | 2022-06-22 | https://github.com/oauth-xx/oauth2/blob/v2.0.1/README.md |
|
38
|
+
| 2.0.0 | 2022-06-21 | https://github.com/oauth-xx/oauth2/blob/v2.0.0/README.md |
|
39
|
+
</details>
|
40
|
+
|
41
|
+
### Older Releases
|
42
|
+
|
36
43
|
<details>
|
37
44
|
<summary>1.4.x Readmes</summary>
|
38
45
|
|
39
46
|
| Version | Release Date | Readme |
|
40
47
|
|---------|--------------|----------------------------------------------------------|
|
48
|
+
| 1.4.9 | Feb 20, 2022 | https://github.com/oauth-xx/oauth2/blob/v1.4.9/README.md |
|
41
49
|
| 1.4.8 | Feb 18, 2022 | https://github.com/oauth-xx/oauth2/blob/v1.4.8/README.md |
|
42
50
|
| 1.4.7 | Mar 19, 2021 | https://github.com/oauth-xx/oauth2/blob/v1.4.7/README.md |
|
43
51
|
| 1.4.6 | Mar 19, 2021 | https://github.com/oauth-xx/oauth2/blob/v1.4.6/README.md |
|
@@ -69,6 +77,8 @@ branch which for version 1.4.x releases. Version 2.0 is coming! ⚠️
|
|
69
77
|
| < 1.0.0 | Find here | https://github.com/oauth-xx/oauth2/tags |
|
70
78
|
</details>
|
71
79
|
|
80
|
+
## Status
|
81
|
+
|
72
82
|
<!--
|
73
83
|
Numbering rows and badges in each row as a visual "database" lookup,
|
74
84
|
as the table is extremely dense, and it can be very difficult to find anything
|
@@ -91,17 +101,20 @@ badge #s:
|
|
91
101
|
🖐
|
92
102
|
🧮
|
93
103
|
📗
|
104
|
+
|
105
|
+
appended indicators:
|
106
|
+
♻️ - URL needs to be updated from SASS integration. Find / Replace is insufficient.
|
94
107
|
-->
|
95
108
|
|
96
|
-
| | Project | oauth2
|
97
|
-
|
98
|
-
| 1️⃣ | name, license, docs | [![RubyGems.org][⛳️name-img]][⛳️gem] [![License: MIT][🖇src-license-img]][🖇src-license] [![FOSSA][🏘fossa-img]][🏘fossa] [![RubyDoc.info][🚎yard-img]][🚎yard] [![InchCI][🖐inch-ci-img]][🚎yard]
|
99
|
-
| 2️⃣ | version & activity | [![Gem Version][⛳️version-img]][⛳️gem] [![Total Downloads][🖇DL-total-img]][⛳️gem] [![Download Rank][🏘DL-rank-img]][⛳️gem] [![Source Code][🚎src-home-img]][🚎src-home] [![Open PRs][🖐prs-
|
100
|
-
| 3️⃣ | maintanence & linting | [![Maintainability][⛳cclim-maint-img]][⛳cclim-maint] [![Helpers][🖇triage-help-img]][🖇triage-help] [![Depfu][🏘depfu-img]][🏘depfu] [![Contributors][🚎contributors-img]][🚎contributors] [![Style][🖐style-wf-img]][🖐style-wf] [![Kloc Roll][🧮kloc-img]][🧮kloc]
|
101
|
-
| 4️⃣ | testing | [![
|
102
|
-
| 5️⃣ | coverage & security | [![CodeClimate][⛳cclim-cov-img]][⛳cclim-cov] [![CodeCov][🖇codecov-img]][🖇codecov] [![Coveralls][🏘coveralls-img]][🏘coveralls] [![Security Policy][🚎sec-pol-img]][🚎sec-pol] [![CodeQL][🖐codeQL-img]][🖐codeQL]
|
103
|
-
| 6️⃣ | resources | [![Discussion][⛳gh-discussions-img]][⛳gh-discussions] [![Get help on Codementor][🖇codementor-img]][🖇codementor] [![Chat][🏘chat-img]][🏘chat] [![Blog][🚎blog-img]][🚎blog] [![Blog][🖐wiki-img]][🖐wiki]
|
104
|
-
| 7️⃣ | spread 💖 | [![Liberapay Patrons][⛳liberapay-img]][⛳liberapay] [![Sponsor Me][🖇sponsor-img]][🖇sponsor] [![Tweet @ Peter][🏘tweet-img]][🏘tweet] [🌏][aboutme] [👼][angelme] [💻][coderme]
|
109
|
+
| | Project | bundle add oauth2 |
|
110
|
+
|:----|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
111
|
+
| 1️⃣ | name, license, docs | [![RubyGems.org][⛳️name-img]][⛳️gem] [![License: MIT][🖇src-license-img]][🖇src-license] [![FOSSA][🏘fossa-img]][🏘fossa] [![RubyDoc.info][🚎yard-img]][🚎yard] [![InchCI][🖐inch-ci-img]][🚎yard] |
|
112
|
+
| 2️⃣ | version & activity | [![Gem Version][⛳️version-img]][⛳️gem] [![Total Downloads][🖇DL-total-img]][⛳️gem] [![Download Rank][🏘DL-rank-img]][⛳️gem] [![Source Code][🚎src-home-img]][🚎src-home] [![Open PRs][🖐prs-o-img]][🖐prs-o] [![Closed PRs][🧮prs-c-img]][🧮prs-c] [![Next Version][📗next-img♻️]][📗next♻️] |
|
113
|
+
| 3️⃣ | maintanence & linting | [![Maintainability][⛳cclim-maint-img♻️]][⛳cclim-maint] [![Helpers][🖇triage-help-img]][🖇triage-help] [![Depfu][🏘depfu-img♻️]][🏘depfu♻️] [![Contributors][🚎contributors-img]][🚎contributors] [![Style][🖐style-wf-img]][🖐style-wf] [![Kloc Roll][🧮kloc-img]][🧮kloc] |
|
114
|
+
| 4️⃣ | testing | [![Open Issues][⛳iss-o-img]][⛳iss-o] [![Closed Issues][🖇iss-c-img]][🖇iss-c] [![Supported][🏘sup-wf-img]][🏘sup-wf] [![Heads][🚎heads-wf-img]][🚎heads-wf] [![Unofficial Support][🖐uns-wf-img]][🖐uns-wf] [![MacOS][🧮mac-wf-img]][🧮mac-wf] [![Windows][📗win-wf-img]][📗win-wf] |
|
115
|
+
| 5️⃣ | coverage & security | [![CodeClimate][⛳cclim-cov-img♻️]][⛳cclim-cov] [![CodeCov][🖇codecov-img♻️]][🖇codecov] [![Coveralls][🏘coveralls-img]][🏘coveralls] [![Security Policy][🚎sec-pol-img]][🚎sec-pol] [![CodeQL][🖐codeQL-img]][🖐codeQL] [![Code Coverage][🧮cov-wf-img]][🧮cov-wf] |
|
116
|
+
| 6️⃣ | resources | [![Discussion][⛳gh-discussions-img]][⛳gh-discussions] [![Get help on Codementor][🖇codementor-img]][🖇codementor] [![Chat][🏘chat-img]][🏘chat] [![Blog][🚎blog-img]][🚎blog] [![Blog][🖐wiki-img]][🖐wiki] |
|
117
|
+
| 7️⃣ | spread 💖 | [![Liberapay Patrons][⛳liberapay-img]][⛳liberapay] [![Sponsor Me][🖇sponsor-img]][🖇sponsor] [![Tweet @ Peter][🏘tweet-img]][🏘tweet] [🌏][aboutme] [👼][angelme] [💻][coderme] |
|
105
118
|
|
106
119
|
<!--
|
107
120
|
The link tokens in the following sections should be kept ordered by the row and badge numbering scheme
|
@@ -124,18 +137,20 @@ The link tokens in the following sections should be kept ordered by the row and
|
|
124
137
|
[🏘DL-rank-img]: https://img.shields.io/gem/rt/oauth2.svg
|
125
138
|
[🚎src-home]: https://github.com/oauth-xx/oauth2
|
126
139
|
[🚎src-home-img]: https://img.shields.io/badge/source-github-brightgreen.svg?style=flat
|
127
|
-
[🖐prs-
|
128
|
-
[🖐prs-
|
129
|
-
[🧮prs-
|
130
|
-
[🧮prs-
|
140
|
+
[🖐prs-o]: https://github.com/oauth-xx/oauth2/pulls
|
141
|
+
[🖐prs-o-img]: https://img.shields.io/github/issues-pr/oauth-xx/oauth2
|
142
|
+
[🧮prs-c]: https://github.com/oauth-xx/oauth2/pulls?q=is%3Apr+is%3Aclosed
|
143
|
+
[🧮prs-c-img]: https://img.shields.io/github/issues-pr-closed/oauth-xx/oauth2
|
144
|
+
[📗next♻️]: https://github.com/oauth-xx/oauth2/milestone/15
|
145
|
+
[📗next-img♻️]: https://img.shields.io/github/milestones/progress/oauth-xx/oauth2/15?label=Next%20Version
|
131
146
|
|
132
147
|
<!-- 3️⃣ maintanence & linting -->
|
133
148
|
[⛳cclim-maint]: https://codeclimate.com/github/oauth-xx/oauth2/maintainability
|
134
|
-
[⛳cclim-maint-img]: https://api.codeclimate.com/v1/badges/688c612528ff90a46955/maintainability
|
149
|
+
[⛳cclim-maint-img♻️]: https://api.codeclimate.com/v1/badges/688c612528ff90a46955/maintainability
|
135
150
|
[🖇triage-help]: https://www.codetriage.com/oauth-xx/oauth2
|
136
151
|
[🖇triage-help-img]: https://www.codetriage.com/oauth-xx/oauth2/badges/users.svg
|
137
|
-
[🏘depfu]: https://depfu.com/github/oauth-xx/oauth2?project_id=4445
|
138
|
-
[🏘depfu-img]: https://badges.depfu.com/badges/6d34dc1ba682bbdf9ae2a97848241743/count.svg
|
152
|
+
[🏘depfu♻️]: https://depfu.com/github/oauth-xx/oauth2?project_id=4445
|
153
|
+
[🏘depfu-img♻️]: https://badges.depfu.com/badges/6d34dc1ba682bbdf9ae2a97848241743/count.svg
|
139
154
|
[🚎contributors]: https://github.com/oauth-xx/oauth2/graphs/contributors
|
140
155
|
[🚎contributors-img]: https://img.shields.io/github/contributors-anon/oauth-xx/oauth2
|
141
156
|
[🖐style-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/style.yml
|
@@ -144,28 +159,34 @@ The link tokens in the following sections should be kept ordered by the row and
|
|
144
159
|
[🧮kloc-img]: https://img.shields.io/tokei/lines/github.com/oauth-xx/oauth2
|
145
160
|
|
146
161
|
<!-- 4️⃣ testing -->
|
147
|
-
[
|
148
|
-
[
|
149
|
-
[🖇
|
150
|
-
[🖇
|
151
|
-
[🏘
|
152
|
-
[🏘
|
153
|
-
[🚎
|
154
|
-
[🚎
|
155
|
-
[🖐
|
156
|
-
[🖐
|
162
|
+
[⛳iss-o]: https://github.com/oauth-xx/oauth2/issues
|
163
|
+
[⛳iss-o-img]: https://img.shields.io/github/issues-raw/oauth-xx/oauth2
|
164
|
+
[🖇iss-c]: https://github.com/oauth-xx/oauth2/issues?q=is%3Aissue+is%3Aclosed
|
165
|
+
[🖇iss-c-img]: https://img.shields.io/github/issues-closed-raw/oauth-xx/oauth2
|
166
|
+
[🏘sup-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/supported.yml
|
167
|
+
[🏘sup-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/supported.yml/badge.svg
|
168
|
+
[🚎heads-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/heads.yml
|
169
|
+
[🚎heads-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/heads.yml/badge.svg
|
170
|
+
[🖐uns-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/unsupported.yml
|
171
|
+
[🖐uns-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/unsupported.yml/badge.svg
|
172
|
+
[🧮mac-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/macos.yml
|
173
|
+
[🧮mac-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/macos.yml/badge.svg
|
174
|
+
[📗win-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/windows.yml
|
175
|
+
[📗win-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/windows.yml/badge.svg
|
157
176
|
|
158
177
|
<!-- 5️⃣ coverage & security -->
|
159
178
|
[⛳cclim-cov]: https://codeclimate.com/github/oauth-xx/oauth2/test_coverage
|
160
|
-
[⛳cclim-cov-img]: https://api.codeclimate.com/v1/badges/688c612528ff90a46955/test_coverage
|
161
|
-
[🖇codecov-img]: https://codecov.io/gh/oauth-xx/oauth2/branch/
|
179
|
+
[⛳cclim-cov-img♻️]: https://api.codeclimate.com/v1/badges/688c612528ff90a46955/test_coverage
|
180
|
+
[🖇codecov-img♻️]: https://codecov.io/gh/oauth-xx/oauth2/branch/master/graph/badge.svg?token=bNqSzNiuo2
|
162
181
|
[🖇codecov]: https://codecov.io/gh/oauth-xx/oauth2
|
163
|
-
[🏘coveralls]: https://coveralls.io/github/oauth-xx/oauth2?branch=
|
164
|
-
[🏘coveralls-img]: https://coveralls.io/repos/github/oauth-xx/oauth2/badge.svg?branch=
|
182
|
+
[🏘coveralls]: https://coveralls.io/github/oauth-xx/oauth2?branch=master
|
183
|
+
[🏘coveralls-img]: https://coveralls.io/repos/github/oauth-xx/oauth2/badge.svg?branch=master
|
165
184
|
[🚎sec-pol]: https://github.com/oauth-xx/oauth2/blob/master/SECURITY.md
|
166
185
|
[🚎sec-pol-img]: https://img.shields.io/badge/security-policy-brightgreen.svg?style=flat
|
167
186
|
[🖐codeQL]: https://github.com/oauth-xx/oauth2/security/code-scanning
|
168
187
|
[🖐codeQL-img]: https://github.com/oauth-xx/oauth2/actions/workflows/codeql-analysis.yml/badge.svg
|
188
|
+
[🧮cov-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/coverage.yml
|
189
|
+
[🧮cov-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/coverage.yml/badge.svg
|
169
190
|
|
170
191
|
<!-- 6️⃣ resources -->
|
171
192
|
[⛳gh-discussions]: https://github.com/oauth-xx/oauth2/discussions
|
@@ -193,31 +214,52 @@ The link tokens in the following sections should be kept ordered by the row and
|
|
193
214
|
[aboutme]: https://about.me/peter.boling
|
194
215
|
[angelme]: https://angel.co/peter-boling
|
195
216
|
[coderme]:http://coderwall.com/pboling
|
196
|
-
[politicme]: https://nationalprogressiveparty.org
|
197
|
-
|
198
217
|
|
199
218
|
## Installation
|
200
219
|
|
201
|
-
|
202
|
-
gem install oauth2
|
203
|
-
```
|
220
|
+
Install the gem and add to the application's Gemfile by executing:
|
204
221
|
|
205
|
-
|
222
|
+
$ bundle add oauth2
|
206
223
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
224
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
225
|
+
|
226
|
+
$ gem install oauth2
|
227
|
+
|
228
|
+
## OAuth2 for Enterprise
|
229
|
+
|
230
|
+
Available as part of the Tidelift Subscription.
|
231
|
+
|
232
|
+
The maintainers of OAuth2 and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. [Learn more.](https://tidelift.com/subscription/pkg/rubygems-oauth2?utm_source=rubygems-oauth2&utm_medium=referral&utm_campaign=enterprise)
|
214
233
|
|
234
|
+
## Security contact information
|
235
|
+
|
236
|
+
To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security).
|
237
|
+
Tidelift will coordinate the fix and disclosure.
|
238
|
+
|
239
|
+
For more see [SECURITY.md][🚎sec-pol].
|
240
|
+
|
241
|
+
## What is new for v2.0?
|
242
|
+
|
243
|
+
- Officially support Ruby versions >= 2.7
|
244
|
+
- Unofficially support Ruby versions >= 2.5
|
245
|
+
- Incidentally support Ruby versions >= 2.2
|
246
|
+
- Drop support for the expired MAC Draft (all versions)
|
247
|
+
- Support IETF rfc7523 JWT Bearer Tokens
|
248
|
+
- Support IETF rfc7231 Relative Location in Redirect
|
249
|
+
- Support IETF rfc6749 Don't set oauth params when nil
|
250
|
+
- 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)
|
251
|
+
- 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`
|
252
|
+
- Adds new option to `OAuth2::Client#get_token`:
|
253
|
+
- `:access_token_class` (`AccessToken`); user specified class to use for all calls to `get_token`
|
254
|
+
- Adds new option to `OAuth2::AccessToken#initialize`:
|
255
|
+
- `:expires_latency` (`nil`); number of seconds by which AccessToken validity will be reduced to offset latency
|
256
|
+
- [... A lot more](https://github.com/oauth-xx/oauth2/blob/master/CHANGELOG.md#2.0.0)
|
215
257
|
|
216
258
|
## Compatibility
|
217
259
|
|
218
260
|
Targeted ruby compatibility is non-EOL versions of Ruby, currently 2.7, 3.0 and
|
219
261
|
3.1. Compatibility is further distinguished by supported and unsupported versions of Ruby.
|
220
|
-
Ruby is limited to
|
262
|
+
Ruby is limited to 2.2+ for 2.x releases. See `1-4-stable` branch for older rubies.
|
221
263
|
|
222
264
|
<details>
|
223
265
|
<summary>Ruby Engine Compatibility Policy</summary>
|
@@ -249,28 +291,82 @@ of a major release, support for that Ruby version may be dropped.
|
|
249
291
|
|
250
292
|
| | Ruby OAuth 2 Version | Maintenance Branch | Supported Officially | Supported Unofficially | Supported Incidentally |
|
251
293
|
|:----|----------------------|--------------------|-------------------------|------------------------|------------------------|
|
252
|
-
| 1️⃣ | 2.0.x
|
253
|
-
| 2️⃣ | 1.4.x | `1-4-stable` | 2.5, 2.6, 2.7, 3.0, 3.1 | 2.1, 2.2, 2.3, 2.4 |
|
294
|
+
| 1️⃣ | 2.0.x | `master` | 2.7, 3.0, 3.1 | 2.5, 2.6 | 2.2, 2.3, 2.4 |
|
295
|
+
| 2️⃣ | 1.4.x | `1-4-stable` | 2.5, 2.6, 2.7, 3.0, 3.1 | 2.1, 2.2, 2.3, 2.4 | 1.9, 2.0 |
|
254
296
|
| 3️⃣ | older | N/A | Best of luck to you! | Please upgrade! | |
|
255
297
|
|
256
|
-
NOTE:
|
298
|
+
NOTE: The 1.4 series will only receive critical bug and security updates.
|
257
299
|
See [SECURITY.md][🚎sec-pol]
|
258
300
|
|
259
301
|
## Usage Examples
|
260
302
|
|
303
|
+
### `authorize_url` and `token_url` are on site root (Just Works!)
|
304
|
+
|
261
305
|
```ruby
|
262
306
|
require 'oauth2'
|
263
|
-
client = OAuth2::Client.new('client_id', 'client_secret', :
|
264
|
-
|
265
|
-
client.auth_code.authorize_url(:
|
266
|
-
# => "https://example.org/oauth/
|
307
|
+
client = OAuth2::Client.new('client_id', 'client_secret', site: 'https://example.org')
|
308
|
+
# => #<OAuth2::Client:0x00000001204c8288 @id="client_id", @secret="client_sec...
|
309
|
+
client.auth_code.authorize_url(redirect_uri: 'http://localhost:8080/oauth2/callback')
|
310
|
+
# => "https://example.org/oauth/authorize?client_id=client_id&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Foauth2%2Fcallback&response_type=code"
|
267
311
|
|
268
|
-
|
269
|
-
response =
|
312
|
+
access = client.auth_code.get_token('authorization_code_value', redirect_uri: 'http://localhost:8080/oauth2/callback', headers: {'Authorization' => 'Basic some_password'})
|
313
|
+
response = access.get('/api/resource', params: {'query_foo' => 'bar'})
|
270
314
|
response.class.name
|
271
315
|
# => OAuth2::Response
|
272
316
|
```
|
273
317
|
|
318
|
+
### Relative `authorize_url` and `token_url` (Not on site root, Just Works!)
|
319
|
+
|
320
|
+
In above example, the default Authorization URL is `oauth/authorize` and default Access Token URL is `oauth/token`, and, as they are missing a leading `/`, both are relative.
|
321
|
+
|
322
|
+
```ruby
|
323
|
+
client = OAuth2::Client.new('client_id', 'client_secret', site: 'https://example.org/nested/directory/on/your/server')
|
324
|
+
# => #<OAuth2::Client:0x00000001204c8288 @id="client_id", @secret="client_sec...
|
325
|
+
client.auth_code.authorize_url(redirect_uri: 'http://localhost:8080/oauth2/callback')
|
326
|
+
# => "https://example.org/nested/directory/on/your/server/oauth/authorize?client_id=client_id&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Foauth2%2Fcallback&response_type=code"
|
327
|
+
```
|
328
|
+
|
329
|
+
### Customize `authorize_url` and `token_url`
|
330
|
+
|
331
|
+
You can specify custom URLs for authorization and access token, and when using a leading `/` they will _not be relative_, as shown below:
|
332
|
+
|
333
|
+
```ruby
|
334
|
+
client = OAuth2::Client.new('client_id', 'client_secret',
|
335
|
+
site: 'https://example.org/nested/directory/on/your/server',
|
336
|
+
authorize_url: '/jaunty/authorize/',
|
337
|
+
token_url: '/stirrups/access_token')
|
338
|
+
# => #<OAuth2::Client:0x00000001204c8288 @id="client_id", @secret="client_sec...
|
339
|
+
client.auth_code.authorize_url(redirect_uri: 'http://localhost:8080/oauth2/callback')
|
340
|
+
# => "https://example.org/jaunty/authorize/?client_id=client_id&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Foauth2%2Fcallback&response_type=code"
|
341
|
+
client.class.name
|
342
|
+
# => OAuth2::Client
|
343
|
+
```
|
344
|
+
|
345
|
+
### snake_case and indifferent access in Response#parsed
|
346
|
+
|
347
|
+
```ruby
|
348
|
+
response = access.get('/api/resource', params: {'query_foo' => 'bar'})
|
349
|
+
# Even if the actual response is CamelCase. it will be made available as snaky:
|
350
|
+
JSON.parse(response.body) # => {"accessToken"=>"aaaaaaaa", "additionalData"=>"additional"}
|
351
|
+
response.parsed # => {"access_token"=>"aaaaaaaa", "additional_data"=>"additional"}
|
352
|
+
response.parsed.access_token # => "aaaaaaaa"
|
353
|
+
response.parsed[:access_token] # => "aaaaaaaa"
|
354
|
+
response.parsed.additional_data # => "additional"
|
355
|
+
response.parsed[:additional_data] # => "additional"
|
356
|
+
response.parsed.class.name # => OAuth2::SnakyHash (subclass of Hashie::Mash::Rash, from `rash_alt` gem)
|
357
|
+
```
|
358
|
+
|
359
|
+
#### What if I hate snakes and/or indifference?
|
360
|
+
|
361
|
+
```ruby
|
362
|
+
response = access.get('/api/resource', params: {'query_foo' => 'bar'}, snaky: false)
|
363
|
+
JSON.parse(response.body) # => {"accessToken"=>"aaaaaaaa", "additionalData"=>"additional"}
|
364
|
+
response.parsed # => {"accessToken"=>"aaaaaaaa", "additionalData"=>"additional"}
|
365
|
+
response.parsed['accessToken'] # => "aaaaaaaa"
|
366
|
+
response.parsed['additionalData'] # => "additional"
|
367
|
+
response.parsed.class.name # => Hash (just, regular old Hash)
|
368
|
+
```
|
369
|
+
|
274
370
|
<details>
|
275
371
|
<summary>Debugging</summary>
|
276
372
|
|
@@ -289,8 +385,8 @@ require 'oauth2'
|
|
289
385
|
client = OAuth2::Client.new(
|
290
386
|
'client_id',
|
291
387
|
'client_secret',
|
292
|
-
:
|
293
|
-
:
|
388
|
+
site: 'https://example.org',
|
389
|
+
logger: Logger.new('example.log', 'weekly')
|
294
390
|
)
|
295
391
|
```
|
296
392
|
</details>
|
@@ -301,7 +397,7 @@ The `AccessToken` methods `#get`, `#post`, `#put` and `#delete` and the generic
|
|
301
397
|
will return an instance of the #OAuth2::Response class.
|
302
398
|
|
303
399
|
This instance contains a `#parsed` method that will parse the response body and
|
304
|
-
return a Hash if the `Content-Type` is `application/x-www-form-urlencoded` or if
|
400
|
+
return a Hash-like [`OAuth2::SnakyHash`](https://github.com/oauth-xx/oauth2/blob/master/lib/oauth2/snaky_hash.rb) if the `Content-Type` is `application/x-www-form-urlencoded` or if
|
305
401
|
the body is a JSON object. It will return an Array if the body is a JSON
|
306
402
|
array. Otherwise, it will return the original body string.
|
307
403
|
|
@@ -331,28 +427,42 @@ Response instance will contain the `OAuth2::Error` instance.
|
|
331
427
|
|
332
428
|
Currently the Authorization Code, Implicit, Resource Owner Password Credentials, Client Credentials, and Assertion
|
333
429
|
authentication grant types have helper strategy classes that simplify client
|
334
|
-
use. They are available via the `#auth_code
|
430
|
+
use. They are available via the [`#auth_code`](https://github.com/oauth-xx/oauth2/blob/master/lib/oauth2/strategy/auth_code.rb), [`#implicit`](https://github.com/oauth-xx/oauth2/blob/master/lib/oauth2/strategy/implicit.rb), [`#password`](https://github.com/oauth-xx/oauth2/blob/master/lib/oauth2/strategy/password.rb), [`#client_credentials`](https://github.com/oauth-xx/oauth2/blob/master/lib/oauth2/strategy/client_credentials.rb), and [`#assertion`](https://github.com/oauth-xx/oauth2/blob/master/lib/oauth2/strategy/assertion.rb) methods respectively.
|
335
431
|
|
432
|
+
These aren't full examples, but demonstrative of the differences between usage for each strategy.
|
336
433
|
```ruby
|
337
|
-
auth_url = client.auth_code.authorize_url(:
|
338
|
-
|
434
|
+
auth_url = client.auth_code.authorize_url(redirect_uri: 'http://localhost:8080/oauth/callback')
|
435
|
+
access = client.auth_code.get_token('code_value', redirect_uri: 'http://localhost:8080/oauth/callback')
|
339
436
|
|
340
|
-
auth_url = client.implicit.authorize_url(:
|
437
|
+
auth_url = client.implicit.authorize_url(redirect_uri: 'http://localhost:8080/oauth/callback')
|
341
438
|
# get the token params in the callback and
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
439
|
+
access = OAuth2::AccessToken.from_kvform(client, query_string)
|
440
|
+
|
441
|
+
access = client.password.get_token('username', 'password')
|
442
|
+
|
443
|
+
access = client.client_credentials.get_token
|
444
|
+
|
445
|
+
# Client Assertion Strategy
|
446
|
+
# see: https://tools.ietf.org/html/rfc7523
|
447
|
+
claimset = {
|
448
|
+
iss: 'http://localhost:3001',
|
449
|
+
aud: 'http://localhost:8080/oauth2/token',
|
450
|
+
sub: 'me@example.com',
|
451
|
+
exp: Time.now.utc.to_i + 3600,
|
452
|
+
}
|
453
|
+
assertion_params = [claimset, 'HS256', 'secret_key']
|
454
|
+
access = client.assertion.get_token(assertion_params)
|
455
|
+
|
456
|
+
# The `access` (i.e. access token) is then used like so:
|
457
|
+
access.token # actual access_token string, if you need it somewhere
|
458
|
+
access.get('/api/stuff') # making api calls with access token
|
349
459
|
```
|
350
460
|
|
351
461
|
If you want to specify additional headers to be sent out with the
|
352
462
|
request, add a 'headers' hash under 'params':
|
353
463
|
|
354
464
|
```ruby
|
355
|
-
|
465
|
+
access = client.auth_code.get_token('code_value', redirect_uri: 'http://localhost:8080/oauth/callback', headers: {'Some' => 'Header'})
|
356
466
|
```
|
357
467
|
|
358
468
|
You can always use the `#request` method on the `OAuth2::Client` instance to make
|
@@ -373,7 +483,7 @@ dependency on this gem using the [Pessimistic Version Constraint][pvc] with two
|
|
373
483
|
For example:
|
374
484
|
|
375
485
|
```ruby
|
376
|
-
spec.add_dependency 'oauth2', '~>
|
486
|
+
spec.add_dependency 'oauth2', '~> 2.0'
|
377
487
|
```
|
378
488
|
|
379
489
|
[semver]: http://semver.org/
|
@@ -395,13 +505,21 @@ spec.add_dependency 'oauth2', '~> 1.4'
|
|
395
505
|
|
396
506
|
## Development
|
397
507
|
|
398
|
-
After checking out the repo, run `
|
508
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
399
509
|
|
400
510
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
401
511
|
|
402
512
|
## Contributing
|
403
513
|
|
404
|
-
|
514
|
+
See [CONTRIBUTING.md][contributing]
|
515
|
+
|
516
|
+
[contributing]: https://github.com/oauth-xx/oauth2/blob/main/CONTRIBUTING.md
|
517
|
+
|
518
|
+
## Contributors
|
519
|
+
|
520
|
+
[![Contributors](https://contrib.rocks/image?repo=oauth-xx/oauth2)]("https://github.com/oauth-xx/oauth2/graphs/contributors")
|
521
|
+
|
522
|
+
Made with [contributors-img](https://contrib.rocks).
|
405
523
|
|
406
524
|
## Code of Conduct
|
407
525
|
|
data/SECURITY.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# Security Policy
|
2
|
+
|
3
|
+
## Supported Versions
|
4
|
+
|
5
|
+
| Version | Supported |
|
6
|
+
|----------|---------------------------|
|
7
|
+
| 2.latest | ✅ |
|
8
|
+
| 1.latest | ✅ (security updates only) |
|
9
|
+
| older | ⛔️ |
|
10
|
+
|
11
|
+
## Reporting a Vulnerability
|
12
|
+
|
13
|
+
To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security).
|
14
|
+
Tidelift will coordinate the fix and disclosure.
|
15
|
+
|
16
|
+
## OAuth2 for Enterprise
|
17
|
+
|
18
|
+
Available as part of the Tidelift Subscription.
|
19
|
+
|
20
|
+
The maintainers of oauth2 and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. [Learn more.](https://tidelift.com/subscription/pkg/rubygems-oauth2?utm_source=rubygems-oauth2&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
|
data/lib/oauth2/access_token.rb
CHANGED
@@ -1,33 +1,36 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module OAuth2
|
4
|
-
class AccessToken
|
5
|
-
attr_reader :client, :token, :expires_in, :expires_at, :params
|
6
|
-
attr_accessor :options, :refresh_token
|
4
|
+
class AccessToken # rubocop:disable Metrics/ClassLength
|
5
|
+
attr_reader :client, :token, :expires_in, :expires_at, :expires_latency, :params
|
6
|
+
attr_accessor :options, :refresh_token, :response
|
7
7
|
|
8
|
-
# Should these methods be deprecated?
|
9
8
|
class << self
|
10
9
|
# Initializes an AccessToken from a Hash
|
11
10
|
#
|
12
|
-
# @param [Client] the OAuth2::Client instance
|
13
|
-
# @param [Hash] a hash of AccessToken property values
|
14
|
-
# @return [AccessToken] the
|
11
|
+
# @param client [Client] the OAuth2::Client instance
|
12
|
+
# @param hash [Hash] a hash of AccessToken property values
|
13
|
+
# @return [AccessToken] the initialized AccessToken
|
15
14
|
def from_hash(client, hash)
|
16
15
|
hash = hash.dup
|
17
|
-
new(client, hash.delete('access_token') || hash.delete(:access_token), hash)
|
16
|
+
new(client, hash.delete('access_token') || hash.delete(:access_token) || hash.delete('token') || hash.delete(:token), hash)
|
18
17
|
end
|
19
18
|
|
20
19
|
# Initializes an AccessToken from a key/value application/x-www-form-urlencoded string
|
21
20
|
#
|
22
21
|
# @param [Client] client the OAuth2::Client instance
|
23
22
|
# @param [String] kvform the application/x-www-form-urlencoded string
|
24
|
-
# @return [AccessToken] the
|
23
|
+
# @return [AccessToken] the initialized AccessToken
|
25
24
|
def from_kvform(client, kvform)
|
26
25
|
from_hash(client, Rack::Utils.parse_query(kvform))
|
27
26
|
end
|
27
|
+
|
28
|
+
def contains_token?(hash)
|
29
|
+
hash.key?('access_token') || hash.key?('id_token') || hash.key?('token')
|
30
|
+
end
|
28
31
|
end
|
29
32
|
|
30
|
-
#
|
33
|
+
# Initialize an AccessToken
|
31
34
|
#
|
32
35
|
# @param [Client] client the OAuth2::Client instance
|
33
36
|
# @param [String] token the Access Token value
|
@@ -35,6 +38,7 @@ module OAuth2
|
|
35
38
|
# @option opts [String] :refresh_token (nil) the refresh_token value
|
36
39
|
# @option opts [FixNum, String] :expires_in (nil) the number of seconds in which the AccessToken will expire
|
37
40
|
# @option opts [FixNum, String] :expires_at (nil) the epoch time in seconds in which AccessToken will expire
|
41
|
+
# @option opts [FixNum, String] :expires_latency (nil) the number of seconds by which AccessToken validity will be reduced to offset latency, @version 2.0+
|
38
42
|
# @option opts [Symbol] :mode (:header) the transmission mode of the Access Token parameter value
|
39
43
|
# one of :header, :body or :query
|
40
44
|
# @option opts [String] :header_format ('Bearer %s') the string format to use for the Authorization header
|
@@ -44,16 +48,18 @@ module OAuth2
|
|
44
48
|
@client = client
|
45
49
|
@token = token.to_s
|
46
50
|
opts = opts.dup
|
47
|
-
[
|
51
|
+
%i[refresh_token expires_in expires_at expires_latency].each do |arg|
|
48
52
|
instance_variable_set("@#{arg}", opts.delete(arg) || opts.delete(arg.to_s))
|
49
53
|
end
|
50
54
|
@expires_in ||= opts.delete('expires')
|
51
55
|
@expires_in &&= @expires_in.to_i
|
52
56
|
@expires_at &&= convert_expires_at(@expires_at)
|
57
|
+
@expires_latency &&= @expires_latency.to_i
|
53
58
|
@expires_at ||= Time.now.to_i + @expires_in if @expires_in
|
54
|
-
@
|
55
|
-
|
56
|
-
:
|
59
|
+
@expires_at -= @expires_latency if @expires_latency
|
60
|
+
@options = {mode: opts.delete(:mode) || :header,
|
61
|
+
header_format: opts.delete(:header_format) || 'Bearer %s',
|
62
|
+
param_name: opts.delete(:param_name) || 'access_token'}
|
57
63
|
@params = opts
|
58
64
|
end
|
59
65
|
|
@@ -75,29 +81,32 @@ module OAuth2
|
|
75
81
|
#
|
76
82
|
# @return [Boolean]
|
77
83
|
def expired?
|
78
|
-
expires? && (expires_at
|
84
|
+
expires? && (expires_at <= Time.now.to_i)
|
79
85
|
end
|
80
86
|
|
81
87
|
# Refreshes the current Access Token
|
82
88
|
#
|
83
89
|
# @return [AccessToken] a new AccessToken
|
84
90
|
# @note options should be carried over to the new AccessToken
|
85
|
-
def refresh
|
91
|
+
def refresh(params = {}, access_token_opts = {})
|
86
92
|
raise('A refresh_token is not available') unless refresh_token
|
87
93
|
|
88
94
|
params[:grant_type] = 'refresh_token'
|
89
95
|
params[:refresh_token] = refresh_token
|
90
|
-
new_token = @client.get_token(params)
|
96
|
+
new_token = @client.get_token(params, access_token_opts)
|
91
97
|
new_token.options = options
|
92
98
|
new_token.refresh_token = refresh_token unless new_token.refresh_token
|
93
99
|
new_token
|
94
100
|
end
|
101
|
+
# A compatibility alias
|
102
|
+
# @note does not modify the receiver, so bang is not the default method
|
103
|
+
alias refresh! refresh
|
95
104
|
|
96
105
|
# Convert AccessToken to a hash which can be used to rebuild itself with AccessToken.from_hash
|
97
106
|
#
|
98
107
|
# @return [Hash] a hash of AccessToken property values
|
99
108
|
def to_hash
|
100
|
-
params.merge(:
|
109
|
+
params.merge(access_token: token, refresh_token: refresh_token, expires_at: expires_at)
|
101
110
|
end
|
102
111
|
|
103
112
|
# Make a request with the Access Token
|
@@ -105,7 +114,7 @@ module OAuth2
|
|
105
114
|
# @param [Symbol] verb the HTTP request method
|
106
115
|
# @param [String] path the HTTP URL path of the request
|
107
116
|
# @param [Hash] opts the options to make the request with
|
108
|
-
#
|
117
|
+
# @see Client#request
|
109
118
|
def request(verb, path, opts = {}, &block)
|
110
119
|
configure_authentication!(opts)
|
111
120
|
@client.request(verb, path, opts, &block)
|
@@ -166,7 +175,7 @@ module OAuth2
|
|
166
175
|
if opts[:body].is_a?(Hash)
|
167
176
|
opts[:body][options[:param_name]] = token
|
168
177
|
else
|
169
|
-
opts[:body]
|
178
|
+
opts[:body] += "&#{options[:param_name]}=#{token}"
|
170
179
|
end
|
171
180
|
# @todo support for multi-part (file uploads)
|
172
181
|
else
|
data/lib/oauth2/authenticator.rb
CHANGED
@@ -37,7 +37,7 @@ module OAuth2
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def self.encode_basic_auth(user, password)
|
40
|
-
|
40
|
+
"Basic #{Base64.strict_encode64("#{user}:#{password}")}"
|
41
41
|
end
|
42
42
|
|
43
43
|
private
|
@@ -45,13 +45,18 @@ module OAuth2
|
|
45
45
|
# Adds client_id and client_secret request parameters if they are not
|
46
46
|
# already set.
|
47
47
|
def apply_params_auth(params)
|
48
|
-
|
48
|
+
result = {}
|
49
|
+
result['client_id'] = id unless id.nil?
|
50
|
+
result['client_secret'] = secret unless secret.nil?
|
51
|
+
result.merge(params)
|
49
52
|
end
|
50
53
|
|
51
54
|
# When using schemes that don't require the client_secret to be passed i.e TLS Client Auth,
|
52
55
|
# we don't want to send the secret
|
53
56
|
def apply_client_id(params)
|
54
|
-
|
57
|
+
result = {}
|
58
|
+
result['client_id'] = id unless id.nil?
|
59
|
+
result.merge(params)
|
55
60
|
end
|
56
61
|
|
57
62
|
# Adds an `Authorization` header with Basic Auth credentials if and only if
|
@@ -59,7 +64,7 @@ module OAuth2
|
|
59
64
|
def apply_basic_auth(params)
|
60
65
|
headers = params.fetch(:headers, {})
|
61
66
|
headers = basic_auth_header.merge(headers)
|
62
|
-
params.merge(:
|
67
|
+
params.merge(headers: headers)
|
63
68
|
end
|
64
69
|
|
65
70
|
# @see https://datatracker.ietf.org/doc/html/rfc2617#section-2
|