openid_connect 0.0.11 → 0.0.12
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/Gemfile.lock +3 -3
- data/README.rdoc +3 -2
- data/VERSION +1 -1
- data/lib/openid_connect/response_object.rb +16 -8
- data/lib/openid_connect/response_object/id_token.rb +8 -12
- data/lib/openid_connect/response_object/user_info/open_id.rb +10 -3
- data/lib/rack/oauth2/server/id_token_response.rb +12 -2
- data/spec/openid_connect/response_object/id_token_spec.rb +11 -21
- data/spec/openid_connect/response_object_spec.rb +21 -7
- data/spec/openid_connect/server/id_token_spec.rb +2 -2
- data/spec/rack/oauth2/server/authorize/code_and_token_spec.rb +22 -1
- data/spec/rack/oauth2/server/authorize/token_spec.rb +21 -1
- data/spec/rack/oauth2/server/token/authorization_code_spec.rb +19 -1
- data/spec/rack/oauth2/server/token/refresh_token_spec.rb +19 -1
- data/spec/spec_helper.rb +13 -1
- metadata +4 -4
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
openid_connect (0.0.
|
4
|
+
openid_connect (0.0.11)
|
5
5
|
activemodel (>= 3)
|
6
6
|
attr_required (>= 0.0.3)
|
7
7
|
json (>= 1.4.3)
|
@@ -62,8 +62,8 @@ GEM
|
|
62
62
|
mail (>= 2.2.5)
|
63
63
|
validate_url (0.2.0)
|
64
64
|
activemodel (>= 3.0.0)
|
65
|
-
webmock (1.7.
|
66
|
-
addressable (
|
65
|
+
webmock (1.7.4)
|
66
|
+
addressable (~> 2.2, > 2.2.5)
|
67
67
|
crack (>= 0.1.7)
|
68
68
|
|
69
69
|
PLATFORMS
|
data/README.rdoc
CHANGED
@@ -8,8 +8,9 @@ OpenID Connect Server & Client Library
|
|
8
8
|
|
9
9
|
== Resources
|
10
10
|
|
11
|
-
* View Source on GitHub (
|
12
|
-
* Report Issues on GitHub (
|
11
|
+
* View Source on GitHub (https://github.com/nov/openid_connect)
|
12
|
+
* Report Issues on GitHub (https://github.com/nov/openid_connect/issues)
|
13
|
+
* Subscribe Update Info (https://www.facebook.com/pages/OpenID-Connect/134681459957370)
|
13
14
|
|
14
15
|
== Examples
|
15
16
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.12
|
@@ -9,36 +9,44 @@ module OpenIDConnect
|
|
9
9
|
class ResponseObject
|
10
10
|
include ActiveModel::Validations, AttrRequired, AttrOptional
|
11
11
|
|
12
|
+
class ValidationFailed < Exception
|
13
|
+
attr_reader :errors
|
14
|
+
|
15
|
+
def initialize(errors)
|
16
|
+
super errors.full_messages.to_sentence
|
17
|
+
@errors = errors
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
12
21
|
def initialize(attributes = {})
|
13
|
-
|
22
|
+
all_attributes.each do |_attr_|
|
14
23
|
self.send :"#{_attr_}=", attributes[_attr_]
|
15
24
|
end
|
16
25
|
attr_missing!
|
17
26
|
end
|
18
27
|
|
19
|
-
def
|
28
|
+
def all_attributes
|
20
29
|
required_attributes + optional_attributes
|
21
30
|
end
|
22
31
|
|
23
32
|
def require_at_least_one_attributes
|
24
|
-
all_blank =
|
33
|
+
all_blank = all_attributes.all? do |key|
|
25
34
|
self.send(key).blank?
|
26
35
|
end
|
27
36
|
errors.add :base, 'At least one attribute is required' if all_blank
|
28
37
|
end
|
29
38
|
|
30
39
|
def as_json(options = {})
|
31
|
-
|
40
|
+
validate!
|
41
|
+
all_attributes.inject({}) do |hash, _attr_|
|
32
42
|
hash.merge! _attr_ => self.send(_attr_)
|
33
43
|
end.delete_if do |key, value|
|
34
44
|
value.nil?
|
35
45
|
end
|
36
46
|
end
|
37
47
|
|
38
|
-
|
39
|
-
|
40
|
-
def hidden_attributes
|
41
|
-
nil
|
48
|
+
def validate!
|
49
|
+
raise ValidationFailed.new(errors) unless valid?
|
42
50
|
end
|
43
51
|
end
|
44
52
|
end
|
@@ -6,10 +6,13 @@ module OpenIDConnect
|
|
6
6
|
class InvalidToken < Exception; end
|
7
7
|
|
8
8
|
attr_required :iss, :user_id, :aud, :exp
|
9
|
-
attr_optional :iso29115, :nonce, :issued_to
|
9
|
+
attr_optional :iso29115, :nonce, :issued_to
|
10
10
|
|
11
11
|
def initialize(attributes = {})
|
12
12
|
super
|
13
|
+
(all_attributes - [:exp]).each do |key|
|
14
|
+
self.send "#{key}=", self.send(key).try(:to_s)
|
15
|
+
end
|
13
16
|
@exp = @exp.to_i
|
14
17
|
end
|
15
18
|
|
@@ -18,19 +21,12 @@ module OpenIDConnect
|
|
18
21
|
raise InvalidToken.new('Invalid audience or expired')
|
19
22
|
end
|
20
23
|
|
21
|
-
def to_jwt
|
22
|
-
|
23
|
-
JWT.encode as_json, secret
|
24
|
+
def to_jwt(key, algorithm = 'RS256')
|
25
|
+
JWT.encode as_json, key, algorithm
|
24
26
|
end
|
25
27
|
|
26
|
-
def self.from_jwt(
|
27
|
-
new JWT.decode(
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
def hidden_attributes
|
33
|
-
:secret
|
28
|
+
def self.from_jwt(jwt_string, key)
|
29
|
+
new JWT.decode(jwt_string, key).with_indifferent_access
|
34
30
|
end
|
35
31
|
end
|
36
32
|
end
|
@@ -7,9 +7,9 @@ module OpenIDConnect
|
|
7
7
|
attr_optional :phone_number
|
8
8
|
|
9
9
|
attr_optional :verified, :gender, :zoneinfo, :locale
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
validates :verified, :inclusion => {:in => [true, false]}, :allow_nil => true
|
11
|
+
validates :gender, :inclusion => {:in => ['male', 'female']}, :allow_nil => true
|
12
|
+
validates :zoneinfo, :inclusion => {:in => TZInfo::TimezoneProxy.all.collect(&:name)}, :allow_nil => true
|
13
13
|
# TODO: validate locale
|
14
14
|
|
15
15
|
attr_optional :birthday, :updated_time
|
@@ -25,6 +25,13 @@ module OpenIDConnect
|
|
25
25
|
|
26
26
|
validate :require_at_least_one_attributes
|
27
27
|
|
28
|
+
def initialize(attributes = {})
|
29
|
+
super
|
30
|
+
(all_attributes - [:verified, :address]).each do |key|
|
31
|
+
self.send "#{key}=", self.send(key).try(:to_s)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
28
35
|
def validate_address
|
29
36
|
errors.add :address, 'cannot be blank' unless address.blank? || address.valid?
|
30
37
|
end
|
@@ -1,11 +1,21 @@
|
|
1
1
|
module Rack::OAuth2::Server
|
2
2
|
module IdTokenResponse
|
3
3
|
def self.included(klass)
|
4
|
-
klass.send :attr_optional, :id_token
|
4
|
+
klass.send :attr_optional, :id_token, :private_key
|
5
5
|
klass.class_eval do
|
6
|
+
def jwt_string
|
7
|
+
case id_token
|
8
|
+
when String
|
9
|
+
id_token
|
10
|
+
when OpenIDConnect::ResponseObject::IdToken
|
11
|
+
raise AttrRequired::AttrMissing.new('private_key is required') unless private_key
|
12
|
+
id_token.to_jwt private_key
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
6
16
|
def protocol_params_with_id_token
|
7
17
|
protocol_params_without_id_token.merge(
|
8
|
-
:id_token =>
|
18
|
+
:id_token => jwt_string
|
9
19
|
)
|
10
20
|
end
|
11
21
|
alias_method_chain :protocol_params, :id_token
|
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe OpenIDConnect::ResponseObject::IdToken do
|
4
|
-
let(:klass)
|
5
|
-
let(:id_token)
|
6
|
-
let(:attributes)
|
7
|
-
let(:ext)
|
4
|
+
let(:klass) { OpenIDConnect::ResponseObject::IdToken }
|
5
|
+
let(:id_token) { klass.new attributes }
|
6
|
+
let(:attributes) { required_attributes }
|
7
|
+
let(:ext) { 10.minutes.from_now }
|
8
8
|
let :required_attributes do
|
9
9
|
{
|
10
10
|
:iss => 'https://server.example.com',
|
@@ -17,7 +17,7 @@ describe OpenIDConnect::ResponseObject::IdToken do
|
|
17
17
|
describe 'attributes' do
|
18
18
|
subject { klass }
|
19
19
|
its(:required_attributes) { should == [:iss, :user_id, :aud, :exp] }
|
20
|
-
its(:optional_attributes) { should == [:iso29115, :nonce, :issued_to
|
20
|
+
its(:optional_attributes) { should == [:iso29115, :nonce, :issued_to] }
|
21
21
|
end
|
22
22
|
|
23
23
|
describe '#verify!' do
|
@@ -40,31 +40,21 @@ describe OpenIDConnect::ResponseObject::IdToken do
|
|
40
40
|
end
|
41
41
|
|
42
42
|
describe '#to_jwt' do
|
43
|
-
subject { id_token.to_jwt }
|
44
|
-
|
45
|
-
context 'when secret is given' do
|
46
|
-
let(:attributes) { required_attributes.merge(:secret => 'secret') }
|
47
|
-
it { should be_a String }
|
48
|
-
end
|
49
|
-
|
50
|
-
context 'otherwise' do
|
51
|
-
it do
|
52
|
-
expect { id_token.to_jwt }.should raise_error OpenIDConnect::Exception, 'Secret Required'
|
53
|
-
end
|
54
|
-
end
|
43
|
+
subject { id_token.to_jwt private_key }
|
44
|
+
it { should be_a String }
|
55
45
|
end
|
56
46
|
|
57
47
|
describe '#as_json' do
|
58
48
|
subject { id_token.as_json }
|
59
|
-
let(:attributes) { required_attributes
|
49
|
+
let(:attributes) { required_attributes }
|
60
50
|
it { should_not include :secret }
|
61
51
|
end
|
62
52
|
|
63
53
|
describe '.from_jwt' do
|
64
|
-
subject { klass.from_jwt id_token.to_jwt,
|
65
|
-
let(:attributes) { required_attributes
|
54
|
+
subject { klass.from_jwt id_token.to_jwt(private_key), public_key }
|
55
|
+
let(:attributes) { required_attributes }
|
66
56
|
it { should be_a klass }
|
67
|
-
[:iss, :user_id, :aud
|
57
|
+
[:iss, :user_id, :aud].each do |key|
|
68
58
|
its(key) { should == attributes[key] }
|
69
59
|
end
|
70
60
|
its(:exp) { should == attributes[:exp].to_i }
|
@@ -4,16 +4,17 @@ describe OpenIDConnect::ResponseObject do
|
|
4
4
|
class OpenIDConnect::ResponseObject::SubClass < OpenIDConnect::ResponseObject
|
5
5
|
attr_required :required
|
6
6
|
attr_optional :optional
|
7
|
+
validates :required, :inclusion => {:in => ['Required', 'required']}, :length => 1..10
|
7
8
|
end
|
8
9
|
|
9
|
-
let(:klass) { OpenIDConnect::ResponseObject::SubClass }
|
10
10
|
subject { klass.new attributes }
|
11
|
+
let(:klass) { OpenIDConnect::ResponseObject::SubClass }
|
12
|
+
let :attributes do
|
13
|
+
{:required => 'Required', :optional => 'Optional'}
|
14
|
+
end
|
11
15
|
|
12
16
|
context 'when required attributes are given' do
|
13
17
|
context 'when optional attributes are given' do
|
14
|
-
let :attributes do
|
15
|
-
{:required => 'Required', :optional => 'Optional'}
|
16
|
-
end
|
17
18
|
its(:required) { should == 'Required' }
|
18
19
|
its(:optional) { should == 'Optional' }
|
19
20
|
end
|
@@ -45,11 +46,24 @@ describe OpenIDConnect::ResponseObject do
|
|
45
46
|
end
|
46
47
|
|
47
48
|
describe '#as_json' do
|
48
|
-
let :attributes do
|
49
|
-
{:required => 'Required', :optional => 'Optional'}
|
50
|
-
end
|
51
49
|
its(:as_json) do
|
52
50
|
should == {:required => 'Required', :optional => 'Optional'}
|
53
51
|
end
|
54
52
|
end
|
53
|
+
|
54
|
+
describe '#validate!' do
|
55
|
+
let(:invalid) do
|
56
|
+
instance = klass.new attributes
|
57
|
+
instance.required = 'Out of List and Too Long'
|
58
|
+
instance
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should raise OpenIDConnect::ResponseObject::ValidationFailed with ActiveModel::Errors' do
|
62
|
+
expect { invalid.validate! }.should raise_error(OpenIDConnect::ResponseObject::ValidationFailed) { |e|
|
63
|
+
e.message.should include 'Required is not included in the list'
|
64
|
+
e.message.should include 'Required is too long (maximum is 10 characters)'
|
65
|
+
e.errors.should be_a ActiveModel::Errors
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
55
69
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper.rb'
|
2
2
|
|
3
3
|
describe OpenIDConnect::Server::IdToken do
|
4
|
-
let(:request)
|
4
|
+
let(:request) { Rack::MockRequest.new app }
|
5
5
|
let :app do
|
6
6
|
OpenIDConnect::Server::IdToken.new do |req, res|
|
7
7
|
res.id_token = id_token
|
@@ -14,7 +14,7 @@ describe OpenIDConnect::Server::IdToken do
|
|
14
14
|
)
|
15
15
|
end
|
16
16
|
let :params do
|
17
|
-
{:id_token => id_token.to_jwt}
|
17
|
+
{:id_token => id_token.to_jwt(private_key) }
|
18
18
|
end
|
19
19
|
let :id_token do
|
20
20
|
OpenIDConnect::ResponseObject::IdToken.new(
|
@@ -24,11 +24,32 @@ describe Rack::OAuth2::Server::Authorize::Extension::CodeAndToken do
|
|
24
24
|
response.code = 'code'
|
25
25
|
response.access_token = bearer_token
|
26
26
|
response.id_token = id_token
|
27
|
+
response.private_key = private_key
|
27
28
|
response.approve!
|
28
29
|
end
|
29
30
|
end
|
30
31
|
its(:status) { should == 302 }
|
31
|
-
its(:location) { should == "#{redirect_uri}?code=code#access_token=access_token&id_token=#{id_token.to_jwt}&token_type=bearer" }
|
32
|
+
its(:location) { should == "#{redirect_uri}?code=code#access_token=access_token&id_token=#{id_token.to_jwt(private_key)}&token_type=bearer" }
|
33
|
+
|
34
|
+
context 'when id_token is String' do
|
35
|
+
let(:id_token) { 'id_token' }
|
36
|
+
its(:location) { should == "#{redirect_uri}?code=code#access_token=access_token&id_token=id_token&token_type=bearer" }
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when private_key is missing' do
|
40
|
+
let :app do
|
41
|
+
Rack::OAuth2::Server::Authorize.new do |request, response|
|
42
|
+
response.redirect_uri = redirect_uri
|
43
|
+
response.code = 'code'
|
44
|
+
response.access_token = bearer_token
|
45
|
+
response.id_token = id_token
|
46
|
+
response.approve!
|
47
|
+
end
|
48
|
+
end
|
49
|
+
it do
|
50
|
+
expect { response }.should raise_error AttrRequired::AttrMissing, 'private_key is required'
|
51
|
+
end
|
52
|
+
end
|
32
53
|
end
|
33
54
|
|
34
55
|
context "otherwise" do
|
@@ -22,11 +22,31 @@ describe Rack::OAuth2::Server::Authorize::Token do
|
|
22
22
|
response.redirect_uri = redirect_uri
|
23
23
|
response.access_token = bearer_token
|
24
24
|
response.id_token = id_token
|
25
|
+
response.private_key = private_key
|
25
26
|
response.approve!
|
26
27
|
end
|
27
28
|
end
|
28
29
|
its(:status) { should == 302 }
|
29
|
-
its(:location) { should == "#{redirect_uri}#access_token=access_token&id_token=#{id_token.to_jwt}&token_type=bearer" }
|
30
|
+
its(:location) { should == "#{redirect_uri}#access_token=access_token&id_token=#{id_token.to_jwt(private_key)}&token_type=bearer" }
|
31
|
+
|
32
|
+
context 'when id_token is String' do
|
33
|
+
let(:id_token) { 'id_token' }
|
34
|
+
its(:location) { should == "#{redirect_uri}#access_token=access_token&id_token=id_token&token_type=bearer" }
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when private_key is missing' do
|
38
|
+
let :app do
|
39
|
+
Rack::OAuth2::Server::Authorize.new do |request, response|
|
40
|
+
response.redirect_uri = redirect_uri
|
41
|
+
response.access_token = bearer_token
|
42
|
+
response.id_token = id_token
|
43
|
+
response.approve!
|
44
|
+
end
|
45
|
+
end
|
46
|
+
it do
|
47
|
+
expect { response }.should raise_error AttrRequired::AttrMissing, 'private_key is required'
|
48
|
+
end
|
49
|
+
end
|
30
50
|
end
|
31
51
|
|
32
52
|
context "otherwise" do
|
@@ -26,10 +26,28 @@ describe Rack::OAuth2::Server::Token::AuthorizationCode do
|
|
26
26
|
Rack::OAuth2::Server::Token.new do |request, response|
|
27
27
|
response.access_token = Rack::OAuth2::AccessToken::Bearer.new(:access_token => 'access_token')
|
28
28
|
response.id_token = id_token
|
29
|
+
response.private_key = private_key
|
29
30
|
end
|
30
31
|
end
|
31
32
|
its(:status) { should == 200 }
|
32
|
-
its(:body) { should include "\"id_token\":\"#{id_token.to_jwt}\"" }
|
33
|
+
its(:body) { should include "\"id_token\":\"#{id_token.to_jwt(private_key)}\"" }
|
34
|
+
|
35
|
+
context 'when id_token is String' do
|
36
|
+
let(:id_token) { 'id_token' }
|
37
|
+
its(:body) { should include "\"id_token\":\"id_token\"" }
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'when private_key is missing' do
|
41
|
+
let :app do
|
42
|
+
Rack::OAuth2::Server::Token.new do |request, response|
|
43
|
+
response.access_token = Rack::OAuth2::AccessToken::Bearer.new(:access_token => 'access_token')
|
44
|
+
response.id_token = id_token
|
45
|
+
end
|
46
|
+
end
|
47
|
+
it do
|
48
|
+
expect { response }.should raise_error AttrRequired::AttrMissing, 'private_key is required'
|
49
|
+
end
|
50
|
+
end
|
33
51
|
end
|
34
52
|
|
35
53
|
context "otherwise" do
|
@@ -25,10 +25,28 @@ describe Rack::OAuth2::Server::Token::RefreshToken do
|
|
25
25
|
Rack::OAuth2::Server::Token.new do |request, response|
|
26
26
|
response.access_token = Rack::OAuth2::AccessToken::Bearer.new(:access_token => 'access_token')
|
27
27
|
response.id_token = id_token
|
28
|
+
response.private_key = private_key
|
28
29
|
end
|
29
30
|
end
|
30
31
|
its(:status) { should == 200 }
|
31
|
-
its(:body) { should include "\"id_token\":\"#{id_token.to_jwt}\"" }
|
32
|
+
its(:body) { should include "\"id_token\":\"#{id_token.to_jwt(private_key)}\"" }
|
33
|
+
|
34
|
+
context 'when id_token is String' do
|
35
|
+
let(:id_token) { 'id_token' }
|
36
|
+
its(:body) { should include "\"id_token\":\"id_token\"" }
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when private_key is missing' do
|
40
|
+
let :app do
|
41
|
+
Rack::OAuth2::Server::Token.new do |request, response|
|
42
|
+
response.access_token = Rack::OAuth2::AccessToken::Bearer.new(:access_token => 'access_token')
|
43
|
+
response.id_token = id_token
|
44
|
+
end
|
45
|
+
end
|
46
|
+
it do
|
47
|
+
expect { response }.should raise_error AttrRequired::AttrMissing, 'private_key is required'
|
48
|
+
end
|
49
|
+
end
|
32
50
|
end
|
33
51
|
|
34
52
|
context "otherwise" do
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,16 @@
|
|
1
1
|
require 'rspec'
|
2
2
|
require 'openid_connect'
|
3
3
|
|
4
|
-
require 'helpers/webmock_helper'
|
4
|
+
require 'helpers/webmock_helper'
|
5
|
+
|
6
|
+
def rsa
|
7
|
+
@rsa ||= OpenSSL::PKey::RSA.generate 2048
|
8
|
+
end
|
9
|
+
|
10
|
+
def public_key
|
11
|
+
@public_key ||= rsa.public_key
|
12
|
+
end
|
13
|
+
|
14
|
+
def private_key
|
15
|
+
@private_key ||= OpenSSL::PKey::RSA.new rsa.export(OpenSSL::Cipher::Cipher.new('aes256'), 'pass-phrase'), 'pass-phrase'
|
16
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openid_connect
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 7
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 12
|
10
|
+
version: 0.0.12
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- nov matake
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-08-
|
18
|
+
date: 2011-08-18 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: activemodel
|