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 +4 -4
- data/.github/FUNDING.yml +3 -0
- data/.github/workflows/spec.yml +30 -0
- data/.travis.yml +4 -3
- data/README.rdoc +0 -6
- data/VERSION +1 -1
- data/lib/rack/oauth2/client.rb +87 -28
- data/lib/rack/oauth2/server/abstract/error.rb +2 -1
- data/lib/rack/oauth2/server/rails/response_ext.rb +5 -5
- data/lib/rack/oauth2/server/resource/error.rb +4 -4
- data/lib/rack/oauth2/server/token/error.rb +3 -1
- data/lib/rack/oauth2/server/token.rb +16 -5
- data/lib/rack/oauth2/urn.rb +3 -3
- data/lib/rack/oauth2/util.rb +6 -2
- data/lib/rack/oauth2.rb +5 -0
- data/rack-oauth2.gemspec +3 -2
- data/spec/rack/oauth2/client_spec.rb +125 -5
- data/spec/rack/oauth2/server/authorize/error_spec.rb +6 -6
- data/spec/rack/oauth2/server/resource/bearer/error_spec.rb +2 -2
- data/spec/rack/oauth2/server/resource/bearer_spec.rb +9 -9
- data/spec/rack/oauth2/server/resource/error_spec.rb +21 -21
- data/spec/rack/oauth2/server/resource/mac/error_spec.rb +2 -2
- data/spec/rack/oauth2/server/resource/mac_spec.rb +10 -10
- data/spec/rack/oauth2/server/token/authorization_code_spec.rb +2 -2
- data/spec/rack/oauth2/server/token/client_credentials_spec.rb +32 -2
- data/spec/rack/oauth2/server/token/error_spec.rb +8 -8
- data/spec/rack/oauth2/server/token_spec.rb +72 -3
- data/spec/rack/oauth2/util_spec.rb +8 -3
- metadata +27 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7303cf85e66a7fb4a89d66d95b4ad35720ecb95459f9740208328314ea54b157
|
4
|
+
data.tar.gz: 061a4a30cbb25212979a37f26e18043cbf71dead3e36981b37f6152fc6899cfd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5fbabf81d770e80f02614d3b00b0fd9db8a63ed695a5b67b74266eee1f09ec6e7045db009ea7e6ee09af84680699809032ecc64d58caee48305573cd3532b5be
|
7
|
+
data.tar.gz: 5bc8cdbdddb9a997560eab574a955ab69d3ad8f9e594554a45d17e077991c2551382c917363c1c09db349abf262f5d9c15a7cfb13c24e56fe27d83cbde62f0f3
|
data/.github/FUNDING.yml
ADDED
@@ -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
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.
|
1
|
+
1.21.3
|
data/lib/rack/oauth2/client.rb
CHANGED
@@ -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 =
|
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#
|
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
|
158
|
+
params[:client_assertion] = if options[:client_assertion].present?
|
159
|
+
options.delete(:client_assertion)
|
160
|
+
else
|
96
161
|
require 'json/jwt'
|
97
|
-
|
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
|
-
|
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.
|
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
|
-
|
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
|
-
@
|
20
|
+
@body
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
24
|
+
def headers
|
25
25
|
ensure_finish do
|
26
|
-
@
|
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, @
|
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
|
-
|
16
|
+
headers = response.headers['WWW-Authenticate'] = "#{scheme} realm=\"#{realm}\""
|
17
17
|
if ErrorMethods::DEFAULT_DESCRIPTION.keys.include?(error)
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
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
|
data/lib/rack/oauth2/urn.rb
CHANGED
@@ -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:
|
7
|
-
REFRESH_TOKEN = 'urn:ietf:params:oauth:token-type:
|
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' #
|
13
|
+
TOKEN_EXCHANGE = 'urn:ietf:params:oauth:grant-type:token-exchange' # RFC8693
|
14
14
|
end
|
15
15
|
|
16
16
|
module ClientAssertionType
|
data/lib/rack/oauth2/util.rb
CHANGED
@@ -4,8 +4,12 @@ module Rack
|
|
4
4
|
module OAuth2
|
5
5
|
module Util
|
6
6
|
class << self
|
7
|
-
def
|
8
|
-
URI.
|
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 = '
|
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', '
|
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:
|
7
|
-
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.
|
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,
|
26
|
+
state, headers, response = error.finish
|
27
27
|
state.should == 302
|
28
|
-
|
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,
|
35
|
+
state, headers, response = error.finish
|
36
36
|
state.should == 302
|
37
|
-
|
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,
|
44
|
+
state, headers, response = error.finish
|
45
45
|
state.should == 302
|
46
|
-
|
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,
|
16
|
-
|
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,
|
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,
|
32
|
+
status, headers, response = request
|
33
33
|
status.should == 401
|
34
|
-
|
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,
|
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,
|
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,
|
98
|
-
|
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,
|
105
|
-
|
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,
|
10
|
+
status, headers, response = error.finish
|
11
11
|
status.should == 400
|
12
|
-
|
13
|
-
response.
|
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,
|
43
|
+
status, headers, response = error_with_scheme.finish
|
44
44
|
status.should == 401
|
45
|
-
|
46
|
-
|
47
|
-
response.
|
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,
|
55
|
-
|
56
|
-
response.
|
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,
|
65
|
-
|
66
|
-
response.
|
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,
|
76
|
-
|
77
|
-
response.
|
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,
|
91
|
+
status, headers, response = error.finish
|
92
92
|
status.should == 403
|
93
|
-
|
94
|
-
response.
|
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,
|
103
|
-
response.
|
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,
|
16
|
-
|
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,
|
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,
|
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,
|
46
|
+
status, headers, response = request
|
47
47
|
status.should == 401
|
48
|
-
|
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,
|
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,
|
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,
|
107
|
-
|
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,
|
114
|
-
|
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.
|
28
|
-
response.
|
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:
|
14
|
-
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,
|
10
|
+
status, headers, response = error.finish
|
11
11
|
status.should == 400
|
12
|
-
|
13
|
-
response.
|
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,
|
25
|
+
status, headers, response = error.finish
|
26
26
|
status.should == 401
|
27
|
-
|
28
|
-
|
29
|
-
response.
|
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,
|
31
|
+
status, headers, response = app.call(env)
|
32
32
|
status.should == 400
|
33
|
-
response.
|
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,
|
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 '.
|
13
|
-
subject { util.
|
14
|
-
it { should == '%3D%2B
|
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.
|
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:
|
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:
|
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:
|
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:
|
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.
|
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:
|