openid_connect 0.0.31 → 0.0.32
Sign up to get free protection for your applications and to get access to all the features.
- 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
|