oauth2 1.4.4 → 1.4.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -2
  3. data/CODE_OF_CONDUCT.md +105 -46
  4. data/LICENSE +1 -1
  5. data/README.md +277 -112
  6. data/lib/oauth2/access_token.rb +11 -8
  7. data/lib/oauth2/authenticator.rb +4 -2
  8. data/lib/oauth2/client.rb +131 -50
  9. data/lib/oauth2/error.rb +3 -1
  10. data/lib/oauth2/mac_token.rb +18 -10
  11. data/lib/oauth2/response.rb +7 -3
  12. data/lib/oauth2/strategy/assertion.rb +6 -4
  13. data/lib/oauth2/strategy/auth_code.rb +3 -1
  14. data/lib/oauth2/strategy/base.rb +2 -0
  15. data/lib/oauth2/strategy/client_credentials.rb +3 -1
  16. data/lib/oauth2/strategy/implicit.rb +3 -1
  17. data/lib/oauth2/strategy/password.rb +5 -3
  18. data/lib/oauth2/version.rb +9 -3
  19. data/lib/oauth2.rb +2 -0
  20. data/spec/fixtures/README.md +11 -0
  21. data/spec/fixtures/RS256/jwtRS256.key +51 -0
  22. data/spec/fixtures/RS256/jwtRS256.key.pub +14 -0
  23. data/spec/helper.rb +33 -0
  24. data/spec/oauth2/access_token_spec.rb +218 -0
  25. data/spec/oauth2/authenticator_spec.rb +86 -0
  26. data/spec/oauth2/client_spec.rb +556 -0
  27. data/spec/oauth2/mac_token_spec.rb +122 -0
  28. data/spec/oauth2/response_spec.rb +96 -0
  29. data/spec/oauth2/strategy/assertion_spec.rb +113 -0
  30. data/spec/oauth2/strategy/auth_code_spec.rb +108 -0
  31. data/spec/oauth2/strategy/base_spec.rb +7 -0
  32. data/spec/oauth2/strategy/client_credentials_spec.rb +71 -0
  33. data/spec/oauth2/strategy/implicit_spec.rb +28 -0
  34. data/spec/oauth2/strategy/password_spec.rb +58 -0
  35. data/spec/oauth2/version_spec.rb +23 -0
  36. metadata +54 -98
  37. data/.document +0 -5
  38. data/.gitignore +0 -19
  39. data/.jrubyrc +0 -1
  40. data/.rspec +0 -2
  41. data/.rubocop.yml +0 -80
  42. data/.rubocop_rspec.yml +0 -26
  43. data/.rubocop_todo.yml +0 -15
  44. data/.ruby-version +0 -1
  45. data/.travis.yml +0 -87
  46. data/CONTRIBUTING.md +0 -18
  47. data/Gemfile +0 -40
  48. data/Rakefile +0 -45
  49. data/gemfiles/jruby_1.7.gemfile +0 -11
  50. data/gemfiles/jruby_9.0.gemfile +0 -7
  51. data/gemfiles/jruby_9.1.gemfile +0 -3
  52. data/gemfiles/jruby_9.2.gemfile +0 -3
  53. data/gemfiles/jruby_head.gemfile +0 -3
  54. data/gemfiles/ruby_1.9.gemfile +0 -11
  55. data/gemfiles/ruby_2.0.gemfile +0 -6
  56. data/gemfiles/ruby_2.1.gemfile +0 -6
  57. data/gemfiles/ruby_2.2.gemfile +0 -3
  58. data/gemfiles/ruby_2.3.gemfile +0 -3
  59. data/gemfiles/ruby_2.4.gemfile +0 -3
  60. data/gemfiles/ruby_2.5.gemfile +0 -3
  61. data/gemfiles/ruby_2.6.gemfile +0 -9
  62. data/gemfiles/ruby_2.7.gemfile +0 -9
  63. data/gemfiles/ruby_head.gemfile +0 -9
  64. data/gemfiles/truffleruby.gemfile +0 -3
  65. data/oauth2.gemspec +0 -52
data/lib/oauth2/client.rb CHANGED
@@ -1,9 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'faraday'
2
4
  require 'logger'
3
5
 
4
6
  module OAuth2
7
+ ConnectionError = Class.new(Faraday::ConnectionFailed)
5
8
  # The OAuth2::Client class
6
9
  class Client # rubocop:disable Metrics/ClassLength
10
+ RESERVED_PARAM_KEYS = %w[headers parse].freeze
11
+
7
12
  attr_reader :id, :secret, :site
8
13
  attr_accessor :options
9
14
  attr_writer :connection
@@ -14,17 +19,18 @@ module OAuth2
14
19
  #
15
20
  # @param [String] client_id the client_id value
16
21
  # @param [String] client_secret the client_secret value
17
- # @param [Hash] opts the options to create the client with
18
- # @option opts [String] :site the OAuth2 provider site host
19
- # @option opts [String] :redirect_uri the absolute URI to the Redirection Endpoint for use in authorization grants and token exchange
20
- # @option opts [String] :authorize_url ('/oauth/authorize') absolute or relative URL path to the Authorization endpoint
21
- # @option opts [String] :token_url ('/oauth/token') absolute or relative URL path to the Token endpoint
22
- # @option opts [Symbol] :token_method (:post) HTTP method to use to request token (:get or :post)
23
- # @option opts [Symbol] :auth_scheme (:basic_auth) HTTP method to use to authorize request (:basic_auth or :request_body)
24
- # @option opts [Hash] :connection_opts ({}) Hash of connection options to pass to initialize Faraday with
25
- # @option opts [FixNum] :max_redirects (5) maximum number of redirects to follow
26
- # @option opts [Boolean] :raise_errors (true) whether or not to raise an OAuth2::Error
27
- # on responses with 400+ status codes
22
+ # @param [Hash] options the options to create the client with
23
+ # @option options [String] :site the OAuth2 provider site host
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)
28
+ # @option options [Symbol] :auth_scheme (:basic_auth) HTTP method to use to authorize request (:basic_auth or :request_body)
29
+ # @option options [Hash] :connection_opts ({}) Hash of connection options to pass to initialize Faraday with
30
+ # @option options [FixNum] :max_redirects (5) maximum number of redirects to follow
31
+ # @option options [Boolean] :raise_errors (true) whether or not to raise an OAuth2::Error on responses with 400+ status codes
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
28
34
  # @yield [builder] The Faraday connection builder
29
35
  def initialize(client_id, client_secret, options = {}, &block)
30
36
  opts = options.dup
@@ -32,20 +38,22 @@ module OAuth2
32
38
  @secret = client_secret
33
39
  @site = opts.delete(:site)
34
40
  ssl = opts.delete(:ssl)
35
- @options = {:authorize_url => '/oauth/authorize',
36
- :token_url => '/oauth/token',
37
- :token_method => :post,
38
- :auth_scheme => :request_body,
39
- :connection_opts => {},
41
+ @options = {:authorize_url => 'oauth/authorize',
42
+ :token_url => 'oauth/token',
43
+ :token_method => :post,
44
+ :auth_scheme => :request_body,
45
+ :connection_opts => {},
40
46
  :connection_build => block,
41
- :max_redirects => 5,
42
- :raise_errors => true}.merge(opts)
47
+ :max_redirects => 5,
48
+ :raise_errors => true,
49
+ :extract_access_token => DEFAULT_EXTRACT_ACCESS_TOKEN, # DEPRECATED
50
+ :logger => ::Logger.new($stdout)}.merge(opts)
43
51
  @options[:connection_opts][:ssl] = ssl if ssl
44
52
  end
45
53
 
46
54
  # Set the site host
47
55
  #
48
- # @param [String] the OAuth2 provider site host
56
+ # @param value [String] the OAuth2 provider site host
49
57
  def site=(value)
50
58
  @connection = nil
51
59
  @site = value
@@ -53,15 +61,16 @@ module OAuth2
53
61
 
54
62
  # The Faraday connection object
55
63
  def connection
56
- @connection ||= begin
57
- conn = Faraday.new(site, options[:connection_opts])
58
- if options[:connection_build]
59
- conn.build do |b|
60
- options[:connection_build].call(b)
64
+ @connection ||=
65
+ Faraday.new(site, options[:connection_opts]) do |builder|
66
+ oauth_debug_logging(builder)
67
+ if options[:connection_build]
68
+ options[:connection_build].call(builder)
69
+ else
70
+ builder.request :url_encoded # form-encode POST params
71
+ builder.adapter Faraday.default_adapter # make requests with Net::HTTP
61
72
  end
62
73
  end
63
- conn
64
- end
65
74
  end
66
75
 
67
76
  # The authorize endpoint URL of the OAuth2 provider
@@ -91,15 +100,18 @@ module OAuth2
91
100
  # code response for this request. Will default to client option
92
101
  # @option opts [Symbol] :parse @see Response::initialize
93
102
  # @yield [req] The Faraday request
94
- def request(verb, url, opts = {}) # rubocop:disable CyclomaticComplexity, MethodLength, Metrics/AbcSize
95
- connection.response :logger, ::Logger.new($stdout) if ENV['OAUTH_DEBUG'] == 'true'
96
-
103
+ def request(verb, url, opts = {}) # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize
97
104
  url = connection.build_url(url).to_s
98
105
 
99
- response = connection.run_request(verb, url, opts[:body], opts[:headers]) do |req|
100
- req.params.update(opts[:params]) if opts[:params]
101
- yield(req) if block_given?
106
+ begin
107
+ response = connection.run_request(verb, url, opts[:body], opts[:headers]) do |req|
108
+ req.params.update(opts[:params]) if opts[:params]
109
+ yield(req) if block_given?
110
+ end
111
+ rescue Faraday::ConnectionFailed => e
112
+ raise ConnectionError, e
102
113
  end
114
+
103
115
  response = Response.new(response, :parse => opts[:parse])
104
116
 
105
117
  case response.status
@@ -107,17 +119,25 @@ module OAuth2
107
119
  opts[:redirect_count] ||= 0
108
120
  opts[:redirect_count] += 1
109
121
  return response if opts[:redirect_count] > options[:max_redirects]
122
+
110
123
  if response.status == 303
111
124
  verb = :get
112
125
  opts.delete(:body)
113
126
  end
114
- request(verb, response.headers['location'], opts)
127
+ location = response.headers['location']
128
+ if location
129
+ request(verb, location, opts)
130
+ else
131
+ error = Error.new(response)
132
+ raise(error, "Got #{response.status} status code, but no Location header was present")
133
+ end
115
134
  when 200..299, 300..399
116
135
  # on non-redirecting 3xx statuses, just return the response
117
136
  response
118
137
  when 400..599
119
138
  error = Error.new(response)
120
139
  raise(error) if opts.fetch(:raise_errors, options[:raise_errors])
140
+
121
141
  response.error = error
122
142
  response
123
143
  else
@@ -128,12 +148,21 @@ module OAuth2
128
148
 
129
149
  # Initializes an AccessToken by making a request to the token endpoint
130
150
  #
131
- # @param [Hash] params a Hash of params for the token endpoint
132
- # @param [Hash] access token options, to pass to the AccessToken object
133
- # @param [Class] class of access token for easier subclassing OAuth2::AccessToken
151
+ # @param params [Hash] a Hash of params for the token endpoint
152
+ # @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
134
154
  # @return [AccessToken] the initialized AccessToken
135
- def get_token(params, access_token_opts = {}, access_token_class = AccessToken) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
136
- params = Authenticator.new(id, secret, options[:auth_scheme]).apply(params)
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
156
+ params = params.map do |key, value|
157
+ if RESERVED_PARAM_KEYS.include?(key)
158
+ [key.to_sym, value]
159
+ else
160
+ [key, value]
161
+ end
162
+ end
163
+ params = Hash[params]
164
+
165
+ params = authenticator.apply(params)
137
166
  opts = {:raise_errors => options[:raise_errors], :parse => params.delete(:parse)}
138
167
  headers = params.delete(:headers) || {}
139
168
  if options[:token_method] == :post
@@ -143,39 +172,55 @@ module OAuth2
143
172
  opts[:params] = params
144
173
  opts[:headers] = {}
145
174
  end
146
- opts[:headers].merge!(headers)
147
- response = request(options[:token_method], token_url, opts)
148
- if options[:raise_errors] && !(response.parsed.is_a?(Hash) && response.parsed['access_token'])
175
+ opts[:headers] = opts[:headers].merge(headers)
176
+ http_method = options[:token_method]
177
+ response = request(http_method, token_url, opts)
178
+
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
149
191
  error = Error.new(response)
150
192
  raise(error)
193
+ elsif !response_contains_token
194
+ return nil
151
195
  end
152
- access_token_class.from_hash(self, response.parsed.merge(access_token_opts))
196
+
197
+ access_token
153
198
  end
154
199
 
155
200
  # The Authorization Code strategy
156
201
  #
157
- # @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.1
202
+ # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.1
158
203
  def auth_code
159
204
  @auth_code ||= OAuth2::Strategy::AuthCode.new(self)
160
205
  end
161
206
 
162
207
  # The Implicit strategy
163
208
  #
164
- # @see http://tools.ietf.org/html/draft-ietf-oauth-v2-26#section-4.2
209
+ # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-26#section-4.2
165
210
  def implicit
166
211
  @implicit ||= OAuth2::Strategy::Implicit.new(self)
167
212
  end
168
213
 
169
214
  # The Resource Owner Password Credentials strategy
170
215
  #
171
- # @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.3
216
+ # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.3
172
217
  def password
173
218
  @password ||= OAuth2::Strategy::Password.new(self)
174
219
  end
175
220
 
176
221
  # The Client Credentials strategy
177
222
  #
178
- # @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.4
223
+ # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.4
179
224
  def client_credentials
180
225
  @client_credentials ||= OAuth2::Strategy::ClientCredentials.new(self)
181
226
  end
@@ -195,10 +240,10 @@ module OAuth2
195
240
  #
196
241
  # @api semipublic
197
242
  #
198
- # @see https://tools.ietf.org/html/rfc6749#section-4.1
199
- # @see https://tools.ietf.org/html/rfc6749#section-4.1.3
200
- # @see https://tools.ietf.org/html/rfc6749#section-4.2.1
201
- # @see https://tools.ietf.org/html/rfc6749#section-10.6
243
+ # @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1
244
+ # @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3
245
+ # @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.2.1
246
+ # @see https://datatracker.ietf.org/doc/html/rfc6749#section-10.6
202
247
  # @return [Hash] the params to add to a request or URL
203
248
  def redirection_params
204
249
  if options[:redirect_uri]
@@ -207,5 +252,41 @@ module OAuth2
207
252
  {}
208
253
  end
209
254
  end
255
+
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
+ private
262
+
263
+ # Returns the authenticator object
264
+ #
265
+ # @return [Authenticator] the initialized Authenticator
266
+ def authenticator
267
+ Authenticator.new(id, secret, options[:auth_scheme])
268
+ end
269
+
270
+ # Builds the access token from the response of the HTTP call
271
+ #
272
+ # @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)
285
+ end
286
+ end
287
+
288
+ def oauth_debug_logging(builder)
289
+ builder.response :logger, options[:logger], :bodies => true if ENV['OAUTH_DEBUG'] == 'true'
290
+ end
210
291
  end
211
292
  end
data/lib/oauth2/error.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module OAuth2
2
4
  class Error < StandardError
3
5
  attr_reader :response, :code, :description
@@ -23,7 +25,7 @@ module OAuth2
23
25
  def error_message(response_body, opts = {})
24
26
  message = []
25
27
 
26
- opts[:error_description] && message << opts[:error_description]
28
+ opts[:error_description] && (message << opts[:error_description])
27
29
 
28
30
  error_message = if opts[:error_description] && opts[:error_description].respond_to?(:encoding)
29
31
  script_encoding = opts[:error_description].encoding
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'base64'
2
4
  require 'digest'
3
5
  require 'openssl'
@@ -95,16 +97,22 @@ module OAuth2
95
97
  #
96
98
  # @param [String] alg the algorithm to use (one of 'hmac-sha-1', 'hmac-sha-256')
97
99
  def algorithm=(alg)
98
- @algorithm = begin
99
- case alg.to_s
100
- when 'hmac-sha-1'
101
- OpenSSL::Digest::SHA1.new
102
- when 'hmac-sha-256'
103
- OpenSSL::Digest::SHA256.new
104
- else
105
- raise(ArgumentError, 'Unsupported algorithm')
106
- end
107
- end
100
+ @algorithm = case alg.to_s
101
+ when 'hmac-sha-1'
102
+ begin
103
+ OpenSSL::Digest('SHA1').new
104
+ rescue StandardError
105
+ OpenSSL::Digest.new('SHA1')
106
+ end
107
+ when 'hmac-sha-256'
108
+ begin
109
+ OpenSSL::Digest('SHA256').new
110
+ rescue StandardError
111
+ OpenSSL::Digest.new('SHA256')
112
+ end
113
+ else
114
+ raise(ArgumentError, 'Unsupported algorithm')
115
+ end
108
116
  end
109
117
 
110
118
  private
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'multi_json'
2
4
  require 'multi_xml'
3
5
  require 'rack'
@@ -11,9 +13,9 @@ module OAuth2
11
13
  # Procs that, when called, will parse a response body according
12
14
  # to the specified format.
13
15
  @@parsers = {
14
- :json => lambda { |body| MultiJson.load(body) rescue body }, # rubocop:disable RescueModifier
16
+ :json => lambda { |body| MultiJson.load(body) rescue body }, # rubocop:disable Style/RescueModifier
15
17
  :query => lambda { |body| Rack::Utils.parse_query(body) },
16
- :text => lambda { |body| body },
18
+ :text => lambda { |body| body },
17
19
  }
18
20
 
19
21
  # Content type assignments for various potential HTTP content types.
@@ -68,6 +70,7 @@ module OAuth2
68
70
  # application/json Content-Type response bodies
69
71
  def parsed
70
72
  return nil unless @@parsers.key?(parser)
73
+
71
74
  @parsed ||= @@parsers[parser].call(body)
72
75
  end
73
76
 
@@ -79,11 +82,12 @@ module OAuth2
79
82
  # Determines the parser that will be used to supply the content of #parsed
80
83
  def parser
81
84
  return options[:parse].to_sym if @@parsers.key?(options[:parse])
85
+
82
86
  @@content_types[content_type]
83
87
  end
84
88
  end
85
89
  end
86
90
 
87
91
  OAuth2::Response.register_parser(:xml, ['text/xml', 'application/rss+xml', 'application/rdf+xml', 'application/atom+xml']) do |body|
88
- MultiXml.parse(body) rescue body # rubocop:disable RescueModifier
92
+ MultiXml.parse(body) rescue body # rubocop:disable Style/RescueModifier
89
93
  end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'jwt'
2
4
 
3
5
  module OAuth2
4
6
  module Strategy
5
7
  # The Client Assertion Strategy
6
8
  #
7
- # @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.1.3
9
+ # @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-10#section-4.1.3
8
10
  #
9
11
  # Sample usage:
10
12
  # client = OAuth2::Client.new(client_id, client_secret,
@@ -50,10 +52,10 @@ module OAuth2
50
52
  def build_request(params)
51
53
  assertion = build_assertion(params)
52
54
  {
53
- :grant_type => 'assertion',
55
+ :grant_type => 'assertion',
54
56
  :assertion_type => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
55
- :assertion => assertion,
56
- :scope => params[:scope],
57
+ :assertion => assertion,
58
+ :scope => params[:scope],
57
59
  }
58
60
  end
59
61
 
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module OAuth2
2
4
  module Strategy
3
5
  # The Authorization Code Strategy
4
6
  #
5
- # @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.1
7
+ # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.1
6
8
  class AuthCode < Base
7
9
  # The required query parameters for the authorize URL
8
10
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module OAuth2
2
4
  module Strategy
3
5
  class Base
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module OAuth2
2
4
  module Strategy
3
5
  # The Client Credentials Strategy
4
6
  #
5
- # @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.4
7
+ # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.4
6
8
  class ClientCredentials < Base
7
9
  # Not used for this strategy
8
10
  #
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module OAuth2
2
4
  module Strategy
3
5
  # The Implicit Strategy
4
6
  #
5
- # @see http://tools.ietf.org/html/draft-ietf-oauth-v2-26#section-4.2
7
+ # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-26#section-4.2
6
8
  class Implicit < Base
7
9
  # The required query parameters for the authorize URL
8
10
  #
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module OAuth2
2
4
  module Strategy
3
5
  # The Resource Owner Password Credentials Authorization Strategy
4
6
  #
5
- # @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.3
7
+ # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.3
6
8
  class Password < Base
7
9
  # Not used for this strategy
8
10
  #
@@ -18,8 +20,8 @@ module OAuth2
18
20
  # @param [Hash] params additional params
19
21
  def get_token(username, password, params = {}, opts = {})
20
22
  params = {'grant_type' => 'password',
21
- 'username' => username,
22
- 'password' => password}.merge(params)
23
+ 'username' => username,
24
+ 'password' => password}.merge(params)
23
25
  @client.get_token(params, opts)
24
26
  end
25
27
  end
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module OAuth2
2
4
  module Version
5
+ VERSION = to_s
6
+
3
7
  module_function
4
8
 
5
9
  # The major version
@@ -20,12 +24,12 @@ module OAuth2
20
24
  #
21
25
  # @return [Integer]
22
26
  def patch
23
- 4
27
+ 9
24
28
  end
25
29
 
26
30
  # The pre-release version, if any
27
31
  #
28
- # @return [Integer, NilClass]
32
+ # @return [String, NilClass]
29
33
  def pre
30
34
  nil
31
35
  end
@@ -53,7 +57,9 @@ module OAuth2
53
57
  #
54
58
  # @return [String]
55
59
  def to_s
56
- to_a.join('.')
60
+ v = [major, minor, patch].compact.join('.')
61
+ v += "-#{pre}" if pre
62
+ v
57
63
  end
58
64
  end
59
65
  end
data/lib/oauth2.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'oauth2/error'
2
4
  require 'oauth2/authenticator'
3
5
  require 'oauth2/client'
@@ -0,0 +1,11 @@
1
+ # RS256
2
+
3
+ ## How keys were made
4
+
5
+ ```shell
6
+ # No passphrase
7
+ # Generates the public and private keys:
8
+ ssh-keygen -t rsa -b 4096 -m PEM -f jwtRS256.key
9
+ # Converts the key to PEM format
10
+ openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256.key.pub
11
+ ```
@@ -0,0 +1,51 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ 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
51
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,14 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5hdXV/4YSymY1T9VNvK2
3
+ bWRfulwIty1RnAPNINQmfh3aRRkV+PNrbC2Crji9G0AHmQwgW1bZ3kgkkpIm6RVn
4
+ 44fHvBvuXkZ9ABgXw0d2cLIHmwOFxSKmWAm/EW//GszUTLLLsMZUe2udtFJW0jxX
5
+ B2GRY0WVYuo6Oo58RCeP719lw3Ags0YF9/IobxKkGd4BautUPw6ZszAa3o+j0zR7
6
+ 4x7ouPxybZAOuPsMxqanyeYJeH4osJjLMYV9qem9uG2sj7GENJ8UszcpmGbqxBhe
7
+ xPEB7mgDeONIF0XJF23zdOf8ANE5mAU2h2v7M6moAfkdUzJ+j48+VT2omHAzAL5y
8
+ Ncmrl2xiWdyoxOw1Y1UmfEmJYV5VgGYyZ12JZRKY+szPT+vR+MDuYxbquF40O7kv
9
+ kFNBfL1yCpzfSQCLnEs4rX8qRzZXciLeyq4Ht5FLuRFgxjA//XI8LAmp0u7gk+Q7
10
+ FUH1UgW3kmJDTG0XaxQxYTBSIO7mcmyjDyBgKVuQmt5E1ycFeteOVdPD/CG/fPYh
11
+ thvc4UytEFwsMdNy3iD6/wuUH68tAKam28UZaOb0qK+00cQQD8fulY9rKtSL10Lv
12
+ JFWUOa/SJyLvk9vUmfvFn182il1nX6GpyxyMmE/FCnH4CT/DjrSZf08mOO8eL5of
13
+ YHMK/oiXr1eODqx+pOwClNsCAwEAAQ==
14
+ -----END PUBLIC KEY-----
data/spec/helper.rb ADDED
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ DEBUG = ENV['DEBUG'] == 'true'
4
+ RUN_COVERAGE = ENV['CI_CODECOV'] || ENV['CI'].nil?
5
+
6
+ ruby_version = Gem::Version.new(RUBY_VERSION)
7
+ minimum_version = ->(version) { ruby_version >= Gem::Version.new(version) && RUBY_ENGINE == 'ruby' }
8
+ coverage = minimum_version.call('2.7') && RUN_COVERAGE
9
+ debug = minimum_version.call('2.5') && DEBUG
10
+
11
+ require 'simplecov' if coverage
12
+ require 'byebug' if debug
13
+
14
+ require 'oauth2'
15
+ require 'addressable/uri'
16
+ require 'rspec'
17
+ require 'rspec/stubbed_env'
18
+ require 'rspec/pending_for'
19
+ require 'silent_stream'
20
+
21
+ RSpec.configure do |config|
22
+ config.expect_with :rspec do |c|
23
+ c.syntax = :expect
24
+ end
25
+ end
26
+
27
+ Faraday.default_adapter = :test
28
+
29
+ RSpec.configure do |conf|
30
+ conf.include SilentStream
31
+ end
32
+
33
+ VERBS = [:get, :post, :put, :delete].freeze