openid_connect 0.0.31 → 0.0.32
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.
- data/.gitignore +1 -1
- data/Gemfile.lock +34 -26
- data/README.rdoc +2 -2
- data/Rakefile +12 -4
- data/VERSION +1 -1
- data/lib/openid_connect/access_token.rb +8 -0
- data/lib/openid_connect/client/registrar.rb +217 -0
- data/lib/openid_connect/client.rb +4 -0
- data/lib/openid_connect/discovery/provider/config/response.rb +40 -5
- data/lib/openid_connect/exception.rb +9 -0
- data/lib/openid_connect/response_object/id_token.rb +6 -28
- data/lib/openid_connect/response_object/user_info/open_id.rb +22 -15
- data/lib/openid_connect/response_object.rb +0 -16
- data/lib/openid_connect.rb +14 -0
- data/openid_connect.gemspec +5 -1
- data/spec/mock_response/discovery/config.json +4 -4
- data/spec/mock_response/id_token.json +3 -3
- data/spec/openid_connect/access_token_spec.rb +46 -26
- data/spec/openid_connect/client/registrar_spec.rb +115 -0
- data/spec/openid_connect/discovery/provider/config/response_spec.rb +46 -0
- data/spec/openid_connect/discovery/provider/config_spec.rb +5 -5
- data/spec/openid_connect/response_object/id_token_spec.rb +32 -7
- data/spec/openid_connect/response_object/user_info/open_id_spec.rb +8 -0
- data/spec/openid_connect/response_object_spec.rb +4 -4
- data/spec/rack/oauth2/server/authorize/extension/code_and_id_token_spec.rb +2 -1
- data/spec/rack/oauth2/server/authorize/extension/id_token_and_token_spec.rb +10 -5
- data/spec/rack/oauth2/server/authorize/extension/id_token_spec.rb +8 -5
- data/spec/spec_helper.rb +7 -0
- metadata +124 -126
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
|
-
"iss": "
|
2
|
+
"iss": "https://server.example.com",
|
3
3
|
"aud": "client_id",
|
4
|
-
"issued_to": "
|
5
|
-
"user_id": "
|
4
|
+
"issued_to": "https://client.example.com",
|
5
|
+
"user_id": "user_id",
|
6
6
|
"exp": 1303852880
|
7
7
|
}
|
@@ -48,45 +48,65 @@ describe OpenIDConnect::AccessToken do
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
access_token
|
51
|
+
shared_examples_for :access_token_error_handling do
|
52
|
+
context 'when bad_request' do
|
53
|
+
it 'should raise OpenIDConnect::Forbidden' do
|
54
|
+
mock_json :get, endpoint, 'errors/invalid_request', :HTTP_AUTHORIZATION => 'Bearer access_token', :status => 400 do
|
55
|
+
expect { request }.should raise_error OpenIDConnect::BadRequest
|
56
|
+
end
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
expect { access_token.user_info! }.should raise_error OpenIDConnect::BadRequest
|
63
|
-
end
|
60
|
+
context 'when unauthorized' do
|
61
|
+
it 'should raise OpenIDConnect::Unauthorized' do
|
62
|
+
mock_json :get, endpoint, 'errors/invalid_access_token', :HTTP_AUTHORIZATION => 'Bearer access_token', :status => 401 do
|
63
|
+
expect { request }.should raise_error OpenIDConnect::Unauthorized
|
64
64
|
end
|
65
65
|
end
|
66
|
+
end
|
66
67
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
68
|
+
context 'when forbidden' do
|
69
|
+
it 'should raise OpenIDConnect::Forbidden' do
|
70
|
+
mock_json :get, endpoint, 'errors/insufficient_scope', :HTTP_AUTHORIZATION => 'Bearer access_token', :status => 403 do
|
71
|
+
expect { request }.should raise_error OpenIDConnect::Forbidden
|
72
72
|
end
|
73
73
|
end
|
74
|
+
end
|
74
75
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
end
|
76
|
+
context 'when unknown' do
|
77
|
+
it 'should raise OpenIDConnect::HttpError' do
|
78
|
+
mock_json :get, endpoint, 'errors/unknown', :HTTP_AUTHORIZATION => 'Bearer access_token', :status => 500 do
|
79
|
+
expect { request }.should raise_error OpenIDConnect::HttpError
|
80
80
|
end
|
81
81
|
end
|
82
|
+
end
|
83
|
+
end
|
82
84
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
end
|
88
|
-
end
|
85
|
+
describe '#user_info!' do
|
86
|
+
it 'should return OpenIDConnect::ResponseObject::UserInfo::OpenID' do
|
87
|
+
mock_json :get, client.user_info_uri, 'user_info/openid', :HTTP_AUTHORIZATION => 'Bearer access_token' do
|
88
|
+
access_token.user_info!.should be_a OpenIDConnect::ResponseObject::UserInfo::OpenID
|
89
89
|
end
|
90
90
|
end
|
91
|
+
|
92
|
+
describe 'error handling' do
|
93
|
+
let(:endpoint) { client.user_info_uri }
|
94
|
+
let(:request) { access_token.user_info! }
|
95
|
+
it_behaves_like :access_token_error_handling
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#id_token!' do
|
100
|
+
it 'should return OpenIDConnect::ResponseObject::UserInfo::OpenID' do
|
101
|
+
mock_json :get, client.check_id_uri, 'id_token', :HTTP_AUTHORIZATION => 'Bearer access_token' do
|
102
|
+
access_token.id_token!.should be_a OpenIDConnect::ResponseObject::IdToken
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe 'error handling' do
|
107
|
+
let(:endpoint) { client.check_id_uri }
|
108
|
+
let(:request) { access_token.id_token! }
|
109
|
+
it_behaves_like :access_token_error_handling
|
110
|
+
end
|
91
111
|
end
|
92
112
|
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OpenIDConnect::Client::Registrar do
|
4
|
+
subject { instance }
|
5
|
+
let(:instance) { OpenIDConnect::Client::Registrar.new(endpoint, attributes) }
|
6
|
+
let(:endpoint) { 'https://server.example.com/clients' }
|
7
|
+
|
8
|
+
context 'when endpoint given' do
|
9
|
+
context 'when attributes given' do
|
10
|
+
context 'when type=client_associate' do
|
11
|
+
let(:attributes) do
|
12
|
+
{
|
13
|
+
:type => :client_associate
|
14
|
+
}
|
15
|
+
end
|
16
|
+
it { should be_valid }
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'when type=client_update' do
|
20
|
+
context 'when client_id given' do
|
21
|
+
let(:attributes) do
|
22
|
+
{
|
23
|
+
:type => :client_update,
|
24
|
+
:client_id => 'client.example.com'
|
25
|
+
}
|
26
|
+
end
|
27
|
+
it { should be_valid }
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'otherwise' do
|
31
|
+
let(:attributes) do
|
32
|
+
{
|
33
|
+
:type => :client_update
|
34
|
+
}
|
35
|
+
end
|
36
|
+
it { should_not be_valid }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'otherwise' do
|
41
|
+
let(:attributes) do
|
42
|
+
{
|
43
|
+
:type => :invalid_type
|
44
|
+
}
|
45
|
+
end
|
46
|
+
it { should_not be_valid }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'otherwise' do
|
51
|
+
let(:instance) { OpenIDConnect::Client::Registrar.new(endpoint) }
|
52
|
+
it do
|
53
|
+
expect do
|
54
|
+
instance
|
55
|
+
end.should_not raise_error
|
56
|
+
end
|
57
|
+
it { should_not be_valid }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'otherwise' do
|
62
|
+
let(:instance) { OpenIDConnect::Client::Registrar.new(endpoint) }
|
63
|
+
let(:endpoint) { '' }
|
64
|
+
|
65
|
+
it do
|
66
|
+
expect do
|
67
|
+
instance
|
68
|
+
end.should raise_error AttrRequired::AttrMissing
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '#sector_identifier' do
|
73
|
+
it :TODO
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#as_json' do
|
77
|
+
it :TODO
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '#associate!' do
|
81
|
+
it :TODO
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '#update!' do
|
85
|
+
it :TODO
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '#validate!' do
|
89
|
+
context 'when valid' do
|
90
|
+
let(:attributes) do
|
91
|
+
{
|
92
|
+
:type => :client_associate
|
93
|
+
}
|
94
|
+
end
|
95
|
+
it do
|
96
|
+
expect do
|
97
|
+
instance.validate!
|
98
|
+
end.should_not raise_error OpenIDConnect::ValidationFailed
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'otherwise' do
|
103
|
+
let(:attributes) do
|
104
|
+
{
|
105
|
+
:type => :client_update
|
106
|
+
}
|
107
|
+
end
|
108
|
+
it do
|
109
|
+
expect do
|
110
|
+
instance.validate!
|
111
|
+
end.should raise_error OpenIDConnect::ValidationFailed
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OpenIDConnect::Discovery::Provider::Config::Response do
|
4
|
+
let :instance do
|
5
|
+
OpenIDConnect::Discovery::Provider::Config::Response.new attributes
|
6
|
+
end
|
7
|
+
let :attributes do
|
8
|
+
{}
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#as_json' do
|
12
|
+
subject {
|
13
|
+
instance.as_json
|
14
|
+
}
|
15
|
+
|
16
|
+
context 'when no attributes given' do
|
17
|
+
it do
|
18
|
+
should == {:version => '3.0'}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'when user_info_endpoint given' do
|
23
|
+
let :attributes do
|
24
|
+
{:user_info_endpoint => 'https://server.example.com/user_info'}
|
25
|
+
end
|
26
|
+
it do
|
27
|
+
should include :userinfo_endpoint
|
28
|
+
end
|
29
|
+
it do
|
30
|
+
should_not include :user_info_endpoint
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when user_info_algs_supported given' do
|
35
|
+
let :attributes do
|
36
|
+
{:user_info_algs_supported => [:HS256, :RS256]}
|
37
|
+
end
|
38
|
+
it do
|
39
|
+
should include :userinfo_algs_supported
|
40
|
+
end
|
41
|
+
it do
|
42
|
+
should_not include :user_info_algs_supported
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -17,13 +17,13 @@ describe OpenIDConnect::Discovery::Provider::Config do
|
|
17
17
|
config.check_id_endpoint.should == 'https://connect-op.heroku.com/id_token'
|
18
18
|
config.refresh_session_endpoint.should be_nil
|
19
19
|
config.end_session_endpoint.should be_nil
|
20
|
-
config.
|
20
|
+
config.jwk_url.should be_nil
|
21
21
|
config.x509_url.should == 'https://connect-op.heroku.com/cert.pem'
|
22
22
|
config.registration_endpoint.should == 'https://connect-op.heroku.com/connect/client'
|
23
|
-
config.scopes_supported.should == ["openid", "profile", "email", "address"
|
24
|
-
config.
|
25
|
-
config.
|
26
|
-
config.
|
23
|
+
config.scopes_supported.should == ["openid", "profile", "email", "address"]
|
24
|
+
config.response_types_supported.should == ["code", "token", "id_token", "code token", "code id_token", "id_token token"]
|
25
|
+
config.acrs_supported.should be_nil
|
26
|
+
config.user_id_types_supported.should == ["public", "pairwise"]
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
@@ -54,13 +54,38 @@ describe OpenIDConnect::ResponseObject::IdToken do
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
-
describe '.
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
57
|
+
describe '.decode' do
|
58
|
+
context 'when key is given' do
|
59
|
+
subject { klass.decode id_token.to_jwt(private_key), public_key }
|
60
|
+
let(:attributes) { required_attributes }
|
61
|
+
it { should be_a klass }
|
62
|
+
[:iss, :user_id, :aud].each do |key|
|
63
|
+
its(key) { should == attributes[key] }
|
64
|
+
end
|
65
|
+
its(:exp) { should == attributes[:exp].to_i }
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'when client is given' do
|
69
|
+
let :client do
|
70
|
+
OpenIDConnect::Client.new(
|
71
|
+
:identifier => 'client_id',
|
72
|
+
:secret => 'client_secret',
|
73
|
+
:host => 'server.example.com'
|
74
|
+
)
|
75
|
+
end
|
76
|
+
subject do
|
77
|
+
mock_json :get, client.check_id_uri, 'id_token', :HTTP_AUTHORIZATION => 'Bearer access_token' do
|
78
|
+
@subject = klass.decode id_token.to_jwt(private_key), client
|
79
|
+
end
|
80
|
+
@subject
|
81
|
+
end
|
82
|
+
let(:attributes) { required_attributes }
|
83
|
+
let(:ext) { 1303852880 }
|
84
|
+
it { should be_a klass }
|
85
|
+
[:iss, :user_id, :aud].each do |key|
|
86
|
+
its(key) { should == attributes[key] }
|
87
|
+
end
|
88
|
+
its(:exp) { should == attributes[:exp].to_i }
|
63
89
|
end
|
64
|
-
its(:exp) { should == attributes[:exp].to_i }
|
65
90
|
end
|
66
91
|
end
|
@@ -36,6 +36,14 @@ describe OpenIDConnect::ResponseObject::UserInfo::OpenID do
|
|
36
36
|
its(:errors) { should include :base }
|
37
37
|
end
|
38
38
|
|
39
|
+
context 'when email is invalid' do
|
40
|
+
let :attributes do
|
41
|
+
{:email => 'nov@localhost'}
|
42
|
+
end
|
43
|
+
its(:valid?) { should be_false }
|
44
|
+
its(:errors) { should include :email }
|
45
|
+
end
|
46
|
+
|
39
47
|
[:verified, :gender, :zoneinfo].each do |one_of_list|
|
40
48
|
context "when #{one_of_list} is invalid" do
|
41
49
|
let :attributes do
|
@@ -58,8 +58,8 @@ describe OpenIDConnect::ResponseObject do
|
|
58
58
|
{:required => 'Out of List and Too Long'}
|
59
59
|
end
|
60
60
|
|
61
|
-
it 'should raise OpenIDConnect::
|
62
|
-
expect { instance.as_json }.should raise_error(OpenIDConnect::
|
61
|
+
it 'should raise OpenIDConnect::ValidationFailed with ActiveModel::Errors' do
|
62
|
+
expect { instance.as_json }.should raise_error(OpenIDConnect::ValidationFailed) { |e|
|
63
63
|
e.message.should include 'Required is not included in the list'
|
64
64
|
e.message.should include 'Required is too long (maximum is 10 characters)'
|
65
65
|
e.errors.should be_a ActiveModel::Errors
|
@@ -79,8 +79,8 @@ describe OpenIDConnect::ResponseObject do
|
|
79
79
|
{:required => 'Out of List and Too Long'}
|
80
80
|
end
|
81
81
|
|
82
|
-
it 'should raise OpenIDConnect::
|
83
|
-
expect { instance.validate! }.should raise_error(OpenIDConnect::
|
82
|
+
it 'should raise OpenIDConnect::ValidationFailed with ActiveModel::Errors' do
|
83
|
+
expect { instance.validate! }.should raise_error(OpenIDConnect::ValidationFailed) { |e|
|
84
84
|
e.message.should include 'Required is not included in the list'
|
85
85
|
e.message.should include 'Required is too long (maximum is 10 characters)'
|
86
86
|
e.errors.should be_a ActiveModel::Errors
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe Rack::OAuth2::Server::Authorize::Extension::CodeAndIdToken do
|
4
4
|
subject { response }
|
5
5
|
let(:request) { Rack::MockRequest.new app }
|
6
|
-
let(:response) { request.get("/?response_type=code%20id_token&client_id=client") }
|
6
|
+
let(:response) { request.get("/?response_type=code%20id_token&client_id=client&state=state") }
|
7
7
|
let(:redirect_uri) { 'http://client.example.com/callback' }
|
8
8
|
let(:code) { 'authorization_code' }
|
9
9
|
let :id_token do
|
@@ -28,6 +28,7 @@ describe Rack::OAuth2::Server::Authorize::Extension::CodeAndIdToken do
|
|
28
28
|
its(:location) { should include "#{redirect_uri}#" }
|
29
29
|
its(:location) { should include "code=#{code}" }
|
30
30
|
its(:location) { should include "id_token=#{id_token}" }
|
31
|
+
its(:location) { should include "state=state" }
|
31
32
|
|
32
33
|
context 'when id_token is String' do
|
33
34
|
let(:id_token) { 'non_jwt_string' }
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe Rack::OAuth2::Server::Authorize::Extension::IdTokenAndToken do
|
4
4
|
subject { response }
|
5
5
|
let(:request) { Rack::MockRequest.new app }
|
6
|
-
let(:response) { request.get(
|
6
|
+
let(:response) { request.get('/?response_type=token%20id_token&client_id=client&state=state') }
|
7
7
|
let(:redirect_uri) { 'http://client.example.com/callback' }
|
8
8
|
let(:bearer_token) { Rack::OAuth2::AccessToken::Bearer.new(:access_token => 'access_token') }
|
9
9
|
let :id_token do
|
@@ -15,7 +15,7 @@ describe Rack::OAuth2::Server::Authorize::Extension::IdTokenAndToken do
|
|
15
15
|
).to_jwt private_key
|
16
16
|
end
|
17
17
|
|
18
|
-
context
|
18
|
+
context 'when id_token is given' do
|
19
19
|
let :app do
|
20
20
|
Rack::OAuth2::Server::Authorize.new do |request, response|
|
21
21
|
response.redirect_uri = redirect_uri
|
@@ -25,15 +25,20 @@ describe Rack::OAuth2::Server::Authorize::Extension::IdTokenAndToken do
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
its(:status) { should == 302 }
|
28
|
-
its(:location) {
|
28
|
+
its(:location) { should_not include '?' }
|
29
|
+
its(:location) { should include '#' }
|
30
|
+
its(:location) { should include 'access_token=access_token' }
|
31
|
+
its(:location) { should include "id_token=#{id_token}" }
|
32
|
+
its(:location) { should include 'token_type=bearer' }
|
33
|
+
its(:location) { should include 'state=state' }
|
29
34
|
|
30
35
|
context 'when id_token is String' do
|
31
36
|
let(:id_token) { 'id_token' }
|
32
|
-
its(:location) { should
|
37
|
+
its(:location) { should include 'id_token=id_token' }
|
33
38
|
end
|
34
39
|
end
|
35
40
|
|
36
|
-
context
|
41
|
+
context 'otherwise' do
|
37
42
|
let :app do
|
38
43
|
Rack::OAuth2::Server::Authorize.new do |request, response|
|
39
44
|
response.redirect_uri = redirect_uri
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe Rack::OAuth2::Server::Authorize::Extension::IdToken do
|
4
4
|
subject { response }
|
5
5
|
let(:request) { Rack::MockRequest.new app }
|
6
|
-
let(:response) { request.get(
|
6
|
+
let(:response) { request.get('/?response_type=id_token&client_id=client&state=state') }
|
7
7
|
let(:redirect_uri) { 'http://client.example.com/callback' }
|
8
8
|
let :id_token do
|
9
9
|
OpenIDConnect::ResponseObject::IdToken.new(
|
@@ -14,7 +14,7 @@ describe Rack::OAuth2::Server::Authorize::Extension::IdToken do
|
|
14
14
|
).to_jwt private_key
|
15
15
|
end
|
16
16
|
|
17
|
-
context
|
17
|
+
context 'when id_token is given' do
|
18
18
|
let :app do
|
19
19
|
Rack::OAuth2::Server::Authorize.new do |request, response|
|
20
20
|
response.redirect_uri = redirect_uri
|
@@ -23,15 +23,18 @@ describe Rack::OAuth2::Server::Authorize::Extension::IdToken do
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
its(:status) { should == 302 }
|
26
|
-
its(:location) {
|
26
|
+
its(:location) { should_not include '?' }
|
27
|
+
its(:location) { should include '#' }
|
28
|
+
its(:location) { should include "id_token=#{id_token}" }
|
29
|
+
its(:location) { should include 'state=state' }
|
27
30
|
|
28
31
|
context 'when id_token is String' do
|
29
32
|
let(:id_token) { 'id_token' }
|
30
|
-
its(:location) { should
|
33
|
+
its(:location) { should include 'id_token=id_token' }
|
31
34
|
end
|
32
35
|
end
|
33
36
|
|
34
|
-
context
|
37
|
+
context 'otherwise' do
|
35
38
|
let :app do
|
36
39
|
Rack::OAuth2::Server::Authorize.new do |request, response|
|
37
40
|
response.redirect_uri = redirect_uri
|