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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/workflows/spec.yml +32 -0
  4. data/CHANGELOG.md +25 -0
  5. data/README.rdoc +1 -26
  6. data/VERSION +1 -1
  7. data/lib/rack/oauth2/access_token/authenticator.rb +1 -10
  8. data/lib/rack/oauth2/access_token/bearer.rb +1 -1
  9. data/lib/rack/oauth2/access_token/mtls.rb +2 -2
  10. data/lib/rack/oauth2/access_token.rb +4 -6
  11. data/lib/rack/oauth2/client.rb +97 -41
  12. data/lib/rack/oauth2/server/abstract/error.rb +2 -1
  13. data/lib/rack/oauth2/server/extension/pkce.rb +1 -1
  14. data/lib/rack/oauth2/server/rails/response_ext.rb +5 -5
  15. data/lib/rack/oauth2/server/resource/error.rb +4 -4
  16. data/lib/rack/oauth2/server/resource.rb +0 -1
  17. data/lib/rack/oauth2/server/token/error.rb +3 -1
  18. data/lib/rack/oauth2/server/token.rb +16 -5
  19. data/lib/rack/oauth2/urn.rb +3 -3
  20. data/lib/rack/oauth2/util.rb +6 -2
  21. data/lib/rack/oauth2.rb +11 -10
  22. data/rack-oauth2.gemspec +7 -5
  23. data/spec/helpers/webmock_helper.rb +8 -2
  24. data/spec/rack/oauth2/access_token/authenticator_spec.rb +2 -22
  25. data/spec/rack/oauth2/access_token/bearer_spec.rb +2 -2
  26. data/spec/rack/oauth2/access_token_spec.rb +0 -17
  27. data/spec/rack/oauth2/client_spec.rb +173 -75
  28. data/spec/rack/oauth2/oauth2_spec.rb +0 -43
  29. data/spec/rack/oauth2/server/authorize/error_spec.rb +6 -6
  30. data/spec/rack/oauth2/server/resource/bearer/error_spec.rb +2 -2
  31. data/spec/rack/oauth2/server/resource/bearer_spec.rb +9 -9
  32. data/spec/rack/oauth2/server/resource/error_spec.rb +21 -21
  33. data/spec/rack/oauth2/server/token/authorization_code_spec.rb +2 -2
  34. data/spec/rack/oauth2/server/token/client_credentials_spec.rb +32 -2
  35. data/spec/rack/oauth2/server/token/error_spec.rb +8 -8
  36. data/spec/rack/oauth2/server/token_spec.rb +72 -3
  37. data/spec/rack/oauth2/util_spec.rb +8 -3
  38. metadata +47 -51
  39. data/.travis.yml +0 -7
  40. data/lib/rack/oauth2/access_token/legacy.rb +0 -19
  41. data/lib/rack/oauth2/access_token/mac/sha256_hex_verifier.rb +0 -17
  42. data/lib/rack/oauth2/access_token/mac/signature.rb +0 -34
  43. data/lib/rack/oauth2/access_token/mac/verifier.rb +0 -44
  44. data/lib/rack/oauth2/access_token/mac.rb +0 -103
  45. data/lib/rack/oauth2/debugger/request_filter.rb +0 -30
  46. data/lib/rack/oauth2/debugger.rb +0 -3
  47. data/lib/rack/oauth2/server/resource/mac/error.rb +0 -24
  48. data/lib/rack/oauth2/server/resource/mac.rb +0 -36
  49. data/spec/mock_response/tokens/legacy.json +0 -5
  50. data/spec/mock_response/tokens/legacy.txt +0 -1
  51. data/spec/mock_response/tokens/legacy_without_expires_in.txt +0 -1
  52. data/spec/mock_response/tokens/mac.json +0 -8
  53. data/spec/rack/oauth2/access_token/legacy_spec.rb +0 -23
  54. data/spec/rack/oauth2/access_token/mac/sha256_hex_verifier_spec.rb +0 -28
  55. data/spec/rack/oauth2/access_token/mac/signature_spec.rb +0 -59
  56. data/spec/rack/oauth2/access_token/mac/verifier_spec.rb +0 -25
  57. data/spec/rack/oauth2/access_token/mac_spec.rb +0 -141
  58. data/spec/rack/oauth2/debugger/request_filter_spec.rb +0 -33
  59. data/spec/rack/oauth2/server/resource/mac/error_spec.rb +0 -52
  60. data/spec/rack/oauth2/server/resource/mac_spec.rb +0 -119
  61. /data/spec/mock_response/{blank → blank.txt} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c09e887a3c902ebbbfd1b2a1698d8a4d68d28e18e75616c629dd75e981e2a9d2
4
- data.tar.gz: c0142a2c05df047f0d8f1e9ac11f428983d27a8945d5ae5be213be41b5615e20
3
+ metadata.gz: 45ba67ac4566f374465673cc5711e71c15006bbe966531a4c1de2473206879b2
4
+ data.tar.gz: 56f8718f283533c369b1743dfd86499e49e5d828a83ac060fa919fac57a935d2
5
5
  SHA512:
6
- metadata.gz: ab1002bdd363b2edab71e8eeebd9872bca8ea2be6ad2a99875543974a2b85340ddcb42fc4ddf22f779b18429dc48a9d36fd91e9059391b76b537c04293ac2056
7
- data.tar.gz: bdffd308340040287a3a8777cdaaaa9de58873d4d27e080dc719b4715ca53fd413bc726f19382b63cd086ad3dd47ac139a665f0acfcd25d2bb2a5e266d8dafce
6
+ metadata.gz: 63316467536c2c98cddea9b2b7907b3ff5fd6b53b892bd338709e1f7a6b014aa4dc20d71b12cd01ffac502c1ab0964218aac7ff6a0e81141ff8aa10e80557cdd
7
+ data.tar.gz: 97e685531853c4837a0e86636c865827033e25f646c4572d254e2584a811f937faa6dc7fe780742814bd9657066c9fc16394723ba87029605761d5acf2d490f7
@@ -0,0 +1,3 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: nov
@@ -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 and MAC token type are supported.
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.12.0
1
+ 2.2.0
@@ -6,18 +6,9 @@ module Rack
6
6
  @token = token
7
7
  end
8
8
 
9
- # Callback called in HTTPClient (before sending a request)
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
@@ -3,7 +3,7 @@ module Rack
3
3
  class AccessToken
4
4
  class Bearer < AccessToken
5
5
  def authenticate(request)
6
- request.header["Authorization"] = "Bearer #{access_token}"
6
+ request.headers["Authorization"] = "Bearer #{access_token}"
7
7
  end
8
8
 
9
9
  def to_mtls(attributes = {})
@@ -7,8 +7,8 @@ module Rack
7
7
  def initialize(attributes = {})
8
8
  super
9
9
  self.token_type = :bearer
10
- httpclient.ssl_config.client_key = private_key
11
- httpclient.ssl_config.client_cert = certificate
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: :httpclient
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 httpclient
22
- @httpclient ||= Rack::OAuth2.http_client("#{self.class} (#{VERSION})") do |config|
23
- config.request_filter << Authenticator.new(self)
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'
@@ -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 = {}, @grant.as_json
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#estract_options! for backward compatibility.
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 params[:client_assertion].blank?
162
+ params[:client_assertion] = if options[:client_assertion].present?
163
+ options.delete(:client_assertion)
164
+ else
96
165
  require 'json/jwt'
97
- params[:client_assertion] = JSON::JWT.new(
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.ssl_config.client_key = private_key
115
- http_client.ssl_config.client_cert = certificate
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
- def absolute_uri_for(endpoint)
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 = JSON.parse(response.body).with_indifferent_access
154
- case (@forced_token_type || token_hash[:token_type]).try(:downcase)
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 = JSON.parse(response.body).with_indifferent_access
226
+ error = response.body.with_indifferent_access
171
227
  raise Error.new(response.status, error)
172
- rescue JSON::ParserError
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.header['Content-Type'] = 'application/json'
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.try(:to_sym)
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
- @response.redirect?
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
- @response.body
20
+ @body
21
21
  end
22
22
  end
23
23
 
24
- def header
24
+ def headers
25
25
  ensure_finish do
26
- @header
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, @header, @response = finish unless finished?
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
- header = response.header['WWW-Authenticate'] = "#{scheme} realm=\"#{realm}\""
16
+ headers = response.headers['WWW-Authenticate'] = "#{scheme} realm=\"#{realm}\""
17
17
  if ErrorMethods::DEFAULT_DESCRIPTION.keys.include?(error)
18
- header << ", error=\"#{error}\""
19
- header << ", error_description=\"#{description}\"" if description.present?
20
- header << ", error_uri=\"#{uri}\"" if uri.present?
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
@@ -52,4 +52,3 @@ end
52
52
 
53
53
  require 'rack/oauth2/server/resource/error'
54
54
  require 'rack/oauth2/server/resource/bearer'
55
- require 'rack/oauth2/server/resource/mac'
@@ -8,7 +8,9 @@ module Rack
8
8
  class Unauthorized < Abstract::Unauthorized
9
9
  def finish
10
10
  super do |response|
11
- response.header['WWW-Authenticate'] = 'Basic realm="OAuth2 Token Endpoint"'
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
- header['Content-Type'] = 'application/json'
73
- header['Cache-Control'] = 'no-store'
74
- header['Pragma'] = 'no-cache'
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
@@ -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:access-token' # draft-ietf-oauth-token-exchange
7
- REFRESH_TOKEN = 'urn:ietf:params:oauth:token-type:refresh-token' # draft-ietf-oauth-token-exchange
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' # draft-ietf-oauth-token-exchange
13
+ TOKEN_EXCHANGE = 'urn:ietf:params:oauth:grant-type:token-exchange' # RFC8693
14
14
  end
15
15
 
16
16
  module ClientAssertionType
@@ -4,8 +4,12 @@ module Rack
4
4
  module OAuth2
5
5
  module Util
6
6
  class << self
7
- def rfc3986_encode(text)
8
- URI.encode(text, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
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 'httpclient'
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
- _http_client_ = HTTPClient.new(
44
- agent_name: agent_name
45
- )
46
- http_config.try(:call, _http_client_)
47
- local_http_config.try(:call, _http_client_) unless local_http_config.nil?
48
- _http_client_.request_filter << Debugger::RequestFilter.new if debugging?
49
- _http_client_
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'