oauth2 1.4.0 → 1.4.5
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 +5 -5
- data/.github/dependabot.yml +8 -0
- data/.github/workflows/style.yml +37 -0
- data/.github/workflows/test.yml +58 -0
- data/.gitignore +19 -0
- data/.jrubyrc +1 -0
- data/.rspec +4 -0
- data/.rubocop.yml +112 -0
- data/.rubocop_rspec.yml +26 -0
- data/.rubocop_todo.yml +113 -0
- data/.ruby-version +1 -0
- data/.travis.yml +75 -0
- data/CHANGELOG.md +160 -0
- data/CODE_OF_CONDUCT.md +133 -0
- data/Gemfile +61 -0
- data/LICENSE +22 -0
- data/README.md +132 -22
- data/Rakefile +45 -0
- data/gemfiles/jruby_1.7.gemfile +11 -0
- data/gemfiles/jruby_9.0.gemfile +7 -0
- data/gemfiles/jruby_9.1.gemfile +3 -0
- data/gemfiles/jruby_9.2.gemfile +3 -0
- data/gemfiles/jruby_head.gemfile +3 -0
- data/gemfiles/ruby_1.9.gemfile +11 -0
- data/gemfiles/ruby_2.0.gemfile +6 -0
- data/gemfiles/ruby_head.gemfile +9 -0
- data/gemfiles/truffleruby.gemfile +3 -0
- data/lib/oauth2/access_token.rb +14 -4
- data/lib/oauth2/authenticator.rb +10 -0
- data/lib/oauth2/client.rb +63 -16
- data/lib/oauth2/mac_token.rb +11 -3
- data/lib/oauth2/response.rb +5 -3
- data/lib/oauth2/strategy/assertion.rb +3 -3
- data/lib/oauth2/strategy/password.rb +2 -2
- data/lib/oauth2/version.rb +2 -1
- data/maintenance-branch +1 -0
- data/oauth2.gemspec +36 -9
- metadata +215 -20
- data/LICENSE.md +0 -20
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# !/usr/bin/env rake
|
4
|
+
|
5
|
+
require 'bundler/gem_tasks'
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'wwtd/tasks'
|
9
|
+
rescue LoadError
|
10
|
+
puts 'failed to load wwtd'
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
require 'rspec/core/rake_task'
|
15
|
+
RSpec::Core::RakeTask.new(:spec)
|
16
|
+
rescue LoadError
|
17
|
+
task :spec do
|
18
|
+
warn 'rspec is disabled'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
task :test => :spec
|
22
|
+
|
23
|
+
begin
|
24
|
+
require 'rubocop/rake_task'
|
25
|
+
RuboCop::RakeTask.new do |task|
|
26
|
+
task.options = ['-D'] # Display the name of the failing cops
|
27
|
+
end
|
28
|
+
rescue LoadError
|
29
|
+
task :rubocop do
|
30
|
+
warn 'RuboCop is disabled'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
namespace :doc do
|
35
|
+
require 'rdoc/task'
|
36
|
+
require 'oauth2/version'
|
37
|
+
RDoc::Task.new do |rdoc|
|
38
|
+
rdoc.rdoc_dir = 'rdoc'
|
39
|
+
rdoc.title = "oauth2 #{OAuth2::Version}"
|
40
|
+
rdoc.main = 'README.md'
|
41
|
+
rdoc.rdoc_files.include('README.md', 'LICENSE.md', 'lib/**/*.rb')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
task :default => [:test, :rubocop]
|
data/lib/oauth2/access_token.rb
CHANGED
@@ -3,6 +3,7 @@ module OAuth2
|
|
3
3
|
attr_reader :client, :token, :expires_in, :expires_at, :params
|
4
4
|
attr_accessor :options, :refresh_token
|
5
5
|
|
6
|
+
# Should these methods be deprecated?
|
6
7
|
class << self
|
7
8
|
# Initializes an AccessToken from a Hash
|
8
9
|
#
|
@@ -46,11 +47,11 @@ module OAuth2
|
|
46
47
|
end
|
47
48
|
@expires_in ||= opts.delete('expires')
|
48
49
|
@expires_in &&= @expires_in.to_i
|
49
|
-
@expires_at &&= @expires_at
|
50
|
+
@expires_at &&= convert_expires_at(@expires_at)
|
50
51
|
@expires_at ||= Time.now.to_i + @expires_in if @expires_in
|
51
|
-
@options = {:mode
|
52
|
+
@options = {:mode => opts.delete(:mode) || :header,
|
52
53
|
:header_format => opts.delete(:header_format) || 'Bearer %s',
|
53
|
-
:param_name
|
54
|
+
:param_name => opts.delete(:param_name) || 'access_token'}
|
54
55
|
@params = opts
|
55
56
|
end
|
56
57
|
|
@@ -81,6 +82,7 @@ module OAuth2
|
|
81
82
|
# @note options should be carried over to the new AccessToken
|
82
83
|
def refresh!(params = {})
|
83
84
|
raise('A refresh_token is not available') unless refresh_token
|
85
|
+
|
84
86
|
params[:grant_type] = 'refresh_token'
|
85
87
|
params[:refresh_token] = refresh_token
|
86
88
|
new_token = @client.get_token(params)
|
@@ -149,7 +151,7 @@ module OAuth2
|
|
149
151
|
|
150
152
|
private
|
151
153
|
|
152
|
-
def configure_authentication!(opts) # rubocop:disable
|
154
|
+
def configure_authentication!(opts) # rubocop:disable Metrics/AbcSize
|
153
155
|
case options[:mode]
|
154
156
|
when :header
|
155
157
|
opts[:headers] ||= {}
|
@@ -169,5 +171,13 @@ module OAuth2
|
|
169
171
|
raise("invalid :mode option of #{options[:mode]}")
|
170
172
|
end
|
171
173
|
end
|
174
|
+
|
175
|
+
def convert_expires_at(expires_at)
|
176
|
+
expires_at_i = expires_at.to_i
|
177
|
+
return expires_at_i if expires_at_i > Time.now.utc.to_i
|
178
|
+
return Time.parse(expires_at).to_i if expires_at.is_a?(String)
|
179
|
+
|
180
|
+
expires_at_i
|
181
|
+
end
|
172
182
|
end
|
173
183
|
end
|
data/lib/oauth2/authenticator.rb
CHANGED
@@ -25,6 +25,10 @@ module OAuth2
|
|
25
25
|
apply_basic_auth(params)
|
26
26
|
when :request_body
|
27
27
|
apply_params_auth(params)
|
28
|
+
when :tls_client_auth
|
29
|
+
apply_client_id(params)
|
30
|
+
when :private_key_jwt
|
31
|
+
params
|
28
32
|
else
|
29
33
|
raise NotImplementedError
|
30
34
|
end
|
@@ -42,6 +46,12 @@ module OAuth2
|
|
42
46
|
{'client_id' => id, 'client_secret' => secret}.merge(params)
|
43
47
|
end
|
44
48
|
|
49
|
+
# When using schemes that don't require the client_secret to be passed i.e TLS Client Auth,
|
50
|
+
# we don't want to send the secret
|
51
|
+
def apply_client_id(params)
|
52
|
+
{'client_id' => id}.merge(params)
|
53
|
+
end
|
54
|
+
|
45
55
|
# Adds an `Authorization` header with Basic Auth credentials if and only if
|
46
56
|
# it is not already set in the params.
|
47
57
|
def apply_basic_auth(params)
|
data/lib/oauth2/client.rb
CHANGED
@@ -4,6 +4,8 @@ require 'logger'
|
|
4
4
|
module OAuth2
|
5
5
|
# The OAuth2::Client class
|
6
6
|
class Client # rubocop:disable Metrics/ClassLength
|
7
|
+
RESERVED_PARAM_KEYS = %w[headers parse].freeze
|
8
|
+
|
7
9
|
attr_reader :id, :secret, :site
|
8
10
|
attr_accessor :options
|
9
11
|
attr_writer :connection
|
@@ -23,8 +25,8 @@ module OAuth2
|
|
23
25
|
# @option opts [Symbol] :auth_scheme (:basic_auth) HTTP method to use to authorize request (:basic_auth or :request_body)
|
24
26
|
# @option opts [Hash] :connection_opts ({}) Hash of connection options to pass to initialize Faraday with
|
25
27
|
# @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
|
-
#
|
28
|
+
# @option opts [Boolean] :raise_errors (true) whether or not to raise an OAuth2::Error on responses with 400+ status codes
|
29
|
+
# @option opts [Proc] :extract_access_token proc that extracts the access token from the response
|
28
30
|
# @yield [builder] The Faraday connection builder
|
29
31
|
def initialize(client_id, client_secret, options = {}, &block)
|
30
32
|
opts = options.dup
|
@@ -32,14 +34,18 @@ module OAuth2
|
|
32
34
|
@secret = client_secret
|
33
35
|
@site = opts.delete(:site)
|
34
36
|
ssl = opts.delete(:ssl)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
37
|
+
|
38
|
+
@options = {
|
39
|
+
:authorize_url => '/oauth/authorize',
|
40
|
+
:token_url => '/oauth/token',
|
41
|
+
:token_method => :post,
|
42
|
+
:auth_scheme => :request_body,
|
43
|
+
:connection_opts => {},
|
44
|
+
:connection_build => block,
|
45
|
+
:max_redirects => 5,
|
46
|
+
:raise_errors => true,
|
47
|
+
:extract_access_token => DEFAULT_EXTRACT_ACCESS_TOKEN,
|
48
|
+
}.merge(opts)
|
43
49
|
@options[:connection_opts][:ssl] = ssl if ssl
|
44
50
|
end
|
45
51
|
|
@@ -91,12 +97,13 @@ module OAuth2
|
|
91
97
|
# code response for this request. Will default to client option
|
92
98
|
# @option opts [Symbol] :parse @see Response::initialize
|
93
99
|
# @yield [req] The Faraday request
|
94
|
-
def request(verb, url, opts = {}) # rubocop:disable
|
100
|
+
def request(verb, url, opts = {}) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
95
101
|
connection.response :logger, ::Logger.new($stdout) if ENV['OAUTH_DEBUG'] == 'true'
|
96
102
|
|
97
|
-
url = connection.build_url(url
|
103
|
+
url = connection.build_url(url).to_s
|
98
104
|
|
99
105
|
response = connection.run_request(verb, url, opts[:body], opts[:headers]) do |req|
|
106
|
+
req.params.update(opts[:params]) if opts[:params]
|
100
107
|
yield(req) if block_given?
|
101
108
|
end
|
102
109
|
response = Response.new(response, :parse => opts[:parse])
|
@@ -106,6 +113,7 @@ module OAuth2
|
|
106
113
|
opts[:redirect_count] ||= 0
|
107
114
|
opts[:redirect_count] += 1
|
108
115
|
return response if opts[:redirect_count] > options[:max_redirects]
|
116
|
+
|
109
117
|
if response.status == 303
|
110
118
|
verb = :get
|
111
119
|
opts.delete(:body)
|
@@ -117,6 +125,7 @@ module OAuth2
|
|
117
125
|
when 400..599
|
118
126
|
error = Error.new(response)
|
119
127
|
raise(error) if opts.fetch(:raise_errors, options[:raise_errors])
|
128
|
+
|
120
129
|
response.error = error
|
121
130
|
response
|
122
131
|
else
|
@@ -130,8 +139,17 @@ module OAuth2
|
|
130
139
|
# @param [Hash] params a Hash of params for the token endpoint
|
131
140
|
# @param [Hash] access token options, to pass to the AccessToken object
|
132
141
|
# @param [Class] class of access token for easier subclassing OAuth2::AccessToken
|
133
|
-
# @return [AccessToken] the
|
134
|
-
def get_token(params, access_token_opts = {},
|
142
|
+
# @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
|
144
|
+
params = params.map do |key, value|
|
145
|
+
if RESERVED_PARAM_KEYS.include?(key)
|
146
|
+
[key.to_sym, value]
|
147
|
+
else
|
148
|
+
[key, value]
|
149
|
+
end
|
150
|
+
end
|
151
|
+
params = Hash[params]
|
152
|
+
|
135
153
|
params = Authenticator.new(id, secret, options[:auth_scheme]).apply(params)
|
136
154
|
opts = {:raise_errors => options[:raise_errors], :parse => params.delete(:parse)}
|
137
155
|
headers = params.delete(:headers) || {}
|
@@ -144,11 +162,18 @@ module OAuth2
|
|
144
162
|
end
|
145
163
|
opts[:headers].merge!(headers)
|
146
164
|
response = request(options[:token_method], token_url, opts)
|
147
|
-
|
165
|
+
|
166
|
+
access_token = begin
|
167
|
+
build_access_token(response, access_token_opts, extract_access_token)
|
168
|
+
rescue StandardError
|
169
|
+
nil
|
170
|
+
end
|
171
|
+
|
172
|
+
if options[:raise_errors] && !access_token
|
148
173
|
error = Error.new(response)
|
149
174
|
raise(error)
|
150
175
|
end
|
151
|
-
|
176
|
+
access_token
|
152
177
|
end
|
153
178
|
|
154
179
|
# The Authorization Code strategy
|
@@ -207,4 +232,26 @@ module OAuth2
|
|
207
232
|
end
|
208
233
|
end
|
209
234
|
end
|
235
|
+
|
236
|
+
DEFAULT_EXTRACT_ACCESS_TOKEN = proc do |client, hash|
|
237
|
+
token = hash.delete('access_token') || hash.delete(:access_token)
|
238
|
+
token && AccessToken.new(client, token, hash)
|
239
|
+
end
|
240
|
+
|
241
|
+
private
|
242
|
+
|
243
|
+
def build_access_token(response, access_token_opts, extract_access_token)
|
244
|
+
parsed_response = response.parsed.dup
|
245
|
+
return unless parsed_response.is_a?(Hash)
|
246
|
+
|
247
|
+
hash = parsed_response.merge(access_token_opts)
|
248
|
+
|
249
|
+
# Provide backwards compatibility for old AcessToken.form_hash pattern
|
250
|
+
# Should be deprecated in 2.x
|
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)
|
255
|
+
end
|
256
|
+
end
|
210
257
|
end
|
data/lib/oauth2/mac_token.rb
CHANGED
@@ -98,9 +98,17 @@ module OAuth2
|
|
98
98
|
@algorithm = begin
|
99
99
|
case alg.to_s
|
100
100
|
when 'hmac-sha-1'
|
101
|
-
|
101
|
+
begin
|
102
|
+
OpenSSL::Digest('SHA1').new
|
103
|
+
rescue StandardError
|
104
|
+
OpenSSL::Digest.new('SHA1')
|
105
|
+
end
|
102
106
|
when 'hmac-sha-256'
|
103
|
-
|
107
|
+
begin
|
108
|
+
OpenSSL::Digest('SHA256').new
|
109
|
+
rescue StandardError
|
110
|
+
OpenSSL::Digest.new('SHA256')
|
111
|
+
end
|
104
112
|
else
|
105
113
|
raise(ArgumentError, 'Unsupported algorithm')
|
106
114
|
end
|
@@ -111,7 +119,7 @@ module OAuth2
|
|
111
119
|
|
112
120
|
# No-op since we need the verb and path
|
113
121
|
# and the MAC always goes in a header
|
114
|
-
def token=(
|
122
|
+
def token=(_noop)
|
115
123
|
end
|
116
124
|
|
117
125
|
# Base64.strict_encode64 is not available on Ruby 1.8.7
|
data/lib/oauth2/response.rb
CHANGED
@@ -11,9 +11,9 @@ module OAuth2
|
|
11
11
|
# Procs that, when called, will parse a response body according
|
12
12
|
# to the specified format.
|
13
13
|
@@parsers = {
|
14
|
-
:json
|
14
|
+
:json => lambda { |body| MultiJson.load(body) rescue body }, # rubocop:disable Style/RescueModifier
|
15
15
|
:query => lambda { |body| Rack::Utils.parse_query(body) },
|
16
|
-
:text
|
16
|
+
:text => lambda { |body| body },
|
17
17
|
}
|
18
18
|
|
19
19
|
# Content type assignments for various potential HTTP content types.
|
@@ -68,6 +68,7 @@ module OAuth2
|
|
68
68
|
# application/json Content-Type response bodies
|
69
69
|
def parsed
|
70
70
|
return nil unless @@parsers.key?(parser)
|
71
|
+
|
71
72
|
@parsed ||= @@parsers[parser].call(body)
|
72
73
|
end
|
73
74
|
|
@@ -79,11 +80,12 @@ module OAuth2
|
|
79
80
|
# Determines the parser that will be used to supply the content of #parsed
|
80
81
|
def parser
|
81
82
|
return options[:parse].to_sym if @@parsers.key?(options[:parse])
|
83
|
+
|
82
84
|
@@content_types[content_type]
|
83
85
|
end
|
84
86
|
end
|
85
87
|
end
|
86
88
|
|
87
89
|
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
|
90
|
+
MultiXml.parse(body) rescue body # rubocop:disable Style/RescueModifier
|
89
91
|
end
|
@@ -50,10 +50,10 @@ module OAuth2
|
|
50
50
|
def build_request(params)
|
51
51
|
assertion = build_assertion(params)
|
52
52
|
{
|
53
|
-
:grant_type
|
53
|
+
:grant_type => 'assertion',
|
54
54
|
:assertion_type => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
55
|
-
:assertion
|
56
|
-
:scope
|
55
|
+
:assertion => assertion,
|
56
|
+
:scope => params[:scope],
|
57
57
|
}
|
58
58
|
end
|
59
59
|
|
@@ -18,8 +18,8 @@ module OAuth2
|
|
18
18
|
# @param [Hash] params additional params
|
19
19
|
def get_token(username, password, params = {}, opts = {})
|
20
20
|
params = {'grant_type' => 'password',
|
21
|
-
'username'
|
22
|
-
'password'
|
21
|
+
'username' => username,
|
22
|
+
'password' => password}.merge(params)
|
23
23
|
@client.get_token(params, opts)
|
24
24
|
end
|
25
25
|
end
|
data/lib/oauth2/version.rb
CHANGED