signet 0.12.0 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +11 -0
- data/CHANGELOG.md +47 -0
- data/CODE_OF_CONDUCT.md +43 -0
- data/README.md +10 -8
- data/SECURITY.md +7 -0
- data/lib/signet/oauth_1/client.rb +51 -72
- data/lib/signet/oauth_1/credential.rb +3 -11
- data/lib/signet/oauth_1/server.rb +8 -30
- data/lib/signet/oauth_1/signature_methods/hmac_sha1.rb +1 -1
- data/lib/signet/oauth_1/signature_methods/plaintext.rb +1 -1
- data/lib/signet/oauth_1/signature_methods/rsa_sha1.rb +2 -2
- data/lib/signet/oauth_1.rb +12 -22
- data/lib/signet/oauth_2/client.rb +63 -58
- data/lib/signet/oauth_2.rb +10 -14
- data/lib/signet/version.rb +1 -1
- data/lib/signet.rb +5 -8
- metadata +34 -43
- data/Gemfile +0 -8
- data/Rakefile +0 -91
- data/signet.gemspec +0 -44
- data/spec/signet/oauth_1/client_spec.rb +0 -810
- data/spec/signet/oauth_1/credential_spec.rb +0 -169
- data/spec/signet/oauth_1/server_spec.rb +0 -839
- data/spec/signet/oauth_1/signature_methods/hmac_sha1_spec.rb +0 -61
- data/spec/signet/oauth_1/signature_methods/plaintext_spec.rb +0 -61
- data/spec/signet/oauth_1/signature_methods/rsa_sha1_spec.rb +0 -126
- data/spec/signet/oauth_1_spec.rb +0 -1010
- data/spec/signet/oauth_2/client_spec.rb +0 -1214
- data/spec/signet/oauth_2_spec.rb +0 -194
- data/spec/signet_spec.rb +0 -78
- data/spec/spec.opts +0 -2
- data/spec/spec_helper.rb +0 -10
- data/spec/spec_helper_spec.rb +0 -17
- data/website/index.html +0 -95
@@ -55,15 +55,13 @@ module Signet
|
|
55
55
|
instance_variable_set "@#{attr}", options[attr]
|
56
56
|
end
|
57
57
|
end
|
58
|
-
# rubocop:disable Naming/UncommunicativeMethodParamName
|
59
58
|
|
60
59
|
# Constant time string comparison.
|
61
|
-
def safe_equals?
|
62
|
-
check =
|
63
|
-
|
60
|
+
def safe_equals? left, right
|
61
|
+
check = left.bytesize ^ right.bytesize
|
62
|
+
left.bytes.zip(right.bytes) { |x, y| check |= x ^ y.to_i }
|
64
63
|
check.zero?
|
65
64
|
end
|
66
|
-
# rubocop:enable Naming/UncommunicativeMethodParamName
|
67
65
|
|
68
66
|
##
|
69
67
|
# Determine if the supplied nonce/timestamp pair is valid by calling
|
@@ -141,8 +139,6 @@ module Signet
|
|
141
139
|
verified = @verifier.call verifier if @verifier.respond_to? :call
|
142
140
|
verified ? true : false
|
143
141
|
end
|
144
|
-
# rubocop:disable Metrics/MethodLength
|
145
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
146
142
|
|
147
143
|
##
|
148
144
|
# Validate and normalize the components from an HTTP request.
|
@@ -156,12 +152,12 @@ module Signet
|
|
156
152
|
# @return [Hash] normalized request components
|
157
153
|
def verify_request_components options = {}
|
158
154
|
if options[:request]
|
159
|
-
if options[:request].is_a?
|
155
|
+
if options[:request].is_a? Faraday::Request
|
160
156
|
request = options[:request]
|
161
157
|
elsif options[:adapter]
|
162
158
|
request = options[:adapter].adapt_request options[:request]
|
163
159
|
end
|
164
|
-
method = request.
|
160
|
+
method = request.http_method
|
165
161
|
uri = request.path
|
166
162
|
headers = request.headers
|
167
163
|
body = request.body
|
@@ -188,8 +184,6 @@ module Signet
|
|
188
184
|
request_components[:body] = body
|
189
185
|
request_components
|
190
186
|
end
|
191
|
-
# rubocop:enable Metrics/MethodLength
|
192
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
193
187
|
|
194
188
|
##
|
195
189
|
# Validate and normalize the HTTP Authorization header.
|
@@ -199,11 +193,7 @@ module Signet
|
|
199
193
|
def verify_auth_header_components headers
|
200
194
|
auth_header = headers.find { |x| x[0] == "Authorization" }
|
201
195
|
raise MalformedAuthorizationError, "Authorization header is missing" if auth_header.nil? || auth_header[1] == ""
|
202
|
-
|
203
|
-
auth_header[1]
|
204
|
-
).each_with_object({}) { |(key, val), acc| acc[key.downcase] = val; }
|
205
|
-
|
206
|
-
auth_hash
|
196
|
+
::Signet::OAuth1.parse_authorization_header(auth_header[1]).to_h.transform_keys(&:downcase)
|
207
197
|
end
|
208
198
|
|
209
199
|
##
|
@@ -232,14 +222,9 @@ module Signet
|
|
232
222
|
|
233
223
|
auth_header = request_components[:headers].find { |x| x[0] == "Authorization" }
|
234
224
|
raise MalformedAuthorizationError, "Authorization header is missing" if auth_header.nil? || auth_header[1] == ""
|
235
|
-
auth_hash = ::Signet::OAuth1.parse_authorization_header(
|
236
|
-
auth_header[1]
|
237
|
-
).each_with_object({}) { |(key, val), acc| acc[key.downcase] = val; }
|
225
|
+
auth_hash = ::Signet::OAuth1.parse_authorization_header(auth_header[1]).to_h.transform_keys(&:downcase)
|
238
226
|
auth_hash["realm"]
|
239
227
|
end
|
240
|
-
# rubocop:disable Metrics/AbcSize
|
241
|
-
# rubocop:disable Metrics/MethodLength
|
242
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
243
228
|
|
244
229
|
##
|
245
230
|
# Authenticates a temporary credential request. If no oauth_callback is
|
@@ -308,7 +293,6 @@ module Signet
|
|
308
293
|
false
|
309
294
|
end
|
310
295
|
end
|
311
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
312
296
|
|
313
297
|
##
|
314
298
|
# Authenticates a token credential request.
|
@@ -379,8 +363,6 @@ module Signet
|
|
379
363
|
temporary_credential: temporary_credential,
|
380
364
|
realm: auth_hash["realm"] }
|
381
365
|
end
|
382
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
383
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
384
366
|
|
385
367
|
##
|
386
368
|
# Authenticates a request for a protected resource.
|
@@ -476,7 +458,7 @@ module Signet
|
|
476
458
|
# can't have been signed correctly(5849#3.4.1.3)
|
477
459
|
unless post_parameters.sort == auth_hash.reject { |k, _v| k.index "oauth_" }.to_a.sort
|
478
460
|
raise MalformedAuthorizationError, "Request is of type application/x-www-form-urlencoded " \
|
479
|
-
|
461
|
+
"but Authentication header did not include form values"
|
480
462
|
end
|
481
463
|
end
|
482
464
|
|
@@ -496,10 +478,6 @@ module Signet
|
|
496
478
|
token_credential: token_credential,
|
497
479
|
realm: auth_hash["realm"] }
|
498
480
|
end
|
499
|
-
# rubocop:enable Metrics/AbcSize
|
500
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
501
|
-
# rubocop:enable Metrics/MethodLength
|
502
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
503
481
|
end
|
504
482
|
end
|
505
483
|
end
|
@@ -3,7 +3,7 @@ require "base64"
|
|
3
3
|
require "openssl"
|
4
4
|
require "signet"
|
5
5
|
|
6
|
-
module Signet
|
6
|
+
module Signet # :nodoc:
|
7
7
|
module OAuth1
|
8
8
|
module RSASHA1
|
9
9
|
def self.generate_signature \
|
@@ -11,7 +11,7 @@ module Signet #:nodoc:
|
|
11
11
|
|
12
12
|
|
13
13
|
private_key = OpenSSL::PKey::RSA.new client_credential_secret
|
14
|
-
signature = private_key.sign OpenSSL::Digest
|
14
|
+
signature = private_key.sign OpenSSL::Digest.new("SHA1"), base_string
|
15
15
|
# using strict_encode64 because the encode64 method adds newline characters after ever 60 chars
|
16
16
|
Base64.strict_encode64(signature).strip
|
17
17
|
end
|
data/lib/signet/oauth_1.rb
CHANGED
@@ -3,7 +3,7 @@ require "signet"
|
|
3
3
|
|
4
4
|
require "securerandom"
|
5
5
|
|
6
|
-
module Signet
|
6
|
+
module Signet # :nodoc:
|
7
7
|
module OAuth1
|
8
8
|
OUT_OF_BAND = "oob".freeze
|
9
9
|
|
@@ -49,9 +49,8 @@ module Signet #:nodoc:
|
|
49
49
|
#
|
50
50
|
# @return [String] A random nonce.
|
51
51
|
def self.generate_nonce
|
52
|
-
SecureRandom.random_bytes(16).unpack("H*").join
|
52
|
+
SecureRandom.random_bytes(16).unpack("H*").join
|
53
53
|
end
|
54
|
-
# rubocop:disable Metrics/MethodLength
|
55
54
|
|
56
55
|
##
|
57
56
|
# Processes an options <code>Hash</code> to find a credential key value.
|
@@ -65,7 +64,7 @@ module Signet #:nodoc:
|
|
65
64
|
# @return [String] The credential key value.
|
66
65
|
def self.extract_credential_key_option credential_type, options
|
67
66
|
# Normalize key to String to allow indifferent access.
|
68
|
-
options = options.
|
67
|
+
options = options.to_h.transform_keys(&:to_s)
|
69
68
|
credential_key = "#{credential_type}_credential_key"
|
70
69
|
credential = "#{credential_type}_credential"
|
71
70
|
if options[credential_key]
|
@@ -107,7 +106,7 @@ module Signet #:nodoc:
|
|
107
106
|
# @return [String] The credential secret value.
|
108
107
|
def self.extract_credential_secret_option credential_type, options
|
109
108
|
# Normalize key to String to allow indifferent access.
|
110
|
-
options = options.
|
109
|
+
options = options.to_h.transform_keys(&:to_s)
|
111
110
|
credential_secret = "#{credential_type}_credential_secret"
|
112
111
|
credential = "#{credential_type}_credential"
|
113
112
|
if options[credential_secret]
|
@@ -136,7 +135,6 @@ module Signet #:nodoc:
|
|
136
135
|
end
|
137
136
|
credential_secret
|
138
137
|
end
|
139
|
-
# rubocop:enable Metrics/MethodLength
|
140
138
|
|
141
139
|
##
|
142
140
|
# Normalizes a set of OAuth parameters according to the algorithm given
|
@@ -178,7 +176,7 @@ module Signet #:nodoc:
|
|
178
176
|
query: parsed_uri.query,
|
179
177
|
fragment: parsed_uri.fragment
|
180
178
|
)
|
181
|
-
uri_parameters = uri.query_values
|
179
|
+
uri_parameters = uri.query_values(Array) || []
|
182
180
|
uri = uri.omit(:query, :fragment).to_s
|
183
181
|
merged_parameters =
|
184
182
|
uri_parameters.concat(parameters.map { |k, v| [k, v] })
|
@@ -214,7 +212,7 @@ module Signet #:nodoc:
|
|
214
212
|
realm = realm.gsub '"', '\"'
|
215
213
|
parameter_list.unshift "realm=\"#{realm}\""
|
216
214
|
end
|
217
|
-
"OAuth
|
215
|
+
"OAuth #{parameter_list.join ', '}"
|
218
216
|
end
|
219
217
|
|
220
218
|
##
|
@@ -228,7 +226,7 @@ module Signet #:nodoc:
|
|
228
226
|
when /^OAuth$/i
|
229
227
|
# Other token types may be supported eventually
|
230
228
|
pairs = Signet.parse_auth_param_list(field_value[/^OAuth\s+(.*)$/i, 1])
|
231
|
-
|
229
|
+
(pairs.each_with_object [] do |(k, v), accu|
|
232
230
|
if k != "realm"
|
233
231
|
k = unencode k
|
234
232
|
v = unencode v
|
@@ -274,24 +272,18 @@ module Signet #:nodoc:
|
|
274
272
|
# be a temporary credential secret when obtaining a token credential
|
275
273
|
# for the first time
|
276
274
|
base_string = generate_base_string method, uri, parameters
|
277
|
-
parameters = parameters.
|
275
|
+
parameters = parameters.to_h.transform_keys(&:to_s)
|
278
276
|
signature_method = parameters["oauth_signature_method"]
|
279
277
|
case signature_method
|
280
278
|
when "HMAC-SHA1"
|
281
279
|
require "signet/oauth_1/signature_methods/hmac_sha1"
|
282
|
-
|
283
|
-
base_string, client_credential_secret, token_credential_secret
|
284
|
-
)
|
280
|
+
Signet::OAuth1::HMACSHA1.generate_signature base_string, client_credential_secret, token_credential_secret
|
285
281
|
when "RSA-SHA1"
|
286
282
|
require "signet/oauth_1/signature_methods/rsa_sha1"
|
287
|
-
|
288
|
-
base_string, client_credential_secret, token_credential_secret
|
289
|
-
)
|
283
|
+
Signet::OAuth1::RSASHA1.generate_signature base_string, client_credential_secret, token_credential_secret
|
290
284
|
when "PLAINTEXT"
|
291
285
|
require "signet/oauth_1/signature_methods/plaintext"
|
292
|
-
|
293
|
-
base_string, client_credential_secret, token_credential_secret
|
294
|
-
)
|
286
|
+
Signet::OAuth1::PLAINTEXT.generate_signature base_string, client_credential_secret, token_credential_secret
|
295
287
|
else
|
296
288
|
raise NotImplementedError,
|
297
289
|
"Unsupported signature method: #{signature_method}"
|
@@ -396,7 +388,7 @@ module Signet #:nodoc:
|
|
396
388
|
raise ArgumentError, "Missing :client_credential_key parameter." if client_credential_key.nil?
|
397
389
|
raise ArgumentError, "Missing :temporary_credential_key parameter." if temporary_credential_key.nil?
|
398
390
|
raise ArgumentError, "Missing :verifier parameter." if options[:verifier].nil?
|
399
|
-
|
391
|
+
[
|
400
392
|
["oauth_consumer_key", client_credential_key],
|
401
393
|
["oauth_token", temporary_credential_key],
|
402
394
|
["oauth_signature_method", options[:signature_method]],
|
@@ -405,8 +397,6 @@ module Signet #:nodoc:
|
|
405
397
|
["oauth_verifier", options[:verifier]],
|
406
398
|
["oauth_version", "1.0"]
|
407
399
|
]
|
408
|
-
# No additional parameters allowed here
|
409
|
-
parameters
|
410
400
|
end
|
411
401
|
|
412
402
|
##
|
@@ -20,6 +20,7 @@ require "signet/errors"
|
|
20
20
|
require "signet/oauth_2"
|
21
21
|
require "jwt"
|
22
22
|
require "date"
|
23
|
+
require "time"
|
23
24
|
|
24
25
|
module Signet
|
25
26
|
module OAuth2
|
@@ -46,6 +47,9 @@ module Signet
|
|
46
47
|
# - <code>:scope</code> -
|
47
48
|
# The scope of the access request, expressed either as an Array
|
48
49
|
# or as a space-delimited String.
|
50
|
+
# - <code>:target_audience</code> -
|
51
|
+
# The final target audience for ID tokens fetched by this client,
|
52
|
+
# as a String.
|
49
53
|
# - <code>:state</code> -
|
50
54
|
# An arbitrary string designed to allow the client to maintain state.
|
51
55
|
# - <code>:code</code> -
|
@@ -101,14 +105,12 @@ module Signet
|
|
101
105
|
@principal = nil
|
102
106
|
@redirect_uri = nil
|
103
107
|
@scope = nil
|
108
|
+
@target_audience = nil
|
104
109
|
@state = nil
|
105
110
|
@username = nil
|
106
111
|
@access_type = nil
|
107
112
|
update! options
|
108
113
|
end
|
109
|
-
# rubocop:disable Metrics/AbcSize
|
110
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
111
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
112
114
|
|
113
115
|
##
|
114
116
|
# Updates an OAuth 2.0 client.
|
@@ -130,6 +132,9 @@ module Signet
|
|
130
132
|
# - <code>:scope</code> -
|
131
133
|
# The scope of the access request, expressed either as an Array
|
132
134
|
# or as a space-delimited String.
|
135
|
+
# - <code>:target_audience</code> -
|
136
|
+
# The final target audience for ID tokens fetched by this client,
|
137
|
+
# as a String.
|
133
138
|
# - <code>:state</code> -
|
134
139
|
# An arbitrary string designed to allow the client to maintain state.
|
135
140
|
# - <code>:code</code> -
|
@@ -181,6 +186,7 @@ module Signet
|
|
181
186
|
self.client_id = options[:client_id] if options.key? :client_id
|
182
187
|
self.client_secret = options[:client_secret] if options.key? :client_secret
|
183
188
|
self.scope = options[:scope] if options.key? :scope
|
189
|
+
self.target_audience = options[:target_audience] if options.key? :target_audience
|
184
190
|
self.state = options[:state] if options.key? :state
|
185
191
|
self.code = options[:code] if options.key? :code
|
186
192
|
self.redirect_uri = options[:redirect_uri] if options.key? :redirect_uri
|
@@ -194,13 +200,10 @@ module Signet
|
|
194
200
|
self.signing_key = options[:signing_key] if options.key? :signing_key
|
195
201
|
self.extension_parameters = options[:extension_parameters] || {}
|
196
202
|
self.additional_parameters = options[:additional_parameters] || {}
|
197
|
-
self.access_type = options.fetch
|
203
|
+
self.access_type = options.fetch :access_type, :offline
|
198
204
|
update_token! options
|
199
205
|
self
|
200
206
|
end
|
201
|
-
# rubocop:enable Metrics/AbcSize
|
202
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
203
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
204
207
|
|
205
208
|
##
|
206
209
|
# Updates an OAuth 2.0 client.
|
@@ -253,10 +256,6 @@ module Signet
|
|
253
256
|
|
254
257
|
self
|
255
258
|
end
|
256
|
-
# rubocop:disable Metrics/AbcSize
|
257
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
258
|
-
# rubocop:disable Metrics/MethodLength
|
259
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
260
259
|
|
261
260
|
##
|
262
261
|
# Returns the authorization URI that the user should be redirected to.
|
@@ -282,9 +281,7 @@ module Signet
|
|
282
281
|
options[:state] = state unless options[:state]
|
283
282
|
options.merge!(additional_parameters.merge(options[:additional_parameters] || {}))
|
284
283
|
options.delete :additional_parameters
|
285
|
-
options =
|
286
|
-
[key.to_s, option]
|
287
|
-
end]
|
284
|
+
options = options.transform_keys(&:to_s)
|
288
285
|
uri = Addressable::URI.parse(
|
289
286
|
::Signet::OAuth2.generate_authorization_uri(
|
290
287
|
@authorization_uri, options
|
@@ -296,10 +293,6 @@ module Signet
|
|
296
293
|
end
|
297
294
|
uri
|
298
295
|
end
|
299
|
-
# rubocop:enable Metrics/AbcSize
|
300
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
301
|
-
# rubocop:enable Metrics/MethodLength
|
302
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
303
296
|
|
304
297
|
##
|
305
298
|
# Sets the authorization URI for this client.
|
@@ -415,7 +408,7 @@ module Signet
|
|
415
408
|
end
|
416
409
|
@scope = new_scope
|
417
410
|
when String
|
418
|
-
@scope = new_scope.split
|
411
|
+
@scope = new_scope.split
|
419
412
|
when nil
|
420
413
|
@scope = nil
|
421
414
|
else
|
@@ -423,6 +416,22 @@ module Signet
|
|
423
416
|
end
|
424
417
|
end
|
425
418
|
|
419
|
+
##
|
420
|
+
# Returns the final target audience for ID tokens fetched by this client.
|
421
|
+
#
|
422
|
+
# @return [String] The target audience.
|
423
|
+
def target_audience
|
424
|
+
@target_audience
|
425
|
+
end
|
426
|
+
|
427
|
+
##
|
428
|
+
# Sets the final target audience for ID tokens fetched by this client.
|
429
|
+
#
|
430
|
+
# @param [String] new_target_audience The new target audience.
|
431
|
+
def target_audience= new_target_audience
|
432
|
+
@target_audience = new_target_audience
|
433
|
+
end
|
434
|
+
|
426
435
|
##
|
427
436
|
# Returns the client's current state value.
|
428
437
|
#
|
@@ -769,12 +778,12 @@ module Signet
|
|
769
778
|
# @param [String, Integer, nil] new_expires_in
|
770
779
|
# The access token lifetime.
|
771
780
|
def expires_in= new_expires_in
|
772
|
-
if
|
773
|
-
@issued_at = Time.now
|
774
|
-
@expires_at = @issued_at + new_expires_in.to_i
|
775
|
-
else
|
781
|
+
if new_expires_in.nil?
|
776
782
|
@expires_at = nil
|
777
783
|
@issued_at = nil
|
784
|
+
else
|
785
|
+
@issued_at = Time.now
|
786
|
+
@expires_at = @issued_at + new_expires_in.to_i
|
778
787
|
end
|
779
788
|
end
|
780
789
|
|
@@ -872,13 +881,13 @@ module Signet
|
|
872
881
|
end
|
873
882
|
|
874
883
|
def grant_type= new_grant_type
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
884
|
+
@grant_type =
|
885
|
+
case new_grant_type
|
886
|
+
when "authorization_code", "refresh_token", "password", "client_credentials"
|
887
|
+
new_grant_type
|
888
|
+
else
|
889
|
+
Addressable::URI.parse new_grant_type
|
890
|
+
end
|
882
891
|
end
|
883
892
|
|
884
893
|
def to_jwt options = {}
|
@@ -893,11 +902,11 @@ module Signet
|
|
893
902
|
"iat" => (now - skew).to_i
|
894
903
|
}
|
895
904
|
assertion["scope"] = scope.join " " unless scope.nil?
|
905
|
+
assertion["target_audience"] = target_audience unless target_audience.nil?
|
896
906
|
assertion["prn"] = person unless person.nil?
|
897
907
|
assertion["sub"] = sub unless sub.nil?
|
898
908
|
JWT.encode assertion, signing_key, signing_algorithm
|
899
909
|
end
|
900
|
-
# rubocop:disable Style/MethodDefParentheses
|
901
910
|
|
902
911
|
##
|
903
912
|
# Serialize the client object to JSON.
|
@@ -905,13 +914,14 @@ module Signet
|
|
905
914
|
# @note A serialized client contains sensitive information. Persist or transmit with care.
|
906
915
|
#
|
907
916
|
# @return [String] A serialized JSON representation of the client.
|
908
|
-
def to_json
|
917
|
+
def to_json *_args
|
909
918
|
MultiJson.dump(
|
910
919
|
"authorization_uri" => authorization_uri ? authorization_uri.to_s : nil,
|
911
920
|
"token_credential_uri" => token_credential_uri ? token_credential_uri.to_s : nil,
|
912
921
|
"client_id" => client_id,
|
913
922
|
"client_secret" => client_secret,
|
914
923
|
"scope" => scope,
|
924
|
+
"target_audience" => target_audience,
|
915
925
|
"state" => state,
|
916
926
|
"code" => code,
|
917
927
|
"redirect_uri" => redirect_uri ? redirect_uri.to_s : nil,
|
@@ -929,11 +939,6 @@ module Signet
|
|
929
939
|
"extension_parameters" => extension_parameters
|
930
940
|
)
|
931
941
|
end
|
932
|
-
# rubocop:enable Style/MethodDefParentheses
|
933
|
-
# rubocop:disable Metrics/AbcSize
|
934
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
935
|
-
# rubocop:disable Metrics/MethodLength
|
936
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
937
942
|
|
938
943
|
##
|
939
944
|
# Generates a request for token credentials.
|
@@ -968,8 +973,8 @@ module Signet
|
|
968
973
|
end
|
969
974
|
parameters.merge! extension_parameters
|
970
975
|
end
|
971
|
-
parameters["client_id"] = client_id
|
972
|
-
parameters["client_secret"] = client_secret
|
976
|
+
parameters["client_id"] = client_id if !options[:use_basic_auth] && !client_id.nil?
|
977
|
+
parameters["client_secret"] = client_secret if !options[:use_basic_auth] && !client_secret.nil?
|
973
978
|
if options[:scope]
|
974
979
|
parameters["scope"] = options[:scope]
|
975
980
|
elsif options[:use_configured_scope] && !scope.nil?
|
@@ -979,8 +984,6 @@ module Signet
|
|
979
984
|
additional.each { |k, v| parameters[k.to_s] = v }
|
980
985
|
parameters
|
981
986
|
end
|
982
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
983
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
984
987
|
|
985
988
|
def fetch_access_token options = {}
|
986
989
|
raise ArgumentError, "Missing token endpoint URI." if token_credential_uri.nil?
|
@@ -988,10 +991,18 @@ module Signet
|
|
988
991
|
options = deep_hash_normalize options
|
989
992
|
|
990
993
|
client = options[:connection] ||= Faraday.default_connection
|
991
|
-
url = Addressable::URI.parse
|
994
|
+
url = Addressable::URI.parse token_credential_uri
|
992
995
|
parameters = generate_access_token_request options
|
993
996
|
if client.is_a? Faraday::Connection
|
994
|
-
|
997
|
+
if options[:use_basic_auth]
|
998
|
+
# The Basic Auth middleware usage differs before and after Faraday v2
|
999
|
+
if Gem::Version.new(Faraday::VERSION).segments.first >= 2
|
1000
|
+
client.request :authorization, :basic, client_id, client_secret
|
1001
|
+
else
|
1002
|
+
client.request :basic_auth, client_id, client_secret
|
1003
|
+
end
|
1004
|
+
end
|
1005
|
+
response = client.post url.normalize.to_s,
|
995
1006
|
Addressable::URI.form_encode(parameters),
|
996
1007
|
"Content-Type" => "application/x-www-form-urlencoded"
|
997
1008
|
status = response.status.to_i
|
@@ -999,7 +1010,11 @@ module Signet
|
|
999
1010
|
content_type = response.headers["Content-type"]
|
1000
1011
|
else
|
1001
1012
|
# Hurley
|
1002
|
-
|
1013
|
+
if options[:use_basic_auth]
|
1014
|
+
url.user = client_id
|
1015
|
+
url.password = client_secret
|
1016
|
+
end
|
1017
|
+
response = client.post url.normalize.to_s, parameters
|
1003
1018
|
status = response.status_code.to_i
|
1004
1019
|
body = response.body
|
1005
1020
|
content_type = response.header[:content_type]
|
@@ -1009,20 +1024,16 @@ module Signet
|
|
1009
1024
|
|
1010
1025
|
message = " Server message:\n#{response.body.to_s.strip}" unless body.to_s.strip.empty?
|
1011
1026
|
if [400, 401, 403].include? status
|
1012
|
-
message = "Authorization failed
|
1013
|
-
raise ::Signet::AuthorizationError.new
|
1014
|
-
message, response: response
|
1015
|
-
)
|
1027
|
+
message = "Authorization failed.#{message}"
|
1028
|
+
raise ::Signet::AuthorizationError.new message, response: response
|
1016
1029
|
elsif status.to_s[0] == "5"
|
1017
|
-
message = "Remote server error
|
1030
|
+
message = "Remote server error.#{message}"
|
1018
1031
|
raise ::Signet::RemoteServerError, message
|
1019
1032
|
else
|
1020
|
-
message = "Unexpected status code: #{response.status}
|
1033
|
+
message = "Unexpected status code: #{response.status}.#{message}"
|
1021
1034
|
raise ::Signet::UnexpectedStatusError, message
|
1022
1035
|
end
|
1023
1036
|
end
|
1024
|
-
# rubocop:enable Metrics/AbcSize
|
1025
|
-
# rubocop:enable Metrics/MethodLength
|
1026
1037
|
|
1027
1038
|
def fetch_access_token! options = {}
|
1028
1039
|
token_hash = fetch_access_token options
|
@@ -1042,9 +1053,6 @@ module Signet
|
|
1042
1053
|
def refresh! options = {}
|
1043
1054
|
fetch_access_token! options
|
1044
1055
|
end
|
1045
|
-
# rubocop:disable Metrics/AbcSize
|
1046
|
-
# rubocop:disable Metrics/MethodLength
|
1047
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
1048
1056
|
|
1049
1057
|
##
|
1050
1058
|
# Generates an authenticated request for protected resources.
|
@@ -1111,9 +1119,6 @@ module Signet
|
|
1111
1119
|
request["Cache-Control"] = "no-store"
|
1112
1120
|
request
|
1113
1121
|
end
|
1114
|
-
# rubocop:enable Metrics/AbcSize
|
1115
|
-
# rubocop:enable Metrics/MethodLength
|
1116
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
1117
1122
|
|
1118
1123
|
##
|
1119
1124
|
# Transmits a request for a protected resource.
|
data/lib/signet/oauth_2.rb
CHANGED
@@ -16,7 +16,7 @@ require "base64"
|
|
16
16
|
require "signet"
|
17
17
|
require "multi_json"
|
18
18
|
|
19
|
-
module Signet
|
19
|
+
module Signet # :nodoc:
|
20
20
|
##
|
21
21
|
# An implementation of http://tools.ietf.org/html/draft-ietf-oauth-v2-10
|
22
22
|
#
|
@@ -28,10 +28,10 @@ module Signet #:nodoc:
|
|
28
28
|
case auth_scheme
|
29
29
|
when /^Basic$/i
|
30
30
|
# HTTP Basic is allowed in OAuth 2
|
31
|
-
|
31
|
+
parse_basic_credentials(field_value[/^Basic\s+(.*)$/i, 1])
|
32
32
|
when /^OAuth$/i
|
33
33
|
# Other token types may be supported eventually
|
34
|
-
|
34
|
+
parse_bearer_credentials(field_value[/^OAuth\s+(.*)$/i, 1])
|
35
35
|
else
|
36
36
|
raise ParseError,
|
37
37
|
"Parsing non-OAuth Authorization headers is out of scope."
|
@@ -43,7 +43,7 @@ module Signet #:nodoc:
|
|
43
43
|
case auth_scheme
|
44
44
|
when /^OAuth$/i
|
45
45
|
# Other token types may be supported eventually
|
46
|
-
|
46
|
+
parse_oauth_challenge(field_value[/^OAuth\s+(.*)$/i, 1])
|
47
47
|
else
|
48
48
|
raise ParseError,
|
49
49
|
"Parsing non-OAuth WWW-Authenticate headers is out of scope."
|
@@ -76,9 +76,9 @@ module Signet #:nodoc:
|
|
76
76
|
raise TypeError, "Expected String, got #{body.class}." unless body.is_a? String
|
77
77
|
case content_type
|
78
78
|
when %r{^application/json.*}
|
79
|
-
|
79
|
+
MultiJson.load body
|
80
80
|
when %r{^application/x-www-form-urlencoded.*}
|
81
|
-
|
81
|
+
Addressable::URI.form_unencode(body).to_h
|
82
82
|
else
|
83
83
|
raise ArgumentError, "Invalid content type '#{content_type}'"
|
84
84
|
end
|
@@ -100,9 +100,8 @@ module Signet #:nodoc:
|
|
100
100
|
raise ArgumentError,
|
101
101
|
"A client identifier may not contain a ':' character."
|
102
102
|
end
|
103
|
-
|
104
|
-
|
105
|
-
).delete("\n")
|
103
|
+
token = Base64.encode64("#{client_id}:#{client_password}").delete("\n")
|
104
|
+
"Basic #{token}"
|
106
105
|
end
|
107
106
|
|
108
107
|
##
|
@@ -121,11 +120,8 @@ module Signet #:nodoc:
|
|
121
120
|
# TODO: escaping?
|
122
121
|
header = "Bearer #{access_token}"
|
123
122
|
if auth_params && !auth_params.empty?
|
124
|
-
|
125
|
-
|
126
|
-
accu << "#{key}=\"#{value}\""
|
127
|
-
end).join(", ")
|
128
|
-
)
|
123
|
+
additional_headers = auth_params.map { |key, value| "#{key}=\"#{value}\"" }
|
124
|
+
header = ([header] + additional_headers).join ", "
|
129
125
|
end
|
130
126
|
header
|
131
127
|
end
|
data/lib/signet/version.rb
CHANGED
data/lib/signet.rb
CHANGED
@@ -14,9 +14,7 @@
|
|
14
14
|
|
15
15
|
require "signet/version"
|
16
16
|
|
17
|
-
module Signet
|
18
|
-
# rubocop:disable Metrics/AbcSize
|
19
|
-
# rubocop:disable Metrics/MethodLength
|
17
|
+
module Signet # :nodoc:
|
20
18
|
def self.parse_auth_param_list auth_param_string
|
21
19
|
# Production rules from:
|
22
20
|
# http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-12
|
@@ -54,11 +52,12 @@ module Signet #:nodoc:
|
|
54
52
|
# Now parse the auth-param pair strings & turn them into key-value pairs.
|
55
53
|
(auth_param_pairs.each_with_object [] do |pair, accu|
|
56
54
|
name, value = pair.split "=", 2
|
57
|
-
|
55
|
+
case value
|
56
|
+
when /^".*"$/
|
58
57
|
value = value.gsub(/^"(.*)"$/, '\1').gsub(/\\(.)/, '\1')
|
59
|
-
|
58
|
+
when /^'.*'$/
|
60
59
|
value = value.gsub(/^'(.*)'$/, '\1').gsub(/\\(.)/, '\1')
|
61
|
-
|
60
|
+
when %r{[()<>@,;:\\"/\[\]?={}]}
|
62
61
|
# Certain special characters are not allowed
|
63
62
|
raise ParseError,
|
64
63
|
"Unexpected characters in auth param " \
|
@@ -68,6 +67,4 @@ module Signet #:nodoc:
|
|
68
67
|
accu << [name, value]
|
69
68
|
end)
|
70
69
|
end
|
71
|
-
# rubocop:enable Metrics/AbcSize
|
72
|
-
# rubocop:enable Metrics/MethodLength
|
73
70
|
end
|