rack-oauth2 1.10.1 → 1.19.0
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/.travis.yml +4 -3
- data/README.rdoc +0 -6
- data/VERSION +1 -1
- data/lib/rack/oauth2/client.rb +12 -4
- data/lib/rack/oauth2/server/abstract/error.rb +1 -0
- data/lib/rack/oauth2/server/rails/response_ext.rb +3 -3
- data/lib/rack/oauth2/server/token/error.rb +3 -1
- data/lib/rack/oauth2/server/token.rb +13 -2
- data/lib/rack/oauth2/urn.rb +3 -3
- data/lib/rack/oauth2/util.rb +10 -5
- data/lib/rack/oauth2.rb +5 -0
- data/rack-oauth2.gemspec +1 -1
- data/spec/rack/oauth2/client_spec.rb +42 -4
- data/spec/rack/oauth2/server/authorize/code_spec.rb +1 -1
- data/spec/rack/oauth2/server/authorize/extensions/code_and_token_spec.rb +1 -1
- data/spec/rack/oauth2/server/authorize/token_spec.rb +1 -1
- data/spec/rack/oauth2/server/resource/error_spec.rb +7 -7
- data/spec/rack/oauth2/server/token/client_credentials_spec.rb +32 -2
- data/spec/rack/oauth2/server/token/error_spec.rb +3 -3
- data/spec/rack/oauth2/server/token_spec.rb +70 -1
- data/spec/rack/oauth2/util_spec.rb +8 -3
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de77a3afabd5ae9ec958c5799970a99a5576cb727b27c80020d1199c986f25f1
|
4
|
+
data.tar.gz: 6225a40427c3bb882890f5d6e54407b1a80bd56f5b74f4278122678a2206ae29
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce7ecffd6ee6aae2296c2d20a1353bfb96cdee665b1d06961859f2e3e1922822cb481e3791d7fefc6a53644ce6d150f7ec6eb63880b6112e66b0a9cd64f27fdf
|
7
|
+
data.tar.gz: 80c3816f4a0e1649c2f7f268fbde3583eb435c2978e155de963f9354cf6e2330778c22b80e15757a592042b800a5cf1fca9466557735aef3d90a59b77db2fd2b
|
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.19.0
|
data/lib/rack/oauth2/client.rb
CHANGED
@@ -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
|
|
@@ -73,16 +73,24 @@ module Rack
|
|
73
73
|
http_client = Rack::OAuth2.http_client
|
74
74
|
|
75
75
|
# NOTE:
|
76
|
-
# Using Array#
|
76
|
+
# Using Array#extract_options! for backward compatibility.
|
77
77
|
# Until v1.0.5, the first argument was 'client_auth_method' in scalar.
|
78
78
|
options = args.extract_options!
|
79
|
-
client_auth_method = args.first || options.delete(:client_auth_method) || :basic
|
79
|
+
client_auth_method = args.first || options.delete(:client_auth_method).try(:to_sym) || :basic
|
80
80
|
|
81
81
|
params[:scope] = Array(options.delete(:scope)).join(' ') if options[:scope].present?
|
82
82
|
params.merge! options
|
83
83
|
|
84
84
|
case client_auth_method
|
85
85
|
when :basic
|
86
|
+
cred = Base64.strict_encode64 [
|
87
|
+
Util.www_form_url_encode(identifier),
|
88
|
+
Util.www_form_url_encode(secret)
|
89
|
+
].join(':')
|
90
|
+
headers.merge!(
|
91
|
+
'Authorization' => "Basic #{cred}"
|
92
|
+
)
|
93
|
+
when :basic_without_www_form_urlencode
|
86
94
|
cred = ["#{identifier}:#{secret}"].pack('m').tr("\n", '')
|
87
95
|
headers.merge!(
|
88
96
|
'Authorization' => "Basic #{cred}"
|
@@ -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,7 +17,7 @@ module Rack
|
|
17
17
|
|
18
18
|
def json
|
19
19
|
ensure_finish do
|
20
|
-
@
|
20
|
+
@body
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -39,7 +39,7 @@ module Rack
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def ensure_finish
|
42
|
-
@status, @header, @
|
42
|
+
@status, @header, @body = finish unless finished?
|
43
43
|
yield
|
44
44
|
end
|
45
45
|
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.header['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
|
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)
|
@@ -35,11 +39,12 @@ module Rack
|
|
35
39
|
|
36
40
|
def redirect_uri(base_uri, location, params)
|
37
41
|
redirect_uri = parse_uri base_uri
|
42
|
+
encoded_response_params = Util.compact_hash(params).to_query.gsub('+', '%20')
|
38
43
|
case location
|
39
44
|
when :query
|
40
|
-
redirect_uri.query = [redirect_uri.query,
|
45
|
+
redirect_uri.query = [redirect_uri.query, encoded_response_params].compact.join('&')
|
41
46
|
when :fragment
|
42
|
-
redirect_uri.fragment =
|
47
|
+
redirect_uri.fragment = encoded_response_params
|
43
48
|
end
|
44
49
|
redirect_uri.to_s
|
45
50
|
end
|
@@ -59,4 +64,4 @@ module Rack
|
|
59
64
|
end
|
60
65
|
end
|
61
66
|
end
|
62
|
-
end
|
67
|
+
end
|
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
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
|
|
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'
|
@@ -1,10 +1,12 @@
|
|
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
11
|
redirect_uri: 'https://client.example.com/callback'
|
10
12
|
)
|
@@ -49,12 +51,12 @@ describe Rack::OAuth2::Client do
|
|
49
51
|
|
50
52
|
context 'when response_type is an Array' do
|
51
53
|
subject { client.authorization_uri(response_type: [:token, :code]) }
|
52
|
-
it { should include 'response_type=token
|
54
|
+
it { should include 'response_type=token%20code' }
|
53
55
|
end
|
54
56
|
|
55
57
|
context 'when scope is given' do
|
56
58
|
subject { client.authorization_uri(scope: [:scope1, :scope2]) }
|
57
|
-
it { should include 'scope=scope1
|
59
|
+
it { should include 'scope=scope1%20scope2' }
|
58
60
|
end
|
59
61
|
end
|
60
62
|
|
@@ -97,6 +99,42 @@ describe Rack::OAuth2::Client do
|
|
97
99
|
client.access_token!
|
98
100
|
end
|
99
101
|
|
102
|
+
context 'when Basic auth method is used' do
|
103
|
+
context 'when client_id is a url' do
|
104
|
+
let(:client_id) { 'https://client.example.com'}
|
105
|
+
|
106
|
+
it 'should be encoded in "application/x-www-form-urlencoded"' do
|
107
|
+
mock_response(
|
108
|
+
:post,
|
109
|
+
'https://server.example.com/oauth2/token',
|
110
|
+
'tokens/bearer.json',
|
111
|
+
request_header: {
|
112
|
+
'Authorization' => 'Basic aHR0cHMlM0ElMkYlMkZjbGllbnQuZXhhbXBsZS5jb206Y2xpZW50X3NlY3JldA=='
|
113
|
+
}
|
114
|
+
)
|
115
|
+
client.access_token!
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'when basic_without_www_form_urlencode method is used' do
|
121
|
+
context 'when client_id is a url' do
|
122
|
+
let(:client_id) { 'https://client.example.com'}
|
123
|
+
|
124
|
+
it 'should be encoded in "application/x-www-form-urlencoded"' do
|
125
|
+
mock_response(
|
126
|
+
:post,
|
127
|
+
'https://server.example.com/oauth2/token',
|
128
|
+
'tokens/bearer.json',
|
129
|
+
request_header: {
|
130
|
+
'Authorization' => 'Basic aHR0cHM6Ly9jbGllbnQuZXhhbXBsZS5jb206Y2xpZW50X3NlY3JldA=='
|
131
|
+
}
|
132
|
+
)
|
133
|
+
client.access_token! :basic_without_www_form_urlencode
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
100
138
|
context 'when jwt_bearer auth method specified' do
|
101
139
|
context 'when client_secret is given' do
|
102
140
|
it 'should be JWT bearer client assertion w/ auto-generated HS256-signed JWT assertion' do
|
@@ -51,7 +51,7 @@ describe Rack::OAuth2::Server::Authorize::Code do
|
|
51
51
|
error: :access_denied,
|
52
52
|
error_description: Rack::OAuth2::Server::Authorize::ErrorMethods::DEFAULT_DESCRIPTION[:access_denied]
|
53
53
|
}
|
54
|
-
response.location.should == "#{redirect_uri}?#{error_message.to_query}&state=state"
|
54
|
+
response.location.should == "#{redirect_uri}?#{error_message.to_query.gsub('+', '%20')}&state=state"
|
55
55
|
end
|
56
56
|
end
|
57
57
|
end
|
@@ -54,7 +54,7 @@ describe Rack::OAuth2::Server::Authorize::Extension::CodeAndToken do
|
|
54
54
|
error: :access_denied,
|
55
55
|
error_description: Rack::OAuth2::Server::Authorize::ErrorMethods::DEFAULT_DESCRIPTION[:access_denied]
|
56
56
|
}
|
57
|
-
response.location.should == "#{redirect_uri}##{error_message.to_query}"
|
57
|
+
response.location.should == "#{redirect_uri}##{error_message.to_query.gsub('+', '%20')}"
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
@@ -67,7 +67,7 @@ describe Rack::OAuth2::Server::Authorize::Token do
|
|
67
67
|
error: :access_denied,
|
68
68
|
error_description: Rack::OAuth2::Server::Authorize::ErrorMethods::DEFAULT_DESCRIPTION[:access_denied]
|
69
69
|
}
|
70
|
-
response.location.should == "#{redirect_uri}##{error_message.to_query}&state=state"
|
70
|
+
response.location.should == "#{redirect_uri}##{error_message.to_query.gsub('+', '%20')}&state=state"
|
71
71
|
end
|
72
72
|
end
|
73
73
|
end
|
@@ -10,7 +10,7 @@ describe Rack::OAuth2::Server::Resource::BadRequest do
|
|
10
10
|
status, header, response = error.finish
|
11
11
|
status.should == 400
|
12
12
|
header['Content-Type'].should == 'application/json'
|
13
|
-
response.
|
13
|
+
response.should == ['{"error":"invalid_request"}']
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -44,7 +44,7 @@ describe Rack::OAuth2::Server::Resource::Unauthorized do
|
|
44
44
|
status.should == 401
|
45
45
|
header['Content-Type'].should == 'application/json'
|
46
46
|
header['WWW-Authenticate'].should == "Scheme realm=\"#{realm}\", error=\"invalid_token\""
|
47
|
-
response.
|
47
|
+
response.should == ['{"error":"invalid_token"}']
|
48
48
|
end
|
49
49
|
|
50
50
|
context 'when error_code is not invalid_token' do
|
@@ -53,7 +53,7 @@ describe Rack::OAuth2::Server::Resource::Unauthorized do
|
|
53
53
|
it 'should have error_code in body but not in WWW-Authenticate header' do
|
54
54
|
status, header, response = error_with_scheme.finish
|
55
55
|
header['WWW-Authenticate'].should == "Scheme realm=\"#{realm}\""
|
56
|
-
response.
|
56
|
+
response.first.should include '"error":"something"'
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
@@ -63,7 +63,7 @@ describe Rack::OAuth2::Server::Resource::Unauthorized do
|
|
63
63
|
it 'should have error_code in body but not in WWW-Authenticate header' do
|
64
64
|
status, header, response = error_with_scheme.finish
|
65
65
|
header['WWW-Authenticate'].should == "Scheme realm=\"#{realm}\""
|
66
|
-
response.
|
66
|
+
response.first.should == '{"error":"unauthorized"}'
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
@@ -74,7 +74,7 @@ describe Rack::OAuth2::Server::Resource::Unauthorized do
|
|
74
74
|
it 'should use given realm' do
|
75
75
|
status, header, response = error_with_scheme.finish
|
76
76
|
header['WWW-Authenticate'].should == "Scheme realm=\"#{realm}\""
|
77
|
-
response.
|
77
|
+
response.first.should include '"error":"something"'
|
78
78
|
end
|
79
79
|
end
|
80
80
|
end
|
@@ -91,7 +91,7 @@ describe Rack::OAuth2::Server::Resource::Forbidden do
|
|
91
91
|
status, header, response = error.finish
|
92
92
|
status.should == 403
|
93
93
|
header['Content-Type'].should == 'application/json'
|
94
|
-
response.
|
94
|
+
response.should == ['{"error":"insufficient_scope"}']
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
@@ -100,7 +100,7 @@ describe Rack::OAuth2::Server::Resource::Forbidden do
|
|
100
100
|
|
101
101
|
it 'should have blank WWW-Authenticate header' do
|
102
102
|
status, header, response = error.finish
|
103
|
-
response.
|
103
|
+
response.first.should include '"scope":"scope1 scope2"'
|
104
104
|
end
|
105
105
|
end
|
106
106
|
end
|
@@ -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
|
@@ -10,7 +10,7 @@ describe Rack::OAuth2::Server::Token::BadRequest do
|
|
10
10
|
status, header, response = error.finish
|
11
11
|
status.should == 400
|
12
12
|
header['Content-Type'].should == 'application/json'
|
13
|
-
response.
|
13
|
+
response.should == ['{"error":"invalid_request"}']
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -26,7 +26,7 @@ describe Rack::OAuth2::Server::Token::Unauthorized do
|
|
26
26
|
status.should == 401
|
27
27
|
header['Content-Type'].should == 'application/json'
|
28
28
|
header['WWW-Authenticate'].should == 'Basic realm="OAuth2 Token Endpoint"'
|
29
|
-
response.
|
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
|
@@ -30,7 +30,7 @@ describe Rack::OAuth2::Server::Token do
|
|
30
30
|
it 'should fail with unsupported_grant_type' do
|
31
31
|
status, header, 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
|
|
@@ -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,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-oauth2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.19.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- nov matake
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-10-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -16,14 +16,14 @@ dependencies:
|
|
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
|
@@ -301,7 +301,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
301
301
|
- !ruby/object:Gem::Version
|
302
302
|
version: '0'
|
303
303
|
requirements: []
|
304
|
-
rubygems_version: 3.
|
304
|
+
rubygems_version: 3.1.4
|
305
305
|
signing_key:
|
306
306
|
specification_version: 4
|
307
307
|
summary: OAuth 2.0 Server & Client Library - Both Bearer and MAC token type are supported
|