rack-oauth2 1.12.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +3 -0
- data/.github/workflows/spec.yml +32 -0
- data/CHANGELOG.md +25 -0
- data/README.rdoc +1 -26
- data/VERSION +1 -1
- data/lib/rack/oauth2/access_token/authenticator.rb +1 -10
- data/lib/rack/oauth2/access_token/bearer.rb +1 -1
- data/lib/rack/oauth2/access_token/mtls.rb +2 -2
- data/lib/rack/oauth2/access_token.rb +4 -6
- data/lib/rack/oauth2/client.rb +97 -41
- data/lib/rack/oauth2/server/abstract/error.rb +2 -1
- data/lib/rack/oauth2/server/extension/pkce.rb +1 -1
- data/lib/rack/oauth2/server/rails/response_ext.rb +5 -5
- data/lib/rack/oauth2/server/resource/error.rb +4 -4
- data/lib/rack/oauth2/server/resource.rb +0 -1
- data/lib/rack/oauth2/server/token/error.rb +3 -1
- data/lib/rack/oauth2/server/token.rb +16 -5
- data/lib/rack/oauth2/urn.rb +3 -3
- data/lib/rack/oauth2/util.rb +6 -2
- data/lib/rack/oauth2.rb +11 -10
- data/rack-oauth2.gemspec +7 -5
- data/spec/helpers/webmock_helper.rb +8 -2
- data/spec/rack/oauth2/access_token/authenticator_spec.rb +2 -22
- data/spec/rack/oauth2/access_token/bearer_spec.rb +2 -2
- data/spec/rack/oauth2/access_token_spec.rb +0 -17
- data/spec/rack/oauth2/client_spec.rb +173 -75
- data/spec/rack/oauth2/oauth2_spec.rb +0 -43
- data/spec/rack/oauth2/server/authorize/error_spec.rb +6 -6
- data/spec/rack/oauth2/server/resource/bearer/error_spec.rb +2 -2
- data/spec/rack/oauth2/server/resource/bearer_spec.rb +9 -9
- data/spec/rack/oauth2/server/resource/error_spec.rb +21 -21
- data/spec/rack/oauth2/server/token/authorization_code_spec.rb +2 -2
- data/spec/rack/oauth2/server/token/client_credentials_spec.rb +32 -2
- data/spec/rack/oauth2/server/token/error_spec.rb +8 -8
- data/spec/rack/oauth2/server/token_spec.rb +72 -3
- data/spec/rack/oauth2/util_spec.rb +8 -3
- metadata +47 -51
- data/.travis.yml +0 -7
- data/lib/rack/oauth2/access_token/legacy.rb +0 -19
- data/lib/rack/oauth2/access_token/mac/sha256_hex_verifier.rb +0 -17
- data/lib/rack/oauth2/access_token/mac/signature.rb +0 -34
- data/lib/rack/oauth2/access_token/mac/verifier.rb +0 -44
- data/lib/rack/oauth2/access_token/mac.rb +0 -103
- data/lib/rack/oauth2/debugger/request_filter.rb +0 -30
- data/lib/rack/oauth2/debugger.rb +0 -3
- data/lib/rack/oauth2/server/resource/mac/error.rb +0 -24
- data/lib/rack/oauth2/server/resource/mac.rb +0 -36
- data/spec/mock_response/tokens/legacy.json +0 -5
- data/spec/mock_response/tokens/legacy.txt +0 -1
- data/spec/mock_response/tokens/legacy_without_expires_in.txt +0 -1
- data/spec/mock_response/tokens/mac.json +0 -8
- data/spec/rack/oauth2/access_token/legacy_spec.rb +0 -23
- data/spec/rack/oauth2/access_token/mac/sha256_hex_verifier_spec.rb +0 -28
- data/spec/rack/oauth2/access_token/mac/signature_spec.rb +0 -59
- data/spec/rack/oauth2/access_token/mac/verifier_spec.rb +0 -25
- data/spec/rack/oauth2/access_token/mac_spec.rb +0 -141
- data/spec/rack/oauth2/debugger/request_filter_spec.rb +0 -33
- data/spec/rack/oauth2/server/resource/mac/error_spec.rb +0 -52
- data/spec/rack/oauth2/server/resource/mac_spec.rb +0 -119
- /data/spec/mock_response/{blank → blank.txt} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 45ba67ac4566f374465673cc5711e71c15006bbe966531a4c1de2473206879b2
|
4
|
+
data.tar.gz: 56f8718f283533c369b1743dfd86499e49e5d828a83ac060fa919fac57a935d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63316467536c2c98cddea9b2b7907b3ff5fd6b53b892bd338709e1f7a6b014aa4dc20d71b12cd01ffac502c1ab0964218aac7ff6a0e81141ff8aa10e80557cdd
|
7
|
+
data.tar.gz: 97e685531853c4837a0e86636c865827033e25f646c4572d254e2584a811f937faa6dc7fe780742814bd9657066c9fc16394723ba87029605761d5acf2d490f7
|
data/.github/FUNDING.yml
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
name: Spec
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- master
|
7
|
+
pull_request:
|
8
|
+
|
9
|
+
permissions:
|
10
|
+
contents: read
|
11
|
+
|
12
|
+
jobs:
|
13
|
+
spec:
|
14
|
+
strategy:
|
15
|
+
matrix:
|
16
|
+
os: ['ubuntu-20.04']
|
17
|
+
ruby-version: ['2.6', '2.7', '3.0', '3.1']
|
18
|
+
# ubuntu 22.04 only supports ssl 3 and thus only ruby 3.1
|
19
|
+
include:
|
20
|
+
- os: 'ubuntu-22.04'
|
21
|
+
ruby-version: '3.1'
|
22
|
+
runs-on: ${{ matrix.os }}
|
23
|
+
|
24
|
+
steps:
|
25
|
+
- uses: actions/checkout@v3
|
26
|
+
- name: Set up Ruby
|
27
|
+
uses: ruby/setup-ruby@v1
|
28
|
+
with:
|
29
|
+
ruby-version: ${{ matrix.ruby-version }}
|
30
|
+
bundler-cache: true
|
31
|
+
- name: Run Specs
|
32
|
+
run: bundle exec rake spec
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
## [Unreleased]
|
2
|
+
|
3
|
+
## [2.1.0] - 2022-10-10
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- accept local_http_config on Rack::OAuth2::Client#access_token! & revoke! to support custom headers etc. by @nov in https://github.com/nov/rack-oauth2/pull/93
|
8
|
+
|
9
|
+
## [2.0.1] - 2022-10-09
|
10
|
+
|
11
|
+
### Fixed
|
12
|
+
|
13
|
+
- changes for mTLS on faraday by @nov in https://github.com/nov/rack-oauth2/pull/92
|
14
|
+
|
15
|
+
## [2.0.0] - 2022-10-09
|
16
|
+
|
17
|
+
### Added
|
18
|
+
|
19
|
+
- start recording CHANGELOG
|
20
|
+
|
21
|
+
### Changed
|
22
|
+
|
23
|
+
- Switch from httpclient to faraday v2 https://github.com/nov/rack-oauth2/pull/91
|
24
|
+
- make url-encoded the default https://github.com/nov/rack-oauth2/commit/98faf139be4f5bf9ec6134d31f65a298259d8a8b
|
25
|
+
- let faraday encode params https://github.com/nov/rack-oauth2/commit/f399b3afb8facb3635b8842baee8584bc4d3ce73
|
data/README.rdoc
CHANGED
@@ -1,9 +1,7 @@
|
|
1
1
|
= rack-oauth2
|
2
2
|
|
3
3
|
OAuth 2.0 Server & Client Library.
|
4
|
-
Both Bearer
|
5
|
-
|
6
|
-
{<img src="https://secure.travis-ci.org/nov/rack-oauth2.png" />}[http://travis-ci.org/nov/rack-oauth2]
|
4
|
+
Both Bearer token type are supported.
|
7
5
|
|
8
6
|
The OAuth 2.0 Authorization Framework (RFC 6749)
|
9
7
|
http://www.rfc-editor.org/rfc/rfc6749.txt
|
@@ -11,9 +9,6 @@ http://www.rfc-editor.org/rfc/rfc6749.txt
|
|
11
9
|
The OAuth 2.0 Authorization Framework: Bearer Token Usage (RFC 6750)
|
12
10
|
http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-06
|
13
11
|
|
14
|
-
HTTP Authentication: MAC Access Authentication (draft 01)
|
15
|
-
http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01
|
16
|
-
|
17
12
|
== Installation
|
18
13
|
|
19
14
|
gem install rack-oauth2
|
@@ -28,40 +23,20 @@ http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01
|
|
28
23
|
|
29
24
|
=== Bearer
|
30
25
|
|
31
|
-
Running on Heroku
|
32
|
-
https://rack-oauth2-sample.heroku.com
|
33
|
-
|
34
26
|
Source on GitHub
|
35
27
|
https://github.com/nov/rack-oauth2-sample
|
36
28
|
|
37
|
-
=== MAC
|
38
|
-
|
39
|
-
Running on Heroku
|
40
|
-
https://rack-oauth2-sample-mac.heroku.com
|
41
|
-
|
42
|
-
Source on GitHub
|
43
|
-
https://github.com/nov/rack-oauth2-sample-mac
|
44
|
-
|
45
29
|
== Sample Client
|
46
30
|
|
47
|
-
=== Common between Bearer and MAC
|
48
|
-
|
49
31
|
Authorization Request (request_type: 'code' and 'token')
|
50
32
|
https://gist.github.com/862393
|
51
33
|
|
52
34
|
Token Request (grant_type: 'client_credentials', 'password', 'authorization_code' and 'refresh_token')
|
53
35
|
https://gist.github.com/883541
|
54
36
|
|
55
|
-
=== Bearer
|
56
|
-
|
57
37
|
Resource Request (request both for resource owner resource and for client resource)
|
58
38
|
https://gist.github.com/883575
|
59
39
|
|
60
|
-
=== MAC
|
61
|
-
|
62
|
-
Resource Request (request both for resource owner resource and for client resource)
|
63
|
-
https://gist.github.com/933885
|
64
|
-
|
65
40
|
== Note on Patches/Pull Requests
|
66
41
|
|
67
42
|
* Fork the project.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2.2.0
|
@@ -6,18 +6,9 @@ module Rack
|
|
6
6
|
@token = token
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
# request:: HTTP::Message
|
11
|
-
def filter_request(request)
|
9
|
+
def authenticate(request)
|
12
10
|
@token.authenticate(request)
|
13
11
|
end
|
14
|
-
|
15
|
-
# Callback called in HTTPClient (after received a response)
|
16
|
-
# response:: HTTP::Message
|
17
|
-
# request:: HTTP::Message
|
18
|
-
def filter_response(response, request)
|
19
|
-
# nothing to do
|
20
|
-
end
|
21
12
|
end
|
22
13
|
end
|
23
14
|
end
|
@@ -7,8 +7,8 @@ module Rack
|
|
7
7
|
def initialize(attributes = {})
|
8
8
|
super
|
9
9
|
self.token_type = :bearer
|
10
|
-
|
11
|
-
|
10
|
+
http_client.ssl.client_key = private_key
|
11
|
+
http_client.ssl.client_cert = certificate
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
@@ -5,7 +5,7 @@ module Rack
|
|
5
5
|
attr_required :access_token, :token_type
|
6
6
|
attr_optional :refresh_token, :expires_in, :scope
|
7
7
|
attr_accessor :raw_attributes
|
8
|
-
delegate :get, :patch, :post, :put, :delete, to: :
|
8
|
+
delegate :get, :patch, :post, :put, :delete, to: :http_client
|
9
9
|
|
10
10
|
alias_method :to_s, :access_token
|
11
11
|
|
@@ -18,9 +18,9 @@ module Rack
|
|
18
18
|
attr_missing!
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
@
|
23
|
-
|
21
|
+
def http_client
|
22
|
+
@http_client ||= Rack::OAuth2.http_client("#{self.class} (#{VERSION})") do |faraday|
|
23
|
+
Authenticator.new(self).authenticate(faraday)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -39,6 +39,4 @@ end
|
|
39
39
|
|
40
40
|
require 'rack/oauth2/access_token/authenticator'
|
41
41
|
require 'rack/oauth2/access_token/bearer'
|
42
|
-
require 'rack/oauth2/access_token/mac'
|
43
|
-
require 'rack/oauth2/access_token/legacy'
|
44
42
|
require 'rack/oauth2/access_token/mtls'
|
data/lib/rack/oauth2/client.rb
CHANGED
@@ -3,7 +3,7 @@ module Rack
|
|
3
3
|
class Client
|
4
4
|
include AttrRequired, AttrOptional
|
5
5
|
attr_required :identifier
|
6
|
-
attr_optional :secret, :private_key, :certificate, :redirect_uri, :scheme, :host, :port, :authorization_endpoint, :token_endpoint
|
6
|
+
attr_optional :secret, :private_key, :certificate, :redirect_uri, :scheme, :host, :port, :authorization_endpoint, :token_endpoint, :revocation_endpoint
|
7
7
|
|
8
8
|
def initialize(attributes = {})
|
9
9
|
(required_attributes + optional_attributes).each do |key|
|
@@ -16,12 +16,12 @@ module Rack
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def authorization_uri(params = {})
|
19
|
+
params[:redirect_uri] ||= self.redirect_uri
|
19
20
|
params[:response_type] ||= :code
|
20
21
|
params[:response_type] = Array(params[:response_type]).join(' ')
|
21
22
|
params[:scope] = Array(params[:scope]).join(' ')
|
22
23
|
Util.redirect_uri absolute_uri_for(authorization_endpoint), :query, params.merge(
|
23
|
-
client_id: self.identifier
|
24
|
-
redirect_uri: self.redirect_uri
|
24
|
+
client_id: self.identifier
|
25
25
|
)
|
26
26
|
end
|
27
27
|
|
@@ -69,20 +69,87 @@ module Rack
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def access_token!(*args)
|
72
|
-
headers, params =
|
72
|
+
headers, params, http_client, options = authenticated_context_from(*args)
|
73
|
+
params[:scope] = Array(options.delete(:scope)).join(' ') if options[:scope].present?
|
74
|
+
params.merge! @grant.as_json
|
75
|
+
params.merge! options
|
76
|
+
handle_response do
|
77
|
+
http_client.post(
|
78
|
+
absolute_uri_for(token_endpoint),
|
79
|
+
Util.compact_hash(params),
|
80
|
+
headers
|
81
|
+
) do |req|
|
82
|
+
yield req if block_given?
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def revoke!(*args)
|
88
|
+
headers, params, http_client, options = authenticated_context_from(*args)
|
89
|
+
|
90
|
+
params.merge! case
|
91
|
+
when access_token = options.delete(:access_token)
|
92
|
+
{
|
93
|
+
token: access_token,
|
94
|
+
token_type_hint: :access_token
|
95
|
+
}
|
96
|
+
when refresh_token = options.delete(:refresh_token)
|
97
|
+
{
|
98
|
+
token: refresh_token,
|
99
|
+
token_type_hint: :refresh_token
|
100
|
+
}
|
101
|
+
when @grant.is_a?(Grant::RefreshToken)
|
102
|
+
{
|
103
|
+
token: @grant.refresh_token,
|
104
|
+
token_type_hint: :refresh_token
|
105
|
+
}
|
106
|
+
when options[:token].blank?
|
107
|
+
raise ArgumentError, 'One of "token", "access_token" and "refresh_token" is required'
|
108
|
+
end
|
109
|
+
params.merge! options
|
110
|
+
|
111
|
+
handle_revocation_response do
|
112
|
+
http_client.post(
|
113
|
+
absolute_uri_for(revocation_endpoint),
|
114
|
+
Util.compact_hash(params),
|
115
|
+
headers
|
116
|
+
) do |req|
|
117
|
+
yield req if block_given?
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def absolute_uri_for(endpoint)
|
125
|
+
_endpoint_ = Util.parse_uri endpoint
|
126
|
+
_endpoint_.scheme ||= self.scheme || 'https'
|
127
|
+
_endpoint_.host ||= self.host
|
128
|
+
_endpoint_.port ||= self.port
|
129
|
+
raise 'No Host Info' unless _endpoint_.host
|
130
|
+
_endpoint_.to_s
|
131
|
+
end
|
132
|
+
|
133
|
+
def authenticated_context_from(*args)
|
134
|
+
headers, params = {}, {}
|
73
135
|
http_client = Rack::OAuth2.http_client
|
74
136
|
|
75
137
|
# NOTE:
|
76
|
-
# Using Array#
|
138
|
+
# Using Array#extract_options! for backward compatibility.
|
77
139
|
# Until v1.0.5, the first argument was 'client_auth_method' in scalar.
|
78
140
|
options = args.extract_options!
|
79
|
-
client_auth_method = args.first || options.delete(:client_auth_method) || :basic
|
80
|
-
|
81
|
-
params[:scope] = Array(options.delete(:scope)).join(' ') if options[:scope].present?
|
82
|
-
params.merge! options
|
141
|
+
client_auth_method = args.first || options.delete(:client_auth_method)&.to_sym || :basic
|
83
142
|
|
84
143
|
case client_auth_method
|
85
144
|
when :basic
|
145
|
+
cred = Base64.strict_encode64 [
|
146
|
+
Util.www_form_url_encode(identifier),
|
147
|
+
Util.www_form_url_encode(secret)
|
148
|
+
].join(':')
|
149
|
+
headers.merge!(
|
150
|
+
'Authorization' => "Basic #{cred}"
|
151
|
+
)
|
152
|
+
when :basic_without_www_form_urlencode
|
86
153
|
cred = ["#{identifier}:#{secret}"].pack('m').tr("\n", '')
|
87
154
|
headers.merge!(
|
88
155
|
'Authorization' => "Basic #{cred}"
|
@@ -92,9 +159,11 @@ module Rack
|
|
92
159
|
client_assertion_type: URN::ClientAssertionType::JWT_BEARER
|
93
160
|
)
|
94
161
|
# NOTE: optionally auto-generate client_assertion.
|
95
|
-
if
|
162
|
+
params[:client_assertion] = if options[:client_assertion].present?
|
163
|
+
options.delete(:client_assertion)
|
164
|
+
else
|
96
165
|
require 'json/jwt'
|
97
|
-
|
166
|
+
JSON::JWT.new(
|
98
167
|
iss: identifier,
|
99
168
|
sub: identifier,
|
100
169
|
aud: absolute_uri_for(token_endpoint),
|
@@ -111,32 +180,16 @@ module Rack
|
|
111
180
|
params.merge!(
|
112
181
|
client_id: identifier
|
113
182
|
)
|
114
|
-
http_client.
|
115
|
-
http_client.
|
183
|
+
http_client.ssl.client_key = private_key
|
184
|
+
http_client.ssl.client_cert = certificate
|
116
185
|
else
|
117
186
|
params.merge!(
|
118
187
|
client_id: identifier,
|
119
188
|
client_secret: secret
|
120
189
|
)
|
121
190
|
end
|
122
|
-
handle_response do
|
123
|
-
http_client.post(
|
124
|
-
absolute_uri_for(token_endpoint),
|
125
|
-
Util.compact_hash(params),
|
126
|
-
headers
|
127
|
-
)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
private
|
132
191
|
|
133
|
-
|
134
|
-
_endpoint_ = Util.parse_uri endpoint
|
135
|
-
_endpoint_.scheme ||= self.scheme || 'https'
|
136
|
-
_endpoint_.host ||= self.host
|
137
|
-
_endpoint_.port ||= self.port
|
138
|
-
raise 'No Host Info' unless _endpoint_.host
|
139
|
-
_endpoint_.to_s
|
192
|
+
[headers, params, http_client, options]
|
140
193
|
end
|
141
194
|
|
142
195
|
def handle_response
|
@@ -149,27 +202,30 @@ module Rack
|
|
149
202
|
end
|
150
203
|
end
|
151
204
|
|
205
|
+
def handle_revocation_response
|
206
|
+
response = yield
|
207
|
+
case response.status
|
208
|
+
when 200..201
|
209
|
+
:success
|
210
|
+
else
|
211
|
+
handle_error_response response
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
152
215
|
def handle_success_response(response)
|
153
|
-
token_hash =
|
154
|
-
case (@forced_token_type || token_hash[:token_type])
|
216
|
+
token_hash = response.body.with_indifferent_access
|
217
|
+
case (@forced_token_type || token_hash[:token_type])&.downcase
|
155
218
|
when 'bearer'
|
156
219
|
AccessToken::Bearer.new(token_hash)
|
157
|
-
when 'mac'
|
158
|
-
AccessToken::MAC.new(token_hash)
|
159
|
-
when nil
|
160
|
-
AccessToken::Legacy.new(token_hash)
|
161
220
|
else
|
162
221
|
raise 'Unknown Token Type'
|
163
222
|
end
|
164
|
-
rescue JSON::ParserError
|
165
|
-
# NOTE: Facebook support (They don't use JSON as token response)
|
166
|
-
AccessToken::Legacy.new Rack::Utils.parse_nested_query(response.body).with_indifferent_access
|
167
223
|
end
|
168
224
|
|
169
225
|
def handle_error_response(response)
|
170
|
-
error =
|
226
|
+
error = response.body.with_indifferent_access
|
171
227
|
raise Error.new(response.status, error)
|
172
|
-
rescue
|
228
|
+
rescue Faraday::ParsingError, NoMethodError
|
173
229
|
raise Error.new(response.status, error: 'Unknown', error_description: response.body)
|
174
230
|
end
|
175
231
|
end
|
@@ -27,7 +27,7 @@ module Rack
|
|
27
27
|
response.status = status
|
28
28
|
yield response if block_given?
|
29
29
|
unless response.redirect?
|
30
|
-
response.
|
30
|
+
response.headers['Content-Type'] = 'application/json'
|
31
31
|
response.write Util.compact_hash(protocol_params).to_json
|
32
32
|
end
|
33
33
|
response.finish
|
@@ -42,6 +42,7 @@ module Rack
|
|
42
42
|
|
43
43
|
class Unauthorized < Error
|
44
44
|
def initialize(error = :unauthorized, description = nil, options = {})
|
45
|
+
@skip_www_authenticate = options[:skip_www_authenticate]
|
45
46
|
super 401, error, description, options
|
46
47
|
end
|
47
48
|
end
|
@@ -27,7 +27,7 @@ module Rack
|
|
27
27
|
|
28
28
|
def verify_code_verifier!(code_challenge, code_challenge_method = :S256)
|
29
29
|
if code_verifier.present? || code_challenge.present?
|
30
|
-
case code_challenge_method
|
30
|
+
case code_challenge_method&.to_sym
|
31
31
|
when :S256
|
32
32
|
code_challenge == Util.urlsafe_base64_encode(
|
33
33
|
OpenSSL::Digest::SHA256.digest(code_verifier.to_s)
|
@@ -5,7 +5,7 @@ module Rack
|
|
5
5
|
module ResponseExt
|
6
6
|
def redirect?
|
7
7
|
ensure_finish do
|
8
|
-
|
8
|
+
super
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
@@ -17,13 +17,13 @@ module Rack
|
|
17
17
|
|
18
18
|
def json
|
19
19
|
ensure_finish do
|
20
|
-
@
|
20
|
+
@body
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
24
|
+
def headers
|
25
25
|
ensure_finish do
|
26
|
-
@
|
26
|
+
@headers
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -39,7 +39,7 @@ module Rack
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def ensure_finish
|
42
|
-
@status, @
|
42
|
+
@status, @headers, @body = finish unless finished?
|
43
43
|
yield
|
44
44
|
end
|
45
45
|
end
|
@@ -13,11 +13,11 @@ module Rack
|
|
13
13
|
def finish
|
14
14
|
super do |response|
|
15
15
|
self.realm ||= DEFAULT_REALM
|
16
|
-
|
16
|
+
headers = response.headers['WWW-Authenticate'] = "#{scheme} realm=\"#{realm}\""
|
17
17
|
if ErrorMethods::DEFAULT_DESCRIPTION.keys.include?(error)
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
headers << ", error=\"#{error}\""
|
19
|
+
headers << ", error_description=\"#{description}\"" if description.present?
|
20
|
+
headers << ", error_uri=\"#{uri}\"" if uri.present?
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
@@ -8,7 +8,9 @@ module Rack
|
|
8
8
|
class Unauthorized < Abstract::Unauthorized
|
9
9
|
def finish
|
10
10
|
super do |response|
|
11
|
-
|
11
|
+
unless @skip_www_authenticate
|
12
|
+
response.headers['WWW-Authenticate'] = 'Basic realm="OAuth2 Token Endpoint"'
|
13
|
+
end
|
12
14
|
end
|
13
15
|
end
|
14
16
|
end
|
@@ -44,16 +44,27 @@ module Rack
|
|
44
44
|
|
45
45
|
class Request < Abstract::Request
|
46
46
|
attr_required :grant_type
|
47
|
-
attr_optional :client_secret
|
47
|
+
attr_optional :client_secret, :client_assertion, :client_assertion_type
|
48
48
|
|
49
49
|
def initialize(env)
|
50
50
|
auth = Rack::Auth::Basic::Request.new(env)
|
51
51
|
if auth.provided? && auth.basic?
|
52
|
-
@client_id, @client_secret = auth.credentials
|
52
|
+
@client_id, @client_secret = auth.credentials.map do |cred|
|
53
|
+
Util.www_form_url_decode cred
|
54
|
+
end
|
53
55
|
super
|
54
56
|
else
|
55
57
|
super
|
56
58
|
@client_secret = params['client_secret']
|
59
|
+
@client_assertion = params['client_assertion']
|
60
|
+
@client_assertion_type = params['client_assertion_type']
|
61
|
+
if client_assertion.present? && client_assertion_type == URN::ClientAssertionType::JWT_BEARER
|
62
|
+
require 'json/jwt'
|
63
|
+
@client_id = JSON::JWT.decode(
|
64
|
+
client_assertion,
|
65
|
+
:skip_verification
|
66
|
+
)[:sub] rescue nil
|
67
|
+
end
|
57
68
|
end
|
58
69
|
@grant_type = params['grant_type'].to_s
|
59
70
|
end
|
@@ -69,9 +80,9 @@ module Rack
|
|
69
80
|
def finish
|
70
81
|
attr_missing!
|
71
82
|
write Util.compact_hash(protocol_params).to_json
|
72
|
-
|
73
|
-
|
74
|
-
|
83
|
+
headers['Content-Type'] = 'application/json'
|
84
|
+
headers['Cache-Control'] = 'no-store'
|
85
|
+
headers['Pragma'] = 'no-cache'
|
75
86
|
super
|
76
87
|
end
|
77
88
|
end
|
data/lib/rack/oauth2/urn.rb
CHANGED
@@ -3,14 +3,14 @@ module Rack
|
|
3
3
|
module URN
|
4
4
|
module TokenType
|
5
5
|
JWT = 'urn:ietf:params:oauth:token-type:jwt' # RFC7519
|
6
|
-
ACCESS_TOKEN = 'urn:ietf:params:oauth:token-type:
|
7
|
-
REFRESH_TOKEN = 'urn:ietf:params:oauth:token-type:
|
6
|
+
ACCESS_TOKEN = 'urn:ietf:params:oauth:token-type:access_token' # RFC8693
|
7
|
+
REFRESH_TOKEN = 'urn:ietf:params:oauth:token-type:refresh_token' # RFC8693
|
8
8
|
end
|
9
9
|
|
10
10
|
module GrantType
|
11
11
|
JWT_BEARER = 'urn:ietf:params:oauth:grant-type:jwt-bearer' # RFC7523
|
12
12
|
SAML2_BEARER = 'urn:ietf:params:oauth:grant-type:saml2-bearer' # RFC7522
|
13
|
-
TOKEN_EXCHANGE = 'urn:ietf:params:oauth:grant-type:token-exchange' #
|
13
|
+
TOKEN_EXCHANGE = 'urn:ietf:params:oauth:grant-type:token-exchange' # RFC8693
|
14
14
|
end
|
15
15
|
|
16
16
|
module ClientAssertionType
|
data/lib/rack/oauth2/util.rb
CHANGED
@@ -4,8 +4,12 @@ module Rack
|
|
4
4
|
module OAuth2
|
5
5
|
module Util
|
6
6
|
class << self
|
7
|
-
def
|
8
|
-
URI.
|
7
|
+
def www_form_url_encode(text)
|
8
|
+
URI.encode_www_form_component(text)
|
9
|
+
end
|
10
|
+
|
11
|
+
def www_form_url_decode(text)
|
12
|
+
URI.decode_www_form_component(text)
|
9
13
|
end
|
10
14
|
|
11
15
|
def base64_encode(text)
|
data/lib/rack/oauth2.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rack'
|
2
|
-
require '
|
2
|
+
require 'faraday'
|
3
|
+
require 'faraday/follow_redirects'
|
3
4
|
require 'logger'
|
4
5
|
require 'active_support'
|
5
6
|
require 'active_support/core_ext'
|
@@ -40,13 +41,15 @@ module Rack
|
|
40
41
|
self.debugging = false
|
41
42
|
|
42
43
|
def self.http_client(agent_name = "Rack::OAuth2 (#{VERSION})", &local_http_config)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
44
|
+
Faraday.new(headers: {user_agent: agent_name}) do |faraday|
|
45
|
+
faraday.request :url_encoded
|
46
|
+
faraday.request :json
|
47
|
+
faraday.response :json
|
48
|
+
faraday.response :logger, Rack::OAuth2.logger, {bodies: true} if debugging?
|
49
|
+
faraday.adapter Faraday.default_adapter
|
50
|
+
local_http_config&.call(faraday)
|
51
|
+
http_config&.call(faraday)
|
52
|
+
end
|
50
53
|
end
|
51
54
|
|
52
55
|
def self.http_config(&block)
|
@@ -56,7 +59,6 @@ module Rack
|
|
56
59
|
def self.reset_http_config!
|
57
60
|
@@http_config = nil
|
58
61
|
end
|
59
|
-
|
60
62
|
end
|
61
63
|
end
|
62
64
|
|
@@ -65,4 +67,3 @@ require 'rack/oauth2/util'
|
|
65
67
|
require 'rack/oauth2/server'
|
66
68
|
require 'rack/oauth2/client'
|
67
69
|
require 'rack/oauth2/access_token'
|
68
|
-
require 'rack/oauth2/debugger'
|