json-jwt 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of json-jwt might be problematic. Click here for more details.
- data/Gemfile.lock +2 -8
- data/VERSION +1 -1
- data/json-jwt.gemspec +0 -2
- data/lib/json/jws.rb +38 -7
- data/lib/json/jwt.rb +6 -5
- data/spec/json/jws_spec.rb +72 -22
- metadata +7 -29
data/Gemfile.lock
CHANGED
@@ -1,21 +1,15 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
json-jwt (0.0.
|
5
|
-
activesupport (>= 2.3)
|
6
|
-
i18n
|
4
|
+
json-jwt (0.0.2)
|
7
5
|
json (>= 1.4.3)
|
8
6
|
url_safe_base64
|
9
7
|
|
10
8
|
GEM
|
11
9
|
remote: http://rubygems.org/
|
12
10
|
specs:
|
13
|
-
activesupport (3.1.0)
|
14
|
-
multi_json (~> 1.0)
|
15
11
|
diff-lcs (1.1.3)
|
16
|
-
|
17
|
-
json (1.6.0)
|
18
|
-
multi_json (1.0.3)
|
12
|
+
json (1.6.1)
|
19
13
|
rake (0.9.2)
|
20
14
|
rcov (0.9.10)
|
21
15
|
rspec (2.6.0)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.3
|
data/json-jwt.gemspec
CHANGED
@@ -12,8 +12,6 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.add_runtime_dependency "json", ">= 1.4.3"
|
14
14
|
s.add_runtime_dependency "url_safe_base64"
|
15
|
-
s.add_runtime_dependency "activesupport", ">= 2.3"
|
16
|
-
s.add_runtime_dependency "i18n"
|
17
15
|
s.add_development_dependency "rake", ">= 0.8"
|
18
16
|
s.add_development_dependency "rcov", ">= 0.9"
|
19
17
|
s.add_development_dependency "rspec", ">= 2"
|
data/lib/json/jws.rb
CHANGED
@@ -14,14 +14,30 @@ module JSON
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def verify(signature_base_string, signature, public_key_or_secret)
|
17
|
-
|
17
|
+
valid?(signature_base_string, signature, public_key_or_secret) or
|
18
18
|
raise VerificationFailed
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
22
22
|
|
23
23
|
def algorithm
|
24
|
-
|
24
|
+
header[:alg] || header['alg']
|
25
|
+
end
|
26
|
+
|
27
|
+
def digest
|
28
|
+
OpenSSL::Digest::Digest.new "SHA#{algorithm.to_s[2, 3]}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def hmac?
|
32
|
+
[:HS256, :HS384, :HS512].collect(&:to_s).include? algorithm.to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
def rsa?
|
36
|
+
[:RS256, :RS384, :RS512].collect(&:to_s).include? algorithm.to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
def ecdsa?
|
40
|
+
[:ES256, :ES384, :ES512].collect(&:to_s).include? algorithm.to_s
|
25
41
|
end
|
26
42
|
|
27
43
|
def signature_base_string
|
@@ -34,15 +50,30 @@ module JSON
|
|
34
50
|
end
|
35
51
|
|
36
52
|
def sign(signature_base_string, private_key_or_secret)
|
37
|
-
|
38
|
-
|
39
|
-
when :HS256, :HS384, :HS512
|
53
|
+
case
|
54
|
+
when hmac?
|
40
55
|
secret = private_key_or_secret
|
41
56
|
OpenSSL::HMAC.digest digest, secret, signature_base_string
|
42
|
-
when
|
57
|
+
when rsa?
|
43
58
|
private_key = private_key_or_secret
|
44
59
|
private_key.sign digest, signature_base_string
|
45
|
-
when
|
60
|
+
when ecdsa?
|
61
|
+
# TODO
|
62
|
+
raise NotImplementedError.new
|
63
|
+
else
|
64
|
+
raise InvalidFormat.new('Unknown Signature Algorithm')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def valid?(signature_base_string, signature, public_key_or_secret)
|
69
|
+
case
|
70
|
+
when hmac?
|
71
|
+
secret = public_key_or_secret
|
72
|
+
sign(signature_base_string, secret) == signature
|
73
|
+
when rsa?
|
74
|
+
public_key = public_key_or_secret
|
75
|
+
public_key.verify digest, signature, signature_base_string
|
76
|
+
when ecdsa?
|
46
77
|
# TODO
|
47
78
|
raise NotImplementedError.new
|
48
79
|
else
|
data/lib/json/jwt.rb
CHANGED
@@ -26,12 +26,12 @@ module JSON
|
|
26
26
|
JWS.new(self).sign!(private_key_or_secret)
|
27
27
|
end
|
28
28
|
|
29
|
-
def verify(signature_base_string, signature = '',
|
29
|
+
def verify(signature_base_string, signature = '', public_key_or_secret = nil)
|
30
30
|
case header[:alg]
|
31
31
|
when :none
|
32
32
|
signature == '' or raise VerificationFailed
|
33
33
|
else
|
34
|
-
JWS.new(self).verify(signature_base_string, signature,
|
34
|
+
JWS.new(self).verify(signature_base_string, signature, public_key_or_secret)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -51,9 +51,10 @@ module JSON
|
|
51
51
|
header, claims, signature = jwt_string.split('.').collect do |segment|
|
52
52
|
UrlSafeBase64.decode64 segment.to_s
|
53
53
|
end
|
54
|
-
|
55
|
-
jwt
|
56
|
-
jwt.
|
54
|
+
signature_base_string = jwt_string.split('.')[0,2].join('.')
|
55
|
+
jwt = new JSON.parse(claims)
|
56
|
+
jwt.header = JSON.parse header
|
57
|
+
jwt.verify signature_base_string, signature, public_key_or_secret
|
57
58
|
jwt
|
58
59
|
rescue JSON::ParserError
|
59
60
|
raise InvalidFormat.new("Invalid JSON Format")
|
data/spec/json/jws_spec.rb
CHANGED
@@ -8,6 +8,8 @@ describe JSON::JWS do
|
|
8
8
|
_jwt_
|
9
9
|
end
|
10
10
|
let(:jws) { JSON::JWS.new jwt }
|
11
|
+
let(:signed) { jws.sign! private_key_or_secret }
|
12
|
+
let(:decoded) { JSON::JWT.decode signed.to_s, public_key_or_secret }
|
11
13
|
let(:claims) do
|
12
14
|
{
|
13
15
|
:iss => 'joe',
|
@@ -15,6 +17,19 @@ describe JSON::JWS do
|
|
15
17
|
'http://example.com/is_root' => true
|
16
18
|
}
|
17
19
|
end
|
20
|
+
let(:expected_signature) {
|
21
|
+
{
|
22
|
+
:HS256 => 'DyuTgO2Ggb5nrhkkhI-RjVYIBe3o8oL4ijkAn94YPxQ',
|
23
|
+
:HS384 => 'a5-7rr61TG8Snv9xxJ7l064ky-SCq1Mswe9t8HEorvoc_nnfIeUy9WQCLMIli34R',
|
24
|
+
:HS512 => 'ce-GlHDaNwaHfmAFRGp3QPPKvrpruTug2hC1bf6yNlbuvkMwJw2jFZgq_4wmIPetRdiBy7XFq7rrtmw1Im7tmQ',
|
25
|
+
:RS256 => 'E5VELqAdla2Bx1axc9KFxO0EiCr0Mw6HPYX070qGQ8zA_XmyxGPUZLyyWU_6Cn399W-oYBWO2ynLlr8pqqjP3jXevyCeYeGRVN0HzLYiBebEugNnc3hevr7WV2UzfksWRA-Ux2bDv2sz9p_LGbL33wWNxGDvIlpDyZUul_a48nCipS0riBjkTLTSE8dfBxQTXEF5GEUUu99ot6aBLzUhc25nHXSXogXF6MHK-hAcE7f4v-vJ0lbPbHLVGUopIoxoqe4XjoBpzE5UvhrVl5LYbdjbyJhu5ZIA8GLsgwtUFh3dfdIechORoR3k5NSFSv8157bAEa8t4iwgWD2MSNSQnw',
|
26
|
+
:RS384 => 'lT5JbytGKgG9QrwkJuxgw7UjmN9tjkEQW9pVGR2XnKEdC0_wLNIzAmT-jTwyMDGBLUkWO7opDOP6Xy6_DOTg58k9PwVkyQzrLnmxJMEng2Q-aMqcitRSIvUk3DPy8kemp8yUPls9NzWmByM2GoUVHbDsR0r-tZN-g_9QYev32mvMhjMr30JI5S2xiRjc9m2GAaXMOQmNTovJgV4bgCp4UjruCrA0BD1JJwDqKYoR_YYr_ALcVjD_LUgy80udJvbi8MAYJVUf0QYtQDrX2wnT_-eiiWjD5XafLuXEQVDRh-v2MKAwdvtXMq5cZ08Zjl2SyHxJ3OqhEeWPvYGltxZh_A',
|
27
|
+
:RS512 => 'EHeGM2Mo3ghhUfSB99AlREehrbC6OPE-nYL_rwf88ysTnJ8L1QQ0UuCrXq4SpRutGLK_bYTK3ZALvFRPoOgK_g0QWmqv6qjQRU_QTxoq8y8APP-IgKKDuIiGH6daBV2rAPLDReqYNKsKjmTvZJo2c0a0e_WZkkj_ZwpgjTG3v0gW9lbDAzLJDz18eqtR4ZO7JTu_fyNrUrNk-w2_wpxSsn9sygIMp0lKE0_pt0b01fz3gjTDjlltU0cKSalUp4geaBDH7QRcexrolIctdQFbNKTXQxoigxD3NLNkKGH7f6A8KZdcOm8AnEjullcZs8_OWGnW43p1qrxoBRSivb9pqQ',
|
28
|
+
:ES256 => :TODO,
|
29
|
+
:ES384 => :TODO,
|
30
|
+
:ES512 => :TODO
|
31
|
+
}
|
32
|
+
}
|
18
33
|
|
19
34
|
shared_examples_for :jwt_with_alg do
|
20
35
|
it { should == jwt }
|
@@ -28,44 +43,79 @@ describe JSON::JWS do
|
|
28
43
|
end
|
29
44
|
|
30
45
|
describe '#sign!' do
|
31
|
-
shared_examples_for :
|
32
|
-
it
|
46
|
+
shared_examples_for :generate_expected_signature do
|
47
|
+
it do
|
33
48
|
UrlSafeBase64.encode64(signed.signature).should == expected_signature[alg]
|
34
49
|
end
|
35
50
|
end
|
36
|
-
let(:expected_signature) {
|
37
|
-
{
|
38
|
-
:HS256 => 'DyuTgO2Ggb5nrhkkhI-RjVYIBe3o8oL4ijkAn94YPxQ',
|
39
|
-
:HS384 => 'a5-7rr61TG8Snv9xxJ7l064ky-SCq1Mswe9t8HEorvoc_nnfIeUy9WQCLMIli34R',
|
40
|
-
:HS512 => 'ce-GlHDaNwaHfmAFRGp3QPPKvrpruTug2hC1bf6yNlbuvkMwJw2jFZgq_4wmIPetRdiBy7XFq7rrtmw1Im7tmQ',
|
41
|
-
:RS256 => 'E5VELqAdla2Bx1axc9KFxO0EiCr0Mw6HPYX070qGQ8zA_XmyxGPUZLyyWU_6Cn399W-oYBWO2ynLlr8pqqjP3jXevyCeYeGRVN0HzLYiBebEugNnc3hevr7WV2UzfksWRA-Ux2bDv2sz9p_LGbL33wWNxGDvIlpDyZUul_a48nCipS0riBjkTLTSE8dfBxQTXEF5GEUUu99ot6aBLzUhc25nHXSXogXF6MHK-hAcE7f4v-vJ0lbPbHLVGUopIoxoqe4XjoBpzE5UvhrVl5LYbdjbyJhu5ZIA8GLsgwtUFh3dfdIechORoR3k5NSFSv8157bAEa8t4iwgWD2MSNSQnw',
|
42
|
-
:RS384 => 'lT5JbytGKgG9QrwkJuxgw7UjmN9tjkEQW9pVGR2XnKEdC0_wLNIzAmT-jTwyMDGBLUkWO7opDOP6Xy6_DOTg58k9PwVkyQzrLnmxJMEng2Q-aMqcitRSIvUk3DPy8kemp8yUPls9NzWmByM2GoUVHbDsR0r-tZN-g_9QYev32mvMhjMr30JI5S2xiRjc9m2GAaXMOQmNTovJgV4bgCp4UjruCrA0BD1JJwDqKYoR_YYr_ALcVjD_LUgy80udJvbi8MAYJVUf0QYtQDrX2wnT_-eiiWjD5XafLuXEQVDRh-v2MKAwdvtXMq5cZ08Zjl2SyHxJ3OqhEeWPvYGltxZh_A',
|
43
|
-
:RS512 => 'EHeGM2Mo3ghhUfSB99AlREehrbC6OPE-nYL_rwf88ysTnJ8L1QQ0UuCrXq4SpRutGLK_bYTK3ZALvFRPoOgK_g0QWmqv6qjQRU_QTxoq8y8APP-IgKKDuIiGH6daBV2rAPLDReqYNKsKjmTvZJo2c0a0e_WZkkj_ZwpgjTG3v0gW9lbDAzLJDz18eqtR4ZO7JTu_fyNrUrNk-w2_wpxSsn9sygIMp0lKE0_pt0b01fz3gjTDjlltU0cKSalUp4geaBDH7QRcexrolIctdQFbNKTXQxoigxD3NLNkKGH7f6A8KZdcOm8AnEjullcZs8_OWGnW43p1qrxoBRSivb9pqQ',
|
44
|
-
:ES256 => :TODO,
|
45
|
-
:ES384 => :TODO,
|
46
|
-
:ES512 => :TODO
|
47
|
-
}
|
48
|
-
}
|
49
|
-
let(:signed) do
|
50
|
-
jws.sign! key
|
51
|
-
end
|
52
51
|
subject { signed }
|
53
52
|
|
54
53
|
[:HS256, :HS384, :HS512].each do |algorithm|
|
55
54
|
describe algorithm do
|
56
|
-
let(:
|
55
|
+
let(:private_key_or_secret) { shared_secret }
|
57
56
|
let(:alg) { algorithm }
|
58
57
|
it_behaves_like :jwt_with_alg
|
59
|
-
it_behaves_like :
|
58
|
+
it_behaves_like :generate_expected_signature
|
60
59
|
end
|
61
60
|
end
|
62
61
|
|
63
62
|
[:RS256, :RS384, :RS512].each do |algorithm|
|
64
63
|
describe algorithm do
|
65
|
-
let(:
|
64
|
+
let(:private_key_or_secret) { private_key }
|
66
65
|
let(:alg) { algorithm }
|
67
66
|
it_behaves_like :jwt_with_alg
|
68
|
-
it_behaves_like :
|
67
|
+
it_behaves_like :generate_expected_signature
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
[:ES256, :ES384, :ES512].each do |algorithm|
|
72
|
+
describe algorithm do
|
73
|
+
let(:alg) { algorithm }
|
74
|
+
it :TODO
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#verify' do
|
80
|
+
shared_examples_for :succes_signature_verification do
|
81
|
+
it do
|
82
|
+
expect { decoded }.should_not raise_error
|
83
|
+
decoded.should be_a JSON::JWT
|
84
|
+
end
|
85
|
+
|
86
|
+
describe 'header' do
|
87
|
+
let(:header) { decoded.header }
|
88
|
+
it 'should be parsed successfully' do
|
89
|
+
header['typ'].should == 'JWT'
|
90
|
+
header['alg'].should == alg.to_s
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe 'claims' do
|
95
|
+
it 'should be parsed successfully' do
|
96
|
+
decoded['iss'].should == 'joe'
|
97
|
+
decoded['exp'].should == 1300819380
|
98
|
+
decoded['http://example.com/is_root'] == true
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
subject { decoded }
|
103
|
+
|
104
|
+
[:HS256, :HS384, :HS512].each do |algorithm|
|
105
|
+
describe algorithm do
|
106
|
+
let(:private_key_or_secret) { shared_secret }
|
107
|
+
let(:public_key_or_secret) { shared_secret }
|
108
|
+
let(:alg) { algorithm }
|
109
|
+
it_behaves_like :succes_signature_verification
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
[:RS256, :RS384, :RS512].each do |algorithm|
|
114
|
+
describe algorithm do
|
115
|
+
let(:private_key_or_secret) { private_key }
|
116
|
+
let(:public_key_or_secret) { public_key }
|
117
|
+
let(:alg) { algorithm }
|
118
|
+
it_behaves_like :succes_signature_verification
|
69
119
|
end
|
70
120
|
end
|
71
121
|
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: json-jwt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.3
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- nov matake
|
@@ -34,61 +34,39 @@ dependencies:
|
|
34
34
|
version: "0"
|
35
35
|
type: :runtime
|
36
36
|
version_requirements: *id002
|
37
|
-
- !ruby/object:Gem::Dependency
|
38
|
-
name: activesupport
|
39
|
-
prerelease: false
|
40
|
-
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
|
-
requirements:
|
43
|
-
- - ">="
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
version: "2.3"
|
46
|
-
type: :runtime
|
47
|
-
version_requirements: *id003
|
48
|
-
- !ruby/object:Gem::Dependency
|
49
|
-
name: i18n
|
50
|
-
prerelease: false
|
51
|
-
requirement: &id004 !ruby/object:Gem::Requirement
|
52
|
-
none: false
|
53
|
-
requirements:
|
54
|
-
- - ">="
|
55
|
-
- !ruby/object:Gem::Version
|
56
|
-
version: "0"
|
57
|
-
type: :runtime
|
58
|
-
version_requirements: *id004
|
59
37
|
- !ruby/object:Gem::Dependency
|
60
38
|
name: rake
|
61
39
|
prerelease: false
|
62
|
-
requirement: &
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
63
41
|
none: false
|
64
42
|
requirements:
|
65
43
|
- - ">="
|
66
44
|
- !ruby/object:Gem::Version
|
67
45
|
version: "0.8"
|
68
46
|
type: :development
|
69
|
-
version_requirements: *
|
47
|
+
version_requirements: *id003
|
70
48
|
- !ruby/object:Gem::Dependency
|
71
49
|
name: rcov
|
72
50
|
prerelease: false
|
73
|
-
requirement: &
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
74
52
|
none: false
|
75
53
|
requirements:
|
76
54
|
- - ">="
|
77
55
|
- !ruby/object:Gem::Version
|
78
56
|
version: "0.9"
|
79
57
|
type: :development
|
80
|
-
version_requirements: *
|
58
|
+
version_requirements: *id004
|
81
59
|
- !ruby/object:Gem::Dependency
|
82
60
|
name: rspec
|
83
61
|
prerelease: false
|
84
|
-
requirement: &
|
62
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
85
63
|
none: false
|
86
64
|
requirements:
|
87
65
|
- - ">="
|
88
66
|
- !ruby/object:Gem::Version
|
89
67
|
version: "2"
|
90
68
|
type: :development
|
91
|
-
version_requirements: *
|
69
|
+
version_requirements: *id005
|
92
70
|
description: JSON Web Token and its family (JSON Web Signature, JSON Web Encryption and JSON Web Key) in Ruby
|
93
71
|
email:
|
94
72
|
- nov@matake.jp
|