oauth2 1.4.11 → 2.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/oauth2/client.rb CHANGED
@@ -22,15 +22,15 @@ module OAuth2
22
22
  # @param [Hash] options the options to create the client with
23
23
  # @option options [String] :site the OAuth2 provider site host
24
24
  # @option options [String] :redirect_uri the absolute URI to the Redirection Endpoint for use in authorization grants and token exchange
25
- # @option options [String] :authorize_url ('oauth/authorize') absolute or relative URL path to the Authorization endpoint
26
- # @option options [String] :token_url ('oauth/token') absolute or relative URL path to the Token endpoint
27
- # @option options [Symbol] :token_method (:post) HTTP method to use to request token (:get or :post)
25
+ # @option options [String] :authorize_url ('/oauth/authorize') absolute or relative URL path to the Authorization endpoint
26
+ # @option options [String] :token_url ('/oauth/token') absolute or relative URL path to the Token endpoint
27
+ # @option options [Symbol] :token_method (:post) HTTP method to use to request token (:get, :post, :post_with_query_string)
28
28
  # @option options [Symbol] :auth_scheme (:basic_auth) HTTP method to use to authorize request (:basic_auth or :request_body)
29
29
  # @option options [Hash] :connection_opts ({}) Hash of connection options to pass to initialize Faraday with
30
30
  # @option options [FixNum] :max_redirects (5) maximum number of redirects to follow
31
31
  # @option options [Boolean] :raise_errors (true) whether or not to raise an OAuth2::Error on responses with 400+ status codes
32
32
  # @option options [Logger] :logger (::Logger.new($stdout)) which logger to use when OAUTH_DEBUG is enabled
33
- # @option options [Proc] (DEPRECATED) :extract_access_token proc that extracts the access token from the response
33
+ # @option options [Proc] :extract_access_token proc that takes the client and the response Hash and extracts the access token from the response (DEPRECATED)
34
34
  # @yield [builder] The Faraday connection builder
35
35
  def initialize(client_id, client_secret, options = {}, &block)
36
36
  opts = options.dup
@@ -38,16 +38,18 @@ module OAuth2
38
38
  @secret = client_secret
39
39
  @site = opts.delete(:site)
40
40
  ssl = opts.delete(:ssl)
41
- @options = {:authorize_url => 'oauth/authorize',
42
- :token_url => 'oauth/token',
43
- :token_method => :post,
44
- :auth_scheme => :request_body,
45
- :connection_opts => {},
46
- :connection_build => block,
47
- :max_redirects => 5,
48
- :raise_errors => true,
49
- :extract_access_token => DEFAULT_EXTRACT_ACCESS_TOKEN, # DEPRECATED
50
- :logger => ::Logger.new($stdout)}.merge(opts)
41
+
42
+ @options = {
43
+ authorize_url: 'oauth/authorize',
44
+ token_url: 'oauth/token',
45
+ token_method: :post,
46
+ auth_scheme: :basic_auth,
47
+ connection_opts: {},
48
+ connection_build: block,
49
+ max_redirects: 5,
50
+ raise_errors: true,
51
+ logger: ::Logger.new($stdout),
52
+ }.merge(opts)
51
53
  @options[:connection_opts][:ssl] = ssl if ssl
52
54
  end
53
55
 
@@ -89,6 +91,9 @@ module OAuth2
89
91
  end
90
92
 
91
93
  # Makes a request relative to the specified site root.
94
+ # Updated HTTP 1.1 specification (IETF RFC 7231) relaxed the original constraint (IETF RFC 2616),
95
+ # allowing the use of relative URLs in Location headers.
96
+ # @see https://datatracker.ietf.org/doc/html/rfc7231#section-7.1.2
92
97
  #
93
98
  # @param [Symbol] verb one of :get, :post, :put, :delete
94
99
  # @param [String] url URL path of request
@@ -100,7 +105,7 @@ module OAuth2
100
105
  # code response for this request. Will default to client option
101
106
  # @option opts [Symbol] :parse @see Response::initialize
102
107
  # @yield [req] The Faraday request
103
- def request(verb, url, opts = {}) # rubocop:disable Metrics/AbcSize
108
+ def request(verb, url, opts = {})
104
109
  url = connection.build_url(url).to_s
105
110
 
106
111
  begin
@@ -112,7 +117,7 @@ module OAuth2
112
117
  raise ConnectionError, e
113
118
  end
114
119
 
115
- response = Response.new(response, :parse => opts[:parse])
120
+ response = Response.new(response, parse: opts[:parse])
116
121
 
117
122
  case response.status
118
123
  when 301, 302, 303, 307
@@ -126,7 +131,8 @@ module OAuth2
126
131
  end
127
132
  location = response.headers['location']
128
133
  if location
129
- request(verb, location, opts)
134
+ full_location = response.response.env.url.merge(location)
135
+ request(verb, full_location, opts)
130
136
  else
131
137
  error = Error.new(response)
132
138
  raise(error, "Got #{response.status} status code, but no Location header was present")
@@ -138,7 +144,6 @@ module OAuth2
138
144
  error = Error.new(response)
139
145
  raise(error) if opts.fetch(:raise_errors, options[:raise_errors])
140
146
 
141
- response.error = error
142
147
  response
143
148
  else
144
149
  error = Error.new(response)
@@ -150,20 +155,20 @@ module OAuth2
150
155
  #
151
156
  # @param params [Hash] a Hash of params for the token endpoint
152
157
  # @param access_token_opts [Hash] access token options, to pass to the AccessToken object
153
- # @param access_token_class [Class] class of access token for easier subclassing OAuth2::AccessToken
158
+ # @param extract_access_token [Proc] proc that extracts the access token from the response (DEPRECATED)
159
+ # @param access_token_class [Class] class of access token for easier subclassing OAuth2::AccessToken, @version 2.0+
154
160
  # @return [AccessToken] the initialized AccessToken
155
- def get_token(params, access_token_opts = {}, extract_access_token = options[:extract_access_token]) # # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity Metrics/AbcSize, Metrics/MethodLength
161
+ def get_token(params, access_token_opts = {}, extract_access_token = options[:extract_access_token], access_token_class: AccessToken)
156
162
  params = params.map do |key, value|
157
163
  if RESERVED_PARAM_KEYS.include?(key)
158
164
  [key.to_sym, value]
159
165
  else
160
166
  [key, value]
161
167
  end
162
- end
163
- params = Hash[params]
168
+ end.to_h
164
169
 
165
170
  params = authenticator.apply(params)
166
- opts = {:raise_errors => options[:raise_errors], :parse => params.delete(:parse)}
171
+ opts = {raise_errors: options[:raise_errors], parse: params.delete(:parse)}
167
172
  headers = params.delete(:headers) || {}
168
173
  if options[:token_method] == :post
169
174
  opts[:body] = params
@@ -172,29 +177,19 @@ module OAuth2
172
177
  opts[:params] = params
173
178
  opts[:headers] = {}
174
179
  end
175
- opts[:headers] = opts[:headers].merge(headers)
180
+ opts[:headers].merge!(headers)
176
181
  http_method = options[:token_method]
182
+ http_method = :post if http_method == :post_with_query_string
177
183
  response = request(http_method, token_url, opts)
178
184
 
179
- access_token = begin
180
- build_access_token(response, access_token_opts, extract_access_token)
181
- rescue StandardError
182
- nil
183
- end
184
-
185
- response_contains_token = access_token || (
186
- response.parsed.is_a?(Hash) &&
187
- (response.parsed['access_token'] || response.parsed['id_token'])
188
- )
189
-
190
- if options[:raise_errors] && !response_contains_token
191
- error = Error.new(response)
192
- raise(error)
193
- elsif !response_contains_token
194
- return nil
185
+ # In v1.4.x, the deprecated extract_access_token option retrieves the token from the response.
186
+ # We preserve this behavior here, but a custom access_token_class that implements #from_hash
187
+ # should be used instead.
188
+ if extract_access_token
189
+ parse_response_with_legacy_extract(response, access_token_opts, extract_access_token)
190
+ else
191
+ parse_response(response, access_token_opts, access_token_class)
195
192
  end
196
-
197
- access_token
198
193
  end
199
194
 
200
195
  # The Authorization Code strategy
@@ -253,11 +248,6 @@ module OAuth2
253
248
  end
254
249
  end
255
250
 
256
- DEFAULT_EXTRACT_ACCESS_TOKEN = proc do |client, hash|
257
- token = hash.delete('access_token') || hash.delete(:access_token)
258
- token && AccessToken.new(client, token, hash)
259
- end
260
-
261
251
  private
262
252
 
263
253
  # Returns the authenticator object
@@ -267,26 +257,50 @@ module OAuth2
267
257
  Authenticator.new(id, secret, options[:auth_scheme])
268
258
  end
269
259
 
260
+ def parse_response_with_legacy_extract(response, access_token_opts, extract_access_token)
261
+ access_token = build_access_token_legacy_extract(response, access_token_opts, extract_access_token)
262
+
263
+ return access_token if access_token
264
+
265
+ if options[:raise_errors]
266
+ error = Error.new(response)
267
+ raise(error)
268
+ end
269
+
270
+ nil
271
+ end
272
+
273
+ def parse_response(response, access_token_opts, access_token_class)
274
+ data = response.parsed
275
+
276
+ if options[:raise_errors] && data.is_a?(Hash) && !access_token_class.contains_token?(data)
277
+ error = Error.new(response)
278
+ raise(error)
279
+ end
280
+
281
+ build_access_token(response, access_token_opts, access_token_class)
282
+ end
283
+
270
284
  # Builds the access token from the response of the HTTP call
271
285
  #
272
286
  # @return [AccessToken] the initialized AccessToken
273
- def build_access_token(response, access_token_opts, extract_access_token)
274
- parsed_response = response.parsed.dup
275
- return unless parsed_response.is_a?(Hash)
276
-
277
- hash = parsed_response.merge(access_token_opts)
278
-
279
- # Provide backwards compatibility for old AccessToken.form_hash pattern
280
- # Will be deprecated in 2.x
281
- if extract_access_token.is_a?(Class) && extract_access_token.respond_to?(:from_hash)
282
- extract_access_token.from_hash(self, hash)
283
- else
284
- extract_access_token.call(self, hash)
287
+ def build_access_token(response, access_token_opts, access_token_class)
288
+ access_token_class.from_hash(self, response.parsed.merge(access_token_opts)).tap do |access_token|
289
+ access_token.response = response if access_token.respond_to?(:response=)
285
290
  end
286
291
  end
287
292
 
293
+ # Builds the access token from the response of the HTTP call with legacy extract_access_token
294
+ #
295
+ # @return [AccessToken] the initialized AccessToken
296
+ def build_access_token_legacy_extract(response, access_token_opts, extract_access_token)
297
+ extract_access_token.call(self, response.parsed.merge(access_token_opts))
298
+ rescue StandardError
299
+ nil
300
+ end
301
+
288
302
  def oauth_debug_logging(builder)
289
- builder.response :logger, options[:logger], :bodies => true if ENV['OAUTH_DEBUG'] == 'true'
303
+ builder.response :logger, options[:logger], bodies: true if ENV['OAUTH_DEBUG'] == 'true'
290
304
  end
291
305
  end
292
306
  end
data/lib/oauth2/error.rb CHANGED
@@ -4,39 +4,48 @@ module OAuth2
4
4
  class Error < StandardError
5
5
  attr_reader :response, :code, :description
6
6
 
7
- # standard error values include:
8
- # :invalid_request, :invalid_client, :invalid_token, :invalid_grant, :unsupported_grant_type, :invalid_scope
7
+ # standard error codes include:
8
+ # 'invalid_request', 'invalid_client', 'invalid_token', 'invalid_grant', 'unsupported_grant_type', 'invalid_scope'
9
9
  def initialize(response)
10
- response.error = self
11
10
  @response = response
11
+ message_opts = {}
12
12
 
13
13
  if response.parsed.is_a?(Hash)
14
14
  @code = response.parsed['error']
15
15
  @description = response.parsed['error_description']
16
- error_description = "#{@code}: #{@description}"
16
+ message_opts = parse_error_description(@code, @description)
17
17
  end
18
18
 
19
- super(error_message(response.body, :error_description => error_description))
19
+ super(error_message(response.body, message_opts))
20
20
  end
21
21
 
22
- # Makes a error message
23
- # @param [String] response_body response body of request
24
- # @param [String] opts :error_description error description to show first line
22
+ private
23
+
25
24
  def error_message(response_body, opts = {})
26
- message = []
25
+ lines = []
26
+
27
+ lines << opts[:error_description] if opts[:error_description]
28
+
29
+ error_string = if response_body.respond_to?(:encode) && opts[:error_description].respond_to?(:encoding)
30
+ script_encoding = opts[:error_description].encoding
31
+ response_body.encode(script_encoding, invalid: :replace, undef: :replace)
32
+ else
33
+ response_body
34
+ end
27
35
 
28
- opts[:error_description] && (message << opts[:error_description])
36
+ lines << error_string
37
+
38
+ lines.join("\n")
39
+ end
29
40
 
30
- error_message = if opts[:error_description] && opts[:error_description].respond_to?(:encoding)
31
- script_encoding = opts[:error_description].encoding
32
- response_body.encode(script_encoding, :invalid => :replace, :undef => :replace)
33
- else
34
- response_body
35
- end
41
+ def parse_error_description(code, description)
42
+ return {} unless code || description
36
43
 
37
- message << error_message
44
+ error_description = ''
45
+ error_description += "#{code}: " if code
46
+ error_description += description if description
38
47
 
39
- message.join("\n")
48
+ {error_description: error_description}
40
49
  end
41
50
  end
42
51
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'multi_json'
3
+ require 'json'
4
4
  require 'multi_xml'
5
5
  require 'rack'
6
6
 
@@ -8,20 +8,17 @@ module OAuth2
8
8
  # OAuth2::Response class
9
9
  class Response
10
10
  attr_reader :response
11
- attr_accessor :error, :options
11
+ attr_accessor :options
12
12
 
13
13
  # Procs that, when called, will parse a response body according
14
14
  # to the specified format.
15
15
  @@parsers = {
16
- :json => lambda { |body| MultiJson.load(body) rescue body }, # rubocop:disable Style/RescueModifier
17
- :query => lambda { |body| Rack::Utils.parse_query(body) },
18
- :text => lambda { |body| body },
16
+ query: ->(body) { Rack::Utils.parse_query(body) },
17
+ text: ->(body) { body },
19
18
  }
20
19
 
21
20
  # Content type assignments for various potential HTTP content types.
22
21
  @@content_types = {
23
- 'application/json' => :json,
24
- 'text/javascript' => :json,
25
22
  'application/x-www-form-urlencoded' => :query,
26
23
  'text/plain' => :text,
27
24
  }
@@ -47,7 +44,7 @@ module OAuth2
47
44
  # :json, or :automatic (determined by Content-Type response header)
48
45
  def initialize(response, opts = {})
49
46
  @response = response
50
- @options = {:parse => :automatic}.merge(opts)
47
+ @options = {parse: :automatic}.merge(opts)
51
48
  end
52
49
 
53
50
  # The HTTP response headers
@@ -65,29 +62,74 @@ module OAuth2
65
62
  response.body || ''
66
63
  end
67
64
 
68
- # The parsed response body.
69
- # Will attempt to parse application/x-www-form-urlencoded and
70
- # application/json Content-Type response bodies
65
+ # The {#response} {#body} as parsed by {#parser}.
66
+ #
67
+ # @return [Object] As returned by {#parser} if it is #call-able.
68
+ # @return [nil] If the {#parser} is not #call-able.
71
69
  def parsed
72
- return nil unless @@parsers.key?(parser)
70
+ return @parsed if defined?(@parsed)
71
+
72
+ @parsed =
73
+ if parser.respond_to?(:call)
74
+ case parser.arity
75
+ when 0
76
+ parser.call
77
+ when 1
78
+ parser.call(body)
79
+ else
80
+ parser.call(body, response)
81
+ end
82
+ end
83
+
84
+ @parsed = OAuth2::SnakyHash.new(@parsed) if @parsed.is_a?(Hash)
73
85
 
74
- @parsed ||= @@parsers[parser].call(body)
86
+ @parsed
75
87
  end
76
88
 
77
89
  # Attempts to determine the content type of the response.
78
90
  def content_type
79
- ((response.headers.values_at('content-type', 'Content-Type').compact.first || '').split(';').first || '').strip
91
+ return nil unless response.headers
92
+
93
+ ((response.headers.values_at('content-type', 'Content-Type').compact.first || '').split(';').first || '').strip.downcase
80
94
  end
81
95
 
82
- # Determines the parser that will be used to supply the content of #parsed
96
+ # Determines the parser (a Proc or other Object which responds to #call)
97
+ # that will be passed the {#body} (and optional {#response}) to supply
98
+ # {#parsed}.
99
+ #
100
+ # The parser can be supplied as the +:parse+ option in the form of a Proc
101
+ # (or other Object responding to #call) or a Symbol. In the latter case,
102
+ # the actual parser will be looked up in {@@parsers} by the supplied Symbol.
103
+ #
104
+ # If no +:parse+ option is supplied, the lookup Symbol will be determined
105
+ # by looking up {#content_type} in {@@content_types}.
106
+ #
107
+ # If {#parser} is a Proc, it will be called with no arguments, just
108
+ # {#body}, or {#body} and {#response}, depending on the Proc's arity.
109
+ #
110
+ # @return [Proc, #call] If a parser was found.
111
+ # @return [nil] If no parser was found.
83
112
  def parser
84
- return options[:parse].to_sym if @@parsers.key?(options[:parse])
113
+ return @parser if defined?(@parser)
114
+
115
+ @parser =
116
+ if options[:parse].respond_to?(:call)
117
+ options[:parse]
118
+ elsif options[:parse]
119
+ @@parsers[options[:parse].to_sym]
120
+ end
85
121
 
86
- @@content_types[content_type]
122
+ @parser ||= @@parsers[@@content_types[content_type]]
87
123
  end
88
124
  end
89
125
  end
90
126
 
91
- OAuth2::Response.register_parser(:xml, ['text/xml', 'application/rss+xml', 'application/rdf+xml', 'application/atom+xml']) do |body|
92
- MultiXml.parse(body) rescue body # rubocop:disable Style/RescueModifier
127
+ OAuth2::Response.register_parser(:xml, ['text/xml', 'application/rss+xml', 'application/rdf+xml', 'application/atom+xml', 'application/xml']) do |body|
128
+ MultiXml.parse(body)
129
+ end
130
+
131
+ OAuth2::Response.register_parser(:json, ['application/json', 'text/javascript', 'application/hal+json', 'application/vnd.collection+json', 'application/vnd.api+json', 'application/problem+json']) do |body|
132
+ body = body.dup.force_encoding(::Encoding::ASCII_8BIT) if body.respond_to?(:force_encoding)
133
+
134
+ ::JSON.parse(body)
93
135
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OAuth2
4
+ # Hash which allow assign string key in camel case
5
+ # and query on both camel and snake case
6
+ class SnakyHash < ::Hashie::Mash::Rash
7
+ end
8
+ end
@@ -10,15 +10,22 @@ module OAuth2
10
10
  #
11
11
  # Sample usage:
12
12
  # client = OAuth2::Client.new(client_id, client_secret,
13
- # :site => 'http://localhost:8080')
13
+ # :site => 'http://localhost:8080',
14
+ # :auth_scheme => :request_body)
14
15
  #
15
- # params = {:hmac_secret => "some secret",
16
- # # or :private_key => "private key string",
17
- # :iss => "http://localhost:3001",
18
- # :prn => "me@here.com",
19
- # :exp => Time.now.utc.to_i + 3600}
16
+ # claim_set = {
17
+ # :iss => "http://localhost:3001",
18
+ # :aud => "http://localhost:8080/oauth2/token"
19
+ # :sub => "me@example.com",
20
+ # :exp => Time.now.utc.to_i + 3600,
21
+ # }
20
22
  #
21
- # access = client.assertion.get_token(params)
23
+ # encoding = {
24
+ # :algorithm => 'HS256',
25
+ # :key => 'secret_key',
26
+ # }
27
+ #
28
+ # access = client.assertion.get_token(claim_set, encoding)
22
29
  # access.token # actual access_token string
23
30
  # access.get("/api/stuff") # making api calls with access token in header
24
31
  #
@@ -32,45 +39,63 @@ module OAuth2
32
39
 
33
40
  # Retrieve an access token given the specified client.
34
41
  #
35
- # @param [Hash] params assertion params
36
- # pass either :hmac_secret or :private_key, but not both.
42
+ # @param [Hash] claims the hash representation of the claims that should be encoded as a JWT (JSON Web Token)
43
+ #
44
+ # For reading on JWT and claim keys:
45
+ # @see https://github.com/jwt/ruby-jwt
46
+ # @see https://datatracker.ietf.org/doc/html/rfc7519#section-4.1
47
+ # @see https://datatracker.ietf.org/doc/html/rfc7523#section-3
48
+ # @see https://www.iana.org/assignments/jwt/jwt.xhtml
49
+ #
50
+ # There are many possible claim keys, and applications may ask for their own custom keys.
51
+ # Some typically required ones:
52
+ # :iss (issuer)
53
+ # :aud (audience)
54
+ # :sub (subject) -- formerly :prn https://datatracker.ietf.org/doc/html/draft-ietf-oauth-json-web-token-06#appendix-F
55
+ # :exp, (expiration time) -- in seconds, e.g. Time.now.utc.to_i + 3600
56
+ #
57
+ # Note that this method does *not* validate presence of those four claim keys indicated as required by RFC 7523.
58
+ # There are endpoints that may not conform with this RFC, and this gem should still work for those use cases.
59
+ #
60
+ # @param [Hash] encoding_opts a hash containing instructions on how the JWT should be encoded
61
+ # @option algorithm [String] the algorithm with which you would like the JWT to be encoded
62
+ # @option key [Object] the key with which you would like to encode the JWT
37
63
  #
38
- # params :hmac_secret, secret string.
39
- # params :private_key, private key string.
64
+ # These two options are passed directly to `JWT.encode`. For supported encoding arguments:
65
+ # @see https://github.com/jwt/ruby-jwt#algorithms-and-usage
66
+ # @see https://datatracker.ietf.org/doc/html/rfc7518#section-3.1
40
67
  #
41
- # params :iss, issuer
42
- # params :aud, audience, optional
43
- # params :prn, principal, current user
44
- # params :exp, expired at, in seconds, like Time.now.utc.to_i + 3600
68
+ # The object type of `:key` may depend on the value of `:algorithm`. Sample arguments:
69
+ # get_token(claim_set, {:algorithm => 'HS256', :key => 'secret_key'})
70
+ # get_token(claim_set, {:algorithm => 'RS256', :key => OpenSSL::PKCS12.new(File.read('my_key.p12'), 'not_secret')})
45
71
  #
46
- # @param [Hash] opts options
47
- def get_token(params = {}, opts = {})
48
- hash = build_request(params)
49
- @client.get_token(hash, opts.merge('refresh_token' => nil))
72
+ # @param [Hash] request_opts options that will be used to assemble the request
73
+ # @option request_opts [String] :scope the url parameter `scope` that may be required by some endpoints
74
+ # @see https://datatracker.ietf.org/doc/html/rfc7521#section-4.1
75
+ #
76
+ # @param [Hash] response_opts this will be merged with the token response to create the AccessToken object
77
+ # @see the access_token_opts argument to Client#get_token
78
+
79
+ def get_token(claims, encoding_opts, request_opts = {}, response_opts = {})
80
+ assertion = build_assertion(claims, encoding_opts)
81
+ params = build_request(assertion, request_opts)
82
+
83
+ @client.get_token(params, response_opts.merge('refresh_token' => nil))
50
84
  end
51
85
 
52
- def build_request(params)
53
- assertion = build_assertion(params)
86
+ private
87
+
88
+ def build_request(assertion, request_opts = {})
54
89
  {
55
- :grant_type => 'assertion',
56
- :assertion_type => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
57
- :assertion => assertion,
58
- :scope => params[:scope],
59
- }
90
+ grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
91
+ assertion: assertion,
92
+ }.merge(request_opts)
60
93
  end
61
94
 
62
- def build_assertion(params)
63
- claims = {
64
- :iss => params[:iss],
65
- :aud => params[:aud],
66
- :prn => params[:prn],
67
- :exp => params[:exp],
68
- }
69
- if params[:hmac_secret]
70
- JWT.encode(claims, params[:hmac_secret], 'HS256')
71
- elsif params[:private_key]
72
- JWT.encode(claims, params[:private_key], 'RS256')
73
- end
95
+ def build_assertion(claims, encoding_opts)
96
+ raise ArgumentError.new(message: 'Please provide an encoding_opts hash with :algorithm and :key') if !encoding_opts.is_a?(Hash) || (%i[algorithm key] - encoding_opts.keys).any?
97
+
98
+ JWT.encode(claims, encoding_opts[:key], encoding_opts[:algorithm])
74
99
  end
75
100
  end
76
101
  end
@@ -17,6 +17,7 @@ module OAuth2
17
17
  #
18
18
  # @param [Hash] params additional query parameters for the URL
19
19
  def authorize_url(params = {})
20
+ assert_valid_params(params)
20
21
  @client.authorize_url(authorize_params.merge(params))
21
22
  end
22
23
 
@@ -28,8 +29,18 @@ module OAuth2
28
29
  # @note that you must also provide a :redirect_uri with most OAuth 2.0 providers
29
30
  def get_token(code, params = {}, opts = {})
30
31
  params = {'grant_type' => 'authorization_code', 'code' => code}.merge(@client.redirection_params).merge(params)
32
+ params_dup = params.dup
33
+ params.each_key do |key|
34
+ params_dup[key.to_s] = params_dup.delete(key) if key.is_a?(Symbol)
35
+ end
31
36
 
32
- @client.get_token(params, opts)
37
+ @client.get_token(params_dup, opts)
38
+ end
39
+
40
+ private
41
+
42
+ def assert_valid_params(params)
43
+ raise(ArgumentError, 'client_secret is not allowed in authorize URL query params') if params.key?(:client_secret) || params.key?('client_secret')
33
44
  end
34
45
  end
35
46
  end
@@ -17,6 +17,7 @@ module OAuth2
17
17
  #
18
18
  # @param [Hash] params additional query parameters for the URL
19
19
  def authorize_url(params = {})
20
+ assert_valid_params(params)
20
21
  @client.authorize_url(authorize_params.merge(params))
21
22
  end
22
23
 
@@ -26,6 +27,12 @@ module OAuth2
26
27
  def get_token(*)
27
28
  raise(NotImplementedError, 'The token is accessed differently in this strategy')
28
29
  end
30
+
31
+ private
32
+
33
+ def assert_valid_params(params)
34
+ raise(ArgumentError, 'client_secret is not allowed in authorize URL query params') if params.key?(:client_secret) || params.key?('client_secret')
35
+ end
29
36
  end
30
37
  end
31
38
  end