oauth2 1.4.5 → 1.4.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -1
- data/LICENSE +1 -1
- data/README.md +276 -111
- data/lib/oauth2/access_token.rb +7 -7
- data/lib/oauth2/authenticator.rb +3 -1
- data/lib/oauth2/client.rb +105 -70
- data/lib/oauth2/error.rb +3 -1
- data/lib/oauth2/mac_token.rb +18 -18
- data/lib/oauth2/response.rb +2 -0
- data/lib/oauth2/strategy/assertion.rb +3 -1
- 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 +3 -1
- data/lib/oauth2/version.rb +8 -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 +49 -90
- data/.document +0 -5
- data/.github/dependabot.yml +0 -8
- data/.github/workflows/style.yml +0 -37
- data/.github/workflows/test.yml +0 -58
- data/.gitignore +0 -19
- data/.jrubyrc +0 -1
- data/.rspec +0 -4
- data/.rubocop.yml +0 -112
- data/.rubocop_rspec.yml +0 -26
- data/.rubocop_todo.yml +0 -113
- data/.ruby-version +0 -1
- data/.travis.yml +0 -75
- data/CONTRIBUTING.md +0 -18
- data/Gemfile +0 -61
- 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_head.gemfile +0 -9
- data/gemfiles/truffleruby.gemfile +0 -3
- data/maintenance-branch +0 -1
- data/oauth2.gemspec +0 -52
data/lib/oauth2/client.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
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
|
7
10
|
RESERVED_PARAM_KEYS = %w[headers parse].freeze
|
@@ -16,17 +19,18 @@ module OAuth2
|
|
16
19
|
#
|
17
20
|
# @param [String] client_id the client_id value
|
18
21
|
# @param [String] client_secret the client_secret value
|
19
|
-
# @param [Hash]
|
20
|
-
# @option
|
21
|
-
# @option
|
22
|
-
# @option
|
23
|
-
# @option
|
24
|
-
# @option
|
25
|
-
# @option
|
26
|
-
# @option
|
27
|
-
# @option
|
28
|
-
# @option
|
29
|
-
# @option
|
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
|
30
34
|
# @yield [builder] The Faraday connection builder
|
31
35
|
def initialize(client_id, client_secret, options = {}, &block)
|
32
36
|
opts = options.dup
|
@@ -34,24 +38,22 @@ module OAuth2
|
|
34
38
|
@secret = client_secret
|
35
39
|
@site = opts.delete(:site)
|
36
40
|
ssl = opts.delete(:ssl)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
:extract_access_token => DEFAULT_EXTRACT_ACCESS_TOKEN,
|
48
|
-
}.merge(opts)
|
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)
|
49
51
|
@options[:connection_opts][:ssl] = ssl if ssl
|
50
52
|
end
|
51
53
|
|
52
54
|
# Set the site host
|
53
55
|
#
|
54
|
-
# @param [String] the OAuth2 provider site host
|
56
|
+
# @param value [String] the OAuth2 provider site host
|
55
57
|
def site=(value)
|
56
58
|
@connection = nil
|
57
59
|
@site = value
|
@@ -59,15 +61,16 @@ module OAuth2
|
|
59
61
|
|
60
62
|
# The Faraday connection object
|
61
63
|
def connection
|
62
|
-
@connection ||=
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
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
|
67
72
|
end
|
68
73
|
end
|
69
|
-
conn
|
70
|
-
end
|
71
74
|
end
|
72
75
|
|
73
76
|
# The authorize endpoint URL of the OAuth2 provider
|
@@ -97,15 +100,18 @@ module OAuth2
|
|
97
100
|
# code response for this request. Will default to client option
|
98
101
|
# @option opts [Symbol] :parse @see Response::initialize
|
99
102
|
# @yield [req] The Faraday request
|
100
|
-
def request(verb, url, opts = {}) # rubocop:disable Metrics/
|
101
|
-
connection.response :logger, ::Logger.new($stdout) if ENV['OAUTH_DEBUG'] == 'true'
|
102
|
-
|
103
|
+
def request(verb, url, opts = {}) # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize
|
103
104
|
url = connection.build_url(url).to_s
|
104
105
|
|
105
|
-
|
106
|
-
|
107
|
-
|
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
|
108
113
|
end
|
114
|
+
|
109
115
|
response = Response.new(response, :parse => opts[:parse])
|
110
116
|
|
111
117
|
case response.status
|
@@ -118,7 +124,13 @@ module OAuth2
|
|
118
124
|
verb = :get
|
119
125
|
opts.delete(:body)
|
120
126
|
end
|
121
|
-
|
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
|
122
134
|
when 200..299, 300..399
|
123
135
|
# on non-redirecting 3xx statuses, just return the response
|
124
136
|
response
|
@@ -136,11 +148,11 @@ module OAuth2
|
|
136
148
|
|
137
149
|
# Initializes an AccessToken by making a request to the token endpoint
|
138
150
|
#
|
139
|
-
# @param [Hash]
|
140
|
-
# @param [Hash] access token options, to pass to the AccessToken object
|
141
|
-
# @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
|
142
154
|
# @return [AccessToken] the initialized AccessToken
|
143
|
-
def get_token(params, access_token_opts = {}, extract_access_token = options[:extract_access_token]) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
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
|
144
156
|
params = params.map do |key, value|
|
145
157
|
if RESERVED_PARAM_KEYS.include?(key)
|
146
158
|
[key.to_sym, value]
|
@@ -150,7 +162,7 @@ module OAuth2
|
|
150
162
|
end
|
151
163
|
params = Hash[params]
|
152
164
|
|
153
|
-
params =
|
165
|
+
params = authenticator.apply(params)
|
154
166
|
opts = {:raise_errors => options[:raise_errors], :parse => params.delete(:parse)}
|
155
167
|
headers = params.delete(:headers) || {}
|
156
168
|
if options[:token_method] == :post
|
@@ -160,8 +172,9 @@ module OAuth2
|
|
160
172
|
opts[:params] = params
|
161
173
|
opts[:headers] = {}
|
162
174
|
end
|
163
|
-
opts[:headers].merge
|
164
|
-
|
175
|
+
opts[:headers] = opts[:headers].merge(headers)
|
176
|
+
http_method = options[:token_method]
|
177
|
+
response = request(http_method, token_url, opts)
|
165
178
|
|
166
179
|
access_token = begin
|
167
180
|
build_access_token(response, access_token_opts, extract_access_token)
|
@@ -169,37 +182,45 @@ module OAuth2
|
|
169
182
|
nil
|
170
183
|
end
|
171
184
|
|
172
|
-
|
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
|
173
191
|
error = Error.new(response)
|
174
192
|
raise(error)
|
193
|
+
elsif !response_contains_token
|
194
|
+
return nil
|
175
195
|
end
|
196
|
+
|
176
197
|
access_token
|
177
198
|
end
|
178
199
|
|
179
200
|
# The Authorization Code strategy
|
180
201
|
#
|
181
|
-
# @see http://
|
202
|
+
# @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.1
|
182
203
|
def auth_code
|
183
204
|
@auth_code ||= OAuth2::Strategy::AuthCode.new(self)
|
184
205
|
end
|
185
206
|
|
186
207
|
# The Implicit strategy
|
187
208
|
#
|
188
|
-
# @see http://
|
209
|
+
# @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-26#section-4.2
|
189
210
|
def implicit
|
190
211
|
@implicit ||= OAuth2::Strategy::Implicit.new(self)
|
191
212
|
end
|
192
213
|
|
193
214
|
# The Resource Owner Password Credentials strategy
|
194
215
|
#
|
195
|
-
# @see http://
|
216
|
+
# @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.3
|
196
217
|
def password
|
197
218
|
@password ||= OAuth2::Strategy::Password.new(self)
|
198
219
|
end
|
199
220
|
|
200
221
|
# The Client Credentials strategy
|
201
222
|
#
|
202
|
-
# @see http://
|
223
|
+
# @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.4
|
203
224
|
def client_credentials
|
204
225
|
@client_credentials ||= OAuth2::Strategy::ClientCredentials.new(self)
|
205
226
|
end
|
@@ -219,10 +240,10 @@ module OAuth2
|
|
219
240
|
#
|
220
241
|
# @api semipublic
|
221
242
|
#
|
222
|
-
# @see https://
|
223
|
-
# @see https://
|
224
|
-
# @see https://
|
225
|
-
# @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
|
226
247
|
# @return [Hash] the params to add to a request or URL
|
227
248
|
def redirection_params
|
228
249
|
if options[:redirect_uri]
|
@@ -231,27 +252,41 @@ module OAuth2
|
|
231
252
|
{}
|
232
253
|
end
|
233
254
|
end
|
234
|
-
end
|
235
255
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
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
|
240
260
|
|
241
|
-
private
|
261
|
+
private
|
242
262
|
|
243
|
-
|
244
|
-
|
245
|
-
return
|
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
|
246
269
|
|
247
|
-
|
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
|
248
287
|
|
249
|
-
|
250
|
-
|
251
|
-
if extract_access_token.is_a?(Class) && extract_access_token.respond_to?(:from_hash)
|
252
|
-
extract_access_token.from_hash(self, hash)
|
253
|
-
else
|
254
|
-
extract_access_token.call(self, hash)
|
288
|
+
def oauth_debug_logging(builder)
|
289
|
+
builder.response :logger, options[:logger], :bodies => true if ENV['OAUTH_DEBUG'] == 'true'
|
255
290
|
end
|
256
291
|
end
|
257
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,24 +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
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
end
|
115
|
-
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
|
116
116
|
end
|
117
117
|
|
118
118
|
private
|
data/lib/oauth2/response.rb
CHANGED
@@ -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,
|
@@ -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
|
#
|
data/lib/oauth2/version.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module OAuth2
|
2
4
|
module Version
|
3
5
|
VERSION = to_s
|
6
|
+
|
4
7
|
module_function
|
5
8
|
|
6
9
|
# The major version
|
@@ -21,12 +24,12 @@ module OAuth2
|
|
21
24
|
#
|
22
25
|
# @return [Integer]
|
23
26
|
def patch
|
24
|
-
|
27
|
+
9
|
25
28
|
end
|
26
29
|
|
27
30
|
# The pre-release version, if any
|
28
31
|
#
|
29
|
-
# @return [
|
32
|
+
# @return [String, NilClass]
|
30
33
|
def pre
|
31
34
|
nil
|
32
35
|
end
|
@@ -54,7 +57,9 @@ module OAuth2
|
|
54
57
|
#
|
55
58
|
# @return [String]
|
56
59
|
def to_s
|
57
|
-
|
60
|
+
v = [major, minor, patch].compact.join('.')
|
61
|
+
v += "-#{pre}" if pre
|
62
|
+
v
|
58
63
|
end
|
59
64
|
end
|
60
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
|