rack-oauth2 1.12.0 → 1.21.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c09e887a3c902ebbbfd1b2a1698d8a4d68d28e18e75616c629dd75e981e2a9d2
4
- data.tar.gz: c0142a2c05df047f0d8f1e9ac11f428983d27a8945d5ae5be213be41b5615e20
3
+ metadata.gz: 7303cf85e66a7fb4a89d66d95b4ad35720ecb95459f9740208328314ea54b157
4
+ data.tar.gz: 061a4a30cbb25212979a37f26e18043cbf71dead3e36981b37f6152fc6899cfd
5
5
  SHA512:
6
- metadata.gz: ab1002bdd363b2edab71e8eeebd9872bca8ea2be6ad2a99875543974a2b85340ddcb42fc4ddf22f779b18429dc48a9d36fd91e9059391b76b537c04293ac2056
7
- data.tar.gz: bdffd308340040287a3a8777cdaaaa9de58873d4d27e080dc719b4715ca53fd413bc726f19382b63cd086ad3dd47ac139a665f0acfcd25d2bb2a5e266d8dafce
6
+ metadata.gz: 5fbabf81d770e80f02614d3b00b0fd9db8a63ed695a5b67b74266eee1f09ec6e7045db009ea7e6ee09af84680699809032ecc64d58caee48305573cd3532b5be
7
+ data.tar.gz: 5bc8cdbdddb9a997560eab574a955ab69d3ad8f9e594554a45d17e077991c2551382c917363c1c09db349abf262f5d9c15a7cfb13c24e56fe27d83cbde62f0f3
@@ -0,0 +1,3 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: nov
@@ -0,0 +1,30 @@
1
+ name: Spec
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ spec:
12
+ strategy:
13
+ matrix:
14
+ os: ['ubuntu-20.04']
15
+ ruby-version: ['2.6', '2.7', '3.0', '3.1']
16
+ # ubuntu 22.04 only supports ssl 3 and thus only ruby 3.1
17
+ include:
18
+ - os: 'ubuntu-22.04'
19
+ ruby-version: '3.1'
20
+ runs-on: ${{ matrix.os }}
21
+
22
+ steps:
23
+ - uses: actions/checkout@v3
24
+ - name: Set up Ruby
25
+ uses: ruby/setup-ruby@v1
26
+ with:
27
+ ruby-version: ${{ matrix.ruby-version }}
28
+ bundler-cache: true
29
+ - name: Run Specs
30
+ run: bundle exec rake spec
data/.travis.yml CHANGED
@@ -2,6 +2,7 @@ before_install:
2
2
  - gem install bundler
3
3
 
4
4
  rvm:
5
- - 2.3.6
6
- - 2.4.3
7
- - 2.5.0
5
+ - 2.6.10
6
+ - 2.7.6
7
+ - 3.0.4
8
+ - 3.1.2
data/README.rdoc CHANGED
@@ -28,17 +28,11 @@ http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01
28
28
 
29
29
  === Bearer
30
30
 
31
- Running on Heroku
32
- https://rack-oauth2-sample.heroku.com
33
-
34
31
  Source on GitHub
35
32
  https://github.com/nov/rack-oauth2-sample
36
33
 
37
34
  === MAC
38
35
 
39
- Running on Heroku
40
- https://rack-oauth2-sample-mac.heroku.com
41
-
42
36
  Source on GitHub
43
37
  https://github.com/nov/rack-oauth2-sample-mac
44
38
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.12.0
1
+ 1.21.3
@@ -3,7 +3,7 @@ module Rack
3
3
  class Client
4
4
  include AttrRequired, AttrOptional
5
5
  attr_required :identifier
6
- attr_optional :secret, :private_key, :certificate, :redirect_uri, :scheme, :host, :port, :authorization_endpoint, :token_endpoint
6
+ attr_optional :secret, :private_key, :certificate, :redirect_uri, :scheme, :host, :port, :authorization_endpoint, :token_endpoint, :revocation_endpoint
7
7
 
8
8
  def initialize(attributes = {})
9
9
  (required_attributes + optional_attributes).each do |key|
@@ -16,12 +16,12 @@ module Rack
16
16
  end
17
17
 
18
18
  def authorization_uri(params = {})
19
+ params[:redirect_uri] ||= self.redirect_uri
19
20
  params[:response_type] ||= :code
20
21
  params[:response_type] = Array(params[:response_type]).join(' ')
21
22
  params[:scope] = Array(params[:scope]).join(' ')
22
23
  Util.redirect_uri absolute_uri_for(authorization_endpoint), :query, params.merge(
23
- client_id: self.identifier,
24
- redirect_uri: self.redirect_uri
24
+ client_id: self.identifier
25
25
  )
26
26
  end
27
27
 
@@ -69,20 +69,83 @@ module Rack
69
69
  end
70
70
 
71
71
  def access_token!(*args)
72
- headers, params = {}, @grant.as_json
72
+ headers, params, http_client, options = authenticated_context_from(*args)
73
+ params[:scope] = Array(options.delete(:scope)).join(' ') if options[:scope].present?
74
+ params.merge! @grant.as_json
75
+ params.merge! options
76
+ handle_response do
77
+ http_client.post(
78
+ absolute_uri_for(token_endpoint),
79
+ Util.compact_hash(params),
80
+ headers
81
+ )
82
+ end
83
+ end
84
+
85
+ def revoke!(*args)
86
+ headers, params, http_client, options = authenticated_context_from(*args)
87
+
88
+ params.merge! case
89
+ when access_token = options.delete(:access_token)
90
+ {
91
+ token: access_token,
92
+ token_type_hint: :access_token
93
+ }
94
+ when refresh_token = options.delete(:refresh_token)
95
+ {
96
+ token: refresh_token,
97
+ token_type_hint: :refresh_token
98
+ }
99
+ when @grant.is_a?(Grant::RefreshToken)
100
+ {
101
+ token: @grant.refresh_token,
102
+ token_type_hint: :refresh_token
103
+ }
104
+ when options[:token].blank?
105
+ raise ArgumentError, 'One of "token", "access_token" and "refresh_token" is required'
106
+ end
107
+ params.merge! options
108
+
109
+ handle_revocation_response do
110
+ http_client.post(
111
+ absolute_uri_for(revocation_endpoint),
112
+ Util.compact_hash(params),
113
+ headers
114
+ )
115
+ end
116
+ end
117
+
118
+ private
119
+
120
+ def absolute_uri_for(endpoint)
121
+ _endpoint_ = Util.parse_uri endpoint
122
+ _endpoint_.scheme ||= self.scheme || 'https'
123
+ _endpoint_.host ||= self.host
124
+ _endpoint_.port ||= self.port
125
+ raise 'No Host Info' unless _endpoint_.host
126
+ _endpoint_.to_s
127
+ end
128
+
129
+ def authenticated_context_from(*args)
130
+ headers, params = {}, {}
73
131
  http_client = Rack::OAuth2.http_client
74
132
 
75
133
  # NOTE:
76
- # Using Array#estract_options! for backward compatibility.
134
+ # Using Array#extract_options! for backward compatibility.
77
135
  # Until v1.0.5, the first argument was 'client_auth_method' in scalar.
78
136
  options = args.extract_options!
79
- client_auth_method = args.first || options.delete(:client_auth_method) || :basic
80
-
81
- params[:scope] = Array(options.delete(:scope)).join(' ') if options[:scope].present?
82
- params.merge! options
137
+ client_auth_method = args.first || options.delete(:client_auth_method).try(:to_sym) || :basic
83
138
 
84
139
  case client_auth_method
85
140
  when :basic
141
+ cred = Base64.strict_encode64 [
142
+ Util.www_form_url_encode(identifier),
143
+ Util.www_form_url_encode(secret)
144
+ ].join(':')
145
+ headers.merge!(
146
+ 'Authorization' => "Basic #{cred}"
147
+ )
148
+ when :basic_without_www_form_urlencode
86
149
  cred = ["#{identifier}:#{secret}"].pack('m').tr("\n", '')
87
150
  headers.merge!(
88
151
  'Authorization' => "Basic #{cred}"
@@ -92,9 +155,11 @@ module Rack
92
155
  client_assertion_type: URN::ClientAssertionType::JWT_BEARER
93
156
  )
94
157
  # NOTE: optionally auto-generate client_assertion.
95
- if params[:client_assertion].blank?
158
+ params[:client_assertion] = if options[:client_assertion].present?
159
+ options.delete(:client_assertion)
160
+ else
96
161
  require 'json/jwt'
97
- params[:client_assertion] = JSON::JWT.new(
162
+ JSON::JWT.new(
98
163
  iss: identifier,
99
164
  sub: identifier,
100
165
  aud: absolute_uri_for(token_endpoint),
@@ -119,24 +184,8 @@ module Rack
119
184
  client_secret: secret
120
185
  )
121
186
  end
122
- handle_response do
123
- http_client.post(
124
- absolute_uri_for(token_endpoint),
125
- Util.compact_hash(params),
126
- headers
127
- )
128
- end
129
- end
130
-
131
- private
132
187
 
133
- def absolute_uri_for(endpoint)
134
- _endpoint_ = Util.parse_uri endpoint
135
- _endpoint_.scheme ||= self.scheme || 'https'
136
- _endpoint_.host ||= self.host
137
- _endpoint_.port ||= self.port
138
- raise 'No Host Info' unless _endpoint_.host
139
- _endpoint_.to_s
188
+ [headers, params, http_client, options]
140
189
  end
141
190
 
142
191
  def handle_response
@@ -149,6 +198,16 @@ module Rack
149
198
  end
150
199
  end
151
200
 
201
+ def handle_revocation_response
202
+ response = yield
203
+ case response.status
204
+ when 200..201
205
+ :success
206
+ else
207
+ handle_error_response response
208
+ end
209
+ end
210
+
152
211
  def handle_success_response(response)
153
212
  token_hash = JSON.parse(response.body).with_indifferent_access
154
213
  case (@forced_token_type || token_hash[:token_type]).try(:downcase)
@@ -27,7 +27,7 @@ module Rack
27
27
  response.status = status
28
28
  yield response if block_given?
29
29
  unless response.redirect?
30
- response.header['Content-Type'] = 'application/json'
30
+ response.headers['Content-Type'] = 'application/json'
31
31
  response.write Util.compact_hash(protocol_params).to_json
32
32
  end
33
33
  response.finish
@@ -42,6 +42,7 @@ module Rack
42
42
 
43
43
  class Unauthorized < Error
44
44
  def initialize(error = :unauthorized, description = nil, options = {})
45
+ @skip_www_authenticate = options[:skip_www_authenticate]
45
46
  super 401, error, description, options
46
47
  end
47
48
  end
@@ -5,7 +5,7 @@ module Rack
5
5
  module ResponseExt
6
6
  def redirect?
7
7
  ensure_finish do
8
- @response.redirect?
8
+ super
9
9
  end
10
10
  end
11
11
 
@@ -17,13 +17,13 @@ module Rack
17
17
 
18
18
  def json
19
19
  ensure_finish do
20
- @response.body
20
+ @body
21
21
  end
22
22
  end
23
23
 
24
- def header
24
+ def headers
25
25
  ensure_finish do
26
- @header
26
+ @headers
27
27
  end
28
28
  end
29
29
 
@@ -39,7 +39,7 @@ module Rack
39
39
  end
40
40
 
41
41
  def ensure_finish
42
- @status, @header, @response = finish unless finished?
42
+ @status, @headers, @body = finish unless finished?
43
43
  yield
44
44
  end
45
45
  end
@@ -13,11 +13,11 @@ module Rack
13
13
  def finish
14
14
  super do |response|
15
15
  self.realm ||= DEFAULT_REALM
16
- header = response.header['WWW-Authenticate'] = "#{scheme} realm=\"#{realm}\""
16
+ headers = response.headers['WWW-Authenticate'] = "#{scheme} realm=\"#{realm}\""
17
17
  if ErrorMethods::DEFAULT_DESCRIPTION.keys.include?(error)
18
- header << ", error=\"#{error}\""
19
- header << ", error_description=\"#{description}\"" if description.present?
20
- header << ", error_uri=\"#{uri}\"" if uri.present?
18
+ headers << ", error=\"#{error}\""
19
+ headers << ", error_description=\"#{description}\"" if description.present?
20
+ headers << ", error_uri=\"#{uri}\"" if uri.present?
21
21
  end
22
22
  end
23
23
  end
@@ -8,7 +8,9 @@ module Rack
8
8
  class Unauthorized < Abstract::Unauthorized
9
9
  def finish
10
10
  super do |response|
11
- response.header['WWW-Authenticate'] = 'Basic realm="OAuth2 Token Endpoint"'
11
+ unless @skip_www_authenticate
12
+ response.headers['WWW-Authenticate'] = 'Basic realm="OAuth2 Token Endpoint"'
13
+ end
12
14
  end
13
15
  end
14
16
  end
@@ -44,16 +44,27 @@ module Rack
44
44
 
45
45
  class Request < Abstract::Request
46
46
  attr_required :grant_type
47
- attr_optional :client_secret
47
+ attr_optional :client_secret, :client_assertion, :client_assertion_type
48
48
 
49
49
  def initialize(env)
50
50
  auth = Rack::Auth::Basic::Request.new(env)
51
51
  if auth.provided? && auth.basic?
52
- @client_id, @client_secret = auth.credentials
52
+ @client_id, @client_secret = auth.credentials.map do |cred|
53
+ Util.www_form_url_decode cred
54
+ end
53
55
  super
54
56
  else
55
57
  super
56
58
  @client_secret = params['client_secret']
59
+ @client_assertion = params['client_assertion']
60
+ @client_assertion_type = params['client_assertion_type']
61
+ if client_assertion.present? && client_assertion_type == URN::ClientAssertionType::JWT_BEARER
62
+ require 'json/jwt'
63
+ @client_id = JSON::JWT.decode(
64
+ client_assertion,
65
+ :skip_verification
66
+ )[:sub] rescue nil
67
+ end
57
68
  end
58
69
  @grant_type = params['grant_type'].to_s
59
70
  end
@@ -69,9 +80,9 @@ module Rack
69
80
  def finish
70
81
  attr_missing!
71
82
  write Util.compact_hash(protocol_params).to_json
72
- header['Content-Type'] = 'application/json'
73
- header['Cache-Control'] = 'no-store'
74
- header['Pragma'] = 'no-cache'
83
+ headers['Content-Type'] = 'application/json'
84
+ headers['Cache-Control'] = 'no-store'
85
+ headers['Pragma'] = 'no-cache'
75
86
  super
76
87
  end
77
88
  end
@@ -3,14 +3,14 @@ module Rack
3
3
  module URN
4
4
  module TokenType
5
5
  JWT = 'urn:ietf:params:oauth:token-type:jwt' # RFC7519
6
- ACCESS_TOKEN = 'urn:ietf:params:oauth:token-type:access-token' # draft-ietf-oauth-token-exchange
7
- REFRESH_TOKEN = 'urn:ietf:params:oauth:token-type:refresh-token' # draft-ietf-oauth-token-exchange
6
+ ACCESS_TOKEN = 'urn:ietf:params:oauth:token-type:access_token' # RFC8693
7
+ REFRESH_TOKEN = 'urn:ietf:params:oauth:token-type:refresh_token' # RFC8693
8
8
  end
9
9
 
10
10
  module GrantType
11
11
  JWT_BEARER = 'urn:ietf:params:oauth:grant-type:jwt-bearer' # RFC7523
12
12
  SAML2_BEARER = 'urn:ietf:params:oauth:grant-type:saml2-bearer' # RFC7522
13
- TOKEN_EXCHANGE = 'urn:ietf:params:oauth:grant-type:token-exchange' # draft-ietf-oauth-token-exchange
13
+ TOKEN_EXCHANGE = 'urn:ietf:params:oauth:grant-type:token-exchange' # RFC8693
14
14
  end
15
15
 
16
16
  module ClientAssertionType
@@ -4,8 +4,12 @@ module Rack
4
4
  module OAuth2
5
5
  module Util
6
6
  class << self
7
- def rfc3986_encode(text)
8
- URI.encode(text, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
7
+ def www_form_url_encode(text)
8
+ URI.encode_www_form_component(text)
9
+ end
10
+
11
+ def www_form_url_decode(text)
12
+ URI.decode_www_form_component(text)
9
13
  end
10
14
 
11
15
  def base64_encode(text)
data/lib/rack/oauth2.rb CHANGED
@@ -43,6 +43,11 @@ module Rack
43
43
  _http_client_ = HTTPClient.new(
44
44
  agent_name: agent_name
45
45
  )
46
+
47
+ # NOTE: httpclient gem seems stopped maintaining root certtificate set, use OS default.
48
+ _http_client_.ssl_config.clear_cert_store
49
+ _http_client_.ssl_config.cert_store.set_default_paths
50
+
46
51
  http_config.try(:call, _http_client_)
47
52
  local_http_config.try(:call, _http_client_) unless local_http_config.nil?
48
53
  _http_client_.request_filter << Debugger::RequestFilter.new if debugging?
data/rack-oauth2.gemspec CHANGED
@@ -7,13 +7,13 @@ Gem::Specification.new do |s|
7
7
  s.email = 'nov@matake.jp'
8
8
  s.extra_rdoc_files = ['LICENSE', 'README.rdoc']
9
9
  s.rdoc_options = ['--charset=UTF-8']
10
- s.homepage = 'http://github.com/nov/rack-oauth2'
10
+ s.homepage = 'https://github.com/nov/rack-oauth2'
11
11
  s.license = 'MIT'
12
12
  s.require_paths = ['lib']
13
13
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
14
  s.files = `git ls-files`.split("\n")
15
15
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
- s.add_runtime_dependency 'rack', '< 2.1'
16
+ s.add_runtime_dependency 'rack', '>= 2.1.0'
17
17
  s.add_runtime_dependency 'httpclient'
18
18
  s.add_runtime_dependency 'activesupport'
19
19
  s.add_runtime_dependency 'attr_required'
@@ -23,4 +23,5 @@ Gem::Specification.new do |s|
23
23
  s.add_development_dependency 'rspec'
24
24
  s.add_development_dependency 'rspec-its'
25
25
  s.add_development_dependency 'webmock'
26
+ s.add_development_dependency 'rexml'
26
27
  end
@@ -1,12 +1,15 @@
1
1
  require 'spec_helper.rb'
2
2
 
3
3
  describe Rack::OAuth2::Client do
4
+ let(:client_id) { 'client_id' }
5
+ let(:client_secret) { 'client_secret' }
4
6
  let :client do
5
7
  Rack::OAuth2::Client.new(
6
- identifier: 'client_id',
7
- secret: 'client_secret',
8
+ identifier: client_id,
9
+ secret: client_secret,
8
10
  host: 'server.example.com',
9
- redirect_uri: 'https://client.example.com/callback'
11
+ redirect_uri: 'https://client.example.com/callback',
12
+ revocation_endpoint: '/oauth2/revoke'
10
13
  )
11
14
  end
12
15
  subject { client }
@@ -15,6 +18,7 @@ describe Rack::OAuth2::Client do
15
18
  its(:secret) { should == 'client_secret' }
16
19
  its(:authorization_endpoint) { should == '/oauth2/authorize' }
17
20
  its(:token_endpoint) { should == '/oauth2/token' }
21
+ its(:revocation_endpoint) { should == '/oauth2/revoke' }
18
22
 
19
23
  context 'when identifier is missing' do
20
24
  it do
@@ -97,6 +101,42 @@ describe Rack::OAuth2::Client do
97
101
  client.access_token!
98
102
  end
99
103
 
104
+ context 'when Basic auth method is used' do
105
+ context 'when client_id is a url' do
106
+ let(:client_id) { 'https://client.example.com'}
107
+
108
+ it 'should be encoded in "application/x-www-form-urlencoded"' do
109
+ mock_response(
110
+ :post,
111
+ 'https://server.example.com/oauth2/token',
112
+ 'tokens/bearer.json',
113
+ request_header: {
114
+ 'Authorization' => 'Basic aHR0cHMlM0ElMkYlMkZjbGllbnQuZXhhbXBsZS5jb206Y2xpZW50X3NlY3JldA=='
115
+ }
116
+ )
117
+ client.access_token!
118
+ end
119
+ end
120
+ end
121
+
122
+ context 'when basic_without_www_form_urlencode method is used' do
123
+ context 'when client_id is a url' do
124
+ let(:client_id) { 'https://client.example.com'}
125
+
126
+ it 'should be encoded in "application/x-www-form-urlencoded"' do
127
+ mock_response(
128
+ :post,
129
+ 'https://server.example.com/oauth2/token',
130
+ 'tokens/bearer.json',
131
+ request_header: {
132
+ 'Authorization' => 'Basic aHR0cHM6Ly9jbGllbnQuZXhhbXBsZS5jb206Y2xpZW50X3NlY3JldA=='
133
+ }
134
+ )
135
+ client.access_token! :basic_without_www_form_urlencode
136
+ end
137
+ end
138
+ end
139
+
100
140
  context 'when jwt_bearer auth method specified' do
101
141
  context 'when client_secret is given' do
102
142
  it 'should be JWT bearer client assertion w/ auto-generated HS256-signed JWT assertion' do
@@ -148,7 +188,7 @@ describe Rack::OAuth2::Client do
148
188
  let :client do
149
189
  Rack::OAuth2::Client.new(
150
190
  identifier: 'client_id',
151
- private_key: OpenSSL::PKey::EC.new('prime256v1').generate_key,
191
+ private_key: OpenSSL::PKey::EC.generate('prime256v1'),
152
192
  host: 'server.example.com',
153
193
  redirect_uri: 'https://client.example.com/callback'
154
194
  )
@@ -408,12 +448,86 @@ describe Rack::OAuth2::Client do
408
448
  end
409
449
  end
410
450
 
451
+ describe '#revoke!' do
452
+ context 'when access_token given' do
453
+ before do
454
+ mock_response(
455
+ :post,
456
+ 'https://server.example.com/oauth2/revoke',
457
+ 'blank',
458
+ status: 200,
459
+ body: {
460
+ token: 'access_token',
461
+ token_type_hint: 'access_token'
462
+ }
463
+ )
464
+ end
465
+ it do
466
+ client.revoke!(access_token: 'access_token').should == :success
467
+ end
468
+ end
469
+
470
+ context 'when refresh_token given' do
471
+ before do
472
+ mock_response(
473
+ :post,
474
+ 'https://server.example.com/oauth2/revoke',
475
+ 'blank',
476
+ status: 200,
477
+ body: {
478
+ token: 'refresh_token',
479
+ token_type_hint: 'refresh_token'
480
+ }
481
+ )
482
+ end
483
+
484
+ context 'as argument' do
485
+ it do
486
+ client.revoke!(refresh_token: 'refresh_token').should == :success
487
+ end
488
+ end
489
+
490
+ context 'as grant' do
491
+ it do
492
+ client.refresh_token = 'refresh_token'
493
+ client.revoke!
494
+ end
495
+ end
496
+ end
497
+
498
+ context 'when error response given' do
499
+ before do
500
+ mock_response(
501
+ :post,
502
+ 'https://server.example.com/oauth2/revoke',
503
+ 'errors/invalid_request.json',
504
+ status: 400
505
+ )
506
+ end
507
+
508
+ it do
509
+ expect do
510
+ client.revoke! access_token: 'access_token'
511
+ end.to raise_error Rack::OAuth2::Client::Error
512
+ end
513
+ end
514
+
515
+ context 'when no token given' do
516
+ it do
517
+ expect do
518
+ client.revoke!
519
+ end.to raise_error ArgumentError
520
+ end
521
+ end
522
+ end
523
+
411
524
  context 'when no host info' do
412
525
  let :client do
413
526
  Rack::OAuth2::Client.new(
414
527
  identifier: 'client_id',
415
528
  secret: 'client_secret',
416
- redirect_uri: 'https://client.example.com/callback'
529
+ redirect_uri: 'https://client.example.com/callback',
530
+ revocation_endpoint: '/oauth2/revoke'
417
531
  )
418
532
  end
419
533
 
@@ -428,5 +542,11 @@ describe Rack::OAuth2::Client do
428
542
  expect { client.access_token! }.to raise_error 'No Host Info'
429
543
  end
430
544
  end
545
+
546
+ describe '#revoke!' do
547
+ it do
548
+ expect { client.revoke! access_token: 'access_token' }.to raise_error 'No Host Info'
549
+ end
550
+ end
431
551
  end
432
552
  end
@@ -23,27 +23,27 @@ describe Rack::OAuth2::Server::Authorize::BadRequest do
23
23
  context 'when protocol_params_location = :query' do
24
24
  before { error.protocol_params_location = :query }
25
25
  it 'should redirect with error in query' do
26
- state, header, response = error.finish
26
+ state, headers, response = error.finish
27
27
  state.should == 302
28
- header["Location"].should == "#{redirect_uri}?error=invalid_request"
28
+ headers["Location"].should == "#{redirect_uri}?error=invalid_request"
29
29
  end
30
30
  end
31
31
 
32
32
  context 'when protocol_params_location = :fragment' do
33
33
  before { error.protocol_params_location = :fragment }
34
34
  it 'should redirect with error in fragment' do
35
- state, header, response = error.finish
35
+ state, headers, response = error.finish
36
36
  state.should == 302
37
- header["Location"].should == "#{redirect_uri}#error=invalid_request"
37
+ headers["Location"].should == "#{redirect_uri}#error=invalid_request"
38
38
  end
39
39
  end
40
40
 
41
41
  context 'otherwise' do
42
42
  before { error.protocol_params_location = :other }
43
43
  it 'should redirect without error' do
44
- state, header, response = error.finish
44
+ state, headers, response = error.finish
45
45
  state.should == 302
46
- header["Location"].should == redirect_uri
46
+ headers["Location"].should == redirect_uri
47
47
  end
48
48
  end
49
49
  end
@@ -12,8 +12,8 @@ describe Rack::OAuth2::Server::Resource::Bearer::Unauthorized do
12
12
 
13
13
  describe '#finish' do
14
14
  it 'should use Bearer scheme' do
15
- status, header, response = error.finish
16
- header['WWW-Authenticate'].should include 'Bearer'
15
+ status, headers, response = error.finish
16
+ headers['WWW-Authenticate'].should include 'Bearer'
17
17
  end
18
18
  end
19
19
  end
@@ -22,29 +22,29 @@ describe Rack::OAuth2::Server::Resource::Bearer do
22
22
 
23
23
  shared_examples_for :authenticated_bearer_request do
24
24
  it 'should be authenticated' do
25
- status, header, response = request
25
+ status, headers, response = request
26
26
  status.should == 200
27
27
  access_token.should == bearer_token
28
28
  end
29
29
  end
30
30
  shared_examples_for :unauthorized_bearer_request do
31
31
  it 'should be unauthorized' do
32
- status, header, response = request
32
+ status, headers, response = request
33
33
  status.should == 401
34
- header['WWW-Authenticate'].should include 'Bearer'
34
+ headers['WWW-Authenticate'].should include 'Bearer'
35
35
  access_token.should be_nil
36
36
  end
37
37
  end
38
38
  shared_examples_for :bad_bearer_request do
39
39
  it 'should be bad_request' do
40
- status, header, response = request
40
+ status, headers, response = request
41
41
  status.should == 400
42
42
  access_token.should be_nil
43
43
  end
44
44
  end
45
45
  shared_examples_for :skipped_authentication_request do
46
46
  it 'should skip OAuth 2.0 authentication' do
47
- status, header, response = request
47
+ status, headers, response = request
48
48
  status.should == 200
49
49
  access_token.should be_nil
50
50
  end
@@ -94,15 +94,15 @@ describe Rack::OAuth2::Server::Resource::Bearer do
94
94
  end
95
95
  end
96
96
  it 'should use specified realm' do
97
- status, header, response = request
98
- header['WWW-Authenticate'].should include "Bearer realm=\"#{realm}\""
97
+ status, headers, response = request
98
+ headers['WWW-Authenticate'].should include "Bearer realm=\"#{realm}\""
99
99
  end
100
100
  end
101
101
 
102
102
  context 'otherwize' do
103
103
  it 'should use default realm' do
104
- status, header, response = request
105
- header['WWW-Authenticate'].should include "Bearer realm=\"#{Rack::OAuth2::Server::Resource::Bearer::DEFAULT_REALM}\""
104
+ status, headers, response = request
105
+ headers['WWW-Authenticate'].should include "Bearer realm=\"#{Rack::OAuth2::Server::Resource::Bearer::DEFAULT_REALM}\""
106
106
  end
107
107
  end
108
108
  end
@@ -7,10 +7,10 @@ describe Rack::OAuth2::Server::Resource::BadRequest do
7
7
 
8
8
  describe '#finish' do
9
9
  it 'should respond in JSON' do
10
- status, header, response = error.finish
10
+ status, headers, response = error.finish
11
11
  status.should == 400
12
- header['Content-Type'].should == 'application/json'
13
- response.body.should == ['{"error":"invalid_request"}']
12
+ headers['Content-Type'].should == 'application/json'
13
+ response.should == ['{"error":"invalid_request"}']
14
14
  end
15
15
  end
16
16
  end
@@ -40,20 +40,20 @@ describe Rack::OAuth2::Server::Resource::Unauthorized do
40
40
 
41
41
  describe '#finish' do
42
42
  it 'should respond in JSON' do
43
- status, header, response = error_with_scheme.finish
43
+ status, headers, response = error_with_scheme.finish
44
44
  status.should == 401
45
- header['Content-Type'].should == 'application/json'
46
- header['WWW-Authenticate'].should == "Scheme realm=\"#{realm}\", error=\"invalid_token\""
47
- response.body.should == ['{"error":"invalid_token"}']
45
+ headers['Content-Type'].should == 'application/json'
46
+ headers['WWW-Authenticate'].should == "Scheme realm=\"#{realm}\", error=\"invalid_token\""
47
+ response.should == ['{"error":"invalid_token"}']
48
48
  end
49
49
 
50
50
  context 'when error_code is not invalid_token' do
51
51
  let(:error) { Rack::OAuth2::Server::Resource::Unauthorized.new(:something) }
52
52
 
53
53
  it 'should have error_code in body but not in WWW-Authenticate header' do
54
- status, header, response = error_with_scheme.finish
55
- header['WWW-Authenticate'].should == "Scheme realm=\"#{realm}\""
56
- response.body.first.should include '"error":"something"'
54
+ status, headers, response = error_with_scheme.finish
55
+ headers['WWW-Authenticate'].should == "Scheme realm=\"#{realm}\""
56
+ response.first.should include '"error":"something"'
57
57
  end
58
58
  end
59
59
 
@@ -61,9 +61,9 @@ describe Rack::OAuth2::Server::Resource::Unauthorized do
61
61
  let(:error) { Rack::OAuth2::Server::Resource::Unauthorized.new }
62
62
 
63
63
  it 'should have error_code in body but not in WWW-Authenticate header' do
64
- status, header, response = error_with_scheme.finish
65
- header['WWW-Authenticate'].should == "Scheme realm=\"#{realm}\""
66
- response.body.first.should == '{"error":"unauthorized"}'
64
+ status, headers, response = error_with_scheme.finish
65
+ headers['WWW-Authenticate'].should == "Scheme realm=\"#{realm}\""
66
+ response.first.should == '{"error":"unauthorized"}'
67
67
  end
68
68
  end
69
69
 
@@ -72,9 +72,9 @@ describe Rack::OAuth2::Server::Resource::Unauthorized do
72
72
  let(:error) { Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(:something, nil, realm: realm) }
73
73
 
74
74
  it 'should use given realm' do
75
- status, header, response = error_with_scheme.finish
76
- header['WWW-Authenticate'].should == "Scheme realm=\"#{realm}\""
77
- response.body.first.should include '"error":"something"'
75
+ status, headers, response = error_with_scheme.finish
76
+ headers['WWW-Authenticate'].should == "Scheme realm=\"#{realm}\""
77
+ response.first.should include '"error":"something"'
78
78
  end
79
79
  end
80
80
  end
@@ -88,10 +88,10 @@ describe Rack::OAuth2::Server::Resource::Forbidden do
88
88
 
89
89
  describe '#finish' do
90
90
  it 'should respond in JSON' do
91
- status, header, response = error.finish
91
+ status, headers, response = error.finish
92
92
  status.should == 403
93
- header['Content-Type'].should == 'application/json'
94
- response.body.should == ['{"error":"insufficient_scope"}']
93
+ headers['Content-Type'].should == 'application/json'
94
+ response.should == ['{"error":"insufficient_scope"}']
95
95
  end
96
96
  end
97
97
 
@@ -99,8 +99,8 @@ describe Rack::OAuth2::Server::Resource::Forbidden do
99
99
  let(:error) { Rack::OAuth2::Server::Resource::Bearer::Forbidden.new(:insufficient_scope, 'Desc', scope: [:scope1, :scope2]) }
100
100
 
101
101
  it 'should have blank WWW-Authenticate header' do
102
- status, header, response = error.finish
103
- response.body.first.should include '"scope":"scope1 scope2"'
102
+ status, headers, response = error.finish
103
+ response.first.should include '"scope":"scope1 scope2"'
104
104
  end
105
105
  end
106
106
  end
@@ -12,8 +12,8 @@ describe Rack::OAuth2::Server::Resource::MAC::Unauthorized do
12
12
 
13
13
  describe '#finish' do
14
14
  it 'should use MAC scheme' do
15
- status, header, response = error.finish
16
- header['WWW-Authenticate'].should =~ /^MAC /
15
+ status, headers, response = error.finish
16
+ headers['WWW-Authenticate'].should =~ /^MAC /
17
17
  end
18
18
  end
19
19
  end
@@ -29,29 +29,29 @@ describe Rack::OAuth2::Server::Resource::MAC do
29
29
 
30
30
  shared_examples_for :non_mac_request do
31
31
  it 'should skip OAuth 2.0 authentication' do
32
- status, header, response = request
32
+ status, headers, response = request
33
33
  status.should == 200
34
34
  access_token.should be_nil
35
35
  end
36
36
  end
37
37
  shared_examples_for :authenticated_mac_request do
38
38
  it 'should be authenticated' do
39
- status, header, response = request
39
+ status, headers, response = request
40
40
  status.should == 200
41
41
  access_token.should == mac_token
42
42
  end
43
43
  end
44
44
  shared_examples_for :unauthorized_mac_request do
45
45
  it 'should be unauthorized' do
46
- status, header, response = request
46
+ status, headers, response = request
47
47
  status.should == 401
48
- header['WWW-Authenticate'].should include 'MAC'
48
+ headers['WWW-Authenticate'].should include 'MAC'
49
49
  access_token.should be_nil
50
50
  end
51
51
  end
52
52
  shared_examples_for :bad_mac_request do
53
53
  it 'should be unauthorized' do
54
- status, header, response = request
54
+ status, headers, response = request
55
55
  status.should == 400
56
56
  access_token.should be_nil
57
57
  end
@@ -60,7 +60,7 @@ describe Rack::OAuth2::Server::Resource::MAC do
60
60
  context 'when no access token is given' do
61
61
  let(:env) { Rack::MockRequest.env_for('/protected_resource') }
62
62
  it 'should skip OAuth 2.0 authentication' do
63
- status, header, response = request
63
+ status, headers, response = request
64
64
  status.should == 200
65
65
  access_token.should be_nil
66
66
  end
@@ -103,15 +103,15 @@ describe Rack::OAuth2::Server::Resource::MAC do
103
103
  end
104
104
  end
105
105
  it 'should use specified realm' do
106
- status, header, response = request
107
- header['WWW-Authenticate'].should include "MAC realm=\"#{realm}\""
106
+ status, headers, response = request
107
+ headers['WWW-Authenticate'].should include "MAC realm=\"#{realm}\""
108
108
  end
109
109
  end
110
110
 
111
111
  context 'otherwize' do
112
112
  it 'should use default realm' do
113
- status, header, response = request
114
- header['WWW-Authenticate'].should include "MAC realm=\"#{Rack::OAuth2::Server::Resource::DEFAULT_REALM}\""
113
+ status, headers, response = request
114
+ headers['WWW-Authenticate'].should include "MAC realm=\"#{Rack::OAuth2::Server::Resource::DEFAULT_REALM}\""
115
115
  end
116
116
  end
117
117
  end
@@ -24,8 +24,8 @@ describe Rack::OAuth2::Server::Token::AuthorizationCode do
24
24
  its(:body) { should include '"token_type":"bearer"' }
25
25
 
26
26
  it 'should prevent to be cached' do
27
- response.header['Cache-Control'].should == 'no-store'
28
- response.header['Pragma'].should == 'no-cache'
27
+ response.headers['Cache-Control'].should == 'no-store'
28
+ response.headers['Pragma'].should == 'no-cache'
29
29
  end
30
30
 
31
31
  [:code].each do |required|
@@ -4,14 +4,19 @@ describe Rack::OAuth2::Server::Token::ClientCredentials do
4
4
  let(:request) { Rack::MockRequest.new app }
5
5
  let(:app) do
6
6
  Rack::OAuth2::Server::Token.new do |request, response|
7
+ unless request.client_id == client_id && request.client_secret == client_secret
8
+ request.invalid_client!
9
+ end
7
10
  response.access_token = Rack::OAuth2::AccessToken::Bearer.new(access_token: 'access_token')
8
11
  end
9
12
  end
13
+ let(:client_id) { 'client_id '}
14
+ let(:client_secret) { 'client_secret' }
10
15
  let(:params) do
11
16
  {
12
17
  grant_type: 'client_credentials',
13
- client_id: 'client_id',
14
- client_secret: 'client_secret'
18
+ client_id: client_id,
19
+ client_secret: client_secret
15
20
  }
16
21
  end
17
22
  subject { request.post('/', params: params) }
@@ -20,4 +25,29 @@ describe Rack::OAuth2::Server::Token::ClientCredentials do
20
25
  its(:content_type) { should == 'application/json' }
21
26
  its(:body) { should include '"access_token":"access_token"' }
22
27
  its(:body) { should include '"token_type":"bearer"' }
28
+
29
+ context 'basic auth' do
30
+ let(:params) do
31
+ { grant_type: 'client_credentials' }
32
+ end
33
+ let(:encoded_creds) do
34
+ Base64.strict_encode64([
35
+ Rack::OAuth2::Util.www_form_url_encode(client_id),
36
+ Rack::OAuth2::Util.www_form_url_encode(client_secret)
37
+ ].join(':'))
38
+ end
39
+ subject do
40
+ request.post('/',
41
+ {params: params, 'HTTP_AUTHORIZATION' => "Basic #{encoded_creds}"})
42
+ end
43
+
44
+ its(:status) { should == 200 }
45
+
46
+ context 'compliance with RFC6749 sec 2.3.1' do
47
+ let(:client_id) { 'client: yes/please!' }
48
+ let(:client_secret) { 'terrible:secret:of:space' }
49
+
50
+ its(:status) { should == 200 }
51
+ end
52
+ end
23
53
  end
@@ -7,10 +7,10 @@ describe Rack::OAuth2::Server::Token::BadRequest do
7
7
 
8
8
  describe '#finish' do
9
9
  it 'should respond in JSON' do
10
- status, header, response = error.finish
10
+ status, headers, response = error.finish
11
11
  status.should == 400
12
- header['Content-Type'].should == 'application/json'
13
- response.body.should == ['{"error":"invalid_request"}']
12
+ headers['Content-Type'].should == 'application/json'
13
+ response.should == ['{"error":"invalid_request"}']
14
14
  end
15
15
  end
16
16
  end
@@ -22,11 +22,11 @@ describe Rack::OAuth2::Server::Token::Unauthorized do
22
22
 
23
23
  describe '#finish' do
24
24
  it 'should respond in JSON' do
25
- status, header, response = error.finish
25
+ status, headers, response = error.finish
26
26
  status.should == 401
27
- header['Content-Type'].should == 'application/json'
28
- header['WWW-Authenticate'].should == 'Basic realm="OAuth2 Token Endpoint"'
29
- response.body.should == ['{"error":"invalid_request"}']
27
+ headers['Content-Type'].should == 'application/json'
28
+ headers['WWW-Authenticate'].should == 'Basic realm="OAuth2 Token Endpoint"'
29
+ response.should == ['{"error":"invalid_request"}']
30
30
  end
31
31
  end
32
32
  end
@@ -74,4 +74,4 @@ describe Rack::OAuth2::Server::Token::ErrorMethods do
74
74
  end
75
75
  end
76
76
  end
77
- end
77
+ end
@@ -28,9 +28,9 @@ describe Rack::OAuth2::Server::Token do
28
28
  )
29
29
  end
30
30
  it 'should fail with unsupported_grant_type' do
31
- status, header, response = app.call(env)
31
+ status, headers, response = app.call(env)
32
32
  status.should == 400
33
- response.body.first.should include '"error":"invalid_request"'
33
+ response.first.should include '"error":"invalid_request"'
34
34
  end
35
35
  end
36
36
 
@@ -43,7 +43,7 @@ describe Rack::OAuth2::Server::Token do
43
43
  )
44
44
  end
45
45
  it 'should ignore duplicates' do
46
- status, header, response = app.call(env)
46
+ status, headers, response = app.call(env)
47
47
  status.should == 200
48
48
  end
49
49
  end
@@ -71,6 +71,60 @@ describe Rack::OAuth2::Server::Token do
71
71
  end
72
72
  end
73
73
 
74
+ context 'when client_id is given via JWT client assertion' do
75
+ before do
76
+ require 'json/jwt'
77
+ params[:client_assertion] = JSON::JWT.new(
78
+ sub: params[:client_id]
79
+ # NOTE: actual client_assertion should have more claims.
80
+ ).sign('client_secret').to_s
81
+ params[:client_assertion_type] = Rack::OAuth2::URN::ClientAssertionType::JWT_BEARER
82
+ params.delete(:client_id)
83
+ end
84
+
85
+ context 'when client_assertion is invalid JWT' do
86
+ before do
87
+ params[:client_assertion] = 'invalid-jwt'
88
+ end
89
+ its(:status) { should == 400 }
90
+ its(:content_type) { should == 'application/json' }
91
+ its(:body) { should include '"error":"invalid_request"' }
92
+ end
93
+
94
+ context 'when client_assertion_type is missing' do
95
+ before do
96
+ params.delete(:client_assertion_type)
97
+ end
98
+ its(:status) { should == 400 }
99
+ its(:content_type) { should == 'application/json' }
100
+ its(:body) { should include '"error":"invalid_request"' }
101
+ end
102
+
103
+ context 'when client_assertion_type is unknown' do
104
+ before do
105
+ params[:client_assertion_type] = 'unknown'
106
+ end
107
+ its(:status) { should == 400 }
108
+ its(:content_type) { should == 'application/json' }
109
+ its(:body) { should include '"error":"invalid_request"' }
110
+ end
111
+
112
+ context 'when client_assertion issuer is different from client_id' do
113
+ before do
114
+ params[:client_id] = 'another_client_id'
115
+ end
116
+ its(:status) { should == 400 }
117
+ its(:content_type) { should == 'application/json' }
118
+ its(:body) { should include '"error":"invalid_request"' }
119
+ end
120
+
121
+ context 'otherwise' do
122
+ its(:status) { should == 200 }
123
+ its(:content_type) { should == 'application/json' }
124
+ its(:body) { should include '"access_token":"access_token"' }
125
+ end
126
+ end
127
+
74
128
  Rack::OAuth2::Server::Token::ErrorMethods::DEFAULT_DESCRIPTION.each do |error, default_message|
75
129
  status = if error == :invalid_client
76
130
  401
@@ -87,7 +141,22 @@ describe Rack::OAuth2::Server::Token do
87
141
  its(:content_type) { should == 'application/json' }
88
142
  its(:body) { should include "\"error\":\"#{error}\"" }
89
143
  its(:body) { should include "\"error_description\":\"#{default_message}\"" }
144
+ if error == :invalid_client
145
+ its(:headers) { should include 'WWW-Authenticate' }
146
+ end
147
+ end
148
+ end
149
+
150
+ context 'when skip_www_authenticate option is specified on invalid_client' do
151
+ let(:app) do
152
+ Rack::OAuth2::Server::Token.new do |request, response|
153
+ request.invalid_client!(
154
+ Rack::OAuth2::Server::Token::ErrorMethods::DEFAULT_DESCRIPTION[:invalid_client],
155
+ skip_www_authenticate: true
156
+ )
157
+ end
90
158
  end
159
+ its(:headers) { should_not include 'WWW-Authenticate' }
91
160
  end
92
161
 
93
162
  context 'when responding' do
@@ -9,9 +9,14 @@ describe Rack::OAuth2::Util do
9
9
  'http://client.example.com/callback'
10
10
  end
11
11
 
12
- describe '.rfc3986_encode' do
13
- subject { util.rfc3986_encode '=+ .-/' }
14
- it { should == '%3D%2B%20.-%2F' }
12
+ describe '.www_form_url_encode' do
13
+ subject { util.www_form_url_encode '=+ .-/' }
14
+ it { should == '%3D%2B+.-%2F' }
15
+ end
16
+
17
+ describe '.www_form_urldecode' do
18
+ subject { util.www_form_url_decode '%3D%2B+.-%2F' }
19
+ it { should == '=+ .-/' }
15
20
  end
16
21
 
17
22
  describe '.base64_encode' do
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-oauth2
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.0
4
+ version: 1.21.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - nov matake
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-25 00:00:00.000000000 Z
11
+ date: 2022-09-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "<"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '2.1'
19
+ version: 2.1.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "<"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '2.1'
26
+ version: 2.1.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: httpclient
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -150,6 +150,20 @@ dependencies:
150
150
  - - ">="
151
151
  - !ruby/object:Gem::Version
152
152
  version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rexml
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
153
167
  description: OAuth 2.0 Server & Client Library. Both Bearer and MAC token type are
154
168
  supported.
155
169
  email: nov@matake.jp
@@ -160,6 +174,8 @@ extra_rdoc_files:
160
174
  - README.rdoc
161
175
  files:
162
176
  - ".document"
177
+ - ".github/FUNDING.yml"
178
+ - ".github/workflows/spec.yml"
163
179
  - ".gitignore"
164
180
  - ".rspec"
165
181
  - ".travis.yml"
@@ -281,11 +297,11 @@ files:
281
297
  - spec/rack/oauth2/server/token_spec.rb
282
298
  - spec/rack/oauth2/util_spec.rb
283
299
  - spec/spec_helper.rb
284
- homepage: http://github.com/nov/rack-oauth2
300
+ homepage: https://github.com/nov/rack-oauth2
285
301
  licenses:
286
302
  - MIT
287
303
  metadata: {}
288
- post_install_message:
304
+ post_install_message:
289
305
  rdoc_options:
290
306
  - "--charset=UTF-8"
291
307
  require_paths:
@@ -301,8 +317,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
301
317
  - !ruby/object:Gem::Version
302
318
  version: '0'
303
319
  requirements: []
304
- rubygems_version: 3.0.3
305
- signing_key:
320
+ rubygems_version: 3.3.7
321
+ signing_key:
306
322
  specification_version: 4
307
323
  summary: OAuth 2.0 Server & Client Library - Both Bearer and MAC token type are supported
308
324
  test_files: