oauth2 1.4.2 → 1.4.9
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +54 -2
- data/CODE_OF_CONDUCT.md +105 -46
- data/LICENSE +1 -1
- data/README.md +290 -102
- data/lib/oauth2/access_token.rb +15 -5
- data/lib/oauth2/authenticator.rb +13 -1
- data/lib/oauth2/client.rb +133 -51
- data/lib/oauth2/error.rb +3 -1
- data/lib/oauth2/mac_token.rb +18 -10
- data/lib/oauth2/response.rb +7 -3
- data/lib/oauth2/strategy/assertion.rb +6 -4
- data/lib/oauth2/strategy/auth_code.rb +3 -1
- data/lib/oauth2/strategy/base.rb +2 -0
- data/lib/oauth2/strategy/client_credentials.rb +3 -1
- data/lib/oauth2/strategy/implicit.rb +3 -1
- data/lib/oauth2/strategy/password.rb +5 -3
- data/lib/oauth2/version.rb +9 -3
- data/lib/oauth2.rb +2 -0
- data/spec/fixtures/README.md +11 -0
- data/spec/fixtures/RS256/jwtRS256.key +51 -0
- data/spec/fixtures/RS256/jwtRS256.key.pub +14 -0
- data/spec/helper.rb +33 -0
- data/spec/oauth2/access_token_spec.rb +218 -0
- data/spec/oauth2/authenticator_spec.rb +86 -0
- data/spec/oauth2/client_spec.rb +556 -0
- data/spec/oauth2/mac_token_spec.rb +122 -0
- data/spec/oauth2/response_spec.rb +96 -0
- data/spec/oauth2/strategy/assertion_spec.rb +113 -0
- data/spec/oauth2/strategy/auth_code_spec.rb +108 -0
- data/spec/oauth2/strategy/base_spec.rb +7 -0
- data/spec/oauth2/strategy/client_credentials_spec.rb +71 -0
- data/spec/oauth2/strategy/implicit_spec.rb +28 -0
- data/spec/oauth2/strategy/password_spec.rb +58 -0
- data/spec/oauth2/version_spec.rb +23 -0
- metadata +57 -95
- data/.document +0 -5
- data/.gitignore +0 -19
- data/.jrubyrc +0 -1
- data/.rspec +0 -2
- data/.rubocop.yml +0 -80
- data/.rubocop_rspec.yml +0 -26
- data/.rubocop_todo.yml +0 -15
- data/.ruby-version +0 -1
- data/.travis.yml +0 -70
- data/CONTRIBUTING.md +0 -18
- data/Gemfile +0 -40
- data/Rakefile +0 -45
- data/gemfiles/jruby_1.7.gemfile +0 -11
- data/gemfiles/jruby_9.0.gemfile +0 -7
- data/gemfiles/jruby_9.1.gemfile +0 -3
- data/gemfiles/jruby_9.2.gemfile +0 -3
- data/gemfiles/jruby_head.gemfile +0 -3
- data/gemfiles/ruby_1.9.gemfile +0 -11
- data/gemfiles/ruby_2.0.gemfile +0 -6
- data/gemfiles/ruby_2.1.gemfile +0 -6
- data/gemfiles/ruby_2.2.gemfile +0 -3
- data/gemfiles/ruby_2.3.gemfile +0 -3
- data/gemfiles/ruby_2.4.gemfile +0 -3
- data/gemfiles/ruby_2.5.gemfile +0 -3
- data/gemfiles/ruby_2.6.gemfile +0 -9
- data/gemfiles/ruby_head.gemfile +0 -9
- data/gemfiles/truffleruby.gemfile +0 -3
- data/oauth2.gemspec +0 -44
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]
|
18
|
-
# @option
|
19
|
-
# @option
|
20
|
-
# @option
|
21
|
-
# @option
|
22
|
-
# @option
|
23
|
-
# @option
|
24
|
-
# @option
|
25
|
-
# @option
|
26
|
-
# @option
|
27
|
-
#
|
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
|
36
|
-
:token_url
|
37
|
-
:token_method
|
38
|
-
:auth_scheme
|
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
|
42
|
-
:raise_errors
|
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 ||=
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
options[:connection_build].call(
|
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,14 +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
|
95
|
-
|
96
|
-
|
97
|
-
url = connection.build_url(url, opts[:params]).to_s
|
103
|
+
def request(verb, url, opts = {}) # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize
|
104
|
+
url = connection.build_url(url).to_s
|
98
105
|
|
99
|
-
|
100
|
-
|
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
|
101
113
|
end
|
114
|
+
|
102
115
|
response = Response.new(response, :parse => opts[:parse])
|
103
116
|
|
104
117
|
case response.status
|
@@ -106,17 +119,25 @@ module OAuth2
|
|
106
119
|
opts[:redirect_count] ||= 0
|
107
120
|
opts[:redirect_count] += 1
|
108
121
|
return response if opts[:redirect_count] > options[:max_redirects]
|
122
|
+
|
109
123
|
if response.status == 303
|
110
124
|
verb = :get
|
111
125
|
opts.delete(:body)
|
112
126
|
end
|
113
|
-
|
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
|
114
134
|
when 200..299, 300..399
|
115
135
|
# on non-redirecting 3xx statuses, just return the response
|
116
136
|
response
|
117
137
|
when 400..599
|
118
138
|
error = Error.new(response)
|
119
139
|
raise(error) if opts.fetch(:raise_errors, options[:raise_errors])
|
140
|
+
|
120
141
|
response.error = error
|
121
142
|
response
|
122
143
|
else
|
@@ -127,12 +148,21 @@ module OAuth2
|
|
127
148
|
|
128
149
|
# Initializes an AccessToken by making a request to the token endpoint
|
129
150
|
#
|
130
|
-
# @param [Hash]
|
131
|
-
# @param [Hash] access token options, to pass to the AccessToken object
|
132
|
-
# @param [Class] class of access token for easier subclassing OAuth2::AccessToken
|
133
|
-
# @return [AccessToken] the
|
134
|
-
def get_token(params, access_token_opts = {},
|
135
|
-
params =
|
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
|
154
|
+
# @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
|
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)
|
136
166
|
opts = {:raise_errors => options[:raise_errors], :parse => params.delete(:parse)}
|
137
167
|
headers = params.delete(:headers) || {}
|
138
168
|
if options[:token_method] == :post
|
@@ -142,39 +172,55 @@ module OAuth2
|
|
142
172
|
opts[:params] = params
|
143
173
|
opts[:headers] = {}
|
144
174
|
end
|
145
|
-
opts[:headers].merge
|
146
|
-
|
147
|
-
|
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
|
148
191
|
error = Error.new(response)
|
149
192
|
raise(error)
|
193
|
+
elsif !response_contains_token
|
194
|
+
return nil
|
150
195
|
end
|
151
|
-
|
196
|
+
|
197
|
+
access_token
|
152
198
|
end
|
153
199
|
|
154
200
|
# The Authorization Code strategy
|
155
201
|
#
|
156
|
-
# @see http://
|
202
|
+
# @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.1
|
157
203
|
def auth_code
|
158
204
|
@auth_code ||= OAuth2::Strategy::AuthCode.new(self)
|
159
205
|
end
|
160
206
|
|
161
207
|
# The Implicit strategy
|
162
208
|
#
|
163
|
-
# @see http://
|
209
|
+
# @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-26#section-4.2
|
164
210
|
def implicit
|
165
211
|
@implicit ||= OAuth2::Strategy::Implicit.new(self)
|
166
212
|
end
|
167
213
|
|
168
214
|
# The Resource Owner Password Credentials strategy
|
169
215
|
#
|
170
|
-
# @see http://
|
216
|
+
# @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.3
|
171
217
|
def password
|
172
218
|
@password ||= OAuth2::Strategy::Password.new(self)
|
173
219
|
end
|
174
220
|
|
175
221
|
# The Client Credentials strategy
|
176
222
|
#
|
177
|
-
# @see http://
|
223
|
+
# @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.4
|
178
224
|
def client_credentials
|
179
225
|
@client_credentials ||= OAuth2::Strategy::ClientCredentials.new(self)
|
180
226
|
end
|
@@ -194,10 +240,10 @@ module OAuth2
|
|
194
240
|
#
|
195
241
|
# @api semipublic
|
196
242
|
#
|
197
|
-
# @see https://
|
198
|
-
# @see https://
|
199
|
-
# @see https://
|
200
|
-
# @see https://
|
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
|
201
247
|
# @return [Hash] the params to add to a request or URL
|
202
248
|
def redirection_params
|
203
249
|
if options[:redirect_uri]
|
@@ -206,5 +252,41 @@ module OAuth2
|
|
206
252
|
{}
|
207
253
|
end
|
208
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
|
209
291
|
end
|
210
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
|
data/lib/oauth2/mac_token.rb
CHANGED
@@ -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 =
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
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
|
data/lib/oauth2/response.rb
CHANGED
@@ -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
|
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
|
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
|
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
|
55
|
+
:grant_type => 'assertion',
|
54
56
|
:assertion_type => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
55
|
-
:assertion
|
56
|
-
: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://
|
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
|
#
|
data/lib/oauth2/strategy/base.rb
CHANGED
@@ -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://
|
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://
|
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://
|
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'
|
22
|
-
'password'
|
23
|
+
'username' => username,
|
24
|
+
'password' => password}.merge(params)
|
23
25
|
@client.get_token(params, opts)
|
24
26
|
end
|
25
27
|
end
|
data/lib/oauth2/version.rb
CHANGED
@@ -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
|
-
|
27
|
+
9
|
24
28
|
end
|
25
29
|
|
26
30
|
# The pre-release version, if any
|
27
31
|
#
|
28
|
-
# @return [
|
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
|
-
|
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
@@ -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
|